Answer flex-auth caller-auth question; refresh two delegation lanes
Inbox triage (ADHOC-2026-08-17), three messages, all answered and marked read. flex-auth (FLEX-WP-0015 T02) reported that /v1/check and /v1/batch_check authenticate no caller — any workload with cluster reach can assert any subject and get an authoritative allow. It lands here because ops-warden owns workload identity in this estate and its pre-sign gate is a flex-auth PEP. Recommendation recorded in wiki/NetKingdomSecurityMap.md: Kubernetes ServiceAccount TokenReview with a projected, audience-scoped token. Rejected the shared-secret header — it manufactures a risk: high lane with a rotation owner per caller on the authorization path, the exact interim-proxy debt WP-0030 exists to stop growing. Deferred mTLS pending an owner for a workload X.509 CA. Bind `system` to the authenticated SA at auth time, but keep the resource-type allowlist in the policy package rather than the admission layer. Warn-only rollout; policy.enabled must not flip while /v1/check answers unauthenticated callers. Recorded as a pattern, not a catalog entry. Catalog delegation refreshes: - coulomb-social-runtime-env: USER_ENGINE_PROXY_SECRET ownership settled (decision 8fe22037, USER-WP-0021) — intended_owner stays railiance-apps, answered clause dropped from blocked_on, consumers: [user-engine] recorded. - rapp-qonto-keycape-client: blocked_on no longer implies an in-flight KEY-WP-0008 dependency; the key-cape-native exchange command is unopened work. uv run pytest -q → 338 passed, 4 deselected. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
263b919c17
commit
3447687bbc
3 changed files with 213 additions and 4 deletions
|
|
@ -543,8 +543,8 @@ entries:
|
|||
delegation:
|
||||
mode: interim
|
||||
intended_owner: key-cape
|
||||
blocked_on: "client_secret_basic exchange is a key-cape protocol procedure, not a KV read; rotation already automatable"
|
||||
reviewed: "2026-08-11"
|
||||
blocked_on: "client_secret_basic exchange is a key-cape protocol procedure, not a KV read; KEY-WP-0008 finished 2026-08-15 without a key-cape-native exchange/rotation command, and the workplan that would add one is not yet opened (key-cape acked interim 2026-08-15)"
|
||||
reviewed: "2026-08-17"
|
||||
risk: high
|
||||
auth_method: "OpenBao platform workload lane; KeyCape client_secret_basic exchange"
|
||||
path_template: "platform/workloads/rapp-qonto/keycape-client"
|
||||
|
|
@ -731,8 +731,13 @@ entries:
|
|||
delegation:
|
||||
mode: interim
|
||||
intended_owner: railiance-apps
|
||||
blocked_on: "Runtime Secret applied via railiance-apps make target; OpenBao path planned; confirm whether user-engine should front USER_ENGINE_PROXY_SECRET"
|
||||
reviewed: "2026-08-15"
|
||||
blocked_on: "Runtime Secret applied via railiance-apps make target; OpenBao path planned but CCR not applied"
|
||||
reviewed: "2026-08-17"
|
||||
# USER_ENGINE_PROXY_SECRET ownership settled 2026-08-16 (Bernd; State Hub decision
|
||||
# 8fe22037-5bbb-4487-bb86-e4beccee454b, USER-WP-0021): it is infrastructure trust
|
||||
# between ingress and workload, not a user-domain fact. intended_owner stays
|
||||
# railiance-apps; user-engine is consumer-only and claims no lane here.
|
||||
consumers: [user-engine]
|
||||
risk: standard
|
||||
# K8s assembly is the live handoff today (same pattern as vergage-teilnahme-env).
|
||||
# OpenBao KV platform/workloads/coulomb/coulomb-social/runtime-env is the
|
||||
|
|
|
|||
|
|
@ -70,6 +70,86 @@ the need.
|
|||
|
||||
---
|
||||
|
||||
## Service-to-service caller authentication (in-cluster)
|
||||
|
||||
**Status:** recommended pattern, 2026-08-17. Raised by flex-auth (FLEX-WP-0015 T02):
|
||||
`POST /v1/check` and `/v1/batch_check` authenticate no caller, so any workload with
|
||||
network reach can assert any subject and receive an authoritative allow.
|
||||
|
||||
**There is no prior estate pattern for this.** What exists covers adjacent needs and
|
||||
none of them covers in-cluster service→service HTTP:
|
||||
|
||||
| Existing mechanism | What it authenticates | Why it does not apply |
|
||||
| --- | --- | --- |
|
||||
| ops-warden SSH certificates | `adm`/`agt`/`atm` actors to hosts | Host reachability, not an HTTP call between pods |
|
||||
| KeyCape `client_credentials` | Workload OIDC clients (`rapp-qonto-keycape-client`) | Needs a client secret per caller — a custodied lane per consumer |
|
||||
| OpenBao AppRole | Host-standing non-interactive workers | `role_id`+`secret_id` on disk; the WP-0030 register already flags AppRole as having **no owner front door** for minting or rotating |
|
||||
|
||||
### Recommendation: Kubernetes ServiceAccount TokenReview
|
||||
|
||||
Use the caller's **projected** ServiceAccount token with an explicit `audience`
|
||||
(e.g. `flex-auth`); the callee verifies it via `TokenReview`.
|
||||
|
||||
Why this and not the alternatives:
|
||||
|
||||
- **It introduces no new secret material.** A shared-secret header (flex-auth's
|
||||
option (c)) would immediately become a `risk: high` credential lane with a
|
||||
rotation owner, per calling system, on the authorization path — precisely the
|
||||
interim-proxy debt the WP-0030 delegation register exists to stop growing.
|
||||
ops-warden would end up fronting it.
|
||||
- **It matches the estate's short-lived-credential doctrine.** A projected,
|
||||
audience-scoped SA token has the same shape as `warden sign` output: bounded TTL,
|
||||
issued by an authority, verified on use, never stored.
|
||||
- **mTLS (option (b)) is the stronger end state but has no owner.** It requires an
|
||||
X.509 workload CA, and no component owns one today — ops-warden issues SSH
|
||||
certificates, not workload X.509. Adopting mTLS means first answering *who owns
|
||||
the workload CA*, which is a permanent-ownership question, not a rollout task.
|
||||
Record it as future direction; do not block on it.
|
||||
|
||||
Implementation notes that matter:
|
||||
|
||||
- Use a **projected token volume with an explicit `audience`**, not the legacy
|
||||
automount token. Keep `automountServiceAccountToken: false` and add the projected
|
||||
volume per Deployment. An audience-scoped token stolen from a pod cannot be
|
||||
replayed against the kube-apiserver or another service.
|
||||
|
||||
### Authenticate *and* bind — but keep policy in the policy engine
|
||||
|
||||
Bind the `system` asserted in the request to the authenticated ServiceAccount and
|
||||
reject a mismatch. That is identity binding, not authorization: it is cheap and it
|
||||
costs nothing extra when a new consumer arrives, since a caller must have a mapping
|
||||
regardless.
|
||||
|
||||
Do **not** put a caller allowlist for resource types ("only ops-warden may ask about
|
||||
`ssh-certificate`") in the authentication middleware. flex-auth is the estate's
|
||||
policy engine; encoding that rule in its own admission layer puts authorization in
|
||||
two places, where only one of them is reviewable and versioned. Express it in the
|
||||
policy package.
|
||||
|
||||
### Rollout
|
||||
|
||||
Warn-only first, then fail-closed. ops-warden adopts the calling side on its own
|
||||
schedule rather than in lockstep. The binding condition is sequencing, not a date:
|
||||
|
||||
```text
|
||||
flex-auth warn-only -> ops-warden pre-sign gate presents its SA token
|
||||
-> logs clean of unauthenticated callers
|
||||
-> flex-auth fail-closed
|
||||
-> policy.enabled: true (ops-warden side, FLEX-WP-0007)
|
||||
```
|
||||
|
||||
`policy.enabled` must not flip anywhere while `/v1/check` still answers
|
||||
unauthenticated callers.
|
||||
|
||||
**Division of the call:** the mechanism above is an architecture recommendation and
|
||||
ops-warden's to make. Accepting the pod-spec change and the rollout timing are the
|
||||
operator's.
|
||||
|
||||
This is a *pattern*, not a credential lane, so it gets no `registry/routing/catalog.yaml`
|
||||
entry — the catalog indexes credential needs and their owners.
|
||||
|
||||
---
|
||||
|
||||
## NetKingdom documents to watch
|
||||
|
||||
| Document | Why ops-warden cares |
|
||||
|
|
|
|||
124
workplans/ADHOC-2026-08-17.md
Normal file
124
workplans/ADHOC-2026-08-17.md
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
---
|
||||
id: ADHOC-2026-08-17
|
||||
type: workplan
|
||||
title: "Ad Hoc Tasks — 2026-08-17"
|
||||
domain: infotech
|
||||
repo: ops-warden
|
||||
status: finished
|
||||
owner: claude
|
||||
topic_slug: custodian
|
||||
created: "2026-08-17"
|
||||
updated: "2026-08-17"
|
||||
---
|
||||
|
||||
# Ad Hoc Tasks — 2026-08-17
|
||||
|
||||
Inbox triage session. Three unread messages, all follow-on from the WP-0030 delegation
|
||||
register plus one new design question that lands on ops-warden as the estate's
|
||||
workload-identity owner.
|
||||
|
||||
### T01 — Answer flex-auth: how should `/v1/check` authenticate its callers?
|
||||
|
||||
```task
|
||||
id: ADHOC-2026-08-17-T01
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
flex-auth (msg `130a148c`, FLEX-WP-0015 T02) reported that `POST /v1/check` and
|
||||
`/v1/batch_check` authenticate no caller — any workload with cluster network reach can
|
||||
assert any subject/tenant and receive an authoritative allow. It lands on ops-warden
|
||||
because ops-warden owns how workloads prove identity in this estate, and because the
|
||||
ops-warden pre-sign gate is a flex-auth PEP that would implement the calling side.
|
||||
|
||||
Their four questions answered in `wiki/NetKingdomSecurityMap.md`
|
||||
§ *Service-to-service caller authentication (in-cluster)*:
|
||||
|
||||
- [x] **Q1 — is there an existing estate pattern to adopt?** No. Surveyed what exists:
|
||||
ops-warden SSH certs (host reachability, not pod→pod HTTP), KeyCape
|
||||
`client_credentials` (`rapp-qonto-keycape-client` — a custodied client secret per
|
||||
caller), OpenBao AppRole (host-standing; the WP-0030 register already flags it as
|
||||
having no owner front door). None covers in-cluster service→service HTTP, so
|
||||
flex-auth is not growing a parallel mechanism by adopting one.
|
||||
- [x] **Q2 — which mechanism?** Confirmed their instinct: **(a) Kubernetes
|
||||
ServiceAccount TokenReview**, with a *projected* token carrying an explicit
|
||||
`audience`, keeping `automountServiceAccountToken: false` and adding the volume
|
||||
per Deployment. Rejected (c) shared-secret header — it manufactures a `risk: high`
|
||||
credential lane with a rotation owner per caller, on the authorization path, which
|
||||
is exactly the interim-proxy debt WP-0030 exists to stop growing, and ops-warden
|
||||
would end up fronting it. Deferred (b) mTLS as the stronger end state that first
|
||||
needs an answer to *who owns the workload X.509 CA* — nobody does; ops-warden
|
||||
issues SSH certs, not workload X.509.
|
||||
- [x] **Q3 — authenticate only, or also constrain?** Both, but split: bind the asserted
|
||||
`system` to the authenticated ServiceAccount and reject a mismatch (identity
|
||||
binding, no per-consumer operational cost). Keep the *resource-type* allowlist
|
||||
("only ops-warden may ask about `ssh-certificate`") in the policy package, not in
|
||||
the auth middleware — flex-auth is the policy engine, and encoding it in its own
|
||||
admission layer puts authorization in two places where only one is versioned.
|
||||
- [x] **Q4 — rollout shape?** Warn-only first, as they proposed; ops-warden adopts the
|
||||
calling side on its own schedule. Binding condition is sequencing, not a date:
|
||||
`policy.enabled` must not flip anywhere while `/v1/check` still answers
|
||||
unauthenticated callers.
|
||||
- [x] Recorded as a **pattern**, not a catalog entry — `registry/routing/catalog.yaml`
|
||||
indexes credential needs and their owners; caller authentication is neither.
|
||||
- [x] Noted the division of the call: mechanism is ops-warden's/architectural; accepting
|
||||
the pod-spec change and rollout timing are the operator's.
|
||||
|
||||
### T02 — user-engine: USER_ENGINE_PROXY_SECRET stays railiance-apps; record consumer-only
|
||||
|
||||
```task
|
||||
id: ADHOC-2026-08-17-T02
|
||||
status: done
|
||||
priority: medium
|
||||
```
|
||||
|
||||
user-engine (msg `2af4a124`) answered the open confirm question on
|
||||
`coulomb-social-runtime-env`. Decided by Bernd 2026-08-16, State Hub decision
|
||||
`8fe22037-5bbb-4487-bb86-e4beccee454b` against USER-WP-0021: the secret is
|
||||
infrastructure trust between ingress and workload, not a user-domain fact; user-engine
|
||||
consumes it and has no authority over custody, rotation, or issuance.
|
||||
|
||||
- [x] `intended_owner` stays `railiance-apps`; dropped the now-answered
|
||||
"confirm whether user-engine should front USER_ENGINE_PROXY_SECRET" clause from
|
||||
`blocked_on`, which now names only the real remaining blocker (CCR not applied).
|
||||
- [x] Recorded the positive signal they asked for: `consumers: [user-engine]` on the
|
||||
lane, with the decision id in a comment.
|
||||
- [x] Their second point — `USER_ENGINE_EVENT_TOKEN`, `USER_ENGINE_MAIL_TOKEN`, and the
|
||||
transactional SMTP lane gating public registration — checked against the catalog:
|
||||
`audit-core-senders` (native → ops-mason) and `email-connect-transactional`
|
||||
(interim → secrets-engine) already cover the event and mail token custody. No new
|
||||
lane opened; told them which ids to watch.
|
||||
|
||||
### T03 — key-cape: `rapp-qonto-keycape-client` interim accepted; refresh the blocker
|
||||
|
||||
```task
|
||||
id: ADHOC-2026-08-17-T03
|
||||
status: done
|
||||
priority: medium
|
||||
```
|
||||
|
||||
key-cape (msg `099b7cba`) acknowledged the lane staying interim on ops-warden.
|
||||
KEY-WP-0008 is finished and its closeout does **not** include a key-cape-native
|
||||
`client_secret_basic` exchange/rotation command; that is a separate, not-yet-opened
|
||||
workplan.
|
||||
|
||||
- [x] Refreshed `blocked_on` so it names the current reality rather than implying an
|
||||
in-flight KEY-WP-0008 dependency: the native command is unopened work, and
|
||||
key-cape has acked the interim hold.
|
||||
- [x] `key-cape-oidc-login` left pointing at `secrets-engine` — key-cape explicitly did
|
||||
not claim the generic-vs-per-lane custody split, and that lane's blocker is the
|
||||
same unanswered secrets-engine question as the other six.
|
||||
- [x] `reviewed` bumped to 2026-08-17 on both touched lanes so `warden route gaps`
|
||||
staleness reflects a real re-check.
|
||||
|
||||
### T04 — Session hygiene
|
||||
|
||||
```task
|
||||
id: ADHOC-2026-08-17-T04
|
||||
status: done
|
||||
priority: low
|
||||
```
|
||||
|
||||
- [x] `uv run pytest -q` → 338 passed, 4 deselected (routing anchor + no-double-source
|
||||
checks green after the catalog edits).
|
||||
- [x] All three messages replied to and marked read; inbox empty.
|
||||
Loading…
Add table
Add a link
Reference in a new issue