RPF-WP-0065-T04: rebuild founder runbook for the silent warden lane
The warden openbao-platform-admin-login lane runs one silent child per login, fails closed on any output and self-revokes; the old runbook printed output and rolled back from a standing session that cannot exist. - scripts/openbao-platform-admin-aal2.sh (+ .py): prepare (backup + assurance claim mapping), apply (gated on this login's assurance_level=aal2, readback of bound_claims), verify, rollback from the backup receipt via a fresh lane login, rollback-body for the docs/openbao.md break-glass path. 0600 O_EXCL receipts, value-free; no stdout/stderr. - Role declaration maps /assurance/level to token metadata assurance_level. - tests/test_openbao_platform_admin_aal2.py: fake bao end-to-end. 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:
parent
87a488cc66
commit
bedab6f5ab
5 changed files with 523 additions and 30 deletions
|
|
@ -8,7 +8,7 @@ status: proposed
|
|||
flavor: implementation
|
||||
owner: claude
|
||||
created: "2026-10-24"
|
||||
updated: "2026-10-24"
|
||||
updated: "2026-10-27"
|
||||
related:
|
||||
- NK-WP-0054
|
||||
- RPF-WP-0022
|
||||
|
|
@ -43,7 +43,8 @@ Live evidence: KeyCape `docs/evidence/2026-10-07-secrets-engine-openbao-issuance
|
|||
records the founder's OIDC login through `openbao-admin` under bound `/assurance/level aal2`
|
||||
(role `credential-broker-...` family, same client and mount). Conclusion: verified from source and
|
||||
prior live evidence; one residual risk is that this exact role has not been exercised with the claim,
|
||||
so the rollout below keeps an open admin session and a one-command rollback.
|
||||
so the rollout below gates the apply on a machine-checked aal2 login and keeps a fresh-login
|
||||
rollback plus the break-glass path.
|
||||
|
||||
## Tasks
|
||||
|
||||
|
|
@ -90,7 +91,9 @@ status: wait
|
|||
priority: high
|
||||
state_hub_task_id: "abd78db2-03ad-5942-a9ef-12bbada54b28"
|
||||
```
|
||||
**Founder live step: apply platform-admin aal2 (needs the founder).** See runbook below.
|
||||
**Founder live step: apply platform-admin aal2 (needs the founder).** See runbook below, rebuilt
|
||||
2026-10-27 for the silent warden lane (`scripts/openbao-platform-admin-aal2.sh`). The declaration
|
||||
now also maps `/assurance/level` to token metadata `assurance_level` for the pre-apply gate.
|
||||
|
||||
```task
|
||||
id: RPF-WP-0065-T05
|
||||
|
|
@ -103,44 +106,83 @@ Then the founder applies the role and later removes `service`.
|
|||
|
||||
## Runbook for the founder (T04)
|
||||
|
||||
Preconditions: you are on the workstation with the `openbao-ui-railiance01` tunnel up
|
||||
(`bridge status`), in the repo checkout, and you are a `net-kingdom-admins` member who completes MFA.
|
||||
Role JSON contains no secrets. Keep one existing admin session open in another terminal
|
||||
until step 4 passes.
|
||||
Rebuilt 2026-10-27 for the warden lane. `warden access openbao-platform-admin-login --exec -- <cmd>`
|
||||
runs one reviewed command after one OIDC/MFA login, fails closed on any child output (even
|
||||
whitespace) and self-revokes the token on every path. There is no interactive or standing session,
|
||||
so every step below is its own login, every child is silent, and rollback never assumes an open
|
||||
terminal. The previous runbook (bao read/write printing output, rollback from a still-open session)
|
||||
cannot work in this lane and is retired; the founder's `--exec -- bash` attempt changed nothing.
|
||||
|
||||
The child is `scripts/openbao-platform-admin-aal2.sh` (Python: `scripts/openbao_platform_admin_aal2.py`,
|
||||
tests `tests/test_openbao_platform_admin_aal2.py`). It reuses the role read/payload/write helpers
|
||||
of `openbao_operator_loopback_callback.py`, prints nothing, writes one `0600` receipt per run
|
||||
(refuses an existing path before touching OpenBao) and exits non-zero on any refusal or mismatch.
|
||||
Receipts hold role configuration and the session's policy set, role name and assurance level only;
|
||||
never a token, accessor, email or username. `scripts/openbao-platform-admin-check.sh` remains the
|
||||
read-only policy/self-revocation check; it does not compare bound claims, so it is optional here.
|
||||
|
||||
**Why three logins.** OpenBao token metadata carries no assurance level today, so the only
|
||||
value-free proof that the founder's `admin` login is `aal2` needs a claim mapping first:
|
||||
`prepare` backs up the live role and adds only `claim_mappings["/assurance/level"] = "assurance_level"`
|
||||
(no new binding, nobody is locked out). `apply` then refuses unless *its own* login's token metadata
|
||||
shows `role=platform-admin` and `assurance_level=aal2`, and only then writes the declared role and
|
||||
checks the readback, including `bound_claims == {"groups":["net-kingdom-admins"],"/assurance/level":"aal2"}`.
|
||||
`verify` proves a fresh login is accepted under the new role. In parallel, the KeyCape sign-in
|
||||
diagnosis page for `openbao-admin` (NK-WP-0051-T02) shows `assurance` for each login: it must read
|
||||
`aal2` at steps 2 and 3. A reused KeyCape/Authelia session can be `aal1` even after earlier MFA; if
|
||||
the page shows `aal1`, step 2 refuses with `gate_login_not_aal2` and nothing changes; report to
|
||||
KeyCape and do not pin MFA on the client without its own go.
|
||||
|
||||
Preconditions: workstation, `openbao-ui-railiance01` tunnel up (`bridge status`), browser opener
|
||||
working, you are `admin` in `net-kingdom-admins` and complete the one-time code at every login.
|
||||
|
||||
```bash
|
||||
set +x; umask 077; unset BAO_TOKEN VAULT_TOKEN
|
||||
cd ~/railiance-platform
|
||||
python3 -m pytest -q tests/test_netkingdom_role_files.py tests/test_openbao_platform_admin_callback_prune.py
|
||||
# 1 backup of the live role (rollback source)
|
||||
warden access openbao-platform-admin-login --exec -- \
|
||||
bao read -format=json auth/netkingdom/role/platform-admin > ~/.local/state/railiance/platform-admin-role.before.json
|
||||
# 2 apply
|
||||
warden access openbao-platform-admin-login --exec -- \
|
||||
bao write auth/netkingdom/role/platform-admin @openbao/auth/netkingdom-platform-admin-role.json
|
||||
# 3 read back
|
||||
warden access openbao-platform-admin-login --exec -- \
|
||||
bao read -format=json auth/netkingdom/role/platform-admin | jq '.data.bound_claims'
|
||||
# expect {"groups":["net-kingdom-admins"],"/assurance/level":"aal2"}
|
||||
python3 -m pytest -q tests/test_openbao_platform_admin_aal2.py tests/test_netkingdom_role_files.py
|
||||
R=~/.local/state/railiance/rpf-wp-0065-t04; mkdir -p -m 700 "$R"
|
||||
S="$PWD/scripts/openbao-platform-admin-aal2.sh"
|
||||
|
||||
# 1 back up the live role and add the assurance mapping (no new restriction)
|
||||
warden access openbao-platform-admin-login --exec -- "$S" prepare --receipt "$R/1-prepare.json"
|
||||
# 2 gate + apply: refuses unless this login is aal2 (diagnosis page must show aal2)
|
||||
warden access openbao-platform-admin-login --exec -- "$S" apply --receipt "$R/2-apply.json"
|
||||
# 3 fresh login under the new role: aal2, platform-admin + operator-custody, role == declaration
|
||||
warden access openbao-platform-admin-login --exec -- "$S" verify --receipt "$R/3-verify.json"
|
||||
|
||||
jq -r '.mode + " " + .status' "$R"/*.json # prepare prepared / apply applied / verify verified
|
||||
```
|
||||
|
||||
Step 4, prove a fresh MFA login still works: in a new terminal run
|
||||
`warden access openbao-platform-admin-login --exec -- bao token lookup -format=json | jq '.data.policies'`
|
||||
and confirm `platform-admin` and `operator-custody`. Only then close the old session.
|
||||
Optionally record a receipt with `scripts/openbao-platform-admin-check.sh --receipt <new-file>`.
|
||||
Each `warden` call must exit 0. On a non-zero exit read only the receipt's `status` (`jq -r .status`)
|
||||
before anything else; codes 11-14 do not prove that nothing changed. Step 1 or 2 refused: the role
|
||||
has at most the harmless mapping; fix the cause and rerun with a new receipt name.
|
||||
|
||||
Rollback (if step 4 fails, from the still-open session):
|
||||
**Rollback A, login still works** (step 3 failed with warden exit 11, e.g. `role_not_declared`,
|
||||
`token_policies_missing`): restore the backed-up role with a fresh lane login.
|
||||
|
||||
```bash
|
||||
# previous declaration = the file as of the parent of this workplan's commit
|
||||
git show 2126457^:openbao/auth/netkingdom-platform-admin-role.json > ~/.local/state/railiance/pa-role.prev.json
|
||||
grep -c assurance ~/.local/state/railiance/pa-role.prev.json # expect 0
|
||||
bao write auth/netkingdom/role/platform-admin @$HOME/.local/state/railiance/pa-role.prev.json
|
||||
warden access openbao-platform-admin-login --exec -- \
|
||||
"$S" rollback --backup "$R/1-prepare.json" --receipt "$R/4-rollback.json"
|
||||
jq -r .status "$R/4-rollback.json" # rolled_back
|
||||
```
|
||||
|
||||
If no session is open, use the break-glass recovery path in `docs/openbao.md`.
|
||||
If the login fails because the token lacks `assurance.level`, check the sign-in diagnosis page of
|
||||
`openbao-admin` (NK-WP-0051-T02) and report to KeyCape; do not pin MFA on the client without its own go.
|
||||
**Rollback B, login itself is refused** (step 3 exits 10 `login_failed`, or OpenBao names the bound
|
||||
claim): no OpenBao session can be obtained, so use the break-glass operator path of
|
||||
`docs/openbao.md` (`kubectl exec` with the offline-escrowed initial token, "Bootstrap Ceremony"
|
||||
step 7). The role body is derived locally from the step 1 backup and contains no secret. The
|
||||
break-glass token is typed at a hidden prompt inside the pod, never in argv, history, a file or a receipt.
|
||||
|
||||
```bash
|
||||
"$S" rollback-body --backup "$R/1-prepare.json" --out "$R/rollback-body.json"
|
||||
kubectl exec -i -n openbao openbao-0 -- sh -c 'cat > /tmp/pa-rollback.json' < "$R/rollback-body.json"
|
||||
kubectl exec -it -n openbao openbao-0 -- sh -c 'printf "break-glass token: "; stty -echo; read -r BAO_TOKEN; stty echo; echo; export BAO_TOKEN; bao write auth/netkingdom/role/platform-admin @/tmp/pa-rollback.json >/dev/null && echo rolled-back; rm -f /tmp/pa-rollback.json'
|
||||
```
|
||||
|
||||
Then confirm logins work with a lane `verify` under a new receipt name: warden exit 11 with
|
||||
`role_not_declared` (not exit 10) is the expected result after rollback. If the initial token was
|
||||
revoked rather than escrowed, this is a break-glass event under `docs/openbao.md`
|
||||
(custody owners, recovery shares): stop and raise it, do not improvise. Record the outcome as a
|
||||
progress event; receipts stay in `$R`, never in Git.
|
||||
|
||||
## Rollout order for the coding-agent role (T05)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue