Traefik Forward Auth for SSO with Dex (Static)
Traefik Forward Auth is incredibly useful to secure services with an additional layer of authentication, provided by an OIDC-compatible provider. The simplest possible provider is a self-hosted instance of CoreOS's Dex, configured with a static username and password. This recipe will "get you started" with Traefik Forward Auth, providing a basic authentication layer. In time, you might want to migrate to a "public" provider, like Google, or GitHub, or to a Keycloak installation.
Ingredients
Ingredients
Already deployed:
- Docker swarm cluster with persistent shared storage
- Traefik configured per design
New:
- DNS entry for your auth host ("auth.yourdomain.com" is a good choice), pointed to your keepalived IP
Preparation
Setup dex config
Create /var/data/config/dex/config.yml
something like the following (this is a bare-bones, minimal example). At the very least, you want to replace all occurances of example.com
with your own domain name. (If you change nothing else, your ID is foo
, your secret is bar
, your username is admin@yourdomain
, and your password is password
):
# The base path of dex and the external name of the OpenID Connect service.
#
# This is the canonical URL that all clients MUST use to refer to dex. If a
# path is provided, dex's HTTP service will listen at a non-root URL.
issuer: https://dex.example.com
storage:
type: sqlite3
config:
file: var/sqlite/dex.db
web:
http: 0.0.0.0:5556
oauth2:
skipApprovalScreen: true
staticClients:
- id: foo
redirectURIs:
- 'https://auth.example.com/_oauth'
name: 'example.com'
secret: bar
enablePasswordDB: true
staticPasswords:
- email: "admin@example.com"
# bcrypt hash of the string "password"
hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W"
username: "admin"
userID: "08a8684b-db88-4b73-90a9-3cd1661f5466"
Prepare Traefik Forward Auth environment
Create /var/data/config/traefik-forward-auth/traefik-forward-auth.env
per the following example configuration:
DEFAULT_PROVIDER=oidc
PROVIDERS_OIDC_CLIENT_ID=foo # (1)!
PROVIDERS_OIDC_CLIENT_SECRET=bar #(2)!
PROVIDERS_OIDC_ISSUER_URL=https://dex.example.com # (3)!
SECRET=imtoosexyformyshorts # (4)!
AUTH_HOST=auth.example.com #(5)!
COOKIE_DOMAIN=example.com #(6)!
- This is the staticClients.id value in config.yml above
- This is the staticClients.secret value in config.yml above
- This is the issuer value in config.yml above, and it has to be reachable via a browser
- Make this up. It's not configured anywhere else
- This should match the value of the traefik hosts labels in Traefik Forward Auth
- This should match your base domain
Setup Docker Stack for Dex
Now create a docker swarm config file in docker-compose syntax (v3), per the following example:
version: '3'
services:
dex:
image: dexidp/dex
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/data/config/dex/config.yml:/config.yml:ro
networks:
- traefik_public
command: ['dex', 'serve','/config.yml']
deploy:
labels:
# traefik
- "traefik.enable=true"
- "traefik.docker.network=traefik_public"
- "traefik.http.routers.dex.rule=Host(`dex.example.com`)"
- "traefik.http.routers.dex.entrypoints=https"
- "traefik.http.services.dex.loadbalancer.server.port=5556"
networks:
traefik_public:
external: true
Fast-track with premix! π
"Premix" is a git repository which includes necessary docker-compose and env files for all published recipes. This means that you can launch any recipe with just a git pull
and a docker stack deploy
π.
π Update: Premix now includes an ansible playbook, enabling you to deploy an entire stack + recipes, with a single ansible command! (more here)
Setup Docker Stack for Traefik Forward Auth
Now create a docker swarm config file for traefik-forward-auth, in docker-compose syntax (v3), per the following example:
version: "3.2"
services:
traefik-forward-auth:
image: thomseddon/traefik-forward-auth:2.2.0
env_file: /var/data/config/traefik-forward-auth/traefik-forward-auth.env
volumes:
- /var/data/config/traefik-forward-auth/config.ini:/config.ini:ro
networks:
- traefik_public
deploy:
labels:
# traefikv1
- "traefik.port=4181"
- "traefik.frontend.rule=Host:auth.example.com"
- "traefik.frontend.auth.forward.address=http://traefik-forward-auth:4181"
- "traefik.frontend.auth.forward.trustForwardHeader=true"
# traefikv2
- "traefik.docker.network=traefik_public"
- "traefik.http.routers.auth.rule=Host(`auth.example.com`)"
- "traefik.http.routers.auth.entrypoints=https"
- "traefik.http.routers.auth.tls=true"
- "traefik.http.routers.auth.tls.domains[0].main=example.com"
- "traefik.http.routers.auth.tls.domains[0].sans=*.example.com"
- "traefik.http.routers.auth.tls.certresolver=main"
- "traefik.http.routers.auth.service=auth@docker"
- "traefik.http.services.auth.loadbalancer.server.port=4181"
- "traefik.http.middlewares.forward-auth.forwardauth.address=http://traefik-forward-auth:4181"
- "traefik.http.middlewares.forward-auth.forwardauth.trustForwardHeader=true"
- "traefik.http.middlewares.forward-auth.forwardauth.authResponseHeaders=X-Forwarded-User"
- "traefik.http.routers.auth.middlewares=forward-auth"
# This simply validates that traefik forward authentication is working
whoami:
image: containous/whoami
networks:
- traefik_public
deploy:
labels:
# traefik
- "traefik.enable=true"
- "traefik.docker.network=traefik_public"
# traefikv1
- "traefik.frontend.rule=Host:whoami.example.com"
- "traefik.http.services.whoami.loadbalancer.server.port=80"
- "traefik.frontend.auth.forward.address=http://traefik-forward-auth:4181"
- "traefik.frontend.auth.forward.authResponseHeaders=X-Forwarded-User"
- "traefik.frontend.auth.forward.trustForwardHeader=true"
# traefikv2
- "traefik.http.routers.whoami.rule=Host(`whoami.example.com`)"
- "traefik.http.routers.whoami.entrypoints=https"
- "traefik.http.services.whoami.loadbalancer.server.port=80"
- "traefik.http.routers.whoami.middlewares=forward-auth"
networks:
traefik_public:
external: true
Serving
Launch
Deploy dex with docker stack deploy dex -c /var/data/dex/dex.yml
, to launch dex, and then deploy Traefik Forward Auth with docker stack deploy traefik-forward-auth -c /var/data/traefik-forward-auth/traefik-forward-auth.yml
Once you redeploy traefik-forward-auth with the above, it should use dex as an OIDC provider, authenticating you against the staticPasswords
username and hashed password described in config.yml
above.
Test
Browse to https://whoami.example.com
(obviously, customized for your domain and having created a DNS record), and all going according to plan, you'll be redirected to a CoreOS Dex login. Once successfully logged in, you'll be directed to the basic whoami page
Protect services
To protect any other service, ensure the service itself is exposed by Traefik. Add the following label:
- "traefik.http.routers.radarr.middlewares=forward-auth"
And re-deploy your services :)
Summary
What have we achieved? By adding an additional label to any service, we can secure any service behind our (static) OIDC provider, with minimal processing / handling overhead.
Summary
Created:
- Traefik-forward-auth configured to authenticate against Dex (static)
Chef's notes π
-
You can remove the
whoami
container once you know Traefik Forward Auth is working properlyΒ β©
Tip your waiter (sponsor) π
Did you receive excellent service? Want to compliment the chef? (..and support development of current and future recipes!) Sponsor me on Github / Ko-Fi / Patreon, or see the contribute page for more (free or paid) ways to say thank you! π
Employ your chef (engage) π€
Is this too much of a geeky PITA? Do you just want results, stat? I do this for a living - I'm a full-time Kubernetes contractor, providing consulting and engineering expertise to businesses needing short-term, short-notice support in the cloud-native space, including AWS/Azure/GKE, Kubernetes, CI/CD and automation.
Learn more about working with me here.
Flirt with waiter (subscribe) π
Want to know now when this recipe gets updated, or when future recipes are added? Subscribe to the RSS feed, or leave your email address below, and we'll keep you updated.