Ship the live-registration check both blocked tasks depend on
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 45s
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 45s
KEY-WP-0013-T02 and KEY-WP-0014-T04 stay blocked on custody and on ops-warden, but each contains a KeyCape-owned piece that had been left as prose. T02 requires proving "live JWKS verification and denied excess scopes without logging values"; T04 step 4 requires verifying a rotated secret, refusing its predecessor and refusing excess scope. Both were describable and neither was runnable, so the proof would have been improvised by hand against production at the moment custody lands -- the worst possible time for it. keycape verify-client does it in one command. Per registration it checks discovery origin, the client_credentials exchange and its RS256 signature against the deployed JWKS, exact sub/tenant/roles/principal_type, that every -deny-scope is refused, and that the token carries no scope that was not requested. That last check is a real gap: the caller commands prove every requested scope was granted, never that nothing extra came back. -previous-secret-env additionally requires the predecessor to be refused, and treats an unchanged secret as a rotation that did not happen. Nothing is written to disk and no value is printed. Failures name the claim, not the observed value, so running this against production cannot turn a verification into a disclosure. Every check runs before it reports, so one failure does not hide the rest. The exact invocation for each approval client is recorded in the verification block of the provisioning packet, so custody admission hands back a command rather than a description. Tests cover the passing case, an over-broad registration, a live predecessor, an identical rotation and output non-disclosure; neutering mustFail makes the suite fail, so the checks have teeth. Also recorded in KEY-WP-0014: read from ops-warden's catalog rather than waiting for a reply, key-cape-oidc-login is asked-and-waiting on us since 2026-08-28 and is a pointer lane with no programmatic consumers, and the rapp-qonto-keycape-client blocker citing an absent native exchange command went stale when service-token shipped on 2026-09-05. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016uV8zoCKpA1WRAxsKRYbdH Assistant: claude-code Assistant-Model: opus Assistant-Process: 1182213@bnt-lap001 Assistant-Session: 966597b9-ae61-46a4-8b9e-1594ab3ec4ad
This commit is contained in:
parent
54b7687903
commit
2a8735173b
8 changed files with 525 additions and 4 deletions
|
|
@ -39,10 +39,29 @@ human_registration:
|
|||
mfa_required: true
|
||||
client_type: public
|
||||
verification:
|
||||
- Validate the new signature against deployed JWKS and all exact claim bindings.
|
||||
- Reject wrong secrets, operator consume, PEP lifecycle scopes, and human consume.
|
||||
# The first two lines are now one runnable command per client; see
|
||||
# docs/native-authentication.md, "Verifying a live registration". It writes
|
||||
# nothing and prints no value, so it is safe to run against production.
|
||||
- command: |
|
||||
keycape verify-client -issuer https://kc.coulomb.social
|
||||
-client-id secrets-engine-approval -audience approval-engine
|
||||
-scope "approval:read approval:consume"
|
||||
-secret-env KEYCAPE_SECRETS_ENGINE_APPROVAL_CLIENT_SECRET
|
||||
-expect-subject service:secrets-engine -expect-tenant tenant:platform
|
||||
-expect-roles secrets-engine
|
||||
-deny-scope "approval:approve approval:revoke approval:supersede"
|
||||
- command: |
|
||||
keycape verify-client -issuer https://kc.coulomb.social
|
||||
-client-id approval-engine-operator -audience approval-engine
|
||||
-scope "approval:create approval:read approval:approve approval:revoke approval:supersede approval:observe approval:emit"
|
||||
-secret-env KEYCAPE_APPROVAL_ENGINE_OPERATOR_CLIENT_SECRET
|
||||
-expect-subject service:approval-engine-operator -expect-tenant tenant:platform
|
||||
-expect-roles approval-operator
|
||||
-deny-scope "approval:consume"
|
||||
- Check KeyCape and consumer readiness without emitting secrets or tokens.
|
||||
- Preserve existing registrations and signing key; record versions and image digest.
|
||||
- Human consume denial is approval-engine's to verify at its resource; KeyCape
|
||||
proves only that the human client is never issued a consume grant.
|
||||
blockers:
|
||||
- Admit exact custody paths, field delivery, consumer identities and lifecycle authority.
|
||||
- Resolve attended first-provision authority through the custody owner.
|
||||
|
|
|
|||
|
|
@ -85,3 +85,50 @@ provider update, not a wrapper around a raw KV read. The reviewed sequence is:
|
|||
No general rotation command is shipped until that cross-owner transaction has
|
||||
an admitted execution and rollback contract. Calling secrets-engine's KV
|
||||
rotation alone would leave the provider and consumers inconsistent.
|
||||
|
||||
Steps 1-3 are custody's and are not automated here. Step 4, and the denial
|
||||
checks step 5 depends on, are KeyCape's and now have a command.
|
||||
|
||||
## Verifying a live registration
|
||||
|
||||
`keycape verify-client` proves a deployed registration behaves as its contract
|
||||
says, without writing a token file or printing any value. It is the evidence for
|
||||
a rollout (KEY-WP-0013-T02) and for step 4 of the rotation sequence above.
|
||||
|
||||
```
|
||||
keycape verify-client \
|
||||
-issuer https://kc.coulomb.social \
|
||||
-client-id secrets-engine-approval \
|
||||
-audience approval-engine \
|
||||
-scope "approval:read approval:consume" \
|
||||
-secret-env KEYCAPE_SECRETS_ENGINE_APPROVAL_CLIENT_SECRET \
|
||||
-expect-subject service:secrets-engine \
|
||||
-expect-tenant tenant:platform \
|
||||
-expect-roles secrets-engine \
|
||||
-deny-scope "approval:approve approval:revoke"
|
||||
```
|
||||
|
||||
It checks, and prints one `PASS`/`FAIL` line per check:
|
||||
|
||||
- discovery resolves over HTTPS and every endpoint shares the issuer origin;
|
||||
- the `client_credentials` exchange succeeds and its access token verifies RS256
|
||||
against the discovered JWKS, with exact issuer, audience and validity window;
|
||||
- `principal_type` is `service`, and `sub`, `tenant` and `roles` match what the
|
||||
registration declares — exact comparison, no alias;
|
||||
- the token carries **no scope that was not requested**, which the caller
|
||||
commands do not check: they prove every requested scope was granted, not that
|
||||
nothing extra came back;
|
||||
- every `-deny-scope` is refused. A success there is the failure.
|
||||
|
||||
Add `-previous-secret-env` after a rotation to require that the predecessor is
|
||||
refused. It also fails if the predecessor and current values are identical,
|
||||
which means no rotation occurred.
|
||||
|
||||
Every check runs before the command reports, so one failure does not hide the
|
||||
rest; the exit status is non-zero if any failed. Failures name the **claim**,
|
||||
never the observed value — running this against production must not turn a
|
||||
verification into a disclosure. Nothing is written to disk.
|
||||
|
||||
The command needs the client secret in the named environment variable, so it
|
||||
runs wherever custody already delivers that value. It never reads OpenBao or
|
||||
Kubernetes itself.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue