diff --git a/credential-change-requests/CCR-2026-0060-nk-bind-executor-approval-keycape-eso-reader.yaml b/credential-change-requests/CCR-2026-0060-nk-bind-executor-approval-keycape-eso-reader.yaml new file mode 100644 index 0000000..959d371 --- /dev/null +++ b/credential-change-requests/CCR-2026-0060-nk-bind-executor-approval-keycape-eso-reader.yaml @@ -0,0 +1,98 @@ +id: CCR-2026-0060 +kind: credential-change-request +schema_version: 1 +request_type: workload-kv-read +title: 'nk-bind-executor-approval: keycape-reader-policy and eso-role (oidc-service-client@0.1)' +status: proposed +created: '2026-10-31' +updated: '2026-10-31' +requester: + agent: railiance-platform + reason: 'NK-WP-0056-T14 first case of SecurityCaseType oidc-service-client 0.1 (net-kingdom + 5d5f15d, proposed; hub decision f8044f81; memo nk-wp-0056-t14-oidc-service-client-r1). + Drafted by the platform owner in its review (docs/credential-lane-designs/oidc-service-client-review.md, + R6). KeyCape reads the client secret of nk-bind-executor-approval only through ESO, as + CCR-2026-0041 for the browser type. Not approved.' +review: + required: true + required_approvers: + - platform-owner + comments: [] +target: + domain: infotech + tenant: platform + workload: keycape + environment: production + purpose: KeyCape reads the nk-bind-executor-approval client secret only through ESO. No metadata, + list, parent or sibling access. +openbao: + mount: platform + kv_path: platform/workloads/keycape/nk-bind-executor-approval + fields: + - CLIENT_SECRET + policy_name: workload-kv-read-keycape-nk-bind-executor-approval-eso + policy_file: openbao/policies/workload-kv-read-keycape-nk-bind-executor-approval-eso.hcl + metadata_read: false + token_self_lifecycle: true + auth: + method: kubernetes + mount: kubernetes + role: external-secrets-keycape-nk-bind-executor-approval + audience: openbao + ttl: 15m + token_max_ttl: 15m + token_explicit_max_ttl: 15m + token_no_default_policy: true + policies: + - workload-kv-read-keycape-nk-bind-executor-approval-eso + bound_claims: + service_account_names: + - external-secrets + service_account_namespaces: + - external-secrets + bound_claims_confirmed: false +access_frontdoor: + type: external-secrets + catalog_id: bounded-executor-keycape-nk-bind-executor-approval + readiness: pending-review + resolvable: false +risk: + classification: high + notes: + - Rendered from a proposed SecurityCaseType; the type is not yet acknowledged and the case + is not yet accepted. + - Exact read on one KV path; no metadata, list, parent or sibling access. + - The value lets its holder obtain approval-engine tokens as service:nk-bind-executor + (approval:create/read/consume, tenant:platform). + - Only ExternalSecrets in namespace sso may use the store; sso namespace writers are + within this boundary (as CCR-2026-0033/0041). +verification: + positive: + - policy workload-kv-read-keycape-nk-bind-executor-approval-eso grants read on + platform/data/workloads/keycape/nk-bind-executor-approval only (capabilities check) + - ExternalSecret sso/keycape-nk-bind-executor-approval-client reports Ready=True (status + only) + negative: + - a default-only identity has no read capability on the path + - sibling GET, parent LIST and metadata read are denied + - wrong ServiceAccount and wrong audience logins are denied + activation_conditions: + - oidc-service-client 0.1 acknowledged by the founder (informed-decision memo, hub decision + f8044f81). + - SecurityCase nk-bind-executor-approval accepted by owner and founder. + - Custody entry minted at version 1 by keycape-client-secret-generate, after the broker + admits the type (review R1). + - platform-owner approval of this exact request and bound_claims_confirmed set to true. +lifecycle: + deactivate: delete ExternalSecret sso/keycape-nk-bind-executor-approval-client, then role + external-secrets-keycape-nk-bind-executor-approval, then the policy (case rollback) + rotate: no rotation lane exists yet (RPF-WP-0055 residual); until then a rotation is an + attended platform-admin write with CAS=current, an ESO refresh and a KeyCape restart + compromised: owner incident process; delete the role first; no automatic read, reseed or + cleanup +security_case: + case: nk-bind-executor-approval + type: oidc-service-client@0.1 + type_status: proposed + type_source: net-kingdom security-case-types/oidc-service-client/type.yaml (5d5f15d) + acceptance_record: pending diff --git a/credential-change-requests/CCR-2026-0061-nk-bind-executor-approval-consumer-reader.yaml b/credential-change-requests/CCR-2026-0061-nk-bind-executor-approval-consumer-reader.yaml new file mode 100644 index 0000000..6349516 --- /dev/null +++ b/credential-change-requests/CCR-2026-0061-nk-bind-executor-approval-consumer-reader.yaml @@ -0,0 +1,127 @@ +id: CCR-2026-0061 +kind: credential-change-request +schema_version: 1 +request_type: workload-kv-read +title: 'nk-bind-executor-approval: consumer reader for sso/nk-bind-executor (oidc-service-client@0.1)' +status: proposed +created: '2026-10-31' +updated: '2026-10-31' +requester: + agent: railiance-platform + reason: 'NK-WP-0056-T14 first case of SecurityCaseType oidc-service-client 0.1 (net-kingdom + 5d5f15d, proposed; hub decision f8044f81). The bounded executor service:nk-bind-executor + reads its approval-engine client secret natively per call to file, read and consume its + own approvals. Drafted by the platform owner in its review + (docs/credential-lane-designs/oidc-service-client-review.md, R2-R6). Not approved.' +review: + required: true + required_approvers: + - platform-owner + - net-kingdom-owner + comments: [] +target: + domain: infotech + tenant: platform + workload: net-kingdom-bind-executor + rapp: rapp-bounded-executor + environment: production + purpose: Let only the executor ServiceAccount sso/nk-bind-executor read the + nk-bind-executor-approval CLIENT_SECRET in memory for its client_credentials exchange; + no write, metadata, list, sibling path, Secret copy or other identity +admission: + standard: net-kingdom canon/standards/bounded-execution-admission_v0.1.md (proposed) + executor_identity: service:nk-bind-executor + note: The role does not evaluate the admission. It bounds what the ServiceAccount can read + to one client secret whose tokens carry approval:create/read/consume at approval-engine. +openbao: + mount: platform + kv_path: platform/workloads/keycape/nk-bind-executor-approval + fields: + - CLIENT_SECRET + policy_name: workload-kv-read-keycape-nk-bind-executor-approval-bounded-executor + policy_file: openbao/policies/workload-kv-read-keycape-nk-bind-executor-approval-bounded-executor.hcl + metadata_read: false + token_self_lifecycle: true + auth: + method: kubernetes + mount: kubernetes + # Review R4: role name ends with -workload-kv-read so the delegated applier admits it; + # the type r1 template (role name = policy name) would be refused. + role: keycape-nk-bind-executor-approval-bounded-executor-workload-kv-read + audience: openbao + ttl: 15m + token_max_ttl: 15m + token_explicit_max_ttl: 15m + token_no_default_policy: true + policies: + - workload-kv-read-keycape-nk-bind-executor-approval-bounded-executor + bound_claims: + service_account_names: + - nk-bind-executor + service_account_namespaces: + - sso + bound_claims_confirmed: false +access_frontdoor: + type: platform-owner + catalog_id: bounded-executor-keycape-nk-bind-executor-approval + readiness: pending-review + resolvable: false +agent_fence: + data_and_metadata_denied: true + note: Coding agents are denied data and metadata of platform/workloads/keycape/nk-bind-executor-approval; + setup checks the deny before the role is created. +lifecycle: + role_window: Standing, as CCR-2026-0059. The executor exchanges a fresh token per call and + keeps neither the secret nor a token beyond the call. + deactivate: Delete the role, then the policy. The custody entry and the ESO reader stay + under CCR-2026-0060 until the case rollback. + rotate: No rotation lane exists yet (RPF-WP-0055 residual). The reader needs no change on + rotation because the path stays the same. + compromised: Remove client nk-bind-executor-approval from KeyCape (register-client rollback, + attended), delete this role, then rotate attended. Approvals already created by the + client still need a human entry under human_control. +delivery: + surface: native-workload-login + namespace: sso + service_account: nk-bind-executor + reader: Executor runtime (net-kingdom bounded-executor/, deployed by rapp-bounded-executor); + logs in with its projected ServiceAccount token, audience openbao, separately from the + CCR-2026-0058 and CCR-2026-0059 roles; the value stays in memory and never reaches a + Secret, log or receipt +risk: + classification: high + notes: + - Reachable value is one confidential client secret; its tokens act as service:nk-bind-executor + at approval-engine (create approvals naming itself, read and consume approvals of + tenant:platform). Approvals still need a human entry under human_control. + - The role is separate from the CCR-2026-0058 write role, the CCR-2026-0059 journey reader + and the CCR-2026-0060 ESO reader; policies are never combined in one role. + - No Secret copy in namespace sso beyond KeyCape's own ESO target; other workloads in sso + without the ServiceAccount cannot log in. + - Node root, cluster admins able to mint tokens for sso/nk-bind-executor and platform-admin + remain inside this credential boundary. +verification: + positive: + - Exact policy and role read back; login as sso/nk-bind-executor (audience openbao) gets + only this policy with a lease of at most 15m + - The executor reports the claims of one client_credentials exchange per scope (sub + service:nk-bind-executor, aud approval-engine, tenant:platform, lifetime <= 15m), value-free + negative: + - Write, metadata read, list, delete and sibling paths (including keycape/secrets-engine-openbao + and directory-binds) are refused + - Wrong ServiceAccount, wrong namespace and wrong audience logins are denied + - Coding-agent boundary denies data and metadata of the path + activation_conditions: + - oidc-service-client acknowledged by the founder and case nk-bind-executor-approval accepted. + - Custody entry minted (CCR-2026-0040 lane, after broker admission R1). + - Explicit platform-owner and net-kingdom-owner approval of this exact request. + - bound_claims_confirmed set to true by the platform-owner (ServiceAccount sso/nk-bind-executor + already exists, uid bd15569c, CCR-2026-0058/0059). + - Attended platform-admin (aal2) session or the delegated applier applies policy and role + from a reviewed commit; positive and negative results recorded in value-free receipts. +security_case: + case: nk-bind-executor-approval + type: oidc-service-client@0.1 + type_status: proposed + type_source: net-kingdom security-case-types/oidc-service-client/type.yaml (5d5f15d) + acceptance_record: pending diff --git a/docs/credential-lane-designs/oidc-service-client-review.md b/docs/credential-lane-designs/oidc-service-client-review.md new file mode 100644 index 0000000..a4b9d69 --- /dev/null +++ b/docs/credential-lane-designs/oidc-service-client-review.md @@ -0,0 +1,162 @@ +# oidc-service-client 0.1: custody lane and consumer role (owner review, railiance-platform) + +Owner review from railiance-platform for net-kingdom's proposed SecurityCaseType `oidc-service-client` 0.1 +(net-kingdom `security-case-types/oidc-service-client/`, commit 5d5f15d; hub decision `f8044f81`; memo +`nk-wp-0056-t14-oidc-service-client-r1`). Request: State Hub message `4ec57765`. Per canon +security-case-types_v0.1 section 8 this review comes before the founder acknowledges the type. + +Everything here is source review and proposed drafts. No OpenBao role, policy, KV entry or Secret was +created, read or changed. + +## Verdict + +**Shape accepted with changes.** The custody path family, the mint grant and the consumer-role shape fit +railiance-platform's contracts. Four type-side corrections are needed (R3, R4, R5, R7), which makes this a +memo r2. One platform-side change is needed before the first mint (R1). Each case needs its own CCRs (R6). +For the first case they are drafted: CCR-2026-0060 (ESO reader) and CCR-2026-0061 (consumer reader), both +`proposed`. + +## Findings + +### R1. Mint lane: the OpenBao grant covers the path, the broker does not admit the type yet. Verdict: platform change required + +- The grant (CCR-2026-0040, policy `keycape-client-secret-generate`) is create-only on + `platform/data/workloads/keycape/+`. It denies exactly the reserved `secrets-engine-openbao`. + `nk-bind-executor-approval` is one segment, matches the type's `client_id` pattern (the same pattern as + the browser type) and is not reserved. The issuer binding (CCR-2026-0043, net-kingdom-admins + aal2) + covers it too. **At the OpenBao layer no new grant, policy or CCR is needed** for service-client paths. +- The broker entry point `scripts/keycape_client_secret_generate.py` refuses the case today, for three + reasons: + - `ACKNOWLEDGED_TYPES` admits only `oidc-browser-relying-party@1.1`. The result is + `case_type_unacknowledged`. + - It requires `parameters.client_type == confidential`, but the service type has no `client_type` + parameter, because it is confidential by construction. The result is `case_client_not_confidential`. + - It requires case status `materialized`. +- Needed: an owner-reviewed source change that adds `oidc-service-client@` to + `ACKNOWLEDGED_TYPES`, with its type-byte and `PARAMETER_PINS` fingerprint. The change must make the + confidential check type-specific: the service type is implicitly confidential. Refusal tests must cover an + unacknowledged service type, a drifted source and a client_id mismatch. It follows the RPF-WP-0060-T01 + rule: no speculative pin before genuine acknowledgment. It can therefore only land after the founder + acknowledges the type. Until then, `mint-secret` for this case refuses (fail closed). +- The KeyCape secret format (48 bytes, URL-safe, confirmed by key-cape for confidential clients) applies + unchanged to `client_credentials` clients. + +### R2. Consumer-reader role shape. Verdict: consistent + +The role shape matches the one-role-per-lane rule and the CCR-2026-0058/0059 shape: + +- Kubernetes auth; +- one ServiceAccount and one namespace; +- audience `openbao`; +- `ttl`, `token_max_ttl` and `token_explicit_max_ttl` all 15m; +- `token_no_default_policy`; +- exactly one policy. + +It is a separate role from the executor's CCR-2026-0058 write role, the CCR-2026-0059 journey reader and the +ESO reader. The policies are never combined. `scripts/credential-change.py` enforces this: the validator +requires `policies == [policy_name]`, and the applier requires "only openbao.policy_name". + +The `auth/token/lookup-self` (read) and `auth/token/revoke-self` (update) stanzas are **correct and +expected**. They are exactly what `token_self_lifecycle: true` generates (`generated_policy_hcl`), and they +match CCR-2026-0033's amendment, CCR-2026-0041/0042, CCR-2026-0059 and the 0058 write policy. Together they let +the reader end its own token after each call. The browser type omitted them, and the platform had to add them +itself for CCR-2026-0042. The service type gets this right. + +Minor wording: verification `custody-allowed-to-consumer` says the policy grants read on the path "and +nothing else". It should say "and nothing else besides its own token's lookup-self and revoke-self". (Type +text; fold into r2.) + +### R3. ESO reader (keycape-reader-policy, eso-role) lags behind the platform shape. Verdict: change the type (r2) + +The type inherited the browser type's bodies: + +- the policy has no self-lifecycle stanzas; +- the role has no `audience: openbao` and no `token_explicit_max_ttl`. + +The applied browser instance (CCR-2026-0041) and CCR-2026-0033 carry `token_self_lifecycle: true`, +`audience: openbao` and `token_explicit_max_ttl: 15m`. A policy rendered from the type as written would fail +the applier's "policy_file does not match generated CCR policy" check. Align both bodies with the consumer +reader. The same gap exists in `oidc-browser-relying-party`; that is a separate note for its owner, not a +blocker here. + +### R4. Consumer role name: the delegated applier refuses the template. Verdict: change the type (r2) + +The type names the role the same as the policy: `workload-kv-read-keycape-{client_id}-{consuming_service}`. +The delegated applier (`applier_policy_violations`) admits Kubernetes roles only if they end with +`-workload-kv-read` or `-secrets-read`, or start with `external-secrets-`. As written, every case could only +be applied by an attended out-of-band tool. CCR-2026-0059's role was applied that way: its name fails the same +check, and it was applied through net-kingdom `executor-lanes.py`. + +Proposed template: `keycape-{client_id}-{consuming_service}-workload-kv-read`. The policy name stays as it +is, because it satisfies the required `workload-kv-read-` prefix. The `hand-over` step, the +`consumer-login-bound` verification and the rollback text must use the role name. CCR-2026-0061 is drafted +with the proposed name, and `applier_policy_violations` returns no violations for it. + +### R5. Rotation is not mint-secret. Verdict: change the type text (r2) + +The README ("Rotation is mint-secret, then the consumer's next exchange") and the catalog entry ("rotation: +mint-secret then verification") are wrong for this lane. The grant is create-only (CAS 0). It refuses an +existing entry with exit 3 and can never write a second version. No rotation lane exists: + +- RPF-WP-0054-T07 was cancelled as transferred; +- the follow-up is the residual RPF-WP-0055, which needs its own CAS/update authority and ESO and KeyCape + coordination. + +State it plainly: until that lane exists, rotation is an attended platform-admin write with CAS=current, +followed by an ESO refresh and a KeyCape restart. The consumer needs no change, because the path stays the +same. + +### R6. Each case needs its own CCRs; the type does not stand in. Verdict: per-case CCRs required (drafted for the first case) + +This is the same as for browser cases. The forgejo-netkingdom-oidc case got its own CCR-2026-0041 (ESO +reader) and CCR-2026-0042 (consumer reader), rendered by ops-mason and carrying a `security_case` block. + +What the acknowledged type replaces: + +- the shape review: the platform does not re-argue the role and policy form; +- a fresh executive summary: the case acceptance is cited instead. + +What it does not replace is the per-case platform-owner approval of the exact names, paths and bound claims, +nor `binding_confirmed`, nor the per-case apply and verification receipts. Only the mint needs no per-case +CCR: the standing CCR-2026-0040 and CCR-2026-0043 grant covers the family, gated by the broker's case +admission (R1). + +For nk-bind-executor-approval, drafted `proposed` (both pass `scripts/credential-change.py validate` and the +delegated-applier checks; `bound_claims_confirmed: false`, so apply stays blocked): + +- `credential-change-requests/CCR-2026-0060-nk-bind-executor-approval-keycape-eso-reader.yaml` + - role `external-secrets-keycape-nk-bind-executor-approval` + - policy `openbao/policies/workload-kv-read-keycape-nk-bind-executor-approval-eso.hcl` + - approver: platform-owner +- `credential-change-requests/CCR-2026-0061-nk-bind-executor-approval-consumer-reader.yaml` + - role `keycape-nk-bind-executor-approval-bounded-executor-workload-kv-read` + - policy `openbao/policies/workload-kv-read-keycape-nk-bind-executor-approval-bounded-executor.hcl` + - bound to `sso/nk-bind-executor`, audience openbao, 15m + - approvers: platform-owner and net-kingdom-owner (an executor lane, as CCR-2026-0058/0059) + +Activation conditions for both: + +- the type is acknowledged; +- the case is accepted; +- the custody entry is minted (after R1); +- the CCR is approved and the binding confirmed. + +If ops-mason later renders the case, these drafts are what the render must reconcile to; they are not +duplicates to be added beside it. The full-repository validate still reports the known, unrelated +CCR-2026-0035 failure. + +### R7. KeyCape gets a Secret copy; the consumer does not. Verdict: accepted, with a note + +KeyCape's ESO target `sso/keycape--client` is a Secret in namespace `sso`, the same namespace as +the consumer. "No Secret copy in the consumer namespace" holds for the consumer's own delivery, but not for +the namespace. The README's control text should say "no copy for the consumer" and name the KeyCape Secret +as the one at-rest copy, as CCR-2026-0033's risk note does ("one value, two copies"). This is a wording +change for r2; the boundary is unchanged (sso namespace writers are already inside it, CCR-2026-0033). + +## Open items + +1. net-kingdom: memo r2 with R3, R4, R5 and R7, plus the R2 wording. +2. Founder: acknowledge the type (r2), then accept the case. +3. railiance-platform: broker admission for `oidc-service-client@` after acknowledgment (R1). This + needs a workplan task here. +4. Founder: approve CCR-2026-0060 and CCR-2026-0061 and confirm their bindings (R6). diff --git a/openbao/policies/workload-kv-read-keycape-nk-bind-executor-approval-bounded-executor.hcl b/openbao/policies/workload-kv-read-keycape-nk-bind-executor-approval-bounded-executor.hcl new file mode 100644 index 0000000..870d909 --- /dev/null +++ b/openbao/policies/workload-kv-read-keycape-nk-bind-executor-approval-bounded-executor.hcl @@ -0,0 +1,15 @@ +# workload-kv-read-keycape-nk-bind-executor-approval-bounded-executor: CCR-2026-0061 (proposed), +# SecurityCase nk-bind-executor-approval (oidc-service-client@0.1, proposed), consumer reader for +# sso/nk-bind-executor. Platform-generated from the CCR. Source only; not applied. + +path "platform/data/workloads/keycape/nk-bind-executor-approval" { + capabilities = ["read"] +} + +path "auth/token/lookup-self" { + capabilities = ["read"] +} + +path "auth/token/revoke-self" { + capabilities = ["update"] +} diff --git a/openbao/policies/workload-kv-read-keycape-nk-bind-executor-approval-eso.hcl b/openbao/policies/workload-kv-read-keycape-nk-bind-executor-approval-eso.hcl new file mode 100644 index 0000000..a1c80ff --- /dev/null +++ b/openbao/policies/workload-kv-read-keycape-nk-bind-executor-approval-eso.hcl @@ -0,0 +1,15 @@ +# workload-kv-read-keycape-nk-bind-executor-approval-eso: CCR-2026-0060 (proposed), SecurityCase +# nk-bind-executor-approval (oidc-service-client@0.1, proposed). Platform-generated from the CCR +# (token_self_lifecycle true, as CCR-2026-0041). Source only; not applied. + +path "platform/data/workloads/keycape/nk-bind-executor-approval" { + capabilities = ["read"] +} + +path "auth/token/lookup-self" { + capabilities = ["read"] +} + +path "auth/token/revoke-self" { + capabilities = ["update"] +}