HeavyCookie

Mettre en place un SSO avec Traefik

J'avais plusieurs applications web sans authentification sur une instance Traefik avec une simple BasicAuth. Cette dernière est suffisante dans une majorité des cas, mais le fait que les OS ne gèrent pas correctement les gestionnaires de mot de passe type 1Password m'ennuyait.

Je me suis dit que Traefik devait bien avoir un moyen de mettre en place un système plus globale et j'ai donc trouvé ce tutoriel, que j'ai suivi, sans le côté Swarm puisque je suis en Docker classique. Je partage donc ici ma réinterprétation du tutoriel.

Middleware

Je ne reviens pas sur mon installation de Traefik, mais pour le contexte, sachez que toutes mes applications sont connectées à Traefik par le réseau traefik_default et que je configure Traefik par les labels Docker.

Voici ma config docker-compose :

# docker-compose.yml
version: '3'
 
services:
  dex:
    image: dexidp/dex
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - ./dex/:/data/
    networks:
      - traefik_default
    command: ['dex', 'serve', '/data/config.yml']
    labels:
      - traefik.enable=true
      - traefik.http.routers.dex.rule=Host(`dex.example.com`)
      - traefik.http.services.dex.loadbalancer.server.port=5556
      - traefik.docker.network=traefik_default
 
  traefik-forward-auth:
    # Attention, il existe une image différente si vous êtes sur un système ARM.
    # Ajoutez "-arm" à la fin de la ligne suivante si vous êtes dans ce cas.
    image: thomseddon/traefik-forward-auth:2.2.0
    env_file: ./traefik-forward-auth/.env
    volumes:
      - ./traefik-forward-auth/config.ini:/config.ini:ro
    networks:
      - traefik_default
    labels:
      - traefik.enable=true
      - traefik.docker.network=traefik_default
      - traefik.http.services.auth.loadbalancer.server.port=4181
      - traefik.http.routers.auth.rule=Host(`auth.example.com`)
 
      # Configuration SSL spécifique pour bénéficier du wildcard, nécessaire pour le cookie d'authentification
      - traefik.http.routers.auth.tls=true
      - traefik.http.routers.auth.tls.certresolver=letsencrypt
      - traefik.http.routers.auth.tls.domains[0].main=example.com
      - traefik.http.routers.auth.tls.domains[0].sans=*.example.com
      - traefik.http.routers.auth.service=auth@docker
 
      # Configuration du middleware
      - 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
 
networks:
  traefik_default:
    external: true

Ma configuration de Dex :

# dex/config.yml
issuer: https://dex.example.com
 
storage:
  type: sqlite3
  config:
    file: /data/dex.db
 
web:
  http: 0.0.0.0:5556
 
oauth2:
  skipApprovalScreen: true
 
staticClients:
  - id: auth-example
    redirectURIs:
      - 'https://auth.example.com/_oauth'
    name: 'example.com'
    secret: 6P6D9UqCGORQI5Gz3vo7
 
enablePasswordDB: true
 
staticPasswords:
  # Configurez vos utilisateurs ici
  - email: 'admin@example.com'
    hash: 'bcrypted password' # Changez ça
    username: 'admin'
    userID: 'c0ea1e12-8e5a-48c7-968e-87a30a093c03'

Pour générer le mot de passe bcrypt, avec Ruby : ruby -e 'require "bcrypt"; puts BCrypt::Password.create("password")'.

La configuration de base Traefik Forward Auth :

# traefik-forward-auth/.env
DEFAULT_PROVIDER=oidc
# This is the staticClients.id value in config.yml above
PROVIDERS_OIDC_CLIENT_ID=auth-example
# This is the staticClients.secret value in config.yml above
PROVIDERS_OIDC_CLIENT_SECRET=6P6D9UqCGORQI5Gz3vo7
# This is the issuer value in config.yml above, and it has to be reachable via a browser
PROVIDERS_OIDC_ISSUER_URL=https://dex.example.com
# Make this up. It's not configured anywhere else
SECRET=IgDpvuikOnZOey8X7HNE
# This should match the value of the traefik hosts labels in Traefik Forward Auth
AUTH_HOST=auth.example.com
COOKIE_DOMAIN=example.com
LOG_LEVEL=info

Ajouter l'authentification sur vos services

Il ne reste plus qu'à ajouter le middleware sur les containers à sécuriser. Par exemple pour mon instance d'Hyperion, les labels dans mon docker-compose seront les suivants :

# [...]
labels:
  - traefik.enable=true
  - traefik.http.routers.hyperion.rule=Host(`hyperion.example.com`)
  - traefik.http.routers.hyperion.middlewares=forward-auth
  - traefik.http.services.hyperion.loadbalancer.server.port=8090
  - traefik.docker.network=traefik_default

La ligne importante étant traefik.http.routers.hyperion.middlewares=forward-auth qui ajoute le middleware à Traefik.

J'ai quelques services non configurés via Docker (par exemple, le dashboard Traefik) et pour cela j'ajoute le middleware dans la configuration dynamique de Traefik. Par exemple pour le dashboard Traefik :

[http.routers]
  [http.routers.dashboard]
    middlewares = ["forward-auth@docker"]
    service = "api@internal"
    rule = "Host(`traefik.example.com`)"

Votre certificat vers auth.example.com reste autosigné

Si c'est votre cas, c'est probablement parce que vous utilisez la méthode TLS Challenge pour générer votre certificat plutôt que DNS Challenge dans la configuration de Traefik. Allez voir de ce côté-là.