# Phase 0a — Pre-cluster Secret Bootstrap This directory contains tooling for the KeePassXC bootstrap phase (T01 Phase 0a). No secrets are stored here — only scripts and documentation. ## Why KeePassXC first? The single-credential bootstrap principle (Decision D1): one master password unlocks the KeePassXC vault; all other credentials are generated inside it. KeePassXC is the pre-cluster source of truth. After the K3s cluster is running, secrets migrate into HashiCorp Vault (T01 Phase 0b) and KeePassXC becomes the break-glass / dev-local backup. ## KeePassXC database structure Create a new `.kdbx` database. Use a strong master password stored in your personal password manager (Bitwarden, 1Password, etc.). Recommended group structure: ``` net-kingdom/ ├── privacyIDEA/ │ ├── pi-admin (username: pi-admin, password: PI_ADMIN_PASSWORD) │ ├── database (username: privacyidea, password: PI_DB_PASSWORD) │ ├── SECRET_KEY (password field only — PI_SECRET_KEY value) │ ├── PI_PEPPER (password field only — PI_PEPPER value) │ └── PI_ENCFILE (binary attachment: pi.enc — generate after deploy) ├── PostgreSQL/ │ ├── postgres root (username: postgres, password: PG_ROOT_PASSWORD) │ ├── keycloak user (username: keycloak, password: PG_KEYCLOAK_PASSWORD) │ └── privacyidea user (username: privacyidea — same password as PI_DB_PASSWORD) ├── Keycloak/ │ ├── admin (username: admin, password: KC_ADMIN_PASSWORD) │ └── database (username: keycloak — same password as PG_KEYCLOAK_PASSWORD) ├── Break-glass/ │ ├── break-glass (username: break-glass, password: BREAKGLASS_PASSWORD) │ └── recovery-otp (TOTP seed — enroll manually after Keycloak is up) └── Vault/ └── (populated in Phase 0b after Vault is deployed) ``` ## Workflow ### Step 1 — Generate secrets ```bash chmod +x gen-secrets.sh ./gen-secrets.sh ./secrets ``` This writes `.env` files to `./secrets/` (gitignored). Inspect each file, then copy each value into the appropriate KeePassXC entry. ### Step 2 — Create the age encryption key (one-time) ```bash age-keygen -o ~/net-kingdom-ops-bundle.key ``` The public key prints to stdout. The private key is in the `.key` file. Store the `.key` file somewhere safe (NOT in this repo; NOT in the secrets/ dir). ### Step 3 — Create the encrypted ops bundle ```bash chmod +x pack-bundle.sh ./pack-bundle.sh ./secrets "age1..." ops-bundle.tar.age ``` Store `ops-bundle.tar.age` offsite (cloud storage, external drive, separate location). ### Step 4 — Shred the generated files ```bash find ./secrets -type f -exec shred -u {} \; rm -rf ./secrets ``` ### Step 5 — PI_ENCFILE (after privacyIDEA container is running — T04) ```bash # Generate the encryption key inside the running container: kubectl exec -n mfa -- pi-manage create_enckey # Extract it: kubectl cp -n mfa :/etc/privacyidea/enckey ./pi.enc # Store as a binary attachment in KeePassXC → net-kingdom/privacyIDEA/PI_ENCFILE # Create the K8s Secret: kubectl create secret generic privacyidea-enckey \ --from-file=PI_ENCFILE=./pi.enc \ --namespace mfa # Shred the local copy: shred -u ./pi.enc ``` ## Notes on secret reuse Some secrets appear in multiple components — this is intentional to avoid drift. When adding to KeePassXC, note the cross-references rather than duplicating the value: - `PI_DB_PASSWORD` == PostgreSQL `privacyidea` user password - `KC_DB_PASSWORD` == PostgreSQL `keycloak` user password Use KeePassXC references (`{REF:P@T:UUID}`) to avoid maintaining two copies. ## Phase 0b — HashiCorp Vault (after T02, once K3s is running) See `../vault/` (created in T01 Phase 0b) for: - Vault Helm chart values - ESO (External Secrets Operator) configuration - Vault secret path layout - Migration procedure: KeePassXC → Vault ## Current bootstrap and recovery handoff `make creds-agent-init` is now attended and public-recipient based. Register `keys/age.pub` from independently established custody first. No root private key is generated or discovered by bootstrap. `--dry-run` is a non-mutating plan. `make creds-emergency-reprint` prints only the non-secret recovery guide. For a resume that needs decryption, use the custody unlock helper below. After apply, the operator must store and independently verify the fresh encrypted ops bundle before acknowledging its handoff. Source tests use disposable keys and a simulated cluster; native acceptance remains NK-WP-0049. ## Custody Age Key Unlock For SOPS-backed drills and incident work, do not copy the custodian age private key into a permanent workstation path. Use the custody helper from the repo root: ```bash make sops-custody-run COMMAND='make -C /home/worsch/inter-hub recovery-drill' ``` The helper prompts for the `AGE-SECRET-KEY-1...` line from the password safe or offline custody packet, validates it against `keys/age.pub`, writes a temporary `SOPS_AGE_KEY_FILE`, runs the command, and removes the temporary file on exit. For multi-step recovery work: ```bash make sops-custody-shell ``` Exit the shell when finished so the temporary key file is removed.