Authentication tells you who is making a request. Authorization tells you what they are allowed to do. SPIFFE handles authentication (via SVIDs). This module adds the authorization layer using Open Policy Agent (OPA).
Authentication vs Authorization
- Authentication (SPIFFE/SPIRE): "This request is from spiffe://example.org/ns/default/sa/orders-api"
- Authorization (OPA): "The orders-api service is allowed to GET /api/orders but NOT DELETE /api/users"
Open Policy Agent (OPA)
OPA is a CNCF graduated project that provides policy-as-code. Policies are written in Rego, a declarative language designed for structured data.
# Basic Rego policy
package authz
default allow = false
# Allow the orders service to read orders
allow {
input.source_spiffe_id == "spiffe://example.org/ns/default/sa/orders-api"
input.request_method == "GET"
startswith(input.request_path, "/api/orders")
}
# Allow the admin service full access
allow {
input.source_spiffe_id == "spiffe://example.org/ns/default/sa/admin"
}
# Deny everything else (default allow = false)
Envoy External Authorization
Envoy proxy can check every request against OPA before forwarding it to the backend service. The flow is: client connects with mTLS (Envoy terminates, extracts SPIFFE ID), Envoy sends the request details and SPIFFE ID to OPA, OPA evaluates the Rego policy and returns allow/deny, and Envoy either forwards the request or returns 403 Forbidden.
Context-Aware Authorization
Beyond simple SPIFFE ID matching, policies can consider the HTTP method and path, request headers, time of day, source namespace and environment, and custom claims in JWT-SVIDs.
Policy Testing
# Test your policies before deploying
# test_authz.rego
package authz
test_orders_api_can_read {
allow with input as {
"source_spiffe_id": "spiffe://example.org/ns/default/sa/orders-api",
"request_method": "GET",
"request_path": "/api/orders/123"
}
}
test_orders_api_cannot_delete_users {
not allow with input as {
"source_spiffe_id": "spiffe://example.org/ns/default/sa/orders-api",
"request_method": "DELETE",
"request_path": "/api/users/456"
}
}
# Run tests: opa test . -v