key-cape/docs/openbao-service-auth-contract.md

87 lines
4 KiB
Markdown
Raw Permalink Normal View History

# KeyCape service authentication for OpenBao
Date: 2026-08-23
Status: accepted provider contract; live client provisioning remains a
deployment and custody operation
## Ownership boundary
KeyCape owns static confidential-client registration, client authentication,
JWT signing, IAM Profile service claims, expiry, and disabling future issuance.
The OpenBao platform owner owns JWT auth mounts, exact-bound roles, policies,
OpenBao token TTL/use limits, and value-safe authorization evidence. The
consumer owns renewal and fail-closed use. OpenBao or its approved delivery
path owns confidential-client secret custody.
KeyCape accepts the `codex-railiance-platform` issuance identity and the
`secrets-engine-openbao` service identity under this contract. It does not own
the resulting OpenBao identity token or policy. ops-warden remains the SSH
certificate issuer only, and user-engine is not in the machine-principal path.
## Exchange and claims
The consumer sends `POST /token` with `grant_type=client_credentials`, HTTP
Basic client authentication, and the allow-listed `scope=openbao:login`.
KeyCape returns an RS256 access token and no ID token or refresh token.
Every issued service token contains:
| Claim | Contract |
| --- | --- |
| `iss` | the configured KeyCape issuer |
| `sub` | the registration's exact `serviceSubject` |
| `aud` | the exact `clientId` |
| `iat`, `exp` | issue and expiry time; maximum per-client lifetime below |
| `principal_type` | `service` |
| `tenant` | the registration's exact tenant |
| `roles` | the registration's allow-listed roles |
| `groups` | an empty array |
| `scope` | only requested scopes from the registration allow-list |
| `assurance` | AAL1, `client_secret`, non-MFA, source `key-cape` |
OpenBao JWT roles must bind the exact issuer, audience, subject,
`principal_type`, tenant, and required role. A consumer must reject an issuer
or claim mismatch before attempting OpenBao login.
## Accepted registrations
The source-of-truth non-secret fragments are in
`config/service-clients.example.yaml`.
| Client | Subject | Role | Scope | Maximum JWT lifetime |
| --- | --- | --- | --- | --- |
| `codex-railiance-platform` | `service:codex:railiance-platform` | `coding-agent` | `openbao:login` | 15 minutes |
| `secrets-engine-openbao` | `service:secrets-engine` | `secrets-engine` | `openbao:login` | 15 minutes |
The confidential secret is referenced as an environment variable in KeyCape
configuration and must be materialized without entering Git, State Hub, chat,
process arguments, or normal logs. Registering or rotating the live value is a
separate approved custody action.
## Renewal, expiry, and revocation
- There is no refresh token. Obtain a new KeyCape JWT with a new
`client_credentials` exchange when no more than three minutes remain.
- A consumer may retry a transient exchange failure with bounded backoff only
while its current JWT is still valid. It must stop the privileged operation
at expiry.
- KeyCape JWTs are self-contained and cannot be recalled after issue. Disabling
the client or rotating its secret stops new issuance; the residual exposure
is bounded by the 15-minute JWT lifetime and the OpenBao token's independent
TTL/use limits.
- After OpenBao login, the consumer must explicitly revoke the OpenBao token in
its cleanup path. A cleanup failure is actionable evidence and must not print
a JWT, OpenBao token, client secret, or token accessor.
## Failure semantics
Authentication, scope, claim, signature, issuer, expiry, OpenBao login, and
cleanup failures are fail-closed. Steady-state consumers must not implicitly
fall back to bootstrap token files, an operator identity, a broader AppRole, or
an unbound JWT role. Bootstrap files remain explicitly selected setup or
break-glass providers and require their own authorization and evidence.
Verification must inspect metadata and capabilities without reading a secret
value. A coding-agent check must prove the `agent-high-risk-boundary` deny wins
against any otherwise readable workload policy.