ops-warden/workplans/WARDEN-WP-0031-policy-caller-identity.md
tegwick 654c05dece
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
WARDEN-WP-0031 T04: prove ops-warden's caller identity against the live pin
flex-auth's binding names system:serviceaccount:ops-warden:ops-warden, and that
ServiceAccount did not exist. deploy/kubernetes/caller-identity.yaml creates it
plus its namespace — no RBAC, automount off; it is never used to call the
Kubernetes API, only to be TokenReviewed. Applied to the railiance01 cluster.

Operator warden.yaml now uses caller_auth mode: command (kubectl create token,
audience flex-auth, 10m). Gate exits 0 live against a port-forward of the pin:
HTTP 200, effect=allow, decision:f3f7c88f9585582a.

The evidence is not that allow — warn allows anonymous callers too. It is that
the pin's "caller authentication warning" count held at 4 across two
authenticated runs. That is the ADHOC-2026-08-17-T01 condition.

Also gives the readiness probe a structurally complete context, so a deny means
the policy said no rather than the probe being malformed.

policy.enabled stays false. T05 waits on flex-auth setting callerAuth.mode:
enforce (their FLEX-WP-0016 T03).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 19:06:04 +02:00

154 lines
6 KiB
Markdown

---
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).