Send a caller identity to flex-auth so policy.enabled can flip
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:
parent
35aff380a3
commit
0a331413a2
9 changed files with 728 additions and 12 deletions
|
|
@ -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 |
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue