NK-WP-0055-T13: king-path restore drilled; split backup and custodian age keys in the crisis list
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 4515@bnt-lap001
Assistant-Session: 0bf96127-6e04-41be-bfda-d11717c3f354
This commit is contained in:
tegwick 2026-10-27 23:55:19 +01:00
parent 317d578193
commit f100aa0542
3 changed files with 7 additions and 3 deletions

View file

@ -29,7 +29,8 @@ circular dependency and is recorded as a gap, not as custody.
| # | Item | Unlocks | Holder and location | Works with SSO and OpenBao down? | Last proven use | Gap or action |
| --- | --- | --- | --- | --- | --- | --- |
| 1 | Custodian/backup age private key (recipient `age1zvry…50syr3tsz`, labelled `railiance-backup`) | Decrypts the identity backup bundle and the SOPS custody files | Founder, independently held password safe or offline packet (Standard §4) | Yes, offline | Entered by the founder at hidden prompts 2026-10-24 (LLDAP, Authelia, privacyIDEA stages) | `TO CONFIRM`: number of copies, who else could recover if the founder cannot |
| 1 | **`railiance-backup` age private key** (recipient `age1zvry…50syr3tsz`). Not the custodian key (item 1b) | Decrypts the identity backup bundle and the break-glass copy on Storage Share (`net-kingdom-kingpath-*.tar.age`) | Founder, password safe (stored as `railiance-backup.key`) | Yes, offline | Identity stages 2026-10-24; king-path verify drill 2026-10-27 (the helper refused the custodian key three times before the correct key was entered, as designed) | `TO CONFIRM`: number of copies, who else could recover if the founder cannot |
| 1b | **Custodian age private key** (recipient `age1aq8t…qdepsh4`, `keys/age.pub` and `.sops.yaml`). Not the backup key | Decrypts the repository's SOPS custody files and greenfield bootstrap bundles (`docs/security-bootstrap-age-custody.md`) | Founder, password safe (stored as the custodian age private key) | Yes, offline | `TO CONFIRM` last proven use | Labels in the safe should name the recipient prefix so the two keys cannot be confused |
| 2 | Storage Share `Backup` account app password | Downloads the protected ciphertext without OpenBao or SSO | Founder, independent custody `TO CONFIRM` | Yes, provider is outside the platform | Fresh retrieval 2026-10-24 | One provider copy only; residual risk accepted until 2027-01-31 (G1, NK-WP-0053) |
| 3 | OpenBao unseal shares (2 of 3 required in the restore drill) | Unseals a restored or production OpenBao; with the shares, `generate-root` can mint a root token for break-glass (ceremony not yet documented) | Founder as `platform-root` holds all three shares (single-holder custody: accepted exception, hub decision `37464824`, 2026-10-27), in the founder's replicated browser password safe under the `kingpath.credentials` pseudo-URL entry (confirmed 2026-10-27) | Yes: the safe is independent of NetKingdom SSO and OpenBao; it depends on the browser vendor's sync account and its own factor | Original shares used in the isolated restore RPF-WP-0064-T07, 2026-10-24 | Single holder = single point of loss (reviewed at NK-WP-0055-T09). The founder holds the initial root token offline (2026-10-27); its validity is checked in the king-path drill (NK-WP-0055-T13) against an isolated restored OpenBao, never production |
| 4 | `platform-root` / king account and factors | Final authority; break-glass | Independently held password safe or offline packet (Standard §4; custody doc) | **No if used through KeyCape**: its login and OTP are validated by the services being recovered | OTP validated against restored privacyIDEA 2026-10-24 (isolated) | `TO CONFIRM` the offline path that works with SSO down (OpenBao shares, item 3) and record it |
@ -37,7 +38,7 @@ circular dependency and is recorded as a gap, not as custody.
| 6 | `admin` password and OTP token | Day-to-day platform administration and, per intent, recovery operation | Founder; OTP enrolled 2026-10-24 | **No**: login and token checks need KeyCape, LLDAP and privacyIDEA | Token not yet in any restored backup; first restore proof after the 2026-10-25 03:45 UTC capture | Same account as the directory bind used by Authelia and privacyIDEA until the split in NK-WP-0054-T05 |
| 7 | `pi-admin` credentials (privacyIDEA internal admin) | Token repair and re-enrolment | Founder's replicated browser password safe (confirmed 2026-10-27) | Custody yes (replicated, independent of SSO); use needs a running privacyIDEA | Not yet drilled | Add a pi-admin check to the drill (NK-WP-0053-T04) |
| 8 | ~~Ops bundle (`make creds-bundle`)~~ **Retired 2026-10-27** (founder decision) | — | — | — | Never drilled; none existed on the workstation | Superseded by the daily identity backup, which carries the current service secrets. `make creds-bundle` packs only greenfield `creds-generate` output and stays a greenfield bootstrap step |
| 9 | Operator access to the production host or cluster for a real restore | Applying a restore to production | Cluster: ops-bridge tunnel `k3s-api-railiance01` (SSH as `tegwick` to `92.205.62.239` with a static key file, local port 16444) plus the attended admin kubeconfig `~/.kube/config-railiance01` (client certificate), today on the founder workstation only. Host: Host Europe provider interface with console and rescue access to railiance01 (confirmed 2026-10-27) | Yes: static key, client certificate and provider console need neither KeyCape nor OpenBao. **No** for ops-warden SSH certificates (warden's backend is OpenBao) | Cluster path used 2026-10-27 | Off-workstation copy uploaded and read back 2026-10-27: `Backup/railiance-backups/net-kingdom-kingpath/net-kingdom-kingpath-20261027T223050Z-8c9d48421f1b.tar.age` (10440 bytes, sha256 `8c9d4842…7dd4aa`, items `admin-kubeconfig`, `tunnel-ssh-key`, recipient `railiance-backup`; receipt on the founder workstation). Decrypt and use not yet drilled (NK-WP-0055-T13). Re-run `kingpath-offsite.py` whenever the kubeconfig or key changes. Open: runs as `tegwick` under an agent-labelled actor; provider console never drilled |
| 9 | Operator access to the production host or cluster for a real restore | Applying a restore to production | Cluster: ops-bridge tunnel `k3s-api-railiance01` (SSH as `tegwick` to `92.205.62.239` with a static key file, local port 16444) plus the attended admin kubeconfig `~/.kube/config-railiance01` (client certificate), today on the founder workstation only. Host: Host Europe provider interface with console and rescue access to railiance01 (confirmed 2026-10-27) | Yes: static key, client certificate and provider console need neither KeyCape nor OpenBao. **No** for ops-warden SSH certificates (warden's backend is OpenBao) | Cluster path used 2026-10-27 | Off-workstation copy uploaded and read back 2026-10-27: `Backup/railiance-backups/net-kingdom-kingpath/net-kingdom-kingpath-20261027T223050Z-8c9d48421f1b.tar.age` (10440 bytes, sha256 `8c9d4842…7dd4aa`, items `admin-kubeconfig`, `tunnel-ssh-key`, recipient `railiance-backup`; receipt on the founder workstation). Restore drilled 2026-10-27: fetched from Storage Share, unrelated key refused, decrypted in memory, both items intact (`kingpath-offsite.py --verify`). Using the restored material against the cluster is not drilled. Re-run `kingpath-offsite.py` whenever the kubeconfig or key changes. Open: runs as `tegwick` under an agent-labelled actor; provider console never drilled |
## Non-secret recovery assets

View file

@ -8,7 +8,8 @@ OpenBao, Forgejo, User Engine or application recovery.
1. Use a trusted recovery workstation. Obtain Storage Share access independently
from your password safe/offline custody, and the independently held
`railiance-backup` age private key. Never paste either into chat or a ticket.
`railiance-backup` age private key (crisis list item 1; **not** the custodian
age key, item 1b, which only opens the repository's SOPS files). Never paste either into chat or a ticket.
The private key must match this **public** recipient:
`age1zvryunvjhvpkmasskauga2heeg0ztnte9ymgppvjge36ekumk50syr3tsz`.
The provider password and encryption key are separate requirements.

View file

@ -320,6 +320,8 @@ root token against an isolated restored OpenBao (never production); use produces
2026-10-27 founder decision (hub decision `732b5c96`): option (b), a standing loopback-only OpenBao listener with `generate-root` enabled, reachable only through `kubectl port-forward`/`exec` with the admin kubeconfig. Owner rapp-openbao prepares the change for founder review and rollout.
2026-10-27: `kingpath-offsite.py --verify UPLOAD_RECEIPT` added for the drill (7 more contained tests, 18 total): run under `sops-custody-unlock.sh --sealed-fd`, it checks the custody key against the recorded recipient, fetches the artifact with ETag/size/digest checks, refuses an unrelated key, decrypts in memory and checks each item's size and shape (kubeconfig with clusters/users, SSH private key header), writing a value-free receipt. The OpenBao part of the drill is RPF-WP-0064-T10 (drill mode being built).
2026-10-27 king-path drill, part 1 (founder-run): `kingpath-offsite.py --verify` restored `net-kingdom-kingpath-20261027T223050Z-8c9d48421f1b.tar.age` from Storage Share, refused an unrelated key, decrypted in memory and found `admin-kubeconfig` (kubeconfig shape) and `tunnel-ssh-key` (SSH private key header) intact. The custody helper refused the custodian age key three times before the founder entered the `railiance-backup` key; crisis list item 1 is split into 1 (backup key) and 1b (custodian key) so the two are not confused again. Part 2 (OpenBao, RPF-WP-0064-T10) is being built.
## Policy import pipeline and platform/tenant package split
```task