Harden production authorization and service auth
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a0217e-8c4c-7383-be6b-f50a6e485306
This commit is contained in:
tegwick 2026-08-23 14:15:42 +02:00
parent f579f3761c
commit 70371649af
20 changed files with 1268 additions and 54 deletions

View file

@ -38,6 +38,7 @@ secrets-engine --version
| `SECRETS_ENGINE_HUB_URL` | `http://127.0.0.1:8000` | State Hub for decisions + evidence (empty to disable) |
| `SECRETS_ENGINE_CATALOG` | `./catalog` | catalog directory |
| `SECRETS_ENGINE_EVIDENCE` | `./.evidence` | local non-secret evidence log |
| `SECRETS_ENGINE_UNSAFE_DEMO` | _(unset)_ | allow a prod-labeled lane only when Hub is disabled and OpenBao is loopback; throwaway demos only |
## Commands
@ -67,7 +68,11 @@ var the token is injected under, per the netkingdom publication-scope policy
`<ref>` is a catalog id or a decision/CCR ref (matched against
`approval.decision_ref`). `plan` and `apply --dry-run` never mutate OpenBao.
For decision-gated lanes they may render with `decision: <none>` when the
approval object is not reachable; non-dry-run `apply` remains decision-gated.
approval object is not reachable. Production live commands remain disabled
until State Hub exposes the durable exact-action authorization object. A legacy
local decision is accepted for a prod-labeled lane only with
`SECRETS_ENGINE_UNSAFE_DEMO=1`, an empty Hub URL, and loopback OpenBao; the demo
scripts set those three conditions themselves.
`handoff` is for `kind: auth-capability` lanes such as `warden-sign`. It mints a
fresh AppRole `secret_id` and writes `role_id` plus `secret_id` to caller-chosen
@ -109,8 +114,8 @@ and evidence stores only a short accessor fingerprint plus cleanup outcome.
counts, canonical decision references, session cleanup outcomes, and State Hub
delivery outcomes for one cataloged lane. Its parser allowlists those fields and
does not echo arbitrary JSONL detail. State Hub failures are recorded locally as
append-only companion receipts; they are visible but are not automatically
replayed.
append-only companion receipts. Edge-relay queued receipts and their non-secret
outbox ids are recorded too; replay remains an operator/State Hub responsibility.
Live `apply`, `provision`, `verify`, `handoff`, `exec`, `revoke`, `suspend`, and
`deactivate` share one evidence guard. It records an attempt before lane approval

View file

@ -31,8 +31,11 @@ contents in this repo.
## H1 — Replace bootstrap token files with OIDC / service auth
- Stand up an OpenBao auth method (OIDC or AppRole bound to a workload identity)
for each stage role.
- Implemented consumer scaffold: exact KeyCape client-credentials exchange,
claim/lifetime preflight, strict client-secret file input, renewal boundary,
and no implicit fallback.
- Remaining: railiance-platform stands up the exact-bound OpenBao JWT auth
mount/role and publishes its non-secret connection contract.
- secrets-engine logs in via that method instead of reading a token file.
- Remove `--bootstrap-token-file` from the steady-state path (keep only for true
break-glass, heavily audited).
@ -44,8 +47,11 @@ contents in this repo.
## H3 — Production dual-control
- Require two-person approval (`approval.model: dual-control`) for prod value
provisioning before automating raw-value writes beyond the pilot.
- Implemented consumer validator: exact action request, decision digest/binding,
validity, State Hub provenance, and caller-selected minimum distinct approval
count.
- Remaining: State Hub stores/serves the durable object and production handlers
resolve it; all production live actions currently fail closed.
## H4 — Rotation & lifecycle states
@ -59,7 +65,9 @@ contents in this repo.
- Implemented: `secrets-engine audit <catalog-id>` summarizes allowlisted local
action/result, decision, session-cleanup, and State Hub delivery evidence.
- Remaining: durable/queued State Hub delivery and OpenBao audit request-id
- Implemented: idempotency/source headers and edge-relay queued-receipt/outbox-id
visibility.
- Remaining: operator replay verification and OpenBao audit request-id
correlation.
## H6 — API service mode

28
docs/service-auth.md Normal file
View file

@ -0,0 +1,28 @@
# KeyCape service-auth consumer boundary
secrets-engine now implements the consumer half of KeyCape's accepted
`secrets-engine-openbao` service-auth contract. It performs one explicit HTTPS
`client_credentials` exchange, keeps the resulting JWT in memory, and validates
the non-cryptographic contract before any future OpenBao login:
- subject `service:secrets-engine`, audience/client `secrets-engine-openbao`;
- service principal in `tenant:coulomb`, role `secrets-engine`;
- exact `openbao:login` scope and KeyCape AAL1 client-secret assurance;
- RS256 declaration, bounded issue/expiry timestamps, maximum 15-minute life,
and renewal when no more than three minutes remain;
- no ID token, refresh token, implicit retry provider, or fallback identity.
The confidential-client secret must be an explicitly selected mode-0600 file
outside every Git worktree. It is sent with HTTP Basic authentication and never
placed in a request body, command argument, evidence record, or object
representation.
This is a scaffold, not a live OpenBao authentication path. JWT payload parsing
does not prove a signature. railiance-platform still owns the exact OpenBao JWT
auth mount/role, issuer keys, claim bindings, token policy, TTL/use limits, and
cryptographic verification. Until that contract is materialized, the provider
is not selected by the CLI and bootstrap/AppRole behavior is not used as an
implicit fallback.
Canonical provider contract:
`key-cape/docs/openbao-service-auth-contract.md` (reviewed 2026-08-23).