-
Notifications
You must be signed in to change notification settings - Fork 33
Add Envoy Proxy integration documentation for Permit.io #599
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
omer9564
wants to merge
6
commits into
master
Choose a base branch
from
omer/per-13495-envoy-filter-pdp-integration
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+247
−0
Open
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
f16a6f0
Add Envoy Proxy integration documentation for Permit.io
omer9564 55c0cf6
Merge branch 'master' into omer/per-13495-envoy-filter-pdp-integration
zeevmoney cb75b24
Merge branch 'master' into omer/per-13495-envoy-filter-pdp-integration
omer9564 2f9f0d9
Document ingesting Envoy decisions into Permit Audit Logs via permiti…
omer9564 0f216da
fix(envoy): remove placeholder link that breaks the Netlify build
omer9564 e508d96
docs(envoy): address review findings on URL mapping, audit logs, and …
omer9564 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
|
omer9564 marked this conversation as resolved.
|
||
| ``` | ||
|
|
||
| `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). | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.