Skip to content

Authorize HTTP requests with Cedar policy text evaluated in-process against the caller's JWT and the request's method, path and query.

The Cedar policy filter evaluates Cedar permit/forbid statements against each request, in-process, and rejects anything no policy permits. Use it when authorization rules involve who the caller is — not just which header or path — and you want those rules as reviewable policy text rather than code.

Enable it

Add the filter to http_filters before the router, with two inline strings: a Cedar schema (entity types, actions and the context shape) and the policies themselves.

http_filters:
  - name: arion.filters.http.cedar
    typed_config:
      "@type": type.googleapis.com/arion.extensions.filters.http.cedar.cedar_policy.v3.CedarPolicy
      schema: |
        entity User;
        entity HttpPath;
        action "GET" appliesTo {
          principal: [User],
          resource:  [HttpPath],
          context: { http: { method: String, path: String, query?: String } }
        };
      policies: |
        permit (
          principal == User::"anonymous",
          action    == Action::"GET",
          resource  == HttpPath::"/health"
        );
  - name: envoy.filters.http.router
    typed_config:
      "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

Cedar denies by default: the example permits exactly GET /health for unauthenticated callers and every other request gets 403 with body Access denied by policy. Policies and schema are validated at startup, so a typo refuses the config rather than failing requests later.

What a policy sees

Arion maps every request onto a Cedar authorization query:

  • Principal — User::"<sub>", taken from the sub claim of a validated JWT. Without a JWT the principal is User::"anonymous". The entity type is configurable via principal_entity_type (default User).
  • Action — Action::"<METHOD>", e.g. Action::"GET". The action namespace is always Action.
  • Resource — HttpPath::"<path>", the request path. Configurable via resource_entity_type (default HttpPath).
  • Context — a record with http: { method, path, query? } and, when a JWT is present, jwt: { sub, iss, aud, exp, iat, nbf, jti } plus any custom claims. So when { context.jwt.tenant == "acme" } works if the token carries a tenant claim.

Declare in the schema exactly the context fields you use; the shapes above are what the filter injects.

Use the JWT principal

Place the filter after JWT authentication and set payload_in_metadata on the JWT provider — that is what hands the validated claims to Cedar. A policy can then name real subjects:

permit (principal == User::"svc-frontend", action == Action::"GET", resource);

If the claims are absent (no JWT filter, or the provider does not set payload_in_metadata), every request evaluates as User::"anonymous".

Modes and entities

Two knobs control behaviour, both defaulting to the strict side:

enforcement_mode: 1   # LOG_ONLY — log denials, let requests through (0 = ENFORCE)
failure_mode: 1       # FAIL_OPEN — pass on evaluation errors (0 = FAIL_CLOSED)

entities accepts a JSON entity list for hierarchies (groups, roles) your policies reference, and validate_schema_per_request: true re-validates each query against the schema at request time.

Verify

With the config above:

curl -i -X GET  http://127.0.0.1:8080/health   # 200
curl -i -X POST http://127.0.0.1:8080/health   # 403 Access denied by policy

Limits

  • schema, policies and entities are inline strings in the filter config; loading them from a file or over xDS is not supported.
  • No per-route override — one policy set applies to the whole filter chain.
  • The principal comes from the JWT sub claim only; other identity sources need your own filter.
  • Exact matches on the resource use the raw request path; there is no path normalization or template matching.

The Cedar documentation covers the policy language itself — conditions, hierarchies and schema syntax.

Continue

Start typing to search the site.

↑ ↓ to move Enter to open Esc to close