Skip to content

Using Traefik Forward Auth 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:

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

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 as follows:

DEFAULT_PROVIDER: oidc
PROVIDERS_OIDC_CLIENT_ID: foo                         # This is the staticClients.id value in config.yml above
PROVIDERS_OIDC_CLIENT_SECRET: bar                     # This is the staticClients.secret value in config.yml above
PROVIDERS_OIDC_ISSUER_URL: https://dex.example.com    # This is the issuer value in config.yml above, and it has to be reachable via a browser
SECRET: imtoosexyformyshorts                          # Make this up. It's not configured anywhere else
AUTH_HOST: auth.example.com                           # This should match the value of the traefik hosts labels in Traefik Forward Auth
COOKIE_DOMAIN: example.com                            # This should match your base domain

Setup Docker Stack for Dex

Create a docker swarm config file in docker-compose syntax (v3), something like this:

Tip

I automatically and instantly share (with my sponsors) a private "premix" git repository, which includes necessary docker-compose and env files for all published recipes. This means that sponsors can launch any recipe with just a git pull and a docker stack deploy 👍.

🚀 Update: Premix now includes an ansible playbook, so that sponsors can deploy an entire stack + recipes, with a single ansible command! (more here)

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: ['serve','/config.yml']
    deploy:
      labels:
      # traefik
      - traefik.enable=true
      - traefik.docker.network=traefik_public

      # traefikv1
      - traefik.frontend.rule=Host:dex.example.com
      - traefik.port=5556
      - traefik.docker.network=traefik_public

      # and for traefikv2:
      - "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

Setup Docker Stack for Traefik Forward Auth

Now create a docker swarm config file in docker-compose syntax (v3), something like this:

version: "3.2"

services:

  traefik-forward-auth:
    image: thomseddon/traefik-forward-auth:2.1.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 (if you were previously using an oauth_proxy for this, you may have to migrate some labels from the oauth_proxy serivce to the service itself). 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 📓


  1. 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 make your waiter happy? (..and support development of current and future recipes!) Sponsor me on Github / Patreon, or see the contribute page for more (free or paid) ways to say thank you! 👏

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. (*double-opt-in, no monkey business, no spam)

Notify me 🔔

Be the first to know when recipes are added / improved!

    We won't send you spam. Unsubscribe at any time. No monkey-business.

    Powered By ConvertKit

    Your comments? 💬


    Last update: January 29, 2021