#!/usr/bin/env bash # Seed the CI domain: the OU tree, the service account, the PS-* groups and the test users. # Runs once from entrypoint.sh, before samba starts, against the local sam.ldb. # Safe to rerun: "already exists" is ignored and every user gets a new random password. # Never prints a password. Writes $PRIVATE/users.json (0600); the entrypoint exports it. set -euo pipefail BASE="DC=pstest,DC=local" PRIVATE=/var/lib/samba/private SAM=$PRIVATE/sam.ldb GROUPS_FILE=/usr/local/share/pstest/groups.txt BIND_SECRET=/run/secrets/ldap_bind_password PS_OU="OU=PacketStation,OU=Groups,OU=PESCO_FARMINGTON" USERS_OU="OU=Users,OU=PESCO_FARMINGTON" MISSING_GROUP="PS-14-Electrical-Lead" # left out on purpose: the reconcile report must flag it EXTRA_GROUP="PS-Extra-Legacy" # added on purpose: the reconcile report must list it NESTED_GROUP="Shop-Welders-Nested" log() { echo "samba-dc seed: $*"; } # Run a command. Ignore "already exists". On any other failure show its output and stop. # Callers never pass a password in a way that ends up in this output. try() { local out if out=$("$@" 2>&1); then return 0 fi if printf '%s' "$out" | grep -qiE 'already exists|ENTRY_ALREADY_EXISTS|already a member'; then return 0 fi log "failed: $1 $2 $3" >&2 printf '%s\n' "$out" | sed 's/^/ /' >&2 exit 1 } add_ou() { local dn="$1,$BASE" local ldif ldif=$(mktemp) printf 'dn: %s\nobjectClass: top\nobjectClass: organizationalUnit\n' "$dn" > "$ldif" try ldbadd -H "$SAM" "$ldif" rm -f "$ldif" } random_password() { # 24 random base64 characters plus a fixed tail with upper, lower, digit and symbol. printf '%s-Pa9' "$(openssl rand -base64 18 | tr -d '\n')" } set_password() { # $1 sam, $2 password. Not logged. Visible only inside this container's process list. try samba-tool user setpassword "$1" --newpassword="$2" } set_attrs() { # $1 dn, $2 display name, $3 employeeID or empty. local ldif ldif=$(mktemp) { printf 'dn: %s\nchangetype: modify\n' "$1" printf 'replace: displayName\ndisplayName: %s\n-\n' "$2" if [ -n "$3" ]; then printf 'replace: employeeID\nemployeeID: %s\n-\n' "$3" fi } > "$ldif" try ldbmodify -H "$SAM" "$ldif" rm -f "$ldif" } # ---- OU tree, mirroring PESCO ---- log "OU tree" add_ou "OU=PESCO_FARMINGTON" add_ou "OU=Groups,OU=PESCO_FARMINGTON" add_ou "$PS_OU" add_ou "$USERS_OU" add_ou "OU=Admin" add_ou "OU=System_Accounts,OU=Admin" # ---- Service account (password = the app's ldap_bind_password secret) ---- log "service account svc-packetstation" if [ ! -s "$BIND_SECRET" ]; then log "missing or empty secret file $BIND_SECRET" >&2 exit 1 fi try samba-tool user create svc-packetstation --random-password --use-username-as-cn \ --userou="OU=System_Accounts,OU=Admin" --description="Packet Station service account (CI)" set_password svc-packetstation "$(head -n 1 "$BIND_SECRET" | tr -d '\r\n')" try samba-tool user setexpiry svc-packetstation --noexpiry # ---- Groups ---- log "PacketStation groups" count=0 while IFS= read -r name || [ -n "$name" ]; do name=$(printf '%s' "$name" | tr -d '\r' | sed 's/[[:space:]]*$//') [ -z "$name" ] && continue [ "$name" = "$MISSING_GROUP" ] && continue try samba-tool group add "$name" --groupou="$PS_OU" \ --group-scope=Global --group-type=Security --description="Packet Station: $name" count=$((count + 1)) done < "$GROUPS_FILE" try samba-tool group add "$EXTRA_GROUP" --groupou="$PS_OU" \ --group-scope=Global --group-type=Security --description="Packet Station: $EXTRA_GROUP" try samba-tool group add "$NESTED_GROUP" --groupou="OU=Groups,OU=PESCO_FARMINGTON" \ --group-scope=Global --group-type=Security --description="Helper group for the nested membership test" try samba-tool group addmembers PS-10-WeldAssembly-Worker "$NESTED_GROUP" log "created $count mapped groups (without $MISSING_GROUP), plus $EXTRA_GROUP and $NESTED_GROUP" # ---- Users: sam|given|surname|employeeID|groups (comma separated) ---- ROSTER='admin.ann|Ann|Admin|1001|PS-Admins clerk.clara|Clara|Clerk|4001|PS-Clerical clerk.leo|Leo|Lead|4002|PS-Clerical-Supervisor wh.wade|Wade|Warehouse|6001|PS-Warehouse qc.quinn|Quinn|QC|5001|PS-QC pm.paula|Paula|PM|7001|PS-ProjectMgmt weld.andy|Andy|Welder|3003|PS-03-WeldParts-Worker lead.lou|Lou|Lead|3002|PS-03-WeldParts-Lead sup.wes|Wes|Super|2003|PS-03-WeldParts-Supervisor multi.mia|Mia|Multi|2006|PS-Clerical,PS-06-CodeBldg2-Supervisor nested.ned|Ned|Nested|3010|Shop-Welders-Nested both.bo|Bo|Both|3011|PS-03-WeldParts-Worker,PS-03-WeldParts-Supervisor disabled.dan|Dan|Disabled|3004|PS-03-WeldParts-Worker noemp.nora|Nora|NoEmp||PS-Warehouse dup.dave|Dave|Dup|9001|PS-03-WeldParts-Worker dup.dina|Dina|Dup|9001|PS-Warehouse norole.nick|Nick|NoRole|8001|' umask 077 rows=$(mktemp) # sam, password, emp, display; tab separated; 0600; removed below log "users" while IFS='|' read -r sam given surname emp groups; do [ -z "$sam" ] && continue display="$given $surname" dn="CN=$sam,$USERS_OU,$BASE" try samba-tool user create "$sam" --random-password --use-username-as-cn \ --userou="$USERS_OU" --given-name="$given" --surname="$surname" \ --mail-address="$sam@pstest.local" pw=$(random_password) set_password "$sam" "$pw" set_attrs "$dn" "$display" "$emp" if [ -n "$groups" ]; then IFS=',' read -r -a list <<< "$groups" for g in "${list[@]}"; do try samba-tool group addmembers "$g" "$sam" done fi printf '%s\t%s\t%s\t%s\n' "$sam" "$pw" "$emp" "$display" >> "$rows" unset pw log " $sam" done <<< "$ROSTER" try samba-tool user disable disabled.dan # ---- users.json, the format in the phase 1 contract ---- python3 - "$rows" "$PRIVATE/users.json" <<'PY' import json, sys users = {} with open(sys.argv[1], encoding="utf-8") as f: for line in f: sam, password, emp, display = line.rstrip("\n").split("\t") users[sam] = {"password": password, "emp_no": emp or None, "display_name": display} with open(sys.argv[2], "w", encoding="utf-8") as f: json.dump({"domain": "pstest.local", "users": users}, f, indent=2, sort_keys=True) f.write("\n") PY rm -f "$rows" chmod 0600 "$PRIVATE/users.json" log "done: $(grep -c '"password"' "$PRIVATE/users.json") users written to users.json"