Skip to main content

Module 7: Authorization and Policy Enforcement

Identity answers who — policy answers what they can do

3 hours. 2 hands-on labs. Free course module.

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

Why This Matters

Authentication tells you WHO is making a request. Without authorization, authenticated services can access anything. OPA with SPIFFE IDs gives you fine-grained, testable, version-controlled authorization — the critical layer between "identified" and "permitted."

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"}
Architecture diagram for Module 7: Authorization and Policy Enforcement.

Lesson Content

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 Use Cases

  • 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

  • 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

  • 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

  • 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

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

Hands-On Labs

  1. Writing Basic Rego Policies

    Learn Rego syntax by writing and testing authorization policies.

    • Write a Rego policy that allows specific SPIFFE IDs
    • Write test cases for the policy
    • Run opa test and verify all tests pass
    • Experiment with more complex rules

    View lab files on GitHub

  2. Integrating OPA with Envoy

    Deploy OPA as an Envoy external authorization filter.

    • Deploy OPA as a sidecar alongside Envoy
    • Configure Envoy ext_authz filter to call OPA
    • Deploy two services with different SPIFFE IDs
    • Verify that policies correctly allow/deny requests

    View lab files on GitHub

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