Docs: Update JWT Verification Guide (#2746) (#2787)

* WIP update

* init mutual auth topic page

* WIP

* update JWT verification guide

* s/Java/Json/g

* remove Mutual Auth topic page and references

The new page will be reviewed and added as a separate PR

* fix JSON capitalization throughout

* copy edit to jwt-verification.md

Co-authored-by: cmo-pomerium <91488121+cmo-pomerium@users.noreply.github.com>

Co-authored-by: Alex Fornuto <afornuto@pomerium.com>
Co-authored-by: cmo-pomerium <91488121+cmo-pomerium@users.noreply.github.com>
This commit is contained in:
backport-actions-token[bot] 2021-11-29 15:18:38 -06:00 committed by GitHub
parent 05dc39215e
commit 86614b5b0e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 200 additions and 190 deletions

View file

@ -5,7 +5,7 @@ description: This article describes how to to get a user's identity with Pomeriu
# Getting the user's identity
This article describes how to retrieve a user's identity from a pomerium managed application. Pomerium uses JSON Web Tokens (JWT) to attest that a given request was handled by Pomerium's authorizer service.
This article describes how to retrieve a user's identity from a pomerium managed application. Pomerium uses JSON web tokens (JWT) to attest that a given request was handled by Pomerium's authorizer service.
## Prerequisites

View file

@ -14,7 +14,7 @@ In enterprise environments where multiple services protected by Pomerium communi
## Abstract
When a User communicates with a service downstream of Pomerium, the service can identify that user by the `X-Pomerium-JWT-Assertion` header, added by Pomerium, which provides as a value a Java Web Token (**JWT**) identifying the user.
When a User communicates with a service downstream of Pomerium, the service can identify that user by the `X-Pomerium-JWT-Assertion` header, added by Pomerium, which provides as a value a JSON web token (**JWT**) identifying the user.
Should that service need to communicate with another Pomerium-protected service to construct the response, that connection should be authorized through Pomerium with a [Service Account](/enterprise/concepts.md#service-accounts). Service accounts should be provided to Pomerium from the first service as a bearer token header, i.e. `Authorization: Bearer Pomerium-${service_acount_jwt}`. This header is how the secondary service authenticates the machine-to-machine interaction.

View file

@ -256,7 +256,7 @@ settings:
::::
:::::
1. After you click **Submit**, the modal presents the Java Web Token (**JWT**) for the service account. Temporarily save it somewhere secure, as you will not be able to view it again:
1. After you click **Submit**, the modal presents the JSON web token (**JWT**) for the service account. Temporarily save it somewhere secure, as you will not be able to view it again:
![Service Account Added](../img/service-account-jwt.png)

View file

@ -276,7 +276,7 @@ Before you begin, confirm you are in the correct Namespace. A service account ca
::::
:::::
1. After you click **Submit**, the modal presents the Java Web Token (**JWT**) for the service account. Temporarily save it somewhere secure, as you will not be able to view it again:
1. After you click **Submit**, the modal presents the JSON web token (**JWT**) for the service account. Temporarily save it somewhere secure, as you will not be able to view it again:
![Service Account Added](../img/service-account-jwt.png)

View file

@ -44,7 +44,7 @@ cache_ttl = 60m
This configuration:
- enables authentication by java web token (**JWT**),
- enables authentication by JSON web token (**JWT**),
- defines the header to look at to provide the JWT,
- associates the email_claim in the JWT with the email of the Grafana user,
- specifies the location of the signing key for the JWT to validate it,

View file

@ -11,28 +11,48 @@ description: >-
# 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.
This guide is a practical demonstration of some of the concept of mutual authentication, using JSON web tokens (**JWTs**).
## Requirements
- [Docker](https://www.docker.com/)
- [Docker Compose](https://docs.docker.com/compose/)
- [mkcert](https://github.com/FiloSottile/mkcert)
This guide assumes you already have a working IdP connection to provide user data. See our [Identity Provider](/docs/identity-providers/readme.md) docs for more information.
## Overview
Two services are configured in a `docker-compose.yaml` file:
Three 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
- `httpbin` as our example legacy application without JWT verifivation.
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).
In our Docker Compose configuration we'll define two networks. `pomerium` and `envoy-jwt-checker` will be on the `frontend` network, simulating your local area network (**LAN**). `envoy-jwt-checker` will also be on the `backend` network, along with `httpbin`. This means that `envoy-jwt-checker` is the only other service that can communicate with `httpbin`.
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).
Once running, the user visits [verify.localhost.pomerium.io], is authenticated through [authenticate.localhost.pomerium.io], and then the HTTP request is sent to envoy which proxies it to the httpbin app.
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`.
## Setup
### 1. Docker Compose
Create a `docker-compose.yaml` file containing:
The configuration presented here assumes a working route to the domain space `*.localhost.pomerium.io`. You can make entries in your `hosts` file for the domains used or change this value to match your local environment.
::: tip
Mac and Linux users can use DNSMasq to map the `*.localhost.pomerium.io` domain (including all subdomains) to a specified test address:
- [Local Development with Wildcard DNS] (macOS)
- [Local Development with Wildcard DNS on Linux]
:::
1. Create a `docker-compose.yaml` file containing:
```yaml
version: "3.8"
version: "3.9"
networks:
frontend:
driver: "bridge"
backend:
driver: "bridge"
services:
pomerium:
image: pomerium/pomerium:latest
@ -48,6 +68,8 @@ services:
- type: bind
source: ./certs/_wildcard.localhost.pomerium.io-key.pem
target: /pomerium/_wildcard.localhost.pomerium.io-key.pem
networks:
- frontend
envoy-jwt-checker:
image: envoyproxy/envoy:v1.17.1
@ -57,10 +79,21 @@ services:
- type: bind
source: ./cfg/envoy.yaml
target: /etc/envoy/envoy.yaml
networks:
frontend:
aliases:
- "httpbin-sidecar"
backend:
httpbin:
image: kennethreitz/httpbin
ports:
- "80:80"
networks:
- backend
```
### 2. Certificates
Using [`mkcert`](https://github.com/FiloSottile/mkcert) generate a certificate for `*.localhost.pomerium.io` in a `certs` directory:
1. Using [`mkcert`](https://github.com/FiloSottile/mkcert), generate a certificate for `*.localhost.pomerium.io` in a `certs` directory:
```bash
mkdir certs
@ -68,10 +101,9 @@ cd certs
mkcert '*.localhost.pomerium.io'
```
### 3. Envoy Configuration
Create a `cfg` directory containing the following `envoy.yaml` file:
1. Create a `cfg` directory containing the following `envoy.yaml` file. Envoy configuration can be quite verbose, but the crucial bit is the HTTP filter (highlighted below):
```yaml
```yaml{30-49}
admin:
access_log_path: /dev/null
address:
@ -92,13 +124,13 @@ static_resources:
route_config:
name: verify
virtual_hosts:
- name: verify
domains: ["*"]
- name: httpbin
domains: ["httpbin-sidecar"]
routes:
- match:
prefix: "/"
route:
cluster: egress-verify
cluster: egress-httpbin
auto_host_rewrite: true
http_filters:
- name: envoy.filters.http.jwt_authn
@ -108,7 +140,7 @@ static_resources:
pomerium:
issuer: authenticate.localhost.pomerium.io
audiences:
- verify.localhost.pomerium.io
- httpbin.localhost.pomerium.io
from_headers:
- name: X-Pomerium-Jwt-Assertion
remote_jwks:
@ -123,24 +155,19 @@ static_resources:
provider_name: pomerium
- name: envoy.filters.http.router
clusters:
- name: egress-verify
- name: egress-httpbin
connect_timeout: 0.25s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: verify
cluster_name: httpbin
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
address: httpbin
port_value: 80
- name: egress-authenticate
connect_timeout: '0.25s'
type: STRICT_DNS
@ -159,38 +186,11 @@ static_resources:
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:
1. Create a `pomerium.yaml` file in the `cfg` directory containing:
```yaml
authenticate_service_url: https://authenticate.localhost.pomerium.io
@ -202,29 +202,39 @@ 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=
cookie_secret: REPLACE_ME
shared_secret: REPLACE_ME
signing_key: REPLACE_ME
routes:
- from: https://verify.localhost.pomerium.io
to: http://envoy-jwt-checker:10000
- from: https://httpbin.localhost.pomerium.io
to: http://httpbin-sidecar:10000
pass_identity_headers: true
policy:
- allow:
or:
- domain:
is: pomerium.com
pass_identity_headers: true
is: example.com
```
You will need to replace the identity provider credentials for this to work.
Replace the identity provider credentials, secrets, and signing key. Adjust the policy to match your configuration.
## Run
You should now be able to run the example with:
You should now be able to run the example with the following steps.
1. Turn on the example configuration in Docker:
```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.
1. Visit [httpbin.localhost.pomerium.io](https://httpbin.localhost.pomerium.io). Login and you will be redirected to the httpbin page.
1. In this network configuration you cannot access `httpbin` directly. However, visiting Envoy directly via [localhost.pomerium.io:10000/](http://localhost.pomerium.io:10000/) will return a `Jwt is missing` error, confirming that you must authenticate with Pomerium to access Envoy, and any services accessible through it.
[authenticate.localhost.pomerium.io]: https://authenticate.localhost.pomerium.io
[httpbin.localhost.pomerium.io]: https://verify.localhost.pomerium.io
[Local Development with Wildcard DNS on Linux]: https://sixfeetup.com/blog/local-development-with-wildcard-dns-on-linux
[Local Development with Wildcard DNS]: https://blog.thesparktree.com/local-development-with-wildcard-dns
[verify.localhost.pomerium.io]: https://verify.localhost.pomerium.io