PATTERN Cited by 1 source
Ingress-layer authorization offload¶
Problem¶
Application services repeatedly re-implement authorization in application code: JWT parsing, scope / role / permission checks, audit logging, metric emission. Every team does it slightly differently. Every team now owns authorization runbooks, bugs, and migrations. Authorization-related incidents become per-app, not centrally manageable.
Solution¶
Move the authorization enforcement point into the ingress / edge proxy. Every incoming request passes through the proxy; the proxy evaluates policy against the request payload and returns allow / deny / custom response before the request reaches the app. Application services become authorization-free on the incoming direction — they accept what the proxy passed through.
Shape¶
- The ingress proxy exposes an authorization filter (e.g., an
opaAuthorizeRequestSkipper filter, an ext-authz Envoy filter, a Kong plugin, an AWS API Gateway Lambda authorizer). - Routes are annotated / configured to invoke the filter with a policy / bundle identifier.
- The proxy hosts (or calls) the policy engine; the developer contract is the annotation, nothing else.
- Observability (decision count, latency, decision IDs for audit) is emitted by the proxy, not by the app.
Developer-facing contract¶
A single Ingress / route annotation:
That is the entire opt-in. The app team does not:
- Deploy a sidecar.
- Configure a bundle fetcher.
- Wire authorization metrics.
- Implement allow/deny in application code.
The app team does need to:
- Maintain a Rego policy bundle named after the application ID.
- Keep the policy bundle valid (CI lint / publish pipeline).
Why this shape works¶
- One authorization implementation, not N. Every team gets the same engine, same observability, same blast-radius envelope.
- Zero app-code entanglement. Apps never import an authz SDK, never version-skew with its upgrades.
- Centralised migration surface. Upgrading the engine, adding a capability (decision streaming, attribute-based), rolling out a new bundle format — all done at the platform layer.
- Audit trail is uniform. Every decision is emitted from the same place; decision IDs are platform-generated.
Trade-offs¶
- Authorization logic that depends on application state is hard
at ingress. If
"may this user modify this document?"requires looking up document-ownership in the app's database, the ingress can't answer. Solutions: pass data into the policy viadata.json, fetch via OPA's external-data capabilities, or split authz into a coarse ingress layer + a fine app-data-dependent layer. - Centralised enforcement point = centralised blast radius. A bad policy rollout affects every app using the filter. Mitigate with canary / staged bundle rollout.
- Platform team now owns an always-on critical path. The SLO obligation is permanent.
Seen in¶
- sources/2024-12-05-zalando-open-policy-agent-in-skipper-ingress — canonical instance. Enabling authorization for an application is "as easy as just stating 'application X should be protected' without touching multiple YAML files, adding monitoring, and inheriting many more responsibilities to be compliant." Paired with patterns/embedded-opa-in-proxy for the enforcement runtime.
Related¶
- systems/skipper-proxy
- systems/open-policy-agent
- systems/envoy — the vanilla OPA Envoy plugin is the canonical alternative instance of this pattern
- concepts/authorization-as-a-service — user-facing shape
- concepts/externalised-authorization — the architectural axis this pattern operationalises
- concepts/platform-team-vs-application-team-split — ownership axis
- patterns/embedded-opa-in-proxy