Helm-adopted the kubectl-managed user-engine pin, promoted sha256:138aa347 warn then enforce. Reviewer volume needs fsGroup 65532; reviewer tokens must use the API default audiences or TokenReview 401s on this k3s. Live probe from user-engine-8569d7cb87-fp7px: 200 / 401 / 403. tenant-engine is unchanged. tenancy.current.A stays 0 until that pin moves.
71 lines
3.3 KiB
Markdown
71 lines
3.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. The reviewer token uses the
|
|
API server's default audiences so it can authenticate to TokenReview; a
|
|
custom audience of `https://kubernetes.default.svc` 401s on this cluster
|
|
(`https://kubernetes.default.svc.cluster.local` and `k3s`). Caller tokens
|
|
keep audience `flex-auth`.
|
|
|
|
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.
|