TokenReview-based caller identity with audience-scoped tokens and exact resource.system to ServiceAccount bindings, per ops-warden's recommendation. Deletes the unwired tenant-engine live-roles adapter (T03) and adds make verify-posture (T05). Source implements A2; running digest is still A0 until promotion, so tenancy.current.A stays 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2.9 KiB
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:
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 reviewed desired
manifests select enforce; promotion still follows FLEX-WP-0011 and requires a
new immutable image digest plus caller rollout evidence.
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
createontokenreviews.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.