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

6 KiB

id type title domain repo status owner topic_slug planning_priority depends_on_workplans related_workplans created updated state_hub_workstream_id
WARDEN-WP-0031 workplan Calling-side identity for flex-auth, so policy.enabled can flip infotech ops-warden active ops-warden netkingdom P1
WARDEN-WP-0007
WARDEN-WP-0009
2026-08-19 2026-08-19 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

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.

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.

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.

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: commandkubectl 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.

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