From aba012eba277cd2419f03350feb6d9682b78f278 Mon Sep 17 00:00:00 2001 From: tegwick Date: Sat, 17 Oct 2026 11:59:02 +0200 Subject: [PATCH] NK-WP-0048-T16 r5: 1.3 step text states how each step runs; canon clarifies kernel-dispatched attended steps Folds NK-1.3-C1/C2, RPF-1.3-C1/C2, RAPPS-1.3-C3, KC-1.3-C1 and the OW-1.3-C1 lane names. Parameters untouched (fingerprint unchanged). The execution_descriptors map is regenerated after the adapter changes land. Co-Authored-By: Claude Opus 5.5 Assistant: claude-code Assistant-Model: opus Assistant-Process: 903097@bnt-lap001 Assistant-Session: aa320c8f-c3fa-46e7-8541-0b58e1e45d23 --- canon/standards/security-case-types_v0.1.md | 13 ++ .../1.3/oidc-browser-relying-party/type.yaml | 123 ++++++++++++------ 2 files changed, 96 insertions(+), 40 deletions(-) diff --git a/canon/standards/security-case-types_v0.1.md b/canon/standards/security-case-types_v0.1.md index c0256f0..e2e38e5 100644 --- a/canon/standards/security-case-types_v0.1.md +++ b/canon/standards/security-case-types_v0.1.md @@ -101,6 +101,19 @@ A type is a directory `security-case-types//` in net-kingdom containing standing credential for the step; that would make it a broker (ops-warden ADR-0002). *(Clarified 2026-10-09 from ops-warden's review, WARDEN-WP-0044-T01 R1–R3.)* + + A step listed in the type's `execution_descriptors` is admissible to + ops-warden's case kernel whatever its `warden_executes` value, and the kernel + enforces the step's mode. For an `attended` step, "kernel-dispatched" means + the reviewed adapter runs only inside the attended human session (descriptor + actor `human`, the step's own lane, parent-session revocation per attempt), + never under an agent or a standing credential. The human session is the + authority, and the kernel is only the dispatcher. For an owner-path step + (neither `warden_executes` nor `attended`), the lane must resolve to the + owner's own delegated path, and ops-warden holds no owner credential. + `warden_executes` keeps its caller-proxy meaning above for unattended steps. + *(Clarified 2026-10-17 from the 1.3 owner reviews, NK-1.3-C2 and ops-warden + KERNEL-3.)* - `verification`: the checks that prove a case is live (positive and negative), runnable without reading a secret value - `rollback`: the exact inverse of each step diff --git a/security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml b/security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml index bb76ec7..de1c772 100644 --- a/security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml +++ b/security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml @@ -232,14 +232,15 @@ spec: follow_through: - id: ensure-groups owner: net-kingdom - entry_point: identity-provisioner ensure_service_groups, run inside the provisioner - pod through the session's contained cluster access (groups {access_groups}; - members exactly {initial_members} as recorded in the accepted case, nothing - else) - auth: operator cluster access in the attended register-client session; the in-pod - call uses the provisioner's own directory binding, and no provisioner or user-engine - token is read or sent - lane: key-cape-oidc-login + entry_point: reviewed adapter net-kingdom/keycape-ensure-groups, dispatched by Warden's + case kernel inside the attended session; it calls identity-provisioner + ensure_service_groups in the provisioner pod through the session's contained + cluster access (groups {access_groups}; members exactly {initial_members} as + recorded in the accepted case, nothing else) + auth: the attended human session on lane keycape-attended-case-change (actor human, + parent-session revoked per attempt); the in-pod call uses the provisioner's own + directory binding, and no provisioner or user-engine token is read or sent + lane: keycape-attended-case-change warden_executes: false attended: true value_handling: none @@ -248,14 +249,17 @@ spec: would act as user-engine (ops-warden review R8; canon section 3; ops-warden ADR-0002). Running it in the session register-client already needs adds no attended session. It can become unattended once the provisioner has an operator credential - of its own and an admitted path. + of its own and an admitted path. Warden's case kernel dispatches the reviewed adapter only inside the one attended human session (descriptor actor human, lane keycape-attended-case-change, parent-session revoked per attempt); the human session is the authority and the kernel is only the dispatcher. - id: mint-secret owner: railiance-platform when: client_type == confidential entry_point: 'railiance-platform credential broker: generate a client secret inside - custody at bao:platform/workloads/keycape/{client_id}' - auth: the caller's broker grant for the case's KV path, through its lane; the - value is generated in custody and never returned + custody at bao:platform/workloads/keycape/{client_id}, through reviewed adapter + railiance-platform/keycape-mint-secret' + auth: a human caller (net-kingdom-admins, aal2) starts it; Warden runs it unattended + through the broker lane under the caller's session, with the caller's broker grant + for the case's KV path; agents and root are refused, and the value is generated + in custody and never returned lane: keycape-client-secret-generate warden_executes: true attended: false @@ -263,10 +267,14 @@ spec: - id: deliver-secret owner: railiance-platform when: client_type == confidential - entry_point: ExternalSecret sso/keycape-{client_id}-client (key client-secret) - from bao:platform/workloads/keycape/{client_id} - auth: railiance-platform ESO delivery through role external-secrets-keycape-{client_id}; - no value leaves custody + entry_point: reviewed adapter railiance-platform/keycape-deliver-secret, dispatched + by Warden's case kernel; Platform's deliver capability applies ExternalSecret + sso/keycape-{client_id}-client (key client-secret) through the ESO store for + role external-secrets-keycape-{client_id}, from bao:platform/workloads/keycape/{client_id} + auth: Platform's own delivery path on lane platform-secret-delivery, inside the human + aal2 session; ESO reads custody through role external-secrets-keycape-{client_id}; + Warden holds no Platform credential, and no value leaves custody + lane: platform-secret-delivery warden_executes: false attended: false value_handling: none @@ -275,23 +283,30 @@ spec: - id: bind-env owner: net-kingdom when: client_type == confidential - entry_point: 'patch Deployment sso/keycape: env KEYCAPE_{client_id:env}_CLIENT_SECRET - from sso/keycape-{client_id}-client' - auth: operator kube credentials for namespace sso - lane: key-cape-oidc-login + entry_point: 'reviewed adapter net-kingdom/keycape-bind-env, dispatched by Warden''s + case kernel inside the attended session: patch Deployment sso/keycape env + KEYCAPE_{client_id:env}_CLIENT_SECRET from sso/keycape-{client_id}-client, by + reference only' + auth: the attended human session on lane keycape-attended-case-change, with operator + kube credentials for namespace sso (actor human, parent-session revoked per attempt) + lane: keycape-attended-case-change warden_executes: false attended: true value_handling: none attended_justification: A change to the live identity provider's Deployment. It runs in the same attended operator session as register-client, before it, because KeyCape resolves a confidential client's secret at start and fails closed if - the binding is missing. + the binding is missing. Warden's case kernel dispatches the reviewed adapter only inside the one attended human session (descriptor actor human, lane keycape-attended-case-change, parent-session revoked per attempt); the human session is the authority and the kernel is only the dispatcher. - id: register-client owner: net-kingdom - entry_point: sso-mfa/k8s/keycape/keycape-client.py apply --client-file - --case-acceptance --check-config (net-kingdom.keycape-client-registration) - auth: operator write access to the live KeyCape client registry in namespace sso - lane: key-cape-oidc-login + entry_point: reviewed adapter net-kingdom/keycape-register-client, dispatched by Warden's + case kernel inside the attended session; it runs sso-mfa/k8s/keycape/keycape-client.py + apply --client-file --case-acceptance --check-config + (net-kingdom.keycape-client-registration) + auth: the attended human session on lane keycape-attended-case-change, with operator + write access to the live KeyCape client registry in namespace sso (actor human, + parent-session revoked per attempt) + lane: keycape-attended-case-change warden_executes: false attended: true value_handling: none @@ -303,37 +318,63 @@ spec: authorization and handoff state in memory, so the restart drops in-flight logins and sessions for every client; runs go in a quiet window (key-cape type review R4). The rendered registry must pass KeyCape''s own startup checks (keycape - check-config, key-cape c528daa, which keycape-client.py runs before it patches) + check-config, the pinned checker artifact sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5 + built from key-cape 3c71226, which keycape-client.py runs before it patches) before the restart: a refused entry stops the single replica and every client - loses sign-in (R1).' + loses sign-in (R1). Warden''s case kernel dispatches the reviewed adapter only inside the one attended human session (descriptor actor human, lane keycape-attended-case-change, parent-session revoked per attempt); the human session is the authority and the kernel is only the dispatcher.' - id: deliver-consumer-secret owner: '{consumer_owner_repo}' when: client_type == confidential - entry_point: Owner applies dedicated ServiceAccount {consumer_namespace}/{consuming_service}-keycape-{client_id}-eso + entry_point: Reviewed adapter net-kingdom/keycape-deliver-consumer-secret, dispatched + by Warden's case kernel inside the attended consumer-owner session; the owner's + apply capability creates dedicated ServiceAccount {consumer_namespace}/{consuming_service}-keycape-{client_id}-eso (automount=false, no legacy tokens), namespace SecretStore openbao-keycape-{client_id}-consumer with serviceAccountRef and audiences [openbao], and ExternalSecret {consuming_service}-netkingdom-oidc-client with Owner target, Retain remote deletion and only CLIENT_SECRET from workloads/keycape/{client_id}. Admission validates actual installed ESO TokenRequest support and common controller RBAC before any owner-approved apply. - auth: Consumer owner contained Kubernetes deployment authority; ESO authenticates - to OpenBao role external-secrets-keycape-{client_id}-{consuming_service} with - a TokenRequest for the dedicated namespaced serviceAccountRef; controller remains - trusted + auth: the attended consumer-owner session on lane consumer-owner-deployment (actor + human, the consumer owner's operator deployment authority, on the founder's go + for the change); ESO authenticates to OpenBao role external-secrets-keycape-{client_id}-{consuming_service} + with a TokenRequest for the dedicated namespaced serviceAccountRef; controller + remains trusted + lane: consumer-owner-deployment warden_executes: false - attended: false + attended: true value_handling: none + attended_justification: The consumer owner's only deployment authority in the consumer + namespace is its operator credential, and production applies need the founder's + go per change. No contained agent deployer identity exists, and the owner does + not want a standing credential an agent could trigger. It runs in one attended + consumer-owner session together with hand-over. Warden's case kernel dispatches + the reviewed adapter only inside that session (actor human, parent-session + revoked per attempt); the human session is the authority and the kernel is only + the dispatcher. ready_check: Namespace SecretStore and ExternalSecret both report Ready=True; dedicated SA has no legacy token refs; status and fixed metadata only, never Secret values. - id: hand-over owner: '{consumer_owner_repo}' - entry_point: configure {consuming_service} with issuer {issuer}, client {client_id} - and custody bao:platform/workloads/keycape/{client_id}, and make it require - one of {access_groups} in the groups claim before sign-in completes - auth: the consumer owner's own deployment path + entry_point: The consumer owner first makes its own Helm change configuring {consuming_service} + with issuer {issuer}, client {client_id} and custody bao:platform/workloads/keycape/{client_id}, + requiring one of {access_groups} in the groups claim before sign-in completes. + Then reviewed adapter net-kingdom/keycape-hand-over, dispatched by Warden's case + kernel inside the attended consumer-owner session, verifies the consumer with + four value-free probes (member-login, non-member-refused, membership-add-grants, + membership-remove-revokes) and records the hand-over receipt. + auth: the attended consumer-owner session on lane apps-consumer-handover (actor human, + parent-session revoked per attempt); the consumer configuration itself is the + owner's own deployment path + lane: apps-consumer-handover warden_executes: false - attended: false + attended: true value_handling: none + attended_justification: The verification needs a real member login and a refused + non-member login, which only an attended session can perform, and it follows + the owner's own Helm change in the same consumer-owner session as + deliver-consumer-secret. Warden's case kernel dispatches the reviewed adapter + only inside that session (actor human, parent-session revoked per attempt); the + human session is the authority and the kernel is only the dispatcher. verification: - id: authorize-accepts-redirects check: GET {issuer}/authorize with client {client_id} and each of {redirect_uris} @@ -389,8 +430,10 @@ spec: reads_secret_value: false rollback: - reverses: hand-over - action: remove the OIDC source for {client_id} from {consuming_service} (owner - {consumer_owner_repo}) + action: the adapter removes only its hand-over receipt. Removing the OIDC source + for {client_id} from {consuming_service} is the owner's Helm rollback (owner + {consumer_owner_repo}), done inside deliver-consumer-secret's owner_remove + before any consumer object is removed - reverses: deliver-consumer-secret when: client_type == confidential action: After reverting consumer secret reference, delete only authenticated created