feat(mvp): working secrets-engine CLI for the whynot-design npm publish lane
Implements SECRETS-WP-0002 end to end as a uv-managed Python package: - catalog: non-secret lane registry + strict validator (build/test/prod) - stage roles + OpenBao ACL policies; guards refuse wildcards, sys/, identity/, admin names, and cross-stage paths before any backend call - plan/apply: dry-run-first, idempotent policy + approle apply, decision-gated - decisions: State Hub lookup with local-fixture fallback; non-secret evidence to JSONL + hub progress, scrubbed of any value - provision/verify: mode-0600 file import + generated test values; positive/ negative checks that never print the value - exec delivery: `exec --catalog ... -- npm publish` injects the token via a temp .npmrc for the child only, cleaned up on exit/failure/interrupt - ops-warden routing contract + hardening backlog docs - 34 tests incl. live OpenBao integration; scripts/demo-e2e.sh runs the full chain against a throwaway bao dev server Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
58c24cff53
commit
a852d3f1ff
47 changed files with 3743 additions and 122 deletions
79
docs/openbao-stage-roles.md
Normal file
79
docs/openbao-stage-roles.md
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
# OpenBao Stage Roles & Bootstrap
|
||||
|
||||
secrets-engine talks to OpenBao through three **stage roles**, never as root or a
|
||||
platform admin. Each role is confined to one stage's KV prefix and a small,
|
||||
explicit capability set.
|
||||
|
||||
| Role | Policy file | KV prefix | May do | May NOT do |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `secrets-engine-build` | `policies/secrets-engine-build.hcl` | `secret/.../build/` | manage build metadata + generated test values; own `se-build-*` policies/roles | touch test/prod, `sys/*`, `auth/token/*`, `identity/*`, act as root |
|
||||
| `secrets-engine-test` | `policies/secrets-engine-test.hcl` | `secret/.../test/` | manage test metadata + values; run positive/negative checks; own `se-test-*` | touch build/prod, `sys/*`, `auth/token/*`, `identity/*`, act as root |
|
||||
| `secrets-engine-prod` | `policies/secrets-engine-prod.hcl` | owner-scoped prod lanes | apply approved prod ACL policies + approle roles; write approved values; own `se-prod-*` | reach build/test, edit the stage roles themselves, admin `sys/auth`, `sys/mounts`, `identity/*`, `auth/token/*`, act as root |
|
||||
|
||||
The **product requirement** is the stage *distinction* and the *denials*, not the
|
||||
exact policy names — those may evolve as info-tech-canon hardens.
|
||||
|
||||
## Negative guarantees (enforced two ways)
|
||||
|
||||
1. **In OpenBao** — each policy carries explicit `deny` stanzas for other stages
|
||||
and for `sys/*`, `auth/token/*`, `identity/*`.
|
||||
2. **In secrets-engine** — `roles.assert_path_in_stage()` and
|
||||
`roles.assert_policy_safe()` reject any *plan* that would touch another
|
||||
stage's prefix, use a wildcard, name itself like an admin policy, or carry a
|
||||
capability outside `create/read/update/delete/list`. A bad plan fails closed
|
||||
**before** any OpenBao call.
|
||||
|
||||
Run the negative checks:
|
||||
|
||||
```bash
|
||||
pytest tests/test_guards.py -q
|
||||
```
|
||||
|
||||
## Bootstrap token files (temporary)
|
||||
|
||||
Until OIDC/service auth exists (see the hardening backlog), a **platform-root
|
||||
operator** may mint a short-lived OpenBao token for a stage role and hand the
|
||||
agent the *file path* — never the token value.
|
||||
|
||||
Requirements for a bootstrap token file:
|
||||
|
||||
- mode **0600**, owned by the invoking user;
|
||||
- located **outside** any Git worktree (e.g. `~/.secrets-engine/bootstrap/`);
|
||||
- named by role + environment only, never by value
|
||||
(e.g. `prod.token`, not `npm_abc123.token`);
|
||||
- **revocable** and **temporary** — tracked with a revocation task;
|
||||
- referenced by path, e.g.:
|
||||
|
||||
```bash
|
||||
secrets-engine apply whynot-design-npm-publish --stage prod \
|
||||
--bootstrap-token-file ~/.secrets-engine/bootstrap/prod.token
|
||||
```
|
||||
|
||||
secrets-engine refuses a bootstrap token file that is group/other-readable or
|
||||
that lives inside the repo worktree (`provision`/`apply` check `st_mode & 0o077`).
|
||||
|
||||
### Minting (operator, one-time, root context)
|
||||
|
||||
```bash
|
||||
# Write each stage policy into OpenBao.
|
||||
bao policy write secrets-engine-build policies/secrets-engine-build.hcl
|
||||
bao policy write secrets-engine-test policies/secrets-engine-test.hcl
|
||||
bao policy write secrets-engine-prod policies/secrets-engine-prod.hcl
|
||||
|
||||
# Mint a short-lived token for one stage role, store mode-0600 outside the repo.
|
||||
install -m 700 -d ~/.secrets-engine/bootstrap
|
||||
bao token create -policy=secrets-engine-prod -ttl=1h -field=token \
|
||||
> ~/.secrets-engine/bootstrap/prod.token
|
||||
chmod 600 ~/.secrets-engine/bootstrap/prod.token
|
||||
```
|
||||
|
||||
### Revocation (always have a path)
|
||||
|
||||
```bash
|
||||
# Revoke by accessor (preferred) or delete the file when the TTL is short.
|
||||
bao token revoke -accessor <accessor>
|
||||
shred -u ~/.secrets-engine/bootstrap/prod.token
|
||||
```
|
||||
|
||||
Every minted bootstrap token MUST have a corresponding revocation task in the
|
||||
hardening backlog (`docs/hardening-backlog.md`).
|
||||
Loading…
Add table
Add a link
Reference in a new issue