docs: close live approval service registration gate
Assistant: codex Assistant-Model: gpt-5.6-luna Assistant-Session: 01a07ff8-19d0-7820-b4d0-1353833cb7fc
This commit is contained in:
parent
cf23469ead
commit
3a4fd38c00
3 changed files with 275 additions and 83 deletions
|
|
@ -1,89 +1,42 @@
|
|||
# Approval service clients: prepared rollout
|
||||
# Approval service clients: live rollout accepted
|
||||
|
||||
`KEY-WP-0013-T02/T06`, 2026-09-08. Prepared candidate; **not deployed**.
|
||||
KEY-WP-0013-T02 is complete on 2026-09-09. Both verifier-side credential requests,
|
||||
CCR-2026-0017 and CCR-2026-0018, have explicit user approval as platform operator
|
||||
and KeyCape owner and are now verified. [Live receipt](evidence/2026-09-09-keycape-verifier-admission.json).
|
||||
|
||||
The implementation and verification correction are published at `dcebd46`.
|
||||
Forgejo's image is `main-dcebd46`, with immutable manifest digest
|
||||
`sha256:7ff54c54e63ee172ae9e6e7fd2da96e427352f712343d74626ee6fe0f6f82611`.
|
||||
The complete Go suite and `go vet ./...` passed. The registry image was pulled
|
||||
by digest and its `/keycape verify-client --help` command enumerated the expected
|
||||
verification options (the Go flag help path exits 1). The verifier is a keycape
|
||||
subcommand, not a separate `/verify-client` executable. No live credential was
|
||||
used in these checks.
|
||||
Both clients run on one ready KeyCape replica using the immutable image
|
||||
`forgejo.coulomb.social/coulomb/key-cape@sha256:7ff54c54e63ee172ae9e6e7fd2da96e427352f712343d74626ee6fe0f6f82611`.
|
||||
NetKingdom owns the deployed manifest, `Recreate` strategy, `/readyz` readiness,
|
||||
and exact client registrations. Both ESO-owned Secrets are synchronized from
|
||||
OpenBao version 1. The signing key and unrelated configuration bytes are preserved.
|
||||
|
||||
Negative evidence now requires the exact token endpoint, issuer error and
|
||||
feature: scope rejection is HTTP 400 / `invalid_profile_usage` / `scope`;
|
||||
predecessor rejection is HTTP 401 / `invalid_profile_usage` / `Authorization`.
|
||||
A timeout, server error, invalid token, JWKS failure or unrelated refusal fails
|
||||
verification. Provider response bodies and tokens are never printed.
|
||||
Both clients passed the native verifier from that pinned image and independent
|
||||
RS256/JWKS verification with exact issuer, audience, subject, tenant, roles,
|
||||
scopes and a 900-second lifetime. Wrong secrets and excess scopes were refused;
|
||||
the human OpenBao client was refused `approval:consume`. Existing human OpenBao
|
||||
login passed before and after cutover, with attended sessions self-revoked.
|
||||
|
||||
## Admission inputs
|
||||
The deployment owner exercised compatible rollback and version-preserving
|
||||
resume. Its executable procedure and reproducible synthetic HTTPS exercise live
|
||||
in `net-kingdom:sso-mfa/k8s/keycape/`, documented in
|
||||
`net-kingdom:docs/keycape-approval-clients-rollout.md`. The verifier runs inside
|
||||
the existing attended owner process; credentials stay in memory and a child
|
||||
environment. The pod's internal-only egress policy remains unchanged.
|
||||
|
||||
1. Platform `CCR-2026-0017` and `CCR-2026-0018` remain proposed. Their named
|
||||
platform-operator and key-cape-owner reviews precede any activation.
|
||||
[provisioning request](approval-engine-provisioning-request.yaml) records the
|
||||
exact paths, `CLIENT_SECRET` field and Kubernetes/env mappings.
|
||||
2. The actual signed upstream issuer is now proved as
|
||||
**`https://auth.coulomb.social`** by the admitted one-shot probe on 2026-09-08
|
||||
at 21:44:44 UTC. Signature, audience, validity window and nonce passed; the
|
||||
Job exited 0 and every temporary resource was removed. See
|
||||
[the live receipt](evidence/2026-09-08-upstream-issuer-live-proof.json).
|
||||
NetKingdom pinned that exact value in `authelia.issuer` on 2026-09-09
|
||||
through its exercised issuer-only helper; Secret revision `58713343`.
|
||||
[Pin receipt](evidence/2026-09-09-upstream-issuer-pin.json) proves all other
|
||||
config bytes and Secret data unchanged. The running Deployment was unchanged. Prove an existing
|
||||
human login before and after cutover; the probe established upstream identity
|
||||
only, while this image also changes redirect and grant binding.
|
||||
3. The platform-owned attended first-provision command uses only
|
||||
`warden access openbao-platform-admin-login --exec -- <reviewed-command>`
|
||||
with its unique metadata-only receipt. It must follow the two CCRs and the
|
||||
platform rollout document. Do not execute the child directly. The unrelated
|
||||
interim credential proxy's explicit policy 403 remains WARDEN-WP-0039-T03;
|
||||
an outage fallback is not permission to get credentials for this rollout.
|
||||
Containerd readiness matches the manifest in `imageID`. Independent JWT checks
|
||||
use the native contract's 30-second future-iat bound while retaining strict
|
||||
not-before and expiry. Natural JWT expiry and actual predecessor rotation were
|
||||
not exercised by this initial provision and remain required for their relevant
|
||||
subsequent owner acceptance.
|
||||
|
||||
## Prepared deployment change
|
||||
The original [candidate patch](approval-clients-deployment.patch.yaml) remains a
|
||||
preparation reference; NetKingdom's manifest is deployment authority. Candidate
|
||||
Go tests and vet passed previously; this window added eight real local OpenBao
|
||||
checks, ten configuration/recovery/clock checks, 53 credential-change regressions,
|
||||
and a complete pinned-image HTTPS/native-verifier exercise with synthetic keys.
|
||||
|
||||
[approval-clients-deployment.patch.yaml](approval-clients-deployment.patch.yaml)
|
||||
is a strategic merge patch for `sso/keycape`, whose deployment source remains
|
||||
`net-kingdom/sso-mfa/k8s/keycape/deployment.yaml`. Transfer the reviewed changes
|
||||
into that owning source at the admitted cutover; this file is a preparation,
|
||||
not a second deployment authority.
|
||||
|
||||
It pins the image, adds the two exact Secret references and uses `/readyz` for
|
||||
traffic readiness while retaining `/healthz` for liveness. `Recreate` ensures
|
||||
one issuer instance during replacement: [operations.md](operations.md) states
|
||||
that login sessions and authorization codes are process-local. The old manifest's
|
||||
default rolling surge can briefly create two instances. Expect a short login
|
||||
interruption and loss of in-flight login sessions during replacement.
|
||||
|
||||
Apply the platform policies/roles, CAS=0 initial custody, two stores and two
|
||||
ExternalSecrets in their owner-prescribed order; require Valid/SecretSynced
|
||||
before deploying the reading image and updated client config together. Do not
|
||||
pre-create ESO-owned Secrets. A dry-run of this patch proves API acceptance of
|
||||
the object shape only; it cannot prove the referenced Secrets or configuration.
|
||||
|
||||
## Acceptance and rollback
|
||||
|
||||
Run the two `verify-client` commands from the provisioning request through the
|
||||
admitted protected delivery. In addition to the tool's checked signature,
|
||||
issuer, audience, subject, tenant, scopes and required roles, record independent
|
||||
verification of the exact role set and `exp - iat = 900`: the tool's
|
||||
`expect-roles` checks inclusion and does not itself establish the 15-minute
|
||||
lifetime. Initial provisioning has no real predecessor to revoke; test a wrong
|
||||
secret and retain predecessor rejection for an actual subsequent rotation.
|
||||
Retain expiry/revocation and excessive-scope evidence, and platform cross-path,
|
||||
wrong-service-account, outside-namespace and parent-listing denials.
|
||||
|
||||
Before mutation the deployment/configuration owner retains the previous image,
|
||||
configuration revision and Secret metadata through its protected recovery path.
|
||||
On failed readiness or existing-human-login regression, restore that compatible
|
||||
configuration/image pair (`main-153258b` is the observed prior image), then
|
||||
follow the platform document's ExternalSecret/policy rollback. An image-only
|
||||
revert does not restore the changed client configuration. Keep custody versions
|
||||
until registrations are explicitly disabled; never destroy as incidental cleanup.
|
||||
|
||||
These CCRs supply **verifier-side copies only**. Client-side read lanes, the
|
||||
approval audit sender credential and live claim/consume proof remain open in
|
||||
RPF-WP-0035-T05, AUDIT-WP-0009-T09, APPROVAL-WP-0002 and GLAS-WP-0015's
|
||||
existing coordination chain. The human UI's real callback and MFA proof remain
|
||||
KEY-WP-0013-T05; they do not block these independent service registrations.
|
||||
KEY-WP-0013-T05 retains the real approval UI callback and MFA/PKCE proof.
|
||||
RPF-WP-0035-T05 retains separate client-side credential delivery; AUDIT-WP-0009-T09
|
||||
retains audit custody. APPROVAL-WP-0002 and SECRETS-WP-0009-T03 retain deployed
|
||||
claim/consume, expiry/revocation and native credential delivery. These verifier
|
||||
copies do not grant client retrieval or factory execution/spending.
|
||||
|
|
|
|||
220
docs/evidence/2026-09-09-keycape-verifier-admission.json
Normal file
220
docs/evidence/2026-09-09-keycape-verifier-admission.json
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
{
|
||||
"schema": "helixforge.keycape-verifier-admission.v1",
|
||||
"recorded_at": "2026-09-09T00:14:27.155338+00:00",
|
||||
"approval": {
|
||||
"schema": "railiance.keycape-custody-user-approval.v1",
|
||||
"recorded_at": "2026-09-08T23:05:19.886216+00:00",
|
||||
"user_response": "I approve, go on.",
|
||||
"approved_question": "Do you approve CCR-2026-0017 and CCR-2026-0018, as platform operator and KeyCape owner, for the verifier-side credential delivery described in the review packet?",
|
||||
"review_packet_revision": "52b24eab9a8aff3396e71b2296d0240a44f3b0db",
|
||||
"roles": [
|
||||
"platform-operator",
|
||||
"key-cape-owner"
|
||||
],
|
||||
"scope": "Two verifier-side KeyCape client credential custody requests, including their governed attended provisioning and compatible rollout sequence.",
|
||||
"client_side_read_authorized": false,
|
||||
"factory_spending_authorized": false,
|
||||
"requests": [
|
||||
{
|
||||
"id": "CCR-2026-0017",
|
||||
"decision_id": "b533a271-b704-4c5c-98a2-9a5951aadfb6",
|
||||
"status": "approved",
|
||||
"decision_status": "resolved",
|
||||
"reviewed_roles": [
|
||||
"platform-operator",
|
||||
"key-cape-owner"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "CCR-2026-0018",
|
||||
"decision_id": "efa90517-0cae-4eb6-a68d-5b0489c84d65",
|
||||
"status": "approved",
|
||||
"decision_status": "resolved",
|
||||
"reviewed_roles": [
|
||||
"platform-operator",
|
||||
"key-cape-owner"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"activation": {
|
||||
"schema": "platform.keycape-approval-custody.v1",
|
||||
"status": "custody_and_service_acceptance_passed_pending_fresh_human_login",
|
||||
"lanes": [
|
||||
{
|
||||
"ccr": "CCR-2026-0017",
|
||||
"source_sha256": "dc2bbd86e213edecc2f2f91a99d0f51136e9a0853a90e55f65dda2b942305416",
|
||||
"custody_seeded": false,
|
||||
"existing_version_reused": true,
|
||||
"kv_version": 1,
|
||||
"initial_request_id": "94c98373-33fa-591d-c201-d36dddf9879b",
|
||||
"policy_applied": false,
|
||||
"role_applied": true,
|
||||
"native_reader_verified": true,
|
||||
"cross_path_denied": true,
|
||||
"parent_listing_denied": true,
|
||||
"auth_ttl": 900,
|
||||
"reader_revocation_verified": true,
|
||||
"wrong_service_account_denied": true,
|
||||
"coding_agent_deny_wins": true,
|
||||
"store_ready": true,
|
||||
"external_secret_ready": true,
|
||||
"delivery_matches": true,
|
||||
"secret_uid": "eaa28bdf-04af-4e4e-a1fc-fe395c7689a7",
|
||||
"secret_resource_version": "58747058",
|
||||
"wrong_namespace_denied": true,
|
||||
"outside_namespace_store_denied": true
|
||||
},
|
||||
{
|
||||
"ccr": "CCR-2026-0018",
|
||||
"source_sha256": "99148133ddf1eb15dce3c03862b51dc001818ca3121045035a0b0e5897c55dad",
|
||||
"custody_seeded": false,
|
||||
"existing_version_reused": true,
|
||||
"kv_version": 1,
|
||||
"initial_request_id": "a62ee5c5-b5ba-b7dc-0f51-e6fd8a6dde32",
|
||||
"policy_applied": false,
|
||||
"role_applied": true,
|
||||
"native_reader_verified": true,
|
||||
"cross_path_denied": true,
|
||||
"parent_listing_denied": true,
|
||||
"auth_ttl": 900,
|
||||
"reader_revocation_verified": true,
|
||||
"wrong_service_account_denied": true,
|
||||
"coding_agent_deny_wins": true,
|
||||
"store_ready": true,
|
||||
"external_secret_ready": true,
|
||||
"delivery_matches": true,
|
||||
"secret_uid": "599a61a7-8244-4618-8c44-6473195bbe4e",
|
||||
"secret_resource_version": "58747062",
|
||||
"wrong_namespace_denied": true,
|
||||
"outside_namespace_store_denied": true
|
||||
}
|
||||
],
|
||||
"started_at": "2026-09-09T00:09:28.852280+00:00",
|
||||
"credential_values_emitted": false,
|
||||
"client_side_read_admitted": false,
|
||||
"keycape_rollout_completed": true,
|
||||
"issuer_pin_revision": "58746187",
|
||||
"phase": "awaiting_fresh_human_login",
|
||||
"custody_versions_unchanged": true,
|
||||
"namespace_probe_cleanup_requested": true,
|
||||
"keycape": {
|
||||
"existing_human_login_before": true,
|
||||
"protected_recovery_retained": true,
|
||||
"deployment_uid": "99ddd83c-cb3f-4847-bcf8-35f1aa87627f",
|
||||
"generation": 38,
|
||||
"pod_uid": "8d62e972-d466-413e-a2e4-08b9a08016bc",
|
||||
"single_ready_replica": true,
|
||||
"acceptance_phase": "passed",
|
||||
"clients": [
|
||||
{
|
||||
"client_id": "secrets-engine-approval",
|
||||
"live_jwks_signature_verified": true,
|
||||
"exact_claims_verified": true,
|
||||
"lifetime_seconds": 900,
|
||||
"maximum_future_iat_seconds": 30,
|
||||
"expiry_leeway_seconds": 0,
|
||||
"excess_scope_denied": true,
|
||||
"wrong_secret_denied": true,
|
||||
"pinned_artifact_verifier_passed": true,
|
||||
"verifier_location": "attended owner process",
|
||||
"verifier_sha256": "4bf93bbe9afe0bf2e21d51c03373a7e2b4be6864586bcc2d472eeb9abc913d92",
|
||||
"real_predecessor_rotation_tested": false,
|
||||
"observed_wall_clock_expiry": false
|
||||
},
|
||||
{
|
||||
"client_id": "approval-engine-operator",
|
||||
"live_jwks_signature_verified": true,
|
||||
"exact_claims_verified": true,
|
||||
"lifetime_seconds": 900,
|
||||
"maximum_future_iat_seconds": 30,
|
||||
"expiry_leeway_seconds": 0,
|
||||
"excess_scope_denied": true,
|
||||
"wrong_secret_denied": true,
|
||||
"pinned_artifact_verifier_passed": true,
|
||||
"verifier_location": "attended owner process",
|
||||
"verifier_sha256": "4bf93bbe9afe0bf2e21d51c03373a7e2b4be6864586bcc2d472eeb9abc913d92",
|
||||
"real_predecessor_rotation_tested": false,
|
||||
"observed_wall_clock_expiry": false
|
||||
}
|
||||
],
|
||||
"acceptance_client": "approval-engine-operator",
|
||||
"human_client_consume_denied": true,
|
||||
"status": "service_acceptance_passed_pending_fresh_human_login",
|
||||
"image": "forgejo.coulomb.social/coulomb/key-cape@sha256:7ff54c54e63ee172ae9e6e7fd2da96e427352f712343d74626ee6fe0f6f82611",
|
||||
"config_resource_version": "58747126",
|
||||
"signing_key_unchanged": true,
|
||||
"unrelated_config_bytes_preserved": true,
|
||||
"existing_human_login_after": false
|
||||
},
|
||||
"finished_at": "2026-09-09T00:10:09.099481+00:00"
|
||||
},
|
||||
"post_rollout_login": {
|
||||
"schema": "netkingdom.keycape-approval-rollout.v1",
|
||||
"status": "service_and_existing_human_login_acceptance_passed",
|
||||
"values_emitted": false,
|
||||
"deployment_uid": "99ddd83c-cb3f-4847-bcf8-35f1aa87627f",
|
||||
"generation": 38,
|
||||
"pod_uid": "8d62e972-d466-413e-a2e4-08b9a08016bc",
|
||||
"single_ready_replica": true,
|
||||
"acceptance_phase": "passed",
|
||||
"clients": [
|
||||
{
|
||||
"client_id": "secrets-engine-approval",
|
||||
"live_jwks_signature_verified": true,
|
||||
"exact_claims_verified": true,
|
||||
"lifetime_seconds": 900,
|
||||
"maximum_future_iat_seconds": 30,
|
||||
"expiry_leeway_seconds": 0,
|
||||
"excess_scope_denied": true,
|
||||
"wrong_secret_denied": true,
|
||||
"pinned_artifact_verifier_passed": true,
|
||||
"verifier_location": "attended owner process",
|
||||
"verifier_sha256": "4bf93bbe9afe0bf2e21d51c03373a7e2b4be6864586bcc2d472eeb9abc913d92",
|
||||
"real_predecessor_rotation_tested": false,
|
||||
"observed_wall_clock_expiry": false
|
||||
},
|
||||
{
|
||||
"client_id": "approval-engine-operator",
|
||||
"live_jwks_signature_verified": true,
|
||||
"exact_claims_verified": true,
|
||||
"lifetime_seconds": 900,
|
||||
"maximum_future_iat_seconds": 30,
|
||||
"expiry_leeway_seconds": 0,
|
||||
"excess_scope_denied": true,
|
||||
"wrong_secret_denied": true,
|
||||
"pinned_artifact_verifier_passed": true,
|
||||
"verifier_location": "attended owner process",
|
||||
"verifier_sha256": "4bf93bbe9afe0bf2e21d51c03373a7e2b4be6864586bcc2d472eeb9abc913d92",
|
||||
"real_predecessor_rotation_tested": false,
|
||||
"observed_wall_clock_expiry": false
|
||||
}
|
||||
],
|
||||
"acceptance_client": "approval-engine-operator",
|
||||
"human_client_consume_denied": true,
|
||||
"existing_human_login_after": true,
|
||||
"receipt_written_at": "2026-09-09T00:11:59.113549+00:00"
|
||||
},
|
||||
"attended_envelope": {
|
||||
"all_attempted_sessions_revoked": true,
|
||||
"successful_activation_exit_code": 0,
|
||||
"post_rollout_login_exit_code": 0
|
||||
},
|
||||
"validation": {
|
||||
"local_openbao_tests": 8,
|
||||
"configuration_and_recovery_tests": 10,
|
||||
"credential_change_tests": 53,
|
||||
"pinned_image_synthetic_https_and_native_verifier": "passed"
|
||||
},
|
||||
"failed_attempt_receipts": [
|
||||
"net-kingdom:docs/evidence/2026-09-09-keycape-activation-attempts.json"
|
||||
],
|
||||
"limits": {
|
||||
"client_side_read_admitted": false,
|
||||
"factory_spending_admitted": false,
|
||||
"wall_clock_jwt_expiry_observed": false,
|
||||
"actual_predecessor_rotation_observed": false
|
||||
},
|
||||
"temporary_probe_namespaces_remaining": 0,
|
||||
"owner_manifest_api_defaults_match_live": true
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue