87 lines
4 KiB
Markdown
87 lines
4 KiB
Markdown
|
|
# 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.
|