November 5, 2020 - Patrick Kerwood
This tutorial is a how-to on setting up a Single Sign-on/OpenID Connect proxy with Pomerium, in front of your application using Docker Compose and Traefik.
OpenID Connect is a great way of utilizing Single Sign-on to avoid managing local user accounts on your web application and/or enhancing security. I've used Keycloak Gatekeeper (opens new window) before to achieve the same goal, but have stumbled upon Pomerium which solves the same challange but without having to maintain a Keycloak instance. That's what makes Pomerium a bit simpler and easier to maintain.
Even though the use case here for Pomerium is really simple, you can actually split it up in different services for high availability. Or instead for proxying traffic through Pomerium you can have the authentication service split out, like in the example below.
The different service modes are.
As usual I have included the necessary Traefik configuration to set it up with my standard Traefik setup. (opens new window)
In this setup I use Traefik, as I always do, as "front" proxy taking care of certificates, routing, etc. The traffic to
hello.example.org will be routed through Pomerium and on to the hello world application. If you are not authenticated, Pomerium will redirect you to the identity provider, which will redirect you back efter you have logged in.
You need to choose an identity provider. Pomerium has some great documentation on how to setup your provider, https://www.pomerium.io/docs/identity-providers/ (opens new window). In this example I use Google.
In below Docker Compose file we need to change a couple of things.
head -c32 /dev/urandom | base64to generate it.
IDP_*variables with the provider you chose.
AUTHENTICATE_SERVICE_URLto your public application URL. (All-in-one mode)
..Host('hello.example.org')to your public application URL.
The only thing left is to define the routes. There are two ways you can configure Pomerium, using a config file or using environment variables. Personally I like to keep my configuration in my compose file. That makes it much easier moving and re-use the same compose/configuration file.
The thing with the routes is that it's multiline, but luckily the developers has made it possible to base64 encode it and provide it in an environment variable, which is what we will do here.
Below is a simple example of a Pomerium Route. It forwards traffic from
https://hello.example.org to the docker container
hello-world on port
3000. You can find more route examples on their documentation site. (opens new window)
- from: https://hello.example.org to: http://hello-world:3000 policy: - allow: or: - email: is: email@example.com - domain: is: pomerium.io
Save the route as
routes.yml, base64 encode it and save the output to the
ROUTES variable in the compose file.
base64 -w 0 routes.yml
Here's the example Docker Compose file.
version: "3.8" networks: traefik-proxy: external: true pomerium: services: pomerium: image: pomerium/pomerium:latest container_name: pomerium restart: unless-stopped environment: INSECURE_SERVER: "true" ADDRESS: :80 COOKIE_SECRET: cookie-secret-here IDP_PROVIDER: google IDP_PROVIDER_URL: https://accounts.google.com IDP_CLIENT_ID: yyyy.apps.googleusercontent.com IDP_CLIENT_SECRET: xxxxxxxxxxxxxxxxxxx AUTHENTICATE_SERVICE_URL: https://hello.example.org ROUTES: LSBmcm9tOiBodHRwczovL2hlbGxvLmV4YW1wbGUub3JnCiAgdG86IGh0dHA6Ly9oZWxsby13b3JsZDozMDAwCiAgYWxsb3dlZF91c2VyczoKICAgIC0gdXNlQGV4YW1wbGUub3JnCg== networks: - traefik-proxy - pomerium labels: - traefik.enable=true - traefik.http.services.pomerium.loadbalancer.server.port=80 - traefik.http.routers.pomerium.rule=Host(`hello.example.org`) - traefik.http.routers.pomerium.tls.certresolver=le - traefik.http.routers.pomerium.entrypoints=websecure - traefik.docker.network=traefik-proxy hello-world: image: kerwood/hello-world container_name: hello-world restart: unless-stopped expose: - 3000 networks: - pomerium