# ---------- 15. First-admin: boot-token setup by default ---------- # Two paths for creating the first admin: # (a) Interactive / web-flow (default): generate a random boot token, # write to /etc/customsso-manager/first-run.token, print the URL. # Operator opens the URL in a browser and fills in the "create first # admin" form. No default admin credential ever lands in the DB, in # shell history, or in a systemd unit — nothing for Shodan-style # scanners to grep for. # (b) Scripted (CI): if --admin-user AND --admin-pass are passed on the # command line, honor the old behavior. Suitable for CI / infra-as- # code deployments where the credential is managed by a secret store. echo "[15/16] first admin" if [[ $MODE_UPDATE -eq 0 ]]; then USER_COUNT=$(mysql -N -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" -e "SELECT COUNT(*) FROM users" 2>/dev/null || echo 0) if [[ "$USER_COUNT" -gt 0 ]]; then echo "[ok] users table already has $USER_COUNT row(s) — skipping first-admin setup" elif [[ -n "$ADMIN_USER" && -n "$ADMIN_PASS" ]]; then # (b) Scripted path — same as the pre-v0.3.2 behavior. PWHASH=$(php -r "echo password_hash(\$argv[1], PASSWORD_DEFAULT);" -- "$ADMIN_PASS") mysql -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" < "$TOKEN_FILE" # root:$WEB_GROUP 660 — web layer can read AND clear (truncate on consume) chown "root:$WEB_GROUP" "$TOKEN_FILE" chmod 660 "$TOKEN_FILE" BOOT_URL="https://$LISTEN_HOST/setup?token=$TOKEN" # If LISTEN_HOST is a loopback-y name (localhost, localhost.localdomain, # a bare hostname with no dot), the URL is unusable from a remote # browser — the setup flow lives on the box but is meant to be claimed # over the LAN/WAN. Also print the primary reachable IP. BOOT_URL_ALT="" if [[ "$LISTEN_HOST" =~ ^localhost(\.|$) ]] || [[ "$LISTEN_HOST" != *.* && "$LISTEN_HOST" != *:* ]]; then PRIMARY_IP=$(hostname -I 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i!~/^127\./ && $i!~/^::1/){print $i; exit}}') if [[ -n "$PRIMARY_IP" ]]; then BOOT_URL_ALT="https://$PRIMARY_IP/setup?token=$TOKEN" fi fi cat <