Architecture Note: Envoy ext_authz with an External gRPC Authorization Service
Centralize request authorization at the edge with Envoy's ext_authz filter and a gRPC authorization service. Learn the minimal design, trust boundaries, and operational checks.
31 Jul 2026, 05:13 UTC

The problem: policy without duplication
When multiple backend services must enforce the same authorization rules, copying that logic into each service creates drift and audit gaps. Envoy's ext_authz HTTP filter moves the decision to a dedicated policy decision point (PDP) while Envoy remains the policy enforcement point (PEP). The useful takeaway: you can centralize north-south authorization with one Envoy ingress per trust zone and a gRPC authorization service, but only if you establish identity before the filter runs, send minimal attributes across the boundary, and verify fail-closed behavior for your specific Envoy version.
Requirements
- Centralize request authorization for north-south traffic without embedding policy in every backend.
- Establish caller identity before
ext_authzruns, using mutual TLS (mTLS) peer certificates or a validated JWT. - Strip or overwrite client-supplied identity headers at the edge so downstream services cannot be spoofed.
- Keep the authorization service reachable and sized for peak QPS; it becomes a hard dependency.
- Confirm version-specific defaults for
failure_mode_allow,status_on_error, and route-level overrides.
Smallest suitable design
Deploy one Envoy edge ingress per trust zone. Attach an HTTP connection manager with the ext_authz filter configured to call a gRPC authorization service over a dedicated cluster. Skip the filter only on explicitly public routes via per-route configuration. Do not add sidecars or mesh features unless east-west traffic needs the same policy.
Trust and data boundaries
Envoy is the PEP; the external authorization service is the PDP. Identity must be established before ext_authz executes: terminate mTLS and extract the peer certificate subject, or validate a JWT in a preceding filter. Strip headers such as x-user-id or x-authenticated-user at ingress and pass only authenticated identity attributes to the decision service.
Treat the ext_authz response as trusted for allow/deny decisions and header injection, but do not let the filter become an exfiltration path. Avoid sending cookies, raw authorization headers, or full request bodies unless policy requires them. Use mTLS or a local socket between Envoy and the authorization service, and scope credentials per zone.
Configuration sketch
The following snippet is illustrative; field names and defaults are version-sensitive and must be validated against the target Envoy release.
http_filters:
- name: envoy.filters.http.ext_authz
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
transport_api_version: V3
failure_mode_allow: false
status_on_error:
code: 403
grpc_service:
envoy_grpc:
cluster_name: authz_service
timeout: 0.25s
To skip authorization on a public route, disable the filter per route:
typed_per_filter_config:
envoy.filters.http.ext_authz:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
disabled: true
Risk: enabling failure_mode_allow: true permits an authorization bypass whenever the decision service is unreachable or returns an error. Do not enable it in production without an audited break-glass procedure.
Operational checks and diagnostics
Validate the bootstrap and route configuration with the target Envoy binary before deploying. Run the following on the Envoy host or CI runner with read access to the config file:
envoy --mode validate -c /path/to/bootstrap.yaml
Expected check: the command exits zero and reports no schema or reference errors. If it fails, the output names the offending field.
After startup, inspect the effective configuration through the admin interface (typically bound to 127.0.0.1:9901; restrict network access):
curl -s http://127.0.0.1:9901/config_dump | jq '.configs[] | select(.type_url | contains("ext_authz"))'
Expected check: the ext_authz filter appears in the HTTP connection manager filter chain and in any route-level overrides.
Watch the ext_authz counters and the authorization service cluster health via the admin /stats endpoint. Exercise four paths before rollout: an allowed request, a denied request, the authorization service down, and a malformed gRPC response. Confirm status codes and logs match policy.
Failure modes
Authorization-service outages are the primary risk. Envoy's ext_authz is normally deployed fail-closed, but the exact defaults for failure_mode_allow and status_on_error vary by version; confirm them for your release. Other modes include added latency from the extra network hop, gRPC metadata header-size limits, transport_api_version mismatches between Envoy and the service, and stale dynamic metadata if the response is not consumed correctly.
Conditions that change the design
If decisions are simple and local, replace the network hop with Envoy's JWT authentication filter or the built-in RBAC filter. If east-west traffic needs the same policy, move to sidecars or a service mesh. For high QPS, multi-tenant isolation, or strict latency budgets, add rate limiting, a WAF, decision caching, or regional policy decision points instead of a single global service.
Rollout and verification
Start with failure_mode_allow: false in staging, then production with a small canary route. Track deny rate, error rate, p99 added latency, and authorization-service saturation. Define an audited break-glass procedure rather than silently enabling fail-open. To verify the result, send a request with a known identity to a protected route and confirm the backend receives only the sanitized, authenticated attributes you intended to propagate.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.