Voici un exemple de docker-compose Traefik, avec son propre nom de domaine et un provider DNS géré par Traefik (OVH dans mon cas, pour la liste complète : https://doc.traefik.io/traefik/https/acme/#providers).
version: '3'
services:
traefik:
container_name: traefik
image: traefik:latest
networks:
- traefik
ports:
- "80:80"
- "443:443"
restart: unless-stopped
command:
- --api.dashboard=true
- --providers.docker=true
- --providers.docker.exposedbydefault=false |
Le réseau "traefik" sera commun à Traefik et aux containers qu'on voudra exposer sur internet, tout passera par la socket Docker sans avoir besoin d'exposer les ports.
On demande à Traefik d'activer son dashboard, mais on n'expose pas par défaut tous les containers. On expose les ports 80 et 443.
- --providers.file.directory=/dynamic_config
- --providers.file.watch=true
- --providers.docker.network=traefik
- --entrypoints.http.address=:80
- --entrypoints.https.address=:443 |
Traefik va chercher les fichiers de config "dynamique" (en opposition à la config lue uniquement au démarrage de Traefik, voir https://doc.traefik.io/traefik/gett [...] overview/) dans un sous-répertoire /dynamic_config
Il va écouter sur les ports 80 et 443.
- --entrypoints.http.http.redirections.entrypoint.to=https
- --entrypoints.http.http.redirections.entrypoint.scheme=https
- --serversTransport.insecureSkipVerify=true |
Traefik va rediriger les connexions entrantes HTTP vers le HTTPS, et pourra se connecter à des URLs internes avec un certificat auto-signé.
- --certificatesresolvers.le.acme.dnschallenge=true
- --certificatesresolvers.le.acme.dnschallenge.provider=ovh
- --certificatesresolvers.le.acme.email=admin@toto.com
- --certificatesresolvers.le.acme.storage=/acme.json
- --certificatesresolvers.le.acme.dnschallenge.delaybeforecheck=0
- --certificatesresolvers.le.acme.dnsChallenge.resolvers=1.1.1.1:53,8.8.8.8:53 |
Tout ce bloc sert à indiquer à Traefik qu'il doit stocker les certificats Let's Encrypt dans le fichier acme.json, qu'il doit utiliser le provider OVH pour le challenge DNS.
- --pilot.token=xxxxxx-xxxxxx-xxxxxx-xxxxxx
|
Depuis Traefik 2.3, un service de monitoring optionnel est proposé, c'est mal pour être prévenu que le reverse proxy n'est plus joignable depuis l'extérieur.
On passe maintenant aux labels
labels:
traefik.enable: "true"
traefik.http.routers.api.rule: "Host(`traefik.toto.com`)"
traefik.http.routers.api.service: "api@internal"
traefik.http.routers.api.entrypoints: "https"
traefik.http.routers.api.tls.certresolver: "le" |
Avec ces labels, on indique à Traefik qu'il doit reverse proxyfier l'accès au dashboard, en HTTPS, en utilisant le resolver "le" pour générer les certificats (celui qu'on a déclaré un peu plus haut).
traefik.http.middlewares.auth.basicauth.users: "admin:$$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" |
De base le dashboard n'est pas sécurisé, on créé ici un middleware fournissant une simple couche d'authentification.
traefik.http.routers.api.tls.domains[0].main: "toto.com"
traefik.http.routers.api.tls.domains[0].sans: "*.toto.com" |
De base Traefik va générer un certificat SSL par URL, mais on peut lui demander de générer un wildcard.
traefik.http.middlewares.securedheaders.headers.forcestsheader: "true"
traefik.http.middlewares.securedheaders.headers.sslRedirect: "true"
traefik.http.middlewares.securedheaders.headers.STSPreload: "true"
traefik.http.middlewares.securedheaders.headers.ContentTypeNosniff: "true"
traefik.http.middlewares.securedheaders.headers.BrowserXssFilter: "true"
traefik.http.middlewares.securedheaders.headers.STSIncludeSubdomains: "true"
traefik.http.middlewares.securedheaders.headers.STSSeconds: "315360000" |
Définition d'un middleware qui permet, en ajoutant des headers, d'avoir A+ au test SSL Labs
traefik.http.routers.api.middlewares: "auth,securedheaders" |
On applique les 2 middlewares (authentification et headers) au dashboard.
environment:
- OVH_ENDPOINT=ovh-eu
- OVH_APPLICATION_KEY=xxxxxxxx
- OVH_APPLICATION_SECRET=xxxxxxxxxxxxxxxx
- OVH_CONSUMER_KEY=xxxxxxxxxxx |
Déclaration des variables nécessaires pour accéder à l'API DNS OVH, on peut aussi passer par des secrets.
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./acme.json:/acme.json
- ./dynamic_config:/dynamic_config:ro
networks:
traefik:
external: true |
La fin est classique.
Quelques paramètres de Traefik sont à définir dans sa configuration dynamique, par exemple /dynamic_config/ssl.yml :
tls:
options:
default:
minVersion: VersionTLS12
curvePreferences:
- secp521r1
- secp384r1
cipherSuites:
- TLS_CHACHA20_POLY1305_SHA256
- TLS_AES_128_GCM_SHA256
- TLS_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
sniStrict: true |
On force ici le TLS 1.2, avec des ciphers "modernes".
Admettons qu'on veuille donner accès à Portainer, il suffit d'ajouter quelques labels à son docker-compose, et le faire appartenir au réseau en commun avec Traefik :
version: "3"
services:
portainer:
container_name: portainer
image: portainer/portainer-ce
networks:
- traefik
labels:
traefik.enable: "true"
traefik.http.routers.portainer.rule: "Host(`portainer.toto.com`)"
traefik.http.routers.portainer.service: "portainer"
traefik.http.routers.portainer.entrypoints: "https"
traefik.http.services.portainer.loadbalancer.server.port: "9000"
traefik.http.routers.portainer.tls.certresolver: "le"
traefik.http.routers.portainer.middlewares: "securedheaders"
volumes:
- /etc/localtime:/etc/localtime:ro
- './data/:/data/'
- /var/run/docker.sock:/var/run/docker.sock
restart: unless-stopped
networks:
traefik:
external: true |
Dans cet exemple, Traefik sait qu'il doit accéder à Portainer via le port 9000 du container dont le service est "portainer", et qu'il doit l'exposer via https://portainer.toto.com (avec génération du certificat wildcard pour toto.com), en appliquant le middleware qui ajoute les headers pour la sécurité.
On peut aussi reverse proxyfier des services qui sont hors Docker, toujours avec la configuration dynamique :
http:
routers:
dsm:
service: dsm
rule: "Host(`syno.toto.com`)"
tls:
certResolver: le
entryPoints:
- https
middlewares:
- securedheaders@docker
services:
dsm:
loadBalancer:
servers:
- url: "http://192.168.1.253:5000" |
Ici, le service sur http://192.168.1.253:5000 sera accessible depuis l'extérieur via https://syno.toto.com, avec un wildcard Let's Encrypt.
Les middlewares permettent plein de trucs, comme faire du rewrite d'URL :
http:
middlewares:
add-path-admin:
addPrefix:
prefix: "/admin" |
Je m'en sers par exemple pour intégrer Authelia, et faire du SSO/MFA.
J'espère que c'est compréhensible
La conf est assez longue à faire la première fois (et c'est du YAML
), mais à chaque ajout de service (dans ou hors Docker), ça prend littéralement 2 minutes 
---------------
Le dernier arrivé est fan de Phil Collins