--- id: WARDEN-WP-0031 type: workplan title: "Calling-side identity for flex-auth, so policy.enabled can flip" domain: infotech repo: ops-warden status: active owner: ops-warden topic_slug: netkingdom planning_priority: P1 depends_on_workplans: - WARDEN-WP-0007 related_workplans: - WARDEN-WP-0009 created: "2026-08-19" updated: "2026-08-19" state_hub_workstream_id: "7284a398-d529-4fe3-871e-e2505064be5e" --- # WARDEN-WP-0031 — Calling-side identity for flex-auth flex-auth shipped `flex-auth-ops-warden` (FLEX-WP-0016 T01/T02): an independently rollable in-cluster pin carrying ops-warden's production registry and policy package, on digest `sha256:138aa347…`, at `flex-auth-ops-warden.flex-auth.svc.cluster.local:8080`. It runs `callerAuth.mode: warn`, and it says why in its own logs: ``` caller authentication warning: caller is not authenticated ``` `src/warden/policy.py` posted `/v1/check` with **no `Authorization` header**. flex-auth authenticates the caller with a Kubernetes TokenReview and binds `resource.system: ops-warden` to `system:serviceaccount:ops-warden:ops-warden`; an unauthenticated caller can only be served in `warn`. So the pin cannot enforce, and per ADHOC-2026-08-17-T01 — `policy.enabled` must not flip anywhere while `/v1/check` still answers unauthenticated callers — `policy.enabled` stays false. The gap is ours, not flex-auth's, and this workplan closes it. Warn is also not A2 evidence: a request that succeeds because failures are downgraded proves nothing about the enforcing path. ## Ownership | Concern | Owner | | --- | --- | | The pin, its digest, `callerAuth.mode` | flex-auth | | Sending a caller identity on `/v1/check` | **ops-warden** (this workplan) | | Choosing the token source on a given host | ops-warden operator | | `policy.enabled: true` in `warden.yaml` | ops-warden operator | ## Design note — fail closed on identity too When a caller token is configured but cannot be obtained, `check_sign_policy` raises under `fail_closed` rather than retrying unauthenticated. Falling back to an anonymous call is precisely the behaviour that keeps the pin in `warn`; a gate that silently degrades to the ungated path is not a gate (ADR-0004's choke point argument, applied to ops-warden as a caller). ## Tasks ```task id: WARDEN-WP-0031-T01 status: done priority: high state_hub_task_id: "cd65c834-214a-4bbb-92e4-262f124fc79d" ``` **Caller identity on the outbound policy call.** `policy.caller_auth` in `warden.yaml` (`mode: none | file | env | command`, `token_path`, `token_env`, `command`, `audience`); `src/warden/caller_identity.py` resolves the token at call time and never caches, logs, or echoes it (ADR-0002); both `check_sign_policy` and `check_fetch_policy` attach `Authorization: Bearer …`. Whitespace-bearing and empty tokens are rejected before the call, because flex-auth rejects them outright. Done 2026-08-19. `mode: none` remains the default, so behaviour is unchanged until an operator opts in. Tests in `tests/test_policy.py`. ```task id: WARDEN-WP-0031-T02 status: done priority: high state_hub_task_id: "9abdeb78-bbbb-4ff5-b3d7-5646116cf68b" ``` **Readiness gate.** `scripts/check_policy_caller_identity.py` — read-only: config loads, mode is not `none`, a token is actually obtainable, and with `--url` a live `/v1/check` against a port-forward of the warn pin. Prints the token's length and a truncated SHA-256 fingerprint only, so its output is safe to paste into a handoff. Exit 0 ready / 1 not ready / 2 bad input. Distinguishes 401 (token not accepted — audience or binding) from 403 (authenticated but not allowed to represent `system: ops-warden`). Done 2026-08-19. ```task id: WARDEN-WP-0031-T03 status: done priority: medium state_hub_task_id: "d3ff2ae1-2dac-45d0-b229-8b23c27c3972" ``` **Docs.** `examples/warden.production.example.yaml` gains the `caller_auth` block with both realistic sources, and its `flex_auth_url` is corrected — the example pointed at `flex-auth.flex-auth.svc.cluster.local`, a Service that does not exist. `wiki/PolicyGatedSigning.md` gains the caller-identity section and the flip sequence. Done 2026-08-19. ```task id: WARDEN-WP-0031-T04 status: done priority: high state_hub_task_id: "3d9d1a05-adea-4dcb-b71b-bfb2235aa198" ``` **Pick the token source and prove it against the warn pin.** Operator work on the real host: `kubectl create token` (workstation) or a projected token (in-cluster PEP), then `python scripts/check_policy_caller_identity.py --url http://127.0.0.1:19090` against a port-forward. Expect `effect=allow` for `agt-state-hub-bridge` while the warn log stops printing `caller authentication warning` — the absence of that line, not the allow, is the evidence. Done 2026-08-19. Source is `mode: command` — `kubectl create token ops-warden -n ops-warden --audience flex-auth --duration 10m` against the railiance01 cluster (tunnel `k3s-api-railiance01`, local `16444`; note `16443` is claimed by two tunnels at once, which is why a `system:masters` cert 401s there). `deploy/kubernetes/caller-identity.yaml` creates the Namespace and ServiceAccount the binding names — no RBAC, `automountServiceAccountToken: false`; it exists only to be TokenReviewed. Gate exits 0 live: `HTTP 200, effect=allow, decision:f3f7c88f9585582a`, and the pin's `caller authentication warning` count held at 4 across two authenticated runs. Evidence: `history/2026-08-19-flex-auth-caller-identity-evidence.md`. ```task id: WARDEN-WP-0031-T05 status: wait priority: high state_hub_task_id: "8b8e68c4-587d-490d-a98f-840aaff0fdb8" ``` **Sequence the flip.** Only after T04: tell flex-auth to set `callerAuth.mode: enforce` on `flex-auth-ops-warden` (their FLEX-WP-0016 T03), re-run the gate against the enforcing pin, and only then set `policy.enabled: true` with `fail_closed: true`. Flipping before enforce buys nothing; flipping before T04 401s every `warden sign`. Asked flex-auth 2026-08-19 with the T04 evidence. Waiting on their enforce. The closing `warden sign` also needs a scoped `VAULT_TOKEN` via `ops-warden-warden-sign-token` (operator authenticates to OpenBao).