From 00a1cb74567b60bdd623240d898c8a8aaf9b91ec Mon Sep 17 00:00:00 2001 From: Caleb Doxsey Date: Wed, 10 Mar 2021 15:45:37 -0700 Subject: [PATCH] docs: add JWT Verification w/Envoy guide (#1974) * docs: add JWT Verification w/Envoy guide * add sentence about usage, fix formatting of config --- docs/.vuepress/config.js | 1 + docs/guides/jwt-verification.md | 227 ++++++++++++++++++++++++++++++++ docs/guides/readme.md | 5 +- 3 files changed, 231 insertions(+), 2 deletions(-) create mode 100644 docs/guides/jwt-verification.md diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 10549e287..4565fe98c 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -156,6 +156,7 @@ module.exports = { "argo", "cloud-run", "istio", + "jwt-verification", "kubernetes", "kubernetes-dashboard", "local-oidc", diff --git a/docs/guides/jwt-verification.md b/docs/guides/jwt-verification.md new file mode 100644 index 000000000..6e8196d81 --- /dev/null +++ b/docs/guides/jwt-verification.md @@ -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. diff --git a/docs/guides/readme.md b/docs/guides/readme.md index b40331e6a..090bf9994 100644 --- a/docs/guides/readme.md +++ b/docs/guides/readme.md @@ -5,9 +5,10 @@ This section contains applications, and scenario specific guides for Pomerium. - The [ad-guard](./ad-guard.md) recipe demonstrates how pomerium can be used to augment web applications that only support simplistic authorization mechanisms like basic-auth with single-sign-on driven access policy. - The [argo](./argo.md) guide demonstrates how pomerium can be used to add access control to [Argo](https://argoproj.github.io/projects/argo). - The [Cloud Run](./cloud-run.md) recipe demonstrates deploying pomerium to Google Cloud Run as well as using it to Authorize users to protected Cloud Run endpoints. -- The [Kubernetes Dashboard](./kubernetes-dashboard.md) guide covers how to secure Kubernetes dashboard using Pomerium. +- The [JWT Verification](./jwt-verification.md) guide demonstrates how to verify the Pomerium JWT assertion header using Envoy. +- The [Kubernetes Dashboard](./kubernetes-dashboard.md) guide covers how to secure Kubernetes dashboard using Pomerium. - The [kubernetes](./kubernetes.md) guide covers how to add authentication and authorization to kubernetes dashboard using helm, and letsencrypt certificates. This guide also shows how third party reverse-proxies like nginx/traefik can be used in conjunction with pomerium using forward-auth. - The [local OIDC](./local-oidc.md) guide demonstrates how pomerium can be used with local OIDC server for dev/testing. - The [mTLS](./mtls.md) guide demonstrates how pomerium can be used to add mutual authentication using client certificates and a custom certificate authority. - The [TiddlyWiki](./tiddlywiki.md) guide demonstrates how pomerium can be used to add authentication and authorization to web application using authenticated header. -- The [visual studio code](./vs-code-server.md) guide demonstrates how pomerium can be used to add access control to third-party applications that don't ship with [fine-grained access control](https://github.com/cdr/code-server/issues/905). \ No newline at end of file +- The [visual studio code](./vs-code-server.md) guide demonstrates how pomerium can be used to add access control to third-party applications that don't ship with [fine-grained access control](https://github.com/cdr/code-server/issues/905).