Policy — OPA/Gatekeeper¶
Two modes of enforcement from a single policy language (Rego):
- Admission control via Gatekeeper — evaluated on every
CREATE/UPDATEof Kubernetes resources - Runtime ext_authz via OPA DaemonSet — evaluated per request by Envoy before forwarding
Admission Policies¶
| Policy | What it enforces |
|---|---|
require-spiffe-id |
Every Pod must have a registered SPIFFE ID; rejects unregistered workloads at deploy time |
no-privileged-containers |
Forbids privileged: true, dangerous capabilities (NET_ADMIN, SYS_ADMIN), hostPID, hostNetwork (except SPIRE agent) |
require-resource-limits |
All containers must declare memory limits — prevents OPA/OTEL DaemonSet starvation |
Runtime OBO Chain Validation¶
validate-obo-chain.rego is the critical runtime policy. It runs per-request on every service and at the ZT gateway.
allow if {
# Path 1: direct call (no OBO token) — caller in allow-list
not has_obo_token
caller_in_allow_list
}
allow if {
# Path 2: delegated call — validate full OBO chain
has_obo_token
valid_obo_token # signature + audience + subject + TTL + chain depth
caller_in_allow_list
}
Decision log output (forwarded to Loki via OTEL Collector):
{
"allow": true,
"caller": "spiffe://chakra.internal/ns/orders/sa/orders-svc",
"destination": "spiffe://chakra.internal/ns/inventory/sa/inventory-svc",
"obo_present": true,
"obo_chain": "spiffe://chakra.internal/ns/api-gateway/sa/api-gateway,spiffe://chakra.internal/ns/orders/sa/orders-svc",
"user_principal": "user@chakra.internal",
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"deny_reason": "none"
}
Every field is indexed in Loki. The trace_id links each authz decision to the corresponding Tempo trace.
OPA DaemonSet Architecture¶
OPA runs as a DaemonSet — one instance per node — so every ext_authz call is to localhost:9191. This bounds authz latency to < 3ms p99 regardless of cluster size.
Node
├── ZT Gateway Pod → ext_authz → localhost:9191
├── Orders Pod sidecar → ext_authz → localhost:9191
├── Inventory Pod sidecar→ ext_authz → localhost:9191
└── OPA DaemonSet Pod ← SPIRE bundle (ConfigMap refresh 60s)
← policy bundle (ConfigMap)
→ /var/log/opa/decisions.json (OTEL filelog)
Testing Policies¶
# Run OPA unit tests
opa test policy/opa/ -v
# Example output:
# PASS: test_direct_allowed_caller (0.001s)
# PASS: test_direct_disallowed_caller (0.001s)
# PASS: test_chain_depth_exceeded (0.001s)
# PASS: test_deny_reason_caller_not_allowed (0.001s)
Kyverno Alternative¶
Kyverno provides admission control with YAML policies. It does not support ext_authz — if OBO chain validation at runtime is required, OPA/Gatekeeper is the only option.
Reference Implementation¶
| File | Purpose |
|---|---|
policy/opa/policies/validate-obo-chain.rego |
Runtime OBO chain validation: JWT signature, audience, chain depth, TTL |
policy/opa/policies/require-spiffe-id.rego |
Admission: reject pods with unregistered SPIFFE IDs |
policy/opa/policies/no-privileged-containers.rego |
Admission: container security hardening |
policy/opa/tests/obo-chain_test.rego |
Unit tests for OBO chain policy |
gateway/obo-enforcement/ext-authz-config.yaml |
OPA DaemonSet deployment + ext_authz server config |
policy/kyverno/ |
Kyverno alternative policies (admission only) |