NK-WP-0048-T05: take ops-warden R8 option 1; ensure-groups attended; memo r4
ensure-groups and its rollback run attended in the register-client session (key-cape-oidc-login), inside the identity-provisioner pod: the provisioner's only HTTP credential is user-engine's runtime token, so a caller-proxy lane would act as user-engine. Lane identity-provisioner-groups is dropped. Memo r4 supersedes r3; acknowledges ops-warden's WARDEN-WP-0044-T01 review. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 469893@bnt-lap001 Assistant-Session: 49296f15-2171-4137-b972-2443bab95edb
This commit is contained in:
parent
fbcb76055d
commit
0e86f957a2
8 changed files with 88 additions and 40 deletions
|
|
@ -40,7 +40,7 @@
|
|||
| workplan | NK-WP-0043 | finished | — | workplans/NK-WP-0043-cadence-assessment-and-operating-guidance.md |
|
||||
| workplan | NK-WP-0044 | finished | — | workplans/NK-WP-0044-runbook-packs-for-runbook-tutorials.md |
|
||||
| workplan | NK-WP-0045 | finished | — | workplans/NK-WP-0045-root-grant-custody-record.md |
|
||||
| workplan | NK-WP-0046 | active | — | workplans/NK-WP-0046-secrets-engine-openbao-client-registration.md |
|
||||
| workplan | NK-WP-0046 | finished | — | workplans/NK-WP-0046-secrets-engine-openbao-client-registration.md |
|
||||
| workplan | NK-WP-0047 | finished | — | workplans/NK-WP-0047-platform-password-reset-mail.md |
|
||||
| workplan | NK-WP-0048 | active | — | workplans/NK-WP-0048-security-case-types.md |
|
||||
| task | NK-WP-ADHOC-2026-07-02-T01 | done | — | workplans/ADHOC-2026-07-02.md |
|
||||
|
|
|
|||
|
|
@ -79,7 +79,8 @@ Secret, and agents do not read or write Secrets. For confidential clients,
|
|||
`bind-env` is attended as well, being a change to the identity provider's
|
||||
Deployment. KeyCape resolves a confidential client's secret from an environment
|
||||
variable at start, so the binding (and `deliver-secret` before it) must exist
|
||||
before registration. ops-warden groups both attended steps into one operator
|
||||
before registration. `ensure-groups` is attended too (see Lanes below). ops-warden
|
||||
groups the attended steps into one operator
|
||||
session (WARDEN-WP-0044). The way to remove this
|
||||
step is a KeyCape change: load clients from a non-secret registry and reference
|
||||
confidential secrets from custody. That would be a 1.1 or 2.0 of this type.
|
||||
|
|
@ -87,14 +88,17 @@ confidential secrets from custody. That would be a 1.1 or 2.0 of this type.
|
|||
## Lanes, value handling and gates
|
||||
|
||||
Every step ops-warden executes or groups names its catalog lane
|
||||
(`identity-provisioner-groups` and `keycape-client-secret-generate` are new and
|
||||
must exist before the first case runs; the attended steps use the existing
|
||||
`key-cape-oidc-login`). "ops-warden executes" means ops-warden invokes the owner's
|
||||
(`keycape-client-secret-generate` is new and must exist before the first case
|
||||
runs; the attended steps use the existing `key-cape-oidc-login`). "ops-warden executes" means ops-warden invokes the owner's
|
||||
entry point through its caller proxy, under the caller's identity: it holds no
|
||||
credential (canon section 3). `mint-secret` generates the value inside the
|
||||
broker and custody (`value_handling: generated_in_custody`), so no value passes
|
||||
through the run. `deliver-secret` gates `bind-env` with a status-only Ready check.
|
||||
`ensure-groups` adds exactly the memberships recorded in the accepted case.
|
||||
`ensure-groups` adds exactly the memberships recorded in the accepted case. It is
|
||||
attended, in the same session as `register-client`, and runs inside the
|
||||
provisioner pod: the provisioner's only HTTP credential is user-engine's runtime
|
||||
token, so a caller-proxy lane would act as user-engine (ops-warden review R8). It
|
||||
can become unattended once the provisioner has an operator credential of its own.
|
||||
|
||||
## Known limits
|
||||
|
||||
|
|
|
|||
|
|
@ -13,10 +13,11 @@ from pathlib import Path
|
|||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
HUB_DECISION = "e418263e-a71f-4f01-a970-c87c9e187c09"
|
||||
# r3 supersedes r2 (built before ops-warden's review) and the first memo (built
|
||||
# before ops-mason's review). Each revision needs a new id: the dispatcher
|
||||
# r4 supersedes r3 (ensure-groups as a caller-proxy step; ops-warden R8), r2
|
||||
# (built before ops-warden's review) and the first memo (built before ops-mason's
|
||||
# review). Each revision needs a new id: the dispatcher
|
||||
# refuses new content under a delivered idempotency key.
|
||||
MEMO_ID = "nk-wp-0048-t05-oidc-browser-relying-party-r3"
|
||||
MEMO_ID = "nk-wp-0048-t05-oidc-browser-relying-party-r4"
|
||||
ITEM = f"{MEMO_ID}-packet"
|
||||
|
||||
packet = (HERE / "packet.md").read_text()
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -4,11 +4,13 @@ Workplan: NK-WP-0048 (net-kingdom), task NK-WP-0048-T05
|
|||
Hub decision: e418263e-a71f-4f01-a970-c87c9e187c09
|
||||
Canon: canon/standards/security-case-types_v0.1.md (accepted 2026-10-08), section 5
|
||||
Type: security-case-types/oidc-browser-relying-party/ (type.yaml, README.md, tests)
|
||||
Memo: nk-wp-0048-t05-oidc-browser-relying-party-r3. It supersedes r2
|
||||
(nk-wp-0048-t05-oidc-browser-relying-party-r2, which lacked ops-warden's review)
|
||||
and the first memo (already returned). Please return r2 with a comment that r3
|
||||
supersedes it, then answer this one. All three serve the same hub decision, and
|
||||
the first accept or decline resolves it.
|
||||
Memo: nk-wp-0048-t05-oidc-browser-relying-party-r4. It supersedes r3 (which
|
||||
made `ensure-groups` an ops-warden caller-proxy step; ops-warden's addendum R8
|
||||
showed the provisioner has no caller credential for that), r2 (which lacked
|
||||
ops-warden's review) and the first memo (already returned). Please return any
|
||||
of r2 and r3 still open with a comment that r4 supersedes them, then answer
|
||||
this one. All memos serve the same hub decision, and the first accept or
|
||||
decline resolves it.
|
||||
|
||||
## Question
|
||||
|
||||
|
|
@ -33,8 +35,9 @@ ops-mason materializes it and ops-warden follows through. The type becomes
|
|||
the request's proposed `platform/workloads/forgejo/netkingdom-oidc-client`.
|
||||
- It does not adopt the existing hand-built clients. Adoption, when it happens,
|
||||
does not change their live registrations.
|
||||
- It does not create the two catalog lanes listed below. They are needed before
|
||||
the first case runs (T06), each through its own owner.
|
||||
- It does not create the new catalog lane listed below
|
||||
(`keycape-client-secret-generate`, railiance-platform). It is needed before
|
||||
the first case runs (T06).
|
||||
|
||||
## The shape being acknowledged
|
||||
|
||||
|
|
@ -49,7 +52,7 @@ Steps, in order, each on its owner's entry point and its catalog lane:
|
|||
|
||||
| Step | Owner | Runs | Lane | Values | Confidential only |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| ensure-groups | net-kingdom (identity-provisioner) | ops-warden | identity-provisioner-groups (new) | none | no |
|
||||
| ensure-groups | net-kingdom (identity-provisioner, in-pod call) | attended operator | key-cape-oidc-login | none | no |
|
||||
| mint-secret | railiance-platform (credential broker) | ops-warden | keycape-client-secret-generate (new) | generated in custody | yes |
|
||||
| deliver-secret | railiance-platform (delivery into the sso namespace) | owner; gate: Ready, status only | — | none | yes |
|
||||
| bind-env | net-kingdom (KeyCape Deployment) | attended operator | key-cape-oidc-login | none | yes |
|
||||
|
|
@ -99,9 +102,14 @@ exact inverse, in reverse order.
|
|||
|
||||
## Attended steps
|
||||
|
||||
`register-client` (and, for confidential clients, `bind-env`) need one attended
|
||||
operator session, grouped by ops-warden on the existing `key-cape-oidc-login`
|
||||
lane. KeyCape's live client registry is a Kubernetes Secret, and agents do not
|
||||
`ensure-groups`, `register-client` (and, for confidential clients, `bind-env`)
|
||||
need one attended operator session, grouped by ops-warden on the existing
|
||||
`key-cape-oidc-login` lane. `ensure-groups` is attended because the
|
||||
identity-provisioner's only HTTP credential is user-engine's runtime token and
|
||||
its NetworkPolicy admits only user-engine pods: a caller-proxy lane would act as
|
||||
user-engine (ops-warden R8). It runs inside the provisioner pod, and no token is
|
||||
read. An operator credential of its own on the provisioner would make it
|
||||
unattended in a later version. KeyCape's live client registry is a Kubernetes Secret, and agents do not
|
||||
read or write Secrets. Removing this needs a KeyCape change (clients from a
|
||||
non-secret registry, secrets referenced from custody). That would be a new
|
||||
version of this type, with its own decision.
|
||||
|
|
|
|||
|
|
@ -125,7 +125,9 @@ def test_confidential_case_renders_custody_delivery_and_one_attended_session():
|
|||
order = [s["id"] for s in r["follow_through"]]
|
||||
assert order == ["ensure-groups", "mint-secret", "deliver-secret", "bind-env", "register-client", "hand-over"]
|
||||
steps = {s["id"]: s for s in r["follow_through"]}
|
||||
assert [s["id"] for s in r["follow_through"] if s.get("attended")] == ["bind-env", "register-client"]
|
||||
attended = [s for s in r["follow_through"] if s.get("attended")]
|
||||
assert [s["id"] for s in attended] == ["ensure-groups", "bind-env", "register-client"]
|
||||
assert {s["lane"] for s in attended} == {"key-cape-oidc-login"}
|
||||
assert "keycape-client.py apply" in steps["register-client"]["entry_point"]
|
||||
assert "sso/keycape-forgejo-client" in steps["deliver-secret"]["entry_point"]
|
||||
assert steps["mint-secret"]["entry_point"].endswith("bao:platform/workloads/keycape/forgejo")
|
||||
|
|
@ -140,7 +142,8 @@ def test_ops_warden_review_is_folded_in():
|
|||
assert msgs(issues) == []
|
||||
steps = {s["id"]: s for s in r["follow_through"]}
|
||||
lanes = {k: s.get("lane") for k, s in steps.items()}
|
||||
assert lanes["ensure-groups"] == "identity-provisioner-groups"
|
||||
assert lanes["ensure-groups"] == lanes["register-client"]
|
||||
assert steps["ensure-groups"]["attended"] and not steps["ensure-groups"]["warden_executes"]
|
||||
assert lanes["mint-secret"] == "keycape-client-secret-generate"
|
||||
assert lanes["bind-env"] == lanes["register-client"] == "key-cape-oidc-login"
|
||||
assert steps["mint-secret"]["value_handling"] == "generated_in_custody"
|
||||
|
|
|
|||
|
|
@ -156,12 +156,19 @@ spec:
|
|||
follow_through:
|
||||
- id: ensure-groups
|
||||
owner: net-kingdom
|
||||
entry_point: "identity-provisioner POST /v1/groups/ensure (groups {access_groups}; members exactly {initial_members} as recorded in the accepted case, nothing else)"
|
||||
auth: "the caller's own identity-provisioner credential through its lane; ops-warden invokes it via the caller proxy and holds no token"
|
||||
lane: identity-provisioner-groups
|
||||
warden_executes: true
|
||||
attended: false
|
||||
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
|
||||
warden_executes: false
|
||||
attended: true
|
||||
value_handling: none
|
||||
attended_justification: >-
|
||||
The provisioner's only HTTP credential is user-engine's runtime token, and
|
||||
its NetworkPolicy admits only user-engine pods. A proxy lane 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.
|
||||
- id: mint-secret
|
||||
owner: railiance-platform
|
||||
when: "client_type == confidential"
|
||||
|
|
@ -265,7 +272,7 @@ spec:
|
|||
when: "client_type == confidential"
|
||||
action: "covered by client-secret-path: one metadata delete, after deliver-secret is reversed"
|
||||
- reverses: ensure-groups
|
||||
action: "identity-provisioner POST /v1/groups/revoke with the case's memberships; delete_if_empty only if no other case uses {access_groups}"
|
||||
action: "identity-provisioner revoke_service_groups inside the provisioner pod (attended, as ensure-groups) with the case's memberships; delete_if_empty only if no other case uses {access_groups}"
|
||||
- reverses: client-secret-path
|
||||
when: "client_type == confidential"
|
||||
action: "delete metadata and all versions at platform/workloads/keycape/{client_id}, after the ExternalSecret is gone (this also reverses mint-secret)"
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ flavor: implementation
|
|||
owner: claude-code
|
||||
topic_slug: netkingdom
|
||||
created: "2026-10-08"
|
||||
updated: "2026-10-08"
|
||||
updated: "2026-10-09"
|
||||
depends_on: []
|
||||
related_repos:
|
||||
- ops-mason
|
||||
|
|
@ -291,6 +291,31 @@ ops-mason's request to create LLDAP group `fluid-telegram-operators` (message
|
|||
259ec26d) was answered: not possible from here by that path; the founder creates
|
||||
it in the LLDAP admin UI.
|
||||
|
||||
2026-10-09: ops-warden finished its side of WARDEN-WP-0044-T01 (message
|
||||
`58d33f40`, commit 0e7ceb8): the revised type conforms to `warden case
|
||||
check-type`, with no further changes asked of the type. This acknowledges that
|
||||
review. Its later addendum R8 (message `af274783`, ops-warden
|
||||
`history/2026-10-08-oidc-rp-follow-through-review.md`) showed that lane
|
||||
`identity-provisioner-groups` cannot exist as declared: the provisioner's only
|
||||
HTTP credential is user-engine's runtime token, and its NetworkPolicy admits
|
||||
only user-engine pods, so a caller-proxy lane would act as user-engine. Taken
|
||||
option 1 (ops-warden's recommendation): `ensure-groups` and its rollback are
|
||||
attended, in the `register-client` session on `key-cape-oidc-login`, run inside
|
||||
the provisioner pod; no token is read. Option 2 (an operator credential of its
|
||||
own on the provisioner) stays open for a later version. `warden case
|
||||
check-type` now reports only `keycape-client-secret-generate`
|
||||
(railiance-platform) as missing. 89 tests pass.
|
||||
|
||||
Hub decision `e418263e` was still open, so memo r4
|
||||
(`nk-wp-0048-t05-oidc-browser-relying-party-r4`, packet `sha256:452ea421…`)
|
||||
replaces r3; it passes informed-decision's and the dispatcher's validation. The
|
||||
founder queues it with the same command, returns r2/r3 if still open with a
|
||||
comment that r4 supersedes them, then answers r4.
|
||||
|
||||
Prerequisites for T06 are now: lane `keycape-client-secret-generate`
|
||||
(railiance-platform), and a founder-approved rollout of an identity-provisioner
|
||||
image containing 630ebfd.
|
||||
|
||||
```task
|
||||
id: NK-WP-0048-T06
|
||||
status: wait
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue