Cela fait quelques mois que j’ai armorcé ma migration de Proxmox vers Docker. Le but de cet article n’est pas de présenter le moteur de conteneurs le plus célèbre du paf d’autres l’ont déjà fait mais d’entamer une série d’articles autour de cette migration. Aujourd’hui je vais aborder le cas du reverse proxy avec Traefik.
Présentation
Traefik est un reverse proxy HTTP moderne et un répartiteur de charge(load balancing). Il se configure selon deux méthodes, la première dite interactive, elle permet de passer la configuration en paramètre de l’exécutable (méthode que j’utilise dans cet article) et la seconde dite déclarative,elle permet de passer par un fichier de paramètrage au format toml ou yaml. Le gros avantage de Traefik c’est qu’il fait de l’auto-discovery via un système de fournisseurs de services comme Kubernetes, Marathon, Consul, Etcd, Rancher, Amazon ECS et dans mon cas Docker et des fichiers.
Configuration
Docker-compose
Comme je l’ai indiqué en introduction mon traefik est configuré de façon interactive. Les paramètres de l’exécutable sont renseignés comme suit dans mon docker-compose :
command:
- --providers.docker # J'utilise le provider Docker
- --providers.docker.exposedbydefault=false # Je n'expose pas par défaut mes conteneurs
- --providers.file.directory=/config/ # je charge les configurations du répertoire
- --providers.file.watch=true # je surveille le répertoire précédent pour charger dynamiquement les configurations
- --entrypoints.http.address=:80 # Création de l'entrypoint nommé http sur le port 80
- --entrypoints.https.address=:443 # Création de l'entrypoint nommé https sur le port 80
- --entrypoints.http.http.redirections.entrypoint.to=https #je redirige tout ce qui arrive sur le http vers le https
- --entrypoints.http.http.redirections.entrypoint.scheme=https # utilisation du point d'entrée https pour la redirection
- --certificatesresolvers.le.acme.email=${TRAEFIKEMAIL} # le courriel pour l'utilisation de Lets'encrypt
- --certificatesresolvers.le.acme.storage=/certificates/acme.json # acme.json pour let's encrypt
- --certificatesresolvers.le.acme.tlschallenge=true
- --global.sendanonymoususage=false # désactivation de l'envoi de donnée
- --accesslog=true # j'active l'accès au logs
- --log.level=INFO # je défini le niveau d'information dans les logs
- --log.filePath=/logs/traefik.log # j'indique ou stocker les logs
- --api # j'active l'Api et l'acès au dashboard
Dans la configuration de traefik, je place un répertoire en écoute --providers.file.directory=/config/ qui est monté sur l’hôte dans /srv/traefik/config. Tous les fichiers présents dans ce répertoire seront chargés dynamiquement --providers.file.watch=true sans avoir à redémarrer la stack. Dans ce répertoire j’y place aussi les paramètres récurrent à chaque services exposés, comme la configuration TLS, les Headers, et autres middlewares. Cela m’évite d’alourdir les docker-compose de mes futurs services avec des paramètres fixent.
Volumes
J’utilise les volumes pour les certificats générés par Let’sencrypt, les secrets contenant les identifiants, l’enregistrement des logs et la configuration dynamique.
[...]
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro # écoute de l'api docker
- /mnt/nas/voljin/volumes/traefik:/certificates # stockage des certificats
- ./secrets:/secrets:ro # stockage des mots de passes
- ./config:/config:ro # configuration dynamique
- ./logs:/logs # enregistrement des logs.
[...]
Identifiant de connexion
L’identifiant de connexion se présente sous la forme d’une ligne de texte a ajouter tel quel dans le fichier traefik_auth_file, contenant le nom d’utilisateur et le mot de passe qui sera chiffré avec openssl. Dans le répertoire /srv/traefik/secrets je crée le fichier composé comme cela.
Création de l’identifiant.
user:mot_de_passe_chiffré
Chiffrage du mot de passe avec openssl.
openssl passwd -apr1 mon_mot_de_passe
Le mot de passe chiffré doit ressembler à cela.
$apr1$89eqM5Ro$CxaFELthUKV21DpI3UTQO.
Je copie-colle avec le nom d’utilisateur.
user:$apr1$89eqM5Ro$CxaFELthUKV21DpI3UTQO.
On mettra une ligne par utilisateurs.
Configuration TLS
Dans le répertoire /srv/traefik/config je crée un fichier tls.yml composé comme cela.
tls:
options:
default:
minVersion: VersionTLS12
sniStrict: true
cipherSuites:
- TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 # TLS 1.2
- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 # TLS 1.2
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 # TLS 1.2
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 # TLS 1.2
- TLS_AES_256_GCM_SHA384 # TLS 1.3
- TLS_CHACHA20_POLY1305_SHA256 # TLS 1.3
- TLS_FALLBACK_SCSV # TLS FALLBACK
curvePreferences:
- secp521r1
- secp384r1
- X25519
- CurveP521
- CurveP384
- CurveP256
modern:
minVersion: VersionTLS13
Il s’agit d’une configuration classique, j’utilise les ciphers autorisés dans la configuration par defaut, avec une version de TLS minimum en 1.2. Je force l’utilisation de TLSv1.3.
Les middlewares
Voici les différents middlewares que j’utilise, je préfère utiliser un fichier par middleware. En effet si je me loupe dans la configuration d’un middleware celui-ci ne sera pas chargé et cela ne pertubera pas le fonctione de Traefik.
Headers et HSTS
Dans un fichier appelé config.yml.
hsts-headers:
headers:
frameDeny: true
browserXssFilter: true
contentTypeNosniff: true
stsIncludeSubdomains: true
stsPreload: true
stsSeconds: 31536000
forceStsHeader: true
Compression
Compression en GZIP dans un fichier appelé compression.yml.
http:
middlewares:
compression:
compress:
excludedContentTypes:
- "text/event-stream"
Accès au Dashboard
Avec le paramètre --api j’active l’accès au dashboard de traefik. Pour ce faire j’ajoute ces labels docker dans ma stack.
labels:
- traefik.enable=true # J'active traefik
- traefik.docker.network=traefik # j'indique le réseau pour l'exposition du conteneur
- traefik.http.middlewares.admin-auth.basicauth.usersfile=/secrets/traefik_auth_file # fichier contenant l'auhentification pour l'accès au dashboard
- traefik.http.routers.traefik.rule=Host(`traffic.${DOMAIN}`) # url d'accès
- traefik.http.routers.traefik.entrypoints=https # port d'écoute utilisé
- traefik.http.routers.traefik.tls=true # chiffrage
- traefik.http.routers.traefik.service=api@internal # service propre à traefik pour le dashboard
- traefik.http.routers.traefik.tls.certresolver=le # Let's encrypt comme resolveur de certificat
- traefik.http.routers.traefik.middlewares=admin-auth # activation de l'authentification via le fichier défini plus haut
- traefik.http.services.traefik.loadbalancer.server.port=8080 # port d'écoute du dashbord non exposé
Docker-compose final
version: "3.8"
networks:
traefik:
external: true
services:
traefik:
image: traefik:banon
restart: unless-stopped
container_name: traefik
ports:
- 80:80
- 443:443
labels:
- traefik.enable=true
- traefik.docker.network=traefik
- traefik.http.middlewares.admin-auth.basicauth.usersfile=/secrets/traefik_auth_file
- traefik.http.routers.traefik.rule=Host(`traffic.${DOMAIN}`)
- traefik.http.routers.traefik.entrypoints=https
- traefik.http.routers.traefik.tls=true
- traefik.http.routers.traefik.service=api@internal
- traefik.http.routers.traefik.tls.certresolver=le
- traefik.http.routers.traefik.middlewares=admin-auth
- traefik.http.middlewares.traefik.compress=true
- traefik.http.middlewares.traefik.compress.excludedcontenttypes=text/event-stream
- traefik.http.services.traefik.loadbalancer.server.port=8080
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- /mnt/nas/voljin/volumes/traefik:/certificates
- ./secrets:/secrets:ro
- ./config:/config:ro
- ./logs:/logs
command:
# ------------------------------------------- providers Docker
- --providers.docker
- --providers.docker.exposedbydefault=false
# ------------------------------------------- Providers Fichier
- --providers.file.directory=/config/
- --providers.file.watch=true
# ------------------------------------------- Ports
- --entrypoints.http.address=:80
- --entrypoints.https.address=:443
# ------------------------------------------- Redirection vers https
- --entrypoints.http.http.redirections.entrypoint.to=https
- --entrypoints.http.http.redirections.entrypoint.scheme=https
# ------------------------------------------- Configuration SSL
- --certificatesresolvers.le.acme.email=${TRAEFIKEMAIL}
- --certificatesresolvers.le.acme.storage=/certificates/acme.json
- --certificatesresolvers.le.acme.tlschallenge=true
# ------------------------------------------- Configuration traefik
- --global.sendanonymoususage=false
- --accesslog=true
- --log.level=INFO
- --log.filePath=/logs/traefik.log
- --api
networks:
- traefik
Conclusion
Je ne fait qu’effleurer les possibilités qu’offre traefik et il y en a tellement, les middlewares, le routage TCP et UDP, la possibilité de servir des machines non docker, la diversité des providers et j’en oublie. J’espère que vous avez apprécié cet article et que vous allez prendre autant de plaisir que moi à jouer avec traefik.
N’hésitez pas à faire vos commentaires ou à suivre les publications sur Mastodon !