---
title: SSO with traefik forward auth and Dex
description: Traefik forward auth needs an authentication backend, but if you don't want to use a cloud provider, you can setup your own simple OIDC backend, using Dex.
---
# Traefik Forward Auth for SSO with Dex (Static)

[Traefik Forward Auth](/docker-swarm/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](https://github.com/dexidp/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][tfa-google], or GitHub, or to a [Keycloak][keycloak] installation.

--8<-- "recipe-tfa-ingredients.md"

## Preparation

### Setup dex config

Create `/var/data/config/dex/config.yml` something like the following (*this is a bare-bones, [minimal example](https://github.com/dexidp/dex/blob/master/config.dev.yaml)*). 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`*):

```yaml
# 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:

```bash
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)!
```

1. This is the staticClients.id value in config.yml above
2. This is the staticClients.secret value in config.yml above
3. This is the issuer value in config.yml above, and it has to be reachable via a browser
4. Make this up. It's not configured anywhere else
5. This should match the value of the traefik hosts labels in Traefik Forward Auth
6. 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:

```yaml
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
```

--8<-- "premix-cta.md"

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

```yaml
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 :thumbsup:

### Protect services

To protect any other service, ensure the service itself is exposed by Traefik. Add the following label:

```yaml
- "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 "Summary"
    Created:

    * [X] Traefik-forward-auth configured to authenticate against Dex (static)

[^1]: You can remove the `whoami` container once you know Traefik Forward Auth is working properly

{% include 'recipe-footer.md' %}
