About
This extension implements Cedar policy evaluation as an inline Envoy HTTP filter. It uses the Cedar-Go library to make authorization decisions on each request without a separate authorization server.
Key Features
- Inline Cedar evaluation: Load and evaluate Cedar policy files directly in the Envoy data path using the Cedar-Go library. No separate authorization server required.
- Structured authorization model: Maps HTTP requests to Cedar's Principal/Action/Resource
model with rich context for fine-grained
whenclause conditions. - Entity hierarchies: Optionally load Cedar entity data to enable group membership and
hierarchical authorization checks (e.g.,
principal in Group::"admins"). - Dry-run mode: Log authorization decisions without enforcing them, useful for testing policies in production.
- Fail-open mode: Allow requests through when policy evaluation encounters an error, instead of returning a 500 response.
- mTLS and SPIFFE identity: Access client certificate attributes including SPIFFE IDs (URI SANs), DNS SANs, and certificate subject in policy context.
- Configurable deny responses: Customize the HTTP status code, body, and headers returned when a request is denied.
Authorization Model
HTTP requests are mapped to Cedar authorization concepts:
- Principal: Derived from a configurable request header (e.g.,
User::"alice"from thex-user-idheader). The entity type is set viaprincipal_typeand the ID is extracted from the header specified byprincipal_id_header. - Action: Derived from the HTTP method (e.g.,
Action::"GET",Action::"POST"). The entity type defaults toActionand can be customized viaaction_type. - Resource: Derived from the request path without query string (e.g.,
Resource::"/api/users"). The entity type defaults toResourceand can be customized viaresource_type. - Context: Rich request attributes available in Cedar
whenclauses.
Context Record Structure
The context record passed to Cedar policies contains the following structure:
{
"request": {
"method": "GET",
"path": "/api/v1/resource?key=value",
"host": "example.com",
"scheme": "https",
"protocol": "HTTP/1.1",
"headers": {
"authorization": "Bearer ...",
"content-type": "application/json"
}
},
"source": {
"address": "10.0.0.1:5000",
"certificate": {
"uri_san": "spiffe://cluster.local/ns/default/sa/client",
"dns_san": "client.default.svc.cluster.local",
"subject": "CN=client,O=example",
"sha256_digest": "abc123..."
}
},
"destination": {
"address": "10.0.0.2:443"
},
"connection": {
"tls_version": "TLSv1.3"
},
"parsed_path": ["api", "v1", "resource"],
"parsed_query": {"key": ["value"]}
}Metrics
The extension exposes the cedar_requests_total counter metric with a decision tag that
tracks authorization outcomes. The possible tag values are:
| Decision | Description |
|---|---|
allowed |
The policy allowed the request. |
denied |
The policy denied the request (or an error occurred with fail-open disabled). |
failopen |
An error occurred during policy evaluation and the request was allowed because fail-open mode is enabled. |
dryrun_allow |
The policy denied the request but it was allowed because dry-run mode is enabled. |
Usage Examples
Basic Path Authorization
Run the Cedar extension with a policy that allows GET requests to any resource and denies everything else.
# Create a simple policy file
cat > /tmp/policy.cedar << 'EOF'
permit(
principal,
action == Action::"GET",
resource
);
EOF
# Run the extension
boe run --extension cedar-auth --config '{
"policy": {"file": "/tmp/policy.cedar"},
"principal_type": "User",
"principal_id_header": "x-user-id"
}'
# Test allowed request
curl -H "x-user-id: alice" http://localhost:10000/public/resource
# => 200 OK (proxied to upstream)
# Test denied request (POST)
curl -X POST -H "x-user-id: alice" http://localhost:10000/api/resource
# => 403 Forbidden Conditional Authorization with When Clauses
Use when and unless clauses to add fine-grained conditions based on
request context. The context record contains the full request attributes
including method, path, headers, source, destination, and connection info.
This example allows read-only access to anyone, but restricts write operations to requests that carry a valid admin token header and are made over HTTPS.
cat > /tmp/policy.cedar << 'EOF'
// Anyone can read
permit(
principal,
action == Action::"GET",
resource
);
// Only allow writes over HTTPS with a valid admin token
permit(
principal,
action,
resource
) when {
context.request.scheme == "https" &&
context.request.headers has "x-admin-token" &&
context.request.headers["x-admin-token"] == "my-secret"
};
// Never allow DELETE regardless of other rules
forbid(
principal,
action == Action::"DELETE",
resource
);
EOF
boe run --extension cedar-auth --config '{
"policy": {"file": "/tmp/policy.cedar"},
"principal_type": "User",
"principal_id_header": "x-user-id"
}'
# Test read (allowed for all users)
curl -H "x-user-id: alice" http://localhost:10000/api/resource
# => 200 OK
# Test write with admin token over HTTPS (allowed)
curl -X POST -H "x-user-id: alice" -H "x-admin-token: my-secret" \
https://localhost:10000/api/resource
# => 200 OK
# Test write without admin token (denied)
curl -X POST -H "x-user-id: alice" http://localhost:10000/api/resource
# => 403 Forbidden
# Test DELETE (always denied by forbid rule)
curl -X DELETE -H "x-user-id: alice" -H "x-admin-token: my-secret" \
https://localhost:10000/api/resource
# => 403 Forbidden User-Based Authorization
Use a policy that restricts access based on the user identity derived from the x-user-id header.
cat > /tmp/policy.cedar << 'EOF'
permit(
principal == User::"admin",
action,
resource
);
EOF
boe run --extension cedar-auth --config '{
"policy": {"file": "/tmp/policy.cedar"},
"principal_type": "User",
"principal_id_header": "x-user-id"
}' Group-Based Authorization with Entities
Use Cedar entity hierarchies to authorize based on group membership. Users that are members of the "admins" group are permitted access.
cat > /tmp/policy.cedar << 'EOF'
permit(
principal in Group::"admins",
action,
resource
);
EOF
cat > /tmp/entities.json << 'EOF'
[
{
"uid": {"type": "User", "id": "alice"},
"parents": [{"type": "Group", "id": "admins"}],
"attrs": {}
},
{
"uid": {"type": "Group", "id": "admins"},
"parents": [],
"attrs": {}
}
]
EOF
boe run --extension cedar-auth --config '{
"policy": {"file": "/tmp/policy.cedar"},
"entities_file": "/tmp/entities.json",
"principal_type": "User",
"principal_id_header": "x-user-id"
}' Custom Deny Response
Customize the HTTP response returned when a request is denied.
boe run --extension cedar-auth --config '{
"policy": {"file": "/tmp/policy.cedar"},
"principal_type": "User",
"principal_id_header": "x-user-id",
"deny_status": 401,
"deny_body": "Authentication Required",
"deny_headers": {"www-authenticate": "Bearer"}
}' Inline Policy Configuration
Instead of loading a policy from a file, you can specify the Cedar policy
directly in the extension configuration using the inline field.
boe run --extension cedar-auth --config '{
"policy": {"inline": "permit(principal, action, resource);"},
"principal_type": "User",
"principal_id_header": "x-user-id"
}' Dry-Run Mode
Enable dry-run mode to log authorization decisions without enforcing them. Useful for testing new policies in production without blocking traffic.
boe run --extension cedar-auth --config '{
"policy": {"file": "/tmp/policy.cedar"},
"principal_type": "User",
"principal_id_header": "x-user-id",
"dry_run": true
}' Fail-Open Mode
Enable fail-open mode to allow requests through when policy evaluation encounters an error, instead of returning a 500 response. Useful for non-critical authorization checks where availability is prioritized over enforcement.
boe run --extension cedar-auth --config '{
"policy": {"file": "/tmp/policy.cedar"},
"principal_type": "User",
"principal_id_header": "x-user-id",
"fail_open": true
}'