Skip to main content

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 Envoy ext_authz gRPC 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.enabled entitlement. Without it, /v1/proxy/evaluate returns 403 and 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.

VariableRequiredDefaultPurpose
OPK_AUTHZ_PDP_BASE_URLYesNoneBase URL of the control plane API.
OPK_AUTHZ_ROUTES_FILEYesNonePath to the route configuration JSON file.
OPK_AUTHZ_LISTEN_ADDRESSNo:9191gRPC listen address for Envoy ext_authz.
OPK_AUTHZ_PDP_TIMEOUTNo2sTimeout 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​

FieldDescription
route_idUnique identifier for the route. Must be unique across HTTP and gRPC routes.
hostHost name the route applies to. Normalized before matching.
methodHTTP method, uppercase.
pathExact path. Must start with / and must not contain ? or #.
operation_id_headerRequest header that carries the operation ID, for example Idempotency-Key.
asset_identityProtected asset: application, service, and environment.
header_allowlistOptional. Additional request headers forwarded to the control plane.

gRPC route fields​

FieldDescription
route_idUnique identifier for the route.
full_methodCanonical gRPC method, for example /package.Service/Method.
rpc_typeOne of unary, client_streaming, server_streaming, or bidirectional_streaming.
operation_id_headerRequest metadata key that carries the operation ID.
asset_identityProtected 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 with 400.

Envoy configuration​

Envoy must enforce the following settings for the proxy to be supported.

  • Fail closed. Set failure_mode_allow: false on the ext_authz filter. If the adapter does not answer within the timeout, Envoy returns 503 and the request is not forwarded.
  • Short timeout. Use a 0.5 second ext_authz timeout.
  • 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_path and merge_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:

  1. The adapter health check succeeds: openprodkt-ext-authz healthcheck.
  2. A request to a protected host and path that is not in the route file is denied with 403.
  3. A request to a configured route without the operation ID header is denied with 400.
  4. 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.