Implement KeyCape provider and service identity contracts
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 25s
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 25s
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a02e3f-7301-7622-9be1-12e5f352881c
This commit is contained in:
parent
cdfb046b80
commit
efce3e9331
15 changed files with 579 additions and 26 deletions
86
docs/openbao-service-auth-contract.md
Normal file
86
docs/openbao-service-auth-contract.md
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
# 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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue