net-kingdom/history/2026-07-02-openbao-greenfield-init-unseal-proof.md

41 lines
2 KiB
Markdown
Raw Permalink Normal View History

# 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
1. **Custody gate refusal** — with no model selected, the script refused
(`unseal custody model is 'unselected'`). Matches earlier negative tests.
2. **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 to
`bao write sys/unseal key=-`, which reads the value from stdin — shares
still never touch argv or logs.
3. **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`.
4. **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).