T2: greenfield live proof against a fresh uninitialized OpenBao 2.5.5 — caught and fixed 'bao operator unseal -' not reading stdin (now 'bao write sys/unseal key=-'); init and reseal-replay paths proven. T3: attended-ceremony selectable — runbook, non-secret ceremony-record template + validator, and a lab/production deployment profile that blocks sops-held-automation in console selection, gates, and the init script. T4: console gate + evidence flags for auto-unseal-transit (Helm seal stanza prepared in railiance-platform). Also: SCOPE.md refreshed to current repo state; adhoc fix for the broken check-secrets Make target (unescaped $). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2.0 KiB
OpenBao greenfield init/unseal proof (NET-WP-0020 T2)
Date: 2026-07-02
Scope: sso-mfa/bootstrap/openbao-init-unseal.sh proven live against a
genuinely uninitialized OpenBao 2.5.5 instance.
Method
A fresh local bao server (file storage, throwaway scratchpad dir) was started
uninitialized and sealed. A kubectl shim forwarded the script's
kubectl -n openbao exec openbao-0 -- bao … calls to the local instance, so
the script ran byte-for-byte unmodified logic against real OpenBao. Console
metadata lived in a scratch file with sops-held-automation selected.
This substitutes for the "rebuild slate" run: it exercises the exact
init/unseal/verify code paths on a greenfield instance. The first 3-node
rebuild will re-run the same script through creds-bootstrap-agent.sh
Phase 7b.
Results
- Custody gate refusal — with no model selected, the script refused
(
unseal custody model is 'unselected'). Matches earlier negative tests. - Bug found and fixed —
bao operator unseal -does not read the share from stdin (OpenBao treats-as the literal key: "'key' must be a valid hex or base64 string"; without an argument it demands a TTY). The live cluster never hit this because it was already unsealed. Fixed tobao write sys/unseal key=-, which reads the value from stdin — shares still never touch argv or logs. - Greenfield init path — uninitialized →
operator init(3 shares, threshold 2) → init.json written 0600 into the age-custody secrets dir → share replay stopped at threshold (share 2 of 3) → post-unseal verified. Evidence:openbao_did_init_this_run=true,openbao_initialized=true,openbao_post_unseal_verified=true. - Restart/reseal path — server restarted (initialized + sealed) →
script skipped init, replayed SOPS-held shares to threshold → verified.
Evidence:
openbao_did_init_this_run=false, both flags true.
Test server, storage, and init material were destroyed after the proof (init.json shredded).