Files
net-kingdom/docs/security-bootstrap-age-custody.md
2026-06-14 19:51:05 +02:00

133 lines
4.5 KiB
Markdown

# Bootstrap Age Custody
Status: draft control-surface contract
Date: 2026-05-24
## Purpose
The custodian age keypair is the bootstrap envelope key for NetKingdom security
setup. It is not a login account and it is not the runtime secret manager.
The public key may be stored in the control surface and used by automation to
encrypt generated bootstrap bundles. The private key is supplied only during an
explicit unlock/apply or recovery ceremony.
## Minimal Secret Model
The minimal human-held secret set is:
| Secret | Home | Notes |
| --- | --- | --- |
| Custodian age private key | Password safe or offline custody packet | Used to decrypt bootstrap bundles for an attended apply/recovery ceremony |
| Platform-root password | Password safe or offline custody packet | Dedicated custody identity, not day-to-day account |
| Platform-root OTP/recovery | Authenticator plus offline recovery | Not stored in Git, State Hub, or bootstrap metadata |
| OpenBao recovery/unseal material | OpenBao custody packet | Created only after custody approval |
Everything else should be generated, encrypted to the custodian public key,
applied to the cluster, then shredded or rotated into OpenBao once OpenBao is
available.
## Trial Versus Custody Mode
Trial mode uses throwaway values to document the steps, UI, failure modes, and
operator instructions. Nothing created in trial mode should be trusted as live
security material.
Custody mode uses real generated secrets. The expected lifecycle is:
1. Register the custodian public age recipient in the control surface.
2. Generate or recover bootstrap secrets.
3. Encrypt the bundle to the custodian public key.
4. Apply secrets to the cluster during an attended ceremony.
5. Shred plaintext `sso-mfa/bootstrap/secrets/`.
6. Record only non-secret fingerprints, timestamps, and state transitions.
## Control Surface Rules
The NetKingdom control surface may store:
- custodian public age recipient;
- public-key fingerprint;
- private-key location reference, such as a password-safe entry label;
- encrypted bundle path and file count;
- whether plaintext bootstrap secrets are currently present; and
- non-secret apply/verification status.
It must not store:
- `AGE-SECRET-KEY-1...` private key material;
- generated passwords;
- OTP seeds;
- OpenBao root tokens or unseal shares;
- decrypted bootstrap files; or
- screenshots of secret output.
## Current Tooling Alignment
`sso-mfa/bootstrap/encrypt-secrets.sh` should accept an age public recipient
directly. Encryption does not require the private key.
`sso-mfa/bootstrap/decrypt-secrets.sh` requires the private key path and should
be used only in an explicit unlock/apply ceremony. After apply, plaintext files
must be shredded.
## Custody Unlock Helper
Use `sso-mfa/bootstrap/sops-custody-unlock.sh` when an operator needs to run a
SOPS-backed drill, recovery, or lockdown command but the private key is only in
the password safe or offline custody packet.
The helper:
- reads the private age key from a hidden prompt, stdin, or a source file;
- derives the public age recipient with `age-keygen -y`;
- refuses to continue unless it matches the expected recipient;
- writes a `0600` temporary `SOPS_AGE_KEY_FILE`;
- runs the requested command or opens a temporary custody shell; and
- shreds/removes the temporary key file when the command or shell exits.
It must not be used to install the private key permanently on a workstation or
server. The source of truth remains the password safe/offline custody packet.
### Validate Custody Material
From the NetKingdom repo, validate the supplied private key against `keys/age.pub`
without keeping it on disk:
```bash
make sops-custody-check
```
### Run A One-Shot Recovery Drill
For inter-hub:
```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 the registered public recipient,
sets `SOPS_AGE_KEY_FILE` for the command, and removes the temporary key after
the drill exits.
If using a password-manager CLI, pipe only the private key field:
```bash
op read 'op://Platform/NetKingdom custodian age key/private-key' \
| sso-mfa/bootstrap/sops-custody-unlock.sh \
--from-stdin \
-- make -C /home/worsch/inter-hub recovery-drill
```
### Open An Incident Shell
For multi-step recovery or lockdown work:
```bash
make sops-custody-shell
```
Run the required SOPS, recovery, or lockdown commands inside that shell. Exit
the shell to shred the temporary key file.