Send a caller identity to flex-auth so policy.enabled can flip
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

flex-auth's flex-auth-ops-warden pin (FLEX-WP-0016) TokenReviews the caller and
binds resource.system: ops-warden to system:serviceaccount:ops-warden:ops-warden.
policy.py posted /v1/check with no Authorization header, so the pin logs
"caller authentication warning" and can only run callerAuth.mode: warn — which,
under ADHOC-2026-08-17-T01, is exactly what blocks policy.enabled: true.

- policy.caller_auth (none | file | env | command) + src/warden/caller_identity.py:
  token resolved per call, never cached, written, or logged (ADR-0002)
- both check_sign_policy and check_fetch_policy attach the bearer header; an
  unobtainable token fails closed rather than retrying anonymously
- scripts/check_policy_caller_identity.py: read-only gate, prints length and a
  truncated fingerprint only, distinguishes 401 (audience/binding) from 403
- example config: caller_auth block, and flex_auth_url corrected — it pointed at
  flex-auth.flex-auth.svc, a Service that does not exist
- WARDEN-WP-0031, PolicyGatedSigning caller-identity section and flip sequence

Default stays mode: none, so behaviour is unchanged until an operator opts in.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
tegwick 2026-08-19 15:08:34 +02:00
parent 35aff380a3
commit 0a331413a2
9 changed files with 728 additions and 12 deletions

View file

@ -66,6 +66,8 @@ policy:
tenant: tenant:platform
subject_env: WARDEN_POLICY_SUBJECT
system: ops-warden
caller_auth: # how ops-warden identifies itself — see "Caller identity"
mode: none
```
| Key | Default | Description |
@ -76,6 +78,7 @@ policy:
| `tenant` | `tenant:platform` | Tenant sent in subject and resource |
| `subject_env` | `WARDEN_POLICY_SUBJECT` | Env var for IAM subject id override |
| `system` | `ops-warden` | Resource system identifier |
| `caller_auth.mode` | `none` | `none` \| `file` \| `env` \| `command` — source of the `Authorization` bearer token flex-auth TokenReviews ([Caller identity](#caller-identity-warden-wp-0031)) |
Set `WARDEN_POLICY_SUBJECT` to the caller's IAM profile `sub` when available.
If unset, the actor name is used as subject id.
@ -196,6 +199,75 @@ Evidence: `history/2026-06-23-flex-auth-policy-gate-production-smoke.md`.
---
## Caller identity (WARDEN-WP-0031)
flex-auth authenticates the **caller** before it evaluates the request. The
in-cluster pin `flex-auth-ops-warden` (FLEX-WP-0016) passes the bearer token to a
Kubernetes TokenReview and requires the principal
`system:serviceaccount:ops-warden:ops-warden` for any request carrying
`resource.system: ops-warden`.
Until ops-warden sends that header the pin logs
```
caller authentication warning: caller is not authenticated
```
and can only run `callerAuth.mode: warn`. Per ADHOC-2026-08-17-T01,
`policy.enabled` must not flip while `/v1/check` still answers unauthenticated
callers — so **the missing header is what blocks the flip**, and it is
ops-warden's to fix, not flex-auth's.
### Configure a token source
```yaml
policy:
caller_auth:
mode: none # none | file | env | command
token_path: /var/run/secrets/flex-auth/token # mode: file
token_env: WARDEN_POLICY_CALLER_TOKEN # mode: env
command: kubectl create token ops-warden -n ops-warden --audience flex-auth --duration 10m
audience: flex-auth
```
| Mode | Use it when |
| --- | --- |
| `none` | Default. No header — pre-FLEX-WP-0016 behaviour, only viable while the pin is in `warn` |
| `file` | In-cluster PEP with a projected, audience-bound ServiceAccount token |
| `env` | The token is already in the environment (CI, a wrapper) |
| `command` | Workstation `warden sign` — mint a short-lived bound token per call |
A workstation is not a ServiceAccount, which is why `command` exists. The token is
read, sent, and dropped: never cached, written, or logged (ADR-0002). If a token
is configured but cannot be obtained, the sign is **refused** under
`fail_closed` — falling back to an anonymous call would defeat the gate.
### Readiness gate
```bash
python scripts/check_policy_caller_identity.py # offline
python scripts/check_policy_caller_identity.py --url http://127.0.0.1:19090 # port-forward of the warn pin
```
Exit 0 ready / 1 not ready / 2 bad input. It prints the token's length and a
truncated fingerprint, never the value, so its output is safe to paste into a
handoff message. A live `401` means the token was sent but rejected (audience or
binding); `403` means it authenticated but may not represent `system: ops-warden`.
### Flip sequence
1. Configure `caller_auth`; `check_policy_caller_identity.py` exits 0 offline.
2. Smoke against a port-forward of the warn pin. The evidence is the **absence**
of `caller authentication warning` in its log, not the `allow` — warn serves
unauthenticated callers too, so an allow alone proves nothing.
3. Ask flex-auth to set `callerAuth.mode: enforce` (their FLEX-WP-0016 T03).
4. Re-run the gate against the enforcing pin.
5. Only then: `policy.enabled: true`, `fail_closed: true`.
Reversing 3 and 5 401s every `warden sign`.
---
## Production rollout
**Keep `policy.enabled: false` until flex-auth is reachable** at `policy.flex_auth_url`
@ -210,7 +282,9 @@ with `fail_closed: true`, unreachable flex-auth blocks all signs.
| 3 | ops-warden | Regenerate registry from inventory: `scripts/build_flex_auth_registry.py` |
| 4 | ops-warden | Local smoke: `./scripts/policy_gate_production_smoke.sh` |
| 5 | operator | Vault smoke: `make credential-exec-ops-warden-smoke` in `railiance-platform` (or manual `SMOKE_VAULT=1` fallback) |
| 6 | operator | Set `policy.flex_auth_url` in `~/.config/warden/warden.yaml` |
| 6 | operator | Set `policy.flex_auth_url` in `~/.config/warden/warden.yaml` — the pin is `flex-auth-ops-warden.flex-auth.svc.cluster.local:8080`, reached from a workstation via port-forward or tunnel |
| 6a | ops-warden | Configure `policy.caller_auth`; `scripts/check_policy_caller_identity.py` exits 0 (see **Caller identity**) |
| 6b | flex-auth | Set `callerAuth.mode: enforce` on the ops-warden pin (FLEX-WP-0016 T03) |
| 7 | operator | Set `policy.enabled: true`; keep `fail_closed: true` |
| 8 | operator | Allow smoke: `warden sign <actor>` — `signatures.log` has `policy_decision_id` |
| 9 | operator | Deny smoke: e.g. `--ttl` above max — CLI shows flex-auth `reason`, no cert |