2026-09-09 07:06:05 +02:00
|
|
|
# Approval consumer identity
|
|
|
|
|
|
|
|
|
|
Implemented for SECRETS-WP-0009-T03 on 2026-09-09. KeyCape's live verifier
|
|
|
|
|
registration is accepted under KEY-WP-0013-T02 and CCR-2026-0017; this consumer
|
|
|
|
|
implementation does not grant retrieval of its confidential client secret.
|
|
|
|
|
|
|
|
|
|
The CLI exchanges a separately supplied `secrets-engine-approval` client secret
|
|
|
|
|
for a fresh access token immediately before each approval HTTP request. Claim
|
|
|
|
|
requests ask for `approval:read`; consume requests ask for `approval:consume`.
|
|
|
|
|
The token exists only in memory for that request. There is no refresh token,
|
|
|
|
|
access-token file, background renewal, retry loop, or fallback identity.
|
|
|
|
|
|
|
|
|
|
| Input | Value |
|
|
|
|
|
| --- | --- |
|
|
|
|
|
| `SECRETS_ENGINE_APPROVAL_CLIENT_SECRET_FILE` | Explicit, protected temporary file outside Git, no group/other access |
|
|
|
|
|
| `SECRETS_ENGINE_KEYCAPE_ISSUER` | `https://kc.coulomb.social` |
|
|
|
|
|
| `SECRETS_ENGINE_KEYCAPE_TOKEN_URL` | `https://kc.coulomb.social/token` |
|
|
|
|
|
| `SECRETS_ENGINE_APPROVAL_URL` | Admitted HTTPS endpoint or literal loopback owner tunnel |
|
|
|
|
|
| `SECRETS_ENGINE_APPROVAL_TOKEN_FILE` | Unset when using the client-secret provider |
|
|
|
|
|
|
|
|
|
|
The existing token-file provider remains explicitly selectable by configuring
|
|
|
|
|
only `SECRETS_ENGINE_APPROVAL_TOKEN_FILE`. Configuring both providers fails
|
|
|
|
|
before either credential is read. An exchange failure never reads that file,
|
|
|
|
|
`BAO_TOKEN`, or `SECRETS_ENGINE_KEYCAPE_CLIENT_SECRET_FILE` (the different
|
|
|
|
|
OpenBao identity's credential).
|
|
|
|
|
|
|
|
|
|
The approval profile fixes client `secrets-engine-approval`, audience
|
|
|
|
|
`approval-engine`, subject `service:secrets-engine`, tenant `tenant:platform`,
|
|
|
|
|
role `secrets-engine`, service principal, empty groups and one requested scope.
|
|
|
|
|
It enforces RS256 shape, at most 900 seconds of life, strict expiry and at most
|
|
|
|
|
30 seconds of future issue time. The KeyCape assurance shape is
|
|
|
|
|
`level: aal1`, `methods: [client_secret]`, `mfa: false`, `source: key-cape`.
|
|
|
|
|
The same wire-shape correction applies to the existing OpenBao exchange; its
|
|
|
|
|
client, audience, tenant and authorization remain separate.
|
|
|
|
|
|
|
|
|
|
Claim parsing is a preflight, not signature verification or authorization.
|
|
|
|
|
Approval Engine verifies signature, issuer, audience and claims against JWKS.
|
|
|
|
|
The existing exact-action claim, access-engine decision and atomic consume gates
|
|
|
|
|
still precede a production OpenBao operation.
|
|
|
|
|
|
|
|
|
|
Credentials are sent through a non-redirecting transport. The KeyCape endpoint
|
|
|
|
|
must be the configured HTTPS issuer's `/token`. The new consumer refuses
|
|
|
|
|
cluster Service DNS from this workstation and plaintext non-loopback approval
|
|
|
|
|
endpoints. A literal loopback address alone cannot identify the responder: the
|
|
|
|
|
operator must establish a `kubectl port-forward` for the admitted cluster and
|
|
|
|
|
named Approval Engine pod. No tunnel or cluster deployment is created here.
|
|
|
|
|
|
|
|
|
|
## Reproducible component exercise
|
|
|
|
|
|
|
|
|
|
Run from this repository with Docker available and the owner source checkouts:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
uv run --with 'PyJWT[crypto]>=2.7,<3' python tools/exercise_approval_identity.py \
|
|
|
|
|
--keycape-source /home/worsch/key-cape \
|
|
|
|
|
--approval-engine-source /home/worsch/approval-engine \
|
2026-09-09 08:55:46 +02:00
|
|
|
--flex-auth-source /home/worsch/flex-auth \
|
2026-09-09 07:06:05 +02:00
|
|
|
--receipt /tmp/<new-metadata-receipt>.json
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The exercise starts the immutable admitted KeyCape image behind local HTTPS,
|
|
|
|
|
uses synthetic signing keys/client secrets, and serves the real Approval Engine
|
|
|
|
|
source with its JWT/JWKS verifier and SQLite store. It drives the actual Secrets
|
2026-09-09 08:55:46 +02:00
|
|
|
Engine production gate through the actual locally compiled Flex Auth evaluator.
|
2026-09-09 07:06:05 +02:00
|
|
|
Operator create/approve, consumer claim/consume, same-digest retry, different
|
|
|
|
|
digest, spent claim, wrong action, wrong secret and operator-scope denial are
|
|
|
|
|
checked. All temporary processes and synthetic credentials are removed.
|
|
|
|
|
|
|
|
|
|
This exercise caught the previous fixture-only `aal`/`method` assumption:
|
|
|
|
|
the real issuer has always emitted `level`/`methods`. The regression suite now
|
|
|
|
|
refuses that obsolete shape. See
|
|
|
|
|
[the component receipt](evidence/2026-09-09-approval-identity-exercise.json).
|
|
|
|
|
|
|
|
|
|
## Live handoff
|
|
|
|
|
|
|
|
|
|
RPF-WP-0035-T06 owns the separate client-side read admission for the existing
|
|
|
|
|
`platform/workloads/secrets-engine/approval-client`, field `CLIENT_SECRET`.
|
|
|
|
|
The admission must name the actual consumer/placement, bounded reader, protected
|
|
|
|
|
temporary delivery and cleanup, refusal/revocation checks and lifecycle owner.
|
|
|
|
|
Do not use the verifier's `sso` Secret or reseed version-1 custody.
|
|
|
|
|
|
2026-09-10 10:15:08 +02:00
|
|
|
### CCR-2026-0019 workstation procedure — consumer return, 2026-09-10
|
|
|
|
|
|
|
|
|
|
This source review answers Platform's procedure request
|
|
|
|
|
`fbf434cf-3f68-4d11-ae98-bb3798e2c5a5`. The current consumer accepts the following
|
|
|
|
|
attended file lifetime. It does not confirm an operator group or admit a reader:
|
|
|
|
|
CCR-2026-0019 remains in flight, its front door unresolvable, and no value was read.
|
|
|
|
|
|
|
|
|
|
1. After the exact group, role and reader admission are accepted, use an
|
|
|
|
|
operator-owned runtime directory outside every Git worktree, with canonical
|
|
|
|
|
non-symlink parents and mode 0700. Prefer the workstation's private runtime
|
|
|
|
|
tmpfs; stop if it is unavailable rather than silently choosing a shared or
|
|
|
|
|
backed-up location. Set `umask 077`. Create a unique session subdirectory and
|
|
|
|
|
a new regular `client-secret` file owned by that operator, mode 0600. Check
|
|
|
|
|
ownership/mode/location without displaying file contents.
|
|
|
|
|
2. Install the session wrapper's EXIT cleanup before delivery. Its INT and TERM
|
|
|
|
|
handlers must exit through that cleanup. The admitted delivery owner fills
|
|
|
|
|
only this file from `platform/workloads/secrets-engine/approval-client`, field
|
|
|
|
|
`CLIENT_SECRET`, existing version 1. Use the reviewed front door after it is
|
|
|
|
|
resolvable; do not replace its pending admission with a direct raw vault read,
|
|
|
|
|
the verifier's Kubernetes Secret, a sibling identity or an interactive paste.
|
|
|
|
|
3. Supply only the absolute file path as
|
|
|
|
|
`SECRETS_ENGINE_APPROVAL_CLIENT_SECRET_FILE` to the intended Secrets Engine
|
|
|
|
|
CLI. Keep the file for both claim and consume exchanges in that one attended
|
|
|
|
|
operation. Unset `SECRETS_ENGINE_APPROVAL_TOKEN_FILE`; configure the existing
|
|
|
|
|
issuer/token endpoint and the admitted Approval Engine endpoint or its
|
|
|
|
|
owner-bound tunnel. Disable shell tracing and credential-bearing HTTP debug
|
|
|
|
|
logs. The file value belongs in neither argv nor an environment variable.
|
|
|
|
|
4. On success, refusal, exception or interruption, cleanup unsets the path,
|
|
|
|
|
removes that exact file, then removes the now-empty session directory and
|
|
|
|
|
verifies both are absent. Record only cleanup status and non-secret request
|
|
|
|
|
IDs. Do not recursively remove a shared directory. SIGKILL, host failure or a
|
|
|
|
|
failed trap requires attended residual-file inspection/removal before reuse;
|
|
|
|
|
automatic cleanup is not provided by the current consumer. Unlink is not a
|
|
|
|
|
claim of secure erasure or token revocation. Tmpfs avoids a persistent copy
|
|
|
|
|
but does not replace host custody controls.
|
|
|
|
|
|
|
|
|
|
The implemented `read_strict_token_file` rejects group/other-accessible files,
|
|
|
|
|
files inside Git worktrees and empty inputs. It does not establish parent-path
|
|
|
|
|
ownership, create or delete this file, or prove session cleanup. Those are the
|
|
|
|
|
explicit delivery/operator responsibilities above, retained in the CCR's risk
|
|
|
|
|
section. `KeyCapeServiceAuthProvider.exchange` rereads the file before each
|
|
|
|
|
request and holds the resulting token in memory. Removing it prevents a later
|
|
|
|
|
exchange; already-issued tokens retain their bounded lifetime. Compromise uses
|
|
|
|
|
KeyCape registration disablement and the CCR's coordinated lifecycle procedure.
|
|
|
|
|
|
|
|
|
|
Required live evidence remains: exact authorized identity and mode/location
|
|
|
|
|
checks, one scoped exchange/claim/consume result, wrong-group and sibling-path
|
|
|
|
|
denials, and cleanup after both success and refusal. Existing synthetic component
|
|
|
|
|
evidence verifies the consumer behavior; it is not that live reader receipt.
|
|
|
|
|
|
|
|
|
|
Real human approval follows INFD-WP-0001-T07/T08's reviewed PKCE client; a future
|
|
|
|
|
service requester needs its own narrow registration. The unpresented operator
|
|
|
|
|
reader CCR-2026-0020 was cancelled on owner withdrawal and is not a dependency.
|
|
|
|
|
Audit receiver and sender custody remains AUDIT-WP-0009-T09 / APPROVAL-WP-0002-T01. The live
|
2026-09-09 07:06:05 +02:00
|
|
|
Approval Engine deployment and native OpenBao delivery remain open. Synthetic
|
|
|
|
|
acceptance grants no production access, action approval or model spending.
|
2026-09-09 07:15:22 +02:00
|
|
|
|
2026-09-09 08:55:46 +02:00
|
|
|
SECRETS-WP-0008-T02 now supplies the implemented FLEX-DEC-2026-012 replay
|
|
|
|
|
and approval join. Verify the deployed PDP implements it before live activation.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 2026-09-09 replay correction accepted locally
|
|
|
|
|
|
|
|
|
|
The updated exercise requires `--flex-auth-source` and compiles its actual Go
|
|
|
|
|
evaluator. It no longer substitutes a decision double. Thirteen checks cover
|
|
|
|
|
signed issuer/claim/consume, registry precedence, changed submission replay,
|
|
|
|
|
wrong action, same/different consume digest and actual dual-control policy.
|
|
|
|
|
See `docs/evidence/2026-09-09-replay-contract-exercise.json`. Earlier nine-check
|
|
|
|
|
PDP-double evidence remains historical. Live custody/deployment gates still apply.
|