mirror of
https://github.com/pomerium/pomerium.git
synced 2025-08-06 10:21:05 +02:00
* 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:
parent
05dc39215e
commit
86614b5b0e
6 changed files with 200 additions and 190 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
||||

|
||||
|
||||
|
|
|
@ -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:
|
||||
|
||||

|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue