generated from coulomb/repo-seed
133 lines
4.5 KiB
Markdown
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.
|