forked from pomerium/pomerium
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs: add JWT Verification w/Envoy guide (pomerium#1974)
* docs: add JWT Verification w/Envoy guide * add sentence about usage, fix formatting of config
- Loading branch information
1 parent
c99ceae
commit 00a1cb7
Showing
3 changed files
with
231 additions
and
2 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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,227 @@ | ||
--- | ||
title: JWT Verification | ||
lang: en-US | ||
meta: | ||
- name: keywords | ||
content: pomerium identity-access-proxy envoy jwt | ||
description: >- | ||
This example demonstrates how to verify the Pomerium JWT assertion header using Envoy. | ||
--- | ||
|
||
# JWT Verification | ||
This example demonstrates how to verify the [Pomerium JWT assertion header](https://www.pomerium.io/reference/#pass-identity-headers) using [Envoy](https://www.envoyproxy.io/). This is useful for legacy or 3rd party applications which can't be modified to perform verification themselves. | ||
|
||
## Requirements | ||
- [Docker](https://www.docker.com/) | ||
- [Docker Compose](https://docs.docker.com/compose/) | ||
- [mkcert](https://github.com/FiloSottile/mkcert) | ||
|
||
## Overview | ||
Two services are configured in a `docker-compose.yaml` file: | ||
|
||
- `pomerium` running an all-in-one deployment of Pomerium on `*.localhost.pomerium.io` | ||
- `envoy-jwt-checker` running envoy with a JWT Authn filter | ||
|
||
Once running, the user visits [verify.localhost.pomerium.io](https://verify.localhost.pomerium.io), is authenticated through [authenticate.localhost.pomerium.io](https://authenticate.localhost.pomerium.io), and then the HTTP request is sent to envoy which proxies it to [`verify.pomerium.com`](https://verify.pomerium.com). | ||
|
||
Before allowing the request Envoy will verify the signed JWT assertion header using the public key defined by [authenticate.localhost.pomerium.io/.well-known/pomerium/jwks.json](https://authenticate.int.example.com/.well-known/pomerium/jwks.json). | ||
|
||
## Setup | ||
|
||
### 1. Docker Compose | ||
Create a `docker-compose.yaml` file containing: | ||
|
||
```yaml | ||
version: "3.8" | ||
services: | ||
pomerium: | ||
image: pomerium/pomerium:latest | ||
ports: | ||
- "443:443" | ||
volumes: | ||
- type: bind | ||
source: ./cfg/pomerium.yaml | ||
target: /pomerium/config.yaml | ||
- type: bind | ||
source: ./certs/_wildcard.localhost.pomerium.io.pem | ||
target: /pomerium/_wildcard.localhost.pomerium.io.pem | ||
- type: bind | ||
source: ./certs/_wildcard.localhost.pomerium.io-key.pem | ||
target: /pomerium/_wildcard.localhost.pomerium.io-key.pem | ||
|
||
envoy-jwt-checker: | ||
image: envoyproxy/envoy:v1.17.1 | ||
ports: | ||
- "10000:10000" | ||
volumes: | ||
- type: bind | ||
source: ./cfg/envoy.yaml | ||
target: /etc/envoy/envoy.yaml | ||
``` | ||
### 2. Certificates | ||
Using [`mkcert`](https://github.com/FiloSottile/mkcert) generate a certificate for `*.localhost.pomerium.io` in a `certs` directory: | ||
|
||
```bash | ||
mkdir certs | ||
cd certs | ||
mkcert '*.localhost.pomerium.io' | ||
``` | ||
|
||
### 3. Envoy Configuration | ||
Create a `cfg` directory containing the following `envoy.yaml` file: | ||
|
||
```yaml | ||
admin: | ||
access_log_path: /dev/null | ||
address: | ||
socket_address: { address: 127.0.0.1, port_value: 9901 } | ||
static_resources: | ||
listeners: | ||
- name: ingress-http | ||
address: | ||
socket_address: { address: 0.0.0.0, port_value: 10000 } | ||
filter_chains: | ||
- filters: | ||
- name: envoy.filters.network.http_connection_manager | ||
typed_config: | ||
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager | ||
stat_prefix: ingress_http | ||
codec_type: AUTO | ||
route_config: | ||
name: verify | ||
virtual_hosts: | ||
- name: verify | ||
domains: ["*"] | ||
routes: | ||
- match: | ||
prefix: "/" | ||
route: | ||
cluster: egress-verify | ||
auto_host_rewrite: true | ||
http_filters: | ||
- name: envoy.filters.http.jwt_authn | ||
typed_config: | ||
"@type": type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication | ||
providers: | ||
pomerium: | ||
issuer: authenticate.localhost.pomerium.io | ||
audiences: | ||
- verify.localhost.pomerium.io | ||
from_headers: | ||
- name: X-Pomerium-Jwt-Assertion | ||
remote_jwks: | ||
http_uri: | ||
uri: https://authenticate.localhost.pomerium.io/.well-known/pomerium/jwks.json | ||
cluster: egress-authenticate | ||
timeout: 1s | ||
rules: | ||
- match: | ||
prefix: / | ||
requires: | ||
provider_name: pomerium | ||
- name: envoy.filters.http.router | ||
clusters: | ||
- name: egress-verify | ||
connect_timeout: 0.25s | ||
type: STRICT_DNS | ||
lb_policy: ROUND_ROBIN | ||
load_assignment: | ||
cluster_name: verify | ||
endpoints: | ||
- lb_endpoints: | ||
- endpoint: | ||
address: | ||
socket_address: | ||
address: verify.pomerium.com | ||
port_value: 443 | ||
transport_socket: | ||
name: tls | ||
typed_config: | ||
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext | ||
sni: verify.pomerium.com | ||
- name: egress-authenticate | ||
connect_timeout: '0.25s' | ||
type: STRICT_DNS | ||
lb_policy: ROUND_ROBIN | ||
load_assignment: | ||
cluster_name: authenticate | ||
endpoints: | ||
- lb_endpoints: | ||
- endpoint: | ||
address: | ||
socket_address: | ||
address: pomerium | ||
port_value: 443 | ||
transport_socket: | ||
name: tls | ||
typed_config: | ||
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext | ||
sni: authenticate.localhost.pomerium.io | ||
``` | ||
|
||
Envoy configuration can be quite verbose, but the crucial bit is the HTTP filter: | ||
|
||
```yaml | ||
- name: envoy.filters.http.jwt_authn | ||
typed_config: | ||
"@type": type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication | ||
providers: | ||
pomerium: | ||
issuer: authenticate.localhost.pomerium.io | ||
audiences: | ||
- verify.localhost.pomerium.io | ||
from_headers: | ||
- name: X-Pomerium-Jwt-Assertion | ||
remote_jwks: | ||
http_uri: | ||
uri: https://authenticate.localhost.pomerium.io/.well-known/pomerium/jwks.json | ||
cluster: egress-authenticate | ||
timeout: 1s | ||
rules: | ||
- match: | ||
prefix: / | ||
requires: | ||
provider_name: pomerium | ||
``` | ||
|
||
This configuration pulls the JWT out of the `X-Pomerium-Jwt-Assertion` header, verifies the `iss` and `aud` claims and checks the signature via the public key defined at the `jwks.json` endpoint. Documentation for additional configuration options is available here: [Envoy JWT Authentication](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/jwt_authn_filter#config-http-filters-jwt-authn). | ||
|
||
### 4. Pomerium Configuration | ||
Create a `pomerium.yaml` file in the `cfg` directory containing: | ||
|
||
```yaml | ||
authenticate_service_url: https://authenticate.localhost.pomerium.io | ||
certificate_file: "/pomerium/_wildcard.localhost.pomerium.io.pem" | ||
certificate_key_file: "/pomerium/_wildcard.localhost.pomerium.io-key.pem" | ||
idp_provider: google | ||
idp_client_id: REPLACE_ME | ||
idp_client_secret: REPLACE_ME | ||
cookie_secret: WwMtDXWaRDMBQCylle8OJ+w4kLIDIGd8W3cB4/zFFtg= | ||
shared_secret: WwMtDXWaRDMBQCylle8OJ+w4kLIDIGd8W3cB4/zFFtg= | ||
signing_key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUdxWllpVzJycVo3TUdKTGp4bnNZVWJJcmZxNFdwR044RlgzQVh2UnRjSHdvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFYVd1UkNKMjFrL2JvUjNNRytPOVlHQjNXR0R1anVXMHFLVWhucUVwVS9JKzFoZmhuZEJ0WApDZGFpaGVGb0FOWXVCRUp3MFZhRml6QnVZb3l5RVAzOXBRPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= | ||
policy: | ||
- from: https://verify.localhost.pomerium.io | ||
to: http://envoy-jwt-checker:10000 | ||
allowed_domains: | ||
- pomerium.com | ||
pass_identity_headers: true | ||
``` | ||
|
||
You will need to replace the identity provider credentials for this to work. | ||
|
||
## Run | ||
You should now be able to run the example with: | ||
|
||
```bash | ||
docker-compose up | ||
``` | ||
|
||
Visit [verify.localhost.pomerium.io](https://verify.localhost.pomerium.io), login and you see the Pomerium verify page. However, visiting Envoy directly via [localhost:10000](http://localhost:10000) should return a `Jwt is missing` error, thus requiring Pomerium to access Envoy. |
This file contains 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