Module 7 of 13

Authorization and Policy Enforcement

Identity answers who — policy answers what they can do

3 hours2 labsFree

Start here

Learning objectives

  • Understand authentication vs authorization in zero trust
  • Write Rego policies with Open Policy Agent (OPA)
  • Implement identity-aware authorization with SPIFFE IDs
  • Integrate OPA with Envoy for runtime policy enforcement
IDENTITY-AWARE AUTHORIZATION FLOWService AEnvoy ProxymTLS terminationExtracts SPIFFE IDOPAPolicy engineRego rulesService BmTLSCheck policyAllowOPA Policy (Rego)allow { input.source == "spiffe://example.org/.../api" input.method == "GET" input.path == "/api/orders"}

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

Real world

Where this shows up

  • API endpoint authorization — which services can call which endpoints
  • Data access control — limiting which services can read sensitive data
  • Multi-tenant isolation — ensuring tenant A services cannot access tenant B data
  • Compliance enforcement — automated policy checks for regulatory requirements

Production notes

Keep these close

  • Always test Rego policies with opa test before deploying. Include both positive and negative test cases.
  • Start with broad allow rules, then tighten incrementally. A deny-all start causes outages.
  • Version your Rego policies in Git and deploy them through CI/CD, just like application code.

Common mistakes

What usually breaks

  • Writing overly permissive policies that allow everything initially and never tightening
  • Not testing policies before deploying — broken policies block legitimate traffic
  • Putting authorization logic in application code instead of a policy engine
  • Confusing OPA with a firewall — OPA makes decisions, Envoy enforces them

Think like an engineer

Questions to answer before shipping

  • Should authorization policies be centralized (one OPA instance) or distributed (per-service OPA)?
  • How do you handle policy updates without restarting services?
  • What is the performance impact of calling OPA on every request?
  • How do you audit authorization decisions for compliance?

Key terms

Vocabulary used in this module

OPA

Open Policy Agent — CNCF graduated policy engine

Rego

Declarative policy language used by OPA

ext_authz

Envoy external authorization filter that calls OPA

RBAC

Role-Based Access Control

ABAC

Attribute-Based Access Control

Labs

Hands-on labs

Writing Basic Rego Policies

Learn Rego syntax by writing and testing authorization policies.

  1. Write a Rego policy that allows specific SPIFFE IDs
  2. Write test cases for the policy
  3. Run opa test and verify all tests pass
  4. Experiment with more complex rules
View lab on GitHub

Integrating OPA with Envoy

Deploy OPA as an Envoy external authorization filter.

  1. Deploy OPA as a sidecar alongside Envoy
  2. Configure Envoy ext_authz filter to call OPA
  3. Deploy two services with different SPIFFE IDs
  4. Verify that policies correctly allow/deny requests
View lab on GitHub

Recap

Key takeaways

  • Authentication (who) and authorization (what) are separate concerns
  • OPA provides policy-as-code with the Rego language
  • Envoy ext_authz integrates OPA into the request path transparently
  • Policies should be tested like code — use opa test
  • SPIFFE IDs in policies enable fine-grained service-to-service authorization

Related resources

Keep learning across CodersSecret