flex-auth/docs/adr/0004-inbound-caller-authentication.md
tegwick fa278674c1
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Pin caller-auth digest in warn on independently rollable overlay pins
The sanctioned Helm chart could not promote ADR 0004 at all, and the
emergency manifests selected enforce. That made a FLEX-WP-0011 apply
either a no-op or a global 401. First production pin is now warn, per
consumer, on CI digest sha256:138aa347… . Enforce stays a later
per-consumer flip so USER-WP-0023-T03 can close without waiting on
tenant-engine.
2026-08-19 12:31:08 +02:00

67 lines
3 KiB
Markdown

# ADR 0004 — authenticate and bind authorization callers
Status: accepted (source implemented; production promotion pending)
Date: 2026-08-18
## Context
`POST /v1/check` and `/v1/batch_check` accepted an asserted subject, tenant and
protected-system name from any workload with network reach. NetworkPolicy
limited reachability but did not establish caller identity. A caller could
therefore represent another protected system and obtain an authoritative
decision under the wrong policy package.
The boundary must authenticate workloads without turning flex-auth into an
identity issuer, sharing a long-lived secret between services, or coupling
authorization availability to an unrelated identity-provider round trip.
## Decision
Use Kubernetes ServiceAccount tokens with audience `flex-auth`. flex-auth calls
the Kubernetes TokenReview API through a separately projected reviewer token
and binds the authenticated ServiceAccount principal to every
`resource.system` in the request. Both the single and batch endpoints use the
same choke point; health remains unauthenticated.
Each deployed policy instance has an explicit, exact binding. For example:
```text
tenant-engine=system:serviceaccount:tenant-engine:tenant-engine
user-engine=system:serviceaccount:user-engine:user-engine
```
Unknown systems, missing or invalid tokens, audience mismatch and principal
mismatch fail closed. TokenReview unavailability returns 503 rather than an
authorization answer. Tokens and reviewer credentials are re-read rather than
cached across rotation.
Three modes support promotion: `disabled`, `warn`, and `enforce`. Warn mode
records the same authentication failures without logging credentials. It is a
bounded migration aid, not a conformant steady state. The first production pin
is `warn` on each independently rollable Deployment; `enforce` is the end
state and is flipped per consumer after that consumer's warn logs are clean.
Promotion follows FLEX-WP-0011. The two production pins must not be flipped
together: user-engine can enforce while tenant-engine stays in warn.
## Rejected alternatives
- NetworkPolicy alone proves network position, not workload identity.
- A shared header secret has broad replay and rotation blast radius and cannot
bind a Kubernetes workload principal.
- Application mTLS would add a separate certificate lifecycle where the
cluster already has short-lived projected workload identity.
- Using the caller token itself to invoke TokenReview would grant callers an
unnecessary API permission. A narrow reviewer ServiceAccount holds only
`create` on `tokenreviews.authentication.k8s.io`.
## Consequences
- Caller identity is bound once at the flex-auth ingress and cannot be swapped
by changing request JSON.
- flex-auth depends on the Kubernetes authentication API for uncached checks;
an outage fails closed with 503.
- Each new protected system needs an explicit binding and projected caller
token. There is no wildcard binding.
- Source and desired state reach A2, while the live declaration remains A0
until the immutable digest is promoted and probed.