diff --git a/docs/integrations/gateways/envoy.mdx b/docs/integrations/gateways/envoy.mdx new file mode 100644 index 00000000..6ae322b5 --- /dev/null +++ b/docs/integrations/gateways/envoy.mdx @@ -0,0 +1,246 @@ +--- +sidebar_position: 4 +title: Envoy Proxy x Permit +--- + +## Overview + +This guide explains how to integrate Envoy's External Authorization (ext_authz) filter with a Permit.io PDP using **GitOps-managed custom Rego policies**. +With this setup, Envoy acts as a gateway that filters application requests by calling the PDP, which evaluates your Permit policies and returns an allow/deny decision. + +URL Mapping (`POST /allowed_url` on the PDP HTTP API) is a separate path. It is not used by the Envoy ext_authz gRPC integration described here — the entrypoint policy must map the request to a Permit resource and action itself. + +--- + +## Prerequisites + +- **Envoy** deployed with the [External Authorization filter](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/ext_authz_filter) available +- **Permit.io account** and project with a running PDP +- **PDP version** **0.9.10 or higher** (required for this integration) +- **GitOps flow** configured for your Permit environment (see below) +- Basic familiarity with Envoy configuration and OPA/Rego + +--- + +## 1. Configure GitOps for Custom Policies + +Use Permit GitOps to manage your PDP policies (including the Envoy entrypoint) in a Git repository. + +1. **Configure GitOps** for your environment (choose one of the following): + - **Using the CLI**: follow the CLI GitOps guide: [Custom Rego (OPA) and GitOps CLI](/how-to/permit-cli/permit-cli-gitops). + - **Using the manual flow**: follow the GitOps integration guide: [Git and Permit](/integrations/gitops/github). +2. **Clone the GitOps repository** locally and check out the branch for the environment you want Envoy to protect. + +Once GitOps is configured, any changes you push to the repository will be synced to the PDP. + +--- + +## 2. Add an Envoy entrypoint policy under `custom` + +In your GitOps repository: + +1. **Create a new Rego file** under the `custom` directory at the root of the GitOps repo, for example: + + ```text + custom/envoy_entrypoint.rego + ``` + +2. Define an entrypoint rule that Envoy's ext_authz integration will call. + The PDP OPA plugin will evaluate the data document at the path you configure (see the `PDP_OPA_PLUGINS` section below). + + Put the policy in the `permit.custom` namespace used by other custom GitOps files, and derive a Permit resource **key** from the path (Permit resource keys cannot contain `/`): + + ```rego + package permit.custom.envoy + + import data.permit.root as check + import input.attributes.request.http as http_request + + # Boolean entrypoint evaluated by the Envoy ext_authz gRPC plugin + default envoy = false + + envoy { + method := http_request.method + + # First path segment is the resource key: /documents/123 -> "documents" + # This key must match a resource configured in Permit. + segments := split(trim_prefix(http_request.path, "/"), "/") + resource_key := segments[0] + + # Example user & tenant extraction (adapt to your authentication setup) + user_key := http_request.headers["x-user-id"] + tenant_key := http_request.headers["x-tenant-id"] + + check_input := { + "user": { + "key": user_key, + }, + "action": method, + "resource": { + "type": resource_key, + "tenant": tenant_key, + }, + } + + # Call "permit.check" + check.allow with input as check_input + } + ``` + + This boolean entrypoint is enough for Envoy to allow or deny the request. The Audit Log **Decision** column is filled from the boolean result. To also fill **user**, **action**, **resource**, and **tenant**, return an object that includes `permitio_metadata` — see [Ingest Envoy decisions into Permit Audit Logs](#5-ingest-envoy-decisions-into-permit-audit-logs). + +3. Commit and **push** the new file to the GitOps repository. +4. Wait for the PDP to sync the updated policies (or trigger a sync if you are using a CI flow). + +For the full structure of the Envoy authorization input (`input`) used in your policy, see the official +[Envoy ext_authz filter documentation](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/ext_authz_filter). + +--- + +## 3. Configure the PDP OPA plugins + +The PDP **must** be started with an `envoy_ext_authz_grpc` plugin that exposes the Envoy-compatible gRPC service and points it at your custom policy entrypoint. + +Set the **`PDP_OPA_PLUGINS`** environment variable when running the PDP: + +```bash +export PDP_OPA_PLUGINS='{"permit_graph":{},"envoy_ext_authz_grpc":{"addr":":9191","path":"permit/custom/envoy"}}' +``` + +- **`permit_graph`** is required for Permit to function correctly. +- **`envoy_ext_authz_grpc`** enables the Envoy gRPC external authorization endpoint. +- **`addr`** is the address/port on which the gRPC server will listen (e.g. `:9191`). +- **`path`** must match the OPA data path of your Envoy entrypoint policy (in the example above, `data.permit.custom.envoy`). + +The gRPC listener also has to be reachable from Envoy. The PDP image does not expose port `9191` by default: + +- **Docker:** add `-p 9191:9191` to the `docker run` command (or a `ports` entry in Compose). +- **Helm:** add the port to the PDP Service via `pdp.additionalPorts`: + + ```yaml + pdp: + additionalPorts: + - name: grpc + port: 9191 + targetPort: 9191 + ``` + +For more advanced plugin and Envoy integration options (timeouts, metadata, headers, etc.), see the OPA Envoy plugin configuration docs: [OPA Envoy Integration Configuration](https://www.openpolicyagent.org/docs/latest/envoy/#configuration). + +Ensure this variable is set alongside your usual PDP configuration (such as `PDP_API_KEY`, `PDP_DEBUG`, etc.). + +--- + +## 4. Configure Envoy to call the PDP + +In your Envoy configuration, add the **HTTP ext_authz filter** and point it to the PDP gRPC endpoint configured above. + +A minimal example (simplified) might look like this: + +```yaml +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 + grpc_service: + envoy_grpc: + cluster_name: permit_pdp_ext_authz + timeout: 0.5s + +clusters: + - name: permit_pdp_ext_authz + type: logical_dns + connect_timeout: 0.25s + lb_policy: round_robin + http2_protocol_options: {} + load_assignment: + cluster_name: permit_pdp_ext_authz + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: pdp + port_value: 9191 +``` + +`http2_protocol_options` is required so the cluster speaks HTTP/2, which gRPC needs. Without it, the upstream defaults to HTTP/1.1 and ext_authz calls to the PDP can fail. + +Adjust the cluster name, address, and port to match your environment. +Consult the [Envoy ext_authz docs](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/ext_authz_filter) +for the full configuration options. + +--- + +## 5. Ingest Envoy decisions into Permit Audit Logs + +Envoy's ext_authz input is **not** a `permit.check` payload. The PDP records a decision log for the Envoy query (`permit/custom/envoy`) and fills the **Decision** column from the boolean result, but the Audit Log page cannot fill **user**, **action**, **resource**, or **tenant** from that Envoy-shaped input. + +To map those columns, include a `permitio_metadata` object on the **policy result**, using the same structure as [`permit.check()`](/how-to/enforce-permissions/check). Permit reads this object as a fallback when the query input is not a standard check. + +Return an object from the Envoy entrypoint (the OPA Envoy plugin uses the `allowed` field for allow/deny). Put `permitio_metadata` on the **default** as well — otherwise default-denied requests (missing headers, unparseable path) produce an empty audit row and are dropped: + +```rego +package permit.custom.envoy + +import data.permit.root as check +import input.attributes.request.http as http_request + +default envoy := { + "allowed": false, + "permitio_metadata": {"allow": false}, +} + +envoy := { + "allowed": allowed, + "permitio_metadata": { + "allow": allowed, + "user": {"key": user_key}, + "action": method, + "resource": { + "type": resource_key, + "tenant": tenant_key, + }, + }, +} { + method := http_request.method + segments := split(trim_prefix(http_request.path, "/"), "/") + resource_key := segments[0] + user_key := http_request.headers["x-user-id"] + tenant_key := http_request.headers["x-tenant-id"] + + check_input := { + "user": {"key": user_key}, + "action": method, + "resource": { + "type": resource_key, + "tenant": tenant_key, + }, + } + + allowed := check.allow with input as check_input +} +``` + +`permitio_metadata` supports the following fields (all optional; omit any you do not have): + +| Field | Audit Log column | +| --- | --- | +| `allow` | Decision (allow / deny) | +| `user.key` | User | +| `user.email` | User email | +| `user.first_name` / `user.last_name` | User display name | +| `action` | Action | +| `resource.type` | Resource | +| `resource.tenant` | Tenant | + +If the query input already has a standard `permit.check` field (for example `input.user.key`), that value wins. `permitio_metadata` is only used when the corresponding input field is missing. + +:::note +Keep the OPA path under the `permit/` prefix (for example `permit/custom/envoy`). Decision logs whose query path does not start with `permit/` are not ingested into the Audit Log. +::: + +After traffic hits Envoy, open the [Audit Log](https://app.permit.io/audit-log) and use the **All** query type. Envoy evaluations are not `permit.check` queries, so they will not appear if you filter to Check only. Missing columns in the table mean that field was not present on `permitio_metadata`. + +For how to read and filter logs, see [Audit Types & Filtering](/how-to/use-audit-logs/types-and-filtering). diff --git a/sidebars.js b/sidebars.js index ccbb886e..74e7df91 100644 --- a/sidebars.js +++ b/sidebars.js @@ -675,6 +675,7 @@ const sidebars = { "integrations/gateways/aws-api-gateway", "integrations/gateways/kong", "integrations/gateways/nginx", + "integrations/gateways/envoy", ], }, {