Transparent Proxy
The transparent proxy places OpenProdkt governance in front of existing HTTP and gRPC services. Each protected request is evaluated by the control plane before it reaches the upstream service, so governance applies without changes to the services themselves.
Components
- Envoy receives client traffic. For each protected request, it asks the authorization adapter for a decision and forwards the request upstream only when the decision allows it.
- Authorization adapter (
openprodkt-ext-authz) implements the Envoyext_authzgRPC interface. It maps each request to a configured protected asset and operation, then asks the control plane to evaluate it. - Control plane evaluates the decision against policy and the account's entitlements.
Requirements
- The
proxy.enabledentitlement. Without it,/v1/proxy/evaluatereturns403and every proxied request is blocked. See Licensing & Distribution. - Envoy 1.32 or later.
- Network access from the adapter to the control plane API, and from Envoy to the adapter over gRPC.
Authorization adapter configuration
The adapter reads its configuration from environment variables. It exits with an error at startup if a required variable is missing or the route file is invalid.
| Variable | Required | Default | Purpose |
|---|---|---|---|
OPK_AUTHZ_PDP_BASE_URL | Yes | None | Base URL of the control plane API. |
OPK_AUTHZ_ROUTES_FILE | Yes | None | Path to the route configuration JSON file. |
OPK_AUTHZ_LISTEN_ADDRESS | No | :9191 | gRPC listen address for Envoy ext_authz. |
OPK_AUTHZ_PDP_TIMEOUT | No | 2s | Timeout for each control plane decision call, as a Go duration such as 500ms or 2s. |
The adapter forwards each request's authorization header to the control plane, so decisions are made for the calling identity. It does not use a shared API key.
Health check
The adapter binary includes a health check subcommand:
openprodkt-ext-authz healthcheck [address]
The command exits with 0 when the gRPC listener accepts TCP connections. The default address is 127.0.0.1:9191. The check confirms that the listener is up. It does not verify control plane connectivity.
Route configuration
The route file is trusted deployment configuration. It declares which HTTP and gRPC operations are protected and which asset each one belongs to. The file is a JSON object with http and grpc arrays. Unknown fields are rejected.
{
"http": [
{
"route_id": "refunds-create-http",
"host": "payments.example.com",
"method": "POST",
"path": "/v1/refunds",
"operation_id_header": "Idempotency-Key",
"asset_identity": {
"application": "payments",
"service": "refunds",
"environment": "production"
},
"header_allowlist": ["content-type"]
}
],
"grpc": [
{
"route_id": "refunds-create-grpc",
"full_method": "/example.refund.v1.RefundService/CreateRefund",
"rpc_type": "unary",
"operation_id_header": "idempotency-key",
"asset_identity": {
"application": "payments",
"service": "refunds",
"environment": "production"
}
}
]
}
HTTP route fields
| Field | Description |
|---|---|
route_id | Unique identifier for the route. Must be unique across HTTP and gRPC routes. |
host | Host name the route applies to. Normalized before matching. |
method | HTTP method, uppercase. |
path | Exact path. Must start with / and must not contain ? or #. |
operation_id_header | Request header that carries the operation ID, for example Idempotency-Key. |
asset_identity | Protected asset: application, service, and environment. |
header_allowlist | Optional. Additional request headers forwarded to the control plane. |
gRPC route fields
| Field | Description |
|---|---|
route_id | Unique identifier for the route. |
full_method | Canonical gRPC method, for example /package.Service/Method. |
rpc_type | One of unary, client_streaming, server_streaming, or bidirectional_streaming. |
operation_id_header | Request metadata key that carries the operation ID. |
asset_identity | Protected asset, as for HTTP routes. |
Rules
- Each host, method, and path combination, and each gRPC
full_method, can be mapped only once. - The asset identity comes only from this file. Requests cannot supply or override it.
- The operation ID value must be 1 to 128 characters from letters, digits,
.,_,:, and-. - A request that matches no configured route is denied with
403. A request without a valid operation ID is denied with400.
Envoy configuration
Envoy must enforce the following settings for the proxy to be supported.
- Fail closed. Set
failure_mode_allow: falseon theext_authzfilter. If the adapter does not answer within the timeout, Envoy returns503and the request is not forwarded. - Short timeout. Use a 0.5 second
ext_authztimeout. - No request bodies. Do not set
with_request_body. Request bodies and stream messages are never sent to the adapter, and the proxy does not store them. - Header allowlist. Send only the headers the adapter needs, such as
authorization,content-type, and the operation ID header. - Strip governance headers. Remove inbound
x-openprodkt-*headers in both directions, so clients cannot spoof governance headers. - Path normalization. Enable
normalize_pathandmerge_slashes, and reject escaped slashes, so route matching uses the same path the upstream receives. - Timeouts. Use a 30 second route timeout for REST routes and no route timeout for gRPC streams.
The following fragment shows the ext_authz filter and the adapter cluster. Replace the placeholder names with values for your environment. Add one cluster per upstream service.
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: ServiceUnavailable
grpc_service:
envoy_grpc:
cluster_name: governance_ext_authz
timeout: 0.5s
allowed_headers:
patterns:
- exact: authorization
- exact: content-type
- exact: idempotency-key
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
clusters:
- name: governance_ext_authz
type: STRICT_DNS
connect_timeout: 1s
typed_extension_protocol_options:
envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
"@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
explicit_http_config:
http2_protocol_options: {}
load_assignment:
cluster_name: governance_ext_authz
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address: { address: openprodkt-ext-authz, port_value: 9191 }
Deployment
The proxy is delivered as a container image. Run the adapter and Envoy as separate containers on a private network. Envoy is the only component that receives client traffic. Do not publish the adapter's gRPC port outside that network.
Verify the setup
After you start the components, check the following:
- The adapter health check succeeds:
openprodkt-ext-authz healthcheck. - A request to a protected host and path that is not in the route file is denied with
403. - A request to a configured route without the operation ID header is denied with
400. - A request to a configured route with a valid operation ID is evaluated by the control plane and recorded in the decision history.
Denied and blocked requests
- Denied requests receive an immediate response. The proxy does not hold a request while approval is pending.
- A request that is blocked for approval can be resubmitted after approval. See Transparent proxy retry and Proxy challenge.
- If Envoy cannot reach the adapter within the timeout, the request fails with
503.
For the proxy endpoint, see API Reference.