Files
2026-06-14 19:51:05 +02:00
..
2026-06-14 19:51:05 +02:00
2026-06-14 19:51:05 +02:00

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

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)

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

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

find ./secrets -type f -exec shred -u {} \;
rm -rf ./secrets

Step 5 — PI_ENCFILE (after privacyIDEA container is running — T04)

# Generate the encryption key inside the running container:
kubectl exec -n mfa <pi-pod-name> -- pi-manage create_enckey

# Extract it:
kubectl cp -n mfa <pi-pod-name>:/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

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:

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:

make sops-custody-shell

Exit the shell when finished so the temporary key file is removed.