Some checks failed
Custody gains LLDAP_SVC_{AUTHELIA,KEYCAPE,PRIVACYIDEA,PROVISIONER}_PASS and
LLDAP_LDAP_USER_PASS becomes ld-admin's. Authelia, KeyCape, the privacyIDEA
resolver and identity-provisioner (new Secret identity-provisioner-ldap) switch
to svc-* accounts; LLDAP's configured super-admin is ld-admin. The LLDAP image
pin waits for the running digest.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 308701@bnt-lap001
Assistant-Session: 5548e447-444c-4dcc-bed1-05c16eebfe56
169 lines
9 KiB
Bash
Executable file
169 lines
9 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# gen-secrets.sh — generate all bootstrap secrets for the net-kingdom SSO/MFA platform
|
|
#
|
|
# Usage:
|
|
# ./gen-secrets.sh [OUTPUT_DIR]
|
|
#
|
|
# Generates all pre-cluster secrets and writes them to OUTPUT_DIR (default: ./secrets).
|
|
# Output files are structured by component to mirror the KeePassXC entry layout.
|
|
#
|
|
# WARNING: The secrets/ directory must NEVER be committed to git.
|
|
# After entering values into KeePassXC, shred the generated files:
|
|
# find secrets/ -type f -exec shred -u {} \;
|
|
#
|
|
# PI_ENCFILE is NOT generated here — it must be produced inside the privacyIDEA
|
|
# container after deployment:
|
|
# Follow ../k8s/privacyidea/KEY-RECOVERY.md; do not generate missing recovery keys.
|
|
# Extract the resulting file, store it in KeePassXC as a binary attachment on the
|
|
# privacyIDEA/PI_ENCFILE entry, then create the k8s secret from it.
|
|
|
|
set +x
|
|
set -euo pipefail
|
|
umask 077
|
|
|
|
OUT_DIR="${1:-./secrets}"
|
|
|
|
if [[ -e "$OUT_DIR" || -L "$OUT_DIR" ]]; then
|
|
echo "ERROR: $OUT_DIR already exists. Preserve it for reconciliation or choose a new path." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Helpers
|
|
rnd_hex() { openssl rand -hex "$1"; }
|
|
rnd_b64() { openssl rand -base64 "$1" | tr -d '\n/+=' | head -c "$2"; }
|
|
|
|
mkdir -p \
|
|
"$OUT_DIR/privacyidea" \
|
|
"$OUT_DIR/postgres" \
|
|
"$OUT_DIR/lldap" \
|
|
"$OUT_DIR/authelia" \
|
|
"$OUT_DIR/keycape" \
|
|
"$OUT_DIR/breakglass"
|
|
|
|
# ── privacyIDEA ────────────────────────────────────────────────────────────────
|
|
PI_SECRET_KEY="$(rnd_hex 32)" # 64 hex chars — Flask/PI app secret
|
|
PI_PEPPER="$(rnd_hex 16)" # 32 hex chars — password hashing pepper
|
|
PI_DB_PASS="$(rnd_b64 32 40)" # 40 printable chars — DB password
|
|
PI_ADMIN_PASS="$(rnd_b64 32 40)"
|
|
|
|
cat > "$OUT_DIR/privacyidea/secrets.env" <<EOF
|
|
# privacyIDEA secrets — KeePassXC group: net-kingdom/privacyIDEA
|
|
# Entry: pi-admin → username=pi-admin password=PI_ADMIN_PASSWORD
|
|
# Entry: database → username=privacyidea password=PI_DB_PASSWORD
|
|
# Entry: SECRET_KEY → password=PI_SECRET_KEY (no username)
|
|
# Entry: PI_PEPPER → password=PI_PEPPER (no username)
|
|
# Entry: PI_ENCFILE → binary attachment (generate AFTER container deploy)
|
|
|
|
PI_SECRET_KEY=$PI_SECRET_KEY
|
|
PI_PEPPER=$PI_PEPPER
|
|
PI_DB_PASSWORD=$PI_DB_PASS
|
|
PI_ADMIN_PASSWORD=$PI_ADMIN_PASS
|
|
|
|
# PI_ENCFILE: generate with:
|
|
# Follow ../k8s/privacyidea/KEY-RECOVERY.md; do not generate missing recovery keys.
|
|
# Export existing keys with enckey-bootstrap.sh and an explicit private custody root.
|
|
# Then store pi.enc as a binary attachment in KeePassXC → net-kingdom/privacyIDEA/PI_ENCFILE
|
|
EOF
|
|
|
|
# ── PostgreSQL ─────────────────────────────────────────────────────────────────
|
|
PG_ROOT_PASS="$(rnd_b64 32 40)"
|
|
# privacyIDEA DB user reuses PI_DB_PASS (single source of truth)
|
|
# Note: no keycloak DB user — Keycloak was replaced by the Authelia+LLDAP+KeyCape stack.
|
|
|
|
cat > "$OUT_DIR/postgres/secrets.env" <<EOF
|
|
# PostgreSQL secrets — KeePassXC group: net-kingdom/PostgreSQL
|
|
# Entry: postgres root → username=postgres password=PG_ROOT_PASSWORD
|
|
# Entry: privacyidea user → username=privacyidea password=PI_DB_PASSWORD (copy from privacyIDEA entry)
|
|
|
|
PG_ROOT_PASSWORD=$PG_ROOT_PASS
|
|
# PI_DB_PASSWORD is in privacyidea/secrets.env — do NOT copy here; link in KeePassXC
|
|
EOF
|
|
|
|
# ── LLDAP ──────────────────────────────────────────────────────────────────────
|
|
LLDAP_JWT_SECRET="$(rnd_hex 32)" # 64 hex chars — LLDAP JWT signing key
|
|
LLDAP_LDAP_USER_PASS="$(rnd_b64 32 40)" # 40 printable chars — ld-admin, LLDAP's configured super-admin (sealed fallback)
|
|
# Per-consumer directory accounts (Service Identity SI-3, NK-WP-0054-T05). The human `admin`
|
|
# password is personal and is never generated into custody.
|
|
LLDAP_SVC_AUTHELIA_PASS="$(rnd_b64 32 40)" # svc-authelia, read-only bind
|
|
LLDAP_SVC_KEYCAPE_PASS="$(rnd_b64 32 40)" # svc-keycape, read-only bind
|
|
LLDAP_SVC_PRIVACYIDEA_PASS="$(rnd_b64 32 40)" # svc-privacyidea, read-only resolver bind
|
|
LLDAP_SVC_PROVISIONER_PASS="$(rnd_b64 32 40)" # svc-provisioner, lldap_admin (exception SI-X6)
|
|
|
|
cat > "$OUT_DIR/lldap/secrets.env" <<EOF
|
|
# LLDAP secrets — KeePassXC group: net-kingdom/LLDAP
|
|
# Entry: jwt-secret → password=LLDAP_JWT_SECRET (no username)
|
|
# Entry: ld-admin → username=ld-admin password=LLDAP_LDAP_USER_PASS
|
|
# Entries: svc-authelia, svc-keycape, svc-privacyidea, svc-provisioner → LLDAP_SVC_*_PASS
|
|
#
|
|
# Each consumer binds with its own account: apply-service-secrets.py (Authelia, provisioner),
|
|
# keycape/create-secrets.py and privacyidea/realm_bootstrap.py read their key from this file.
|
|
|
|
LLDAP_JWT_SECRET=$LLDAP_JWT_SECRET
|
|
LLDAP_LDAP_USER_PASS=$LLDAP_LDAP_USER_PASS
|
|
LLDAP_SVC_AUTHELIA_PASS=$LLDAP_SVC_AUTHELIA_PASS
|
|
LLDAP_SVC_KEYCAPE_PASS=$LLDAP_SVC_KEYCAPE_PASS
|
|
LLDAP_SVC_PRIVACYIDEA_PASS=$LLDAP_SVC_PRIVACYIDEA_PASS
|
|
LLDAP_SVC_PROVISIONER_PASS=$LLDAP_SVC_PROVISIONER_PASS
|
|
EOF
|
|
|
|
# ── Authelia ───────────────────────────────────────────────────────────────────
|
|
AUTHELIA_JWT_SECRET="$(rnd_hex 32)" # 64 hex chars — session JWT signing
|
|
AUTHELIA_SESSION_SECRET="$(rnd_hex 32)" # 64 hex chars — session cookie encryption
|
|
AUTHELIA_STORAGE_ENCRYPTION_KEY="$(rnd_b64 48 64)" # 64 printable chars — SQLite encryption
|
|
AUTHELIA_OIDC_HMAC_SECRET="$(rnd_hex 32)" # 64 hex chars — OIDC HMAC
|
|
AUTHELIA_KEYCAPE_CLIENT_SECRET="$(rnd_b64 32 40)" # 40 printable chars — Authelia→KeyCape OIDC client secret
|
|
# OIDC issuer private key: generated by authelia/create-secrets.sh on first run,
|
|
# or set AUTHELIA_OIDC_PRIVATE_KEY_FILE to a path below and generate with:
|
|
# openssl genrsa -out secrets/authelia/oidc_private_key.pem 2048
|
|
|
|
cat > "$OUT_DIR/authelia/secrets.env" <<EOF
|
|
# Authelia secrets — KeePassXC group: net-kingdom/Authelia
|
|
# Entry: jwt-secret → password=AUTHELIA_JWT_SECRET (no username)
|
|
# Entry: session-secret → password=AUTHELIA_SESSION_SECRET (no username)
|
|
# Entry: storage-encryption-key → password=AUTHELIA_STORAGE_ENCRYPTION_KEY (no username)
|
|
# Entry: oidc-hmac-secret → password=AUTHELIA_OIDC_HMAC_SECRET (no username)
|
|
# Entry: keycape-client-secret → password=AUTHELIA_KEYCAPE_CLIENT_SECRET (no username)
|
|
# Entry: oidc-private-key → binary attachment oidc_private_key.pem
|
|
#
|
|
# AUTHELIA_KEYCAPE_CLIENT_SECRET is the plaintext — authelia/create-secrets.sh hashes
|
|
# it with bcrypt before storing in the K8s Secret.
|
|
# The same plaintext goes into KeyCape's config as authelia.clientSecret.
|
|
|
|
AUTHELIA_JWT_SECRET=$AUTHELIA_JWT_SECRET
|
|
AUTHELIA_SESSION_SECRET=$AUTHELIA_SESSION_SECRET
|
|
AUTHELIA_STORAGE_ENCRYPTION_KEY=$AUTHELIA_STORAGE_ENCRYPTION_KEY
|
|
AUTHELIA_OIDC_HMAC_SECRET=$AUTHELIA_OIDC_HMAC_SECRET
|
|
AUTHELIA_KEYCAPE_CLIENT_SECRET=$AUTHELIA_KEYCAPE_CLIENT_SECRET
|
|
# AUTHELIA_OIDC_PRIVATE_KEY_FILE= # leave blank — authelia/create-secrets.sh will generate
|
|
EOF
|
|
|
|
# ── KeyCape ────────────────────────────────────────────────────────────────────
|
|
# KeyCape has no generated secrets here:
|
|
# key.pem — RSA signing key, generated by keycape/create-secrets.sh on first run
|
|
# Factor JWT — delivered through RPF-WP-0040 ESO, never stored in this directory.
|
|
# We create the directory and a placeholder file so operators know to check it.
|
|
|
|
cat > "$OUT_DIR/keycape/secrets.env" <<EOF
|
|
# KeyCape secrets — KeePassXC group: net-kingdom/KeyCape
|
|
# Entry: jwt-signing-key → binary attachment key.pem (generated by keycape/create-secrets.sh)
|
|
# Factor credential: owner-managed ESO mount; no personal admin token.
|
|
#
|
|
# This file is a placeholder. No values to set here manually.
|
|
# First-bootstrap create-secrets.sh generates only a missing signing key.
|
|
EOF
|
|
|
|
# ── Break-glass ────────────────────────────────────────────────────────────────
|
|
BG_PASS="$(rnd_b64 32 40)"
|
|
|
|
cat > "$OUT_DIR/breakglass/secrets.env" <<EOF
|
|
# Break-glass secrets — KeePassXC group: net-kingdom/Break-glass
|
|
# Entry: break-glass → username=break-glass password=BREAKGLASS_PASSWORD
|
|
# Entry: recovery-otp → TOTP seed (enroll manually after Keycloak is up)
|
|
|
|
BREAKGLASS_PASSWORD=$BG_PASS
|
|
EOF
|
|
|
|
# Values and even prefixes must stay out of normal output and shell tracing.
|
|
printf '%s\n' 'Bootstrap service files generated with private permissions.'
|
|
printf '%s\n' 'Encrypt to the registered public age recipient, verify off-host recovery, then remove plaintext.'
|
|
printf '%s\n' 'Never paste these files into chat, logs, metadata or Git.'
|