[Traefik] Tutoriel et retour d'expérience

Tags: Tutoriel Informatique Docker Traefik


Bonjour à tous,

Comme j’en parlais dans cet article de juillet dernier, j’ai migré mon infrastructure et mes services sous Docker (avec Docker-compose). Aujourd’hui, je vais vous présenter un outil que j’ai fortement utilisé dans cette infrastructure (spoilé par le titre): Traefik.

Traefik: Qu’es aquò ?

Un reverse-proxy

En français, on dirait proxy inverse, simplement, vous n’entendrez jamais cette traduction, donc on parlera ici de reverse-proxy.

Rien ne vaut un petit schéma maison pour décrire plus simplement ce que c’est.

Schéma de principe d'un reverse-proxy
Schéma de principe d'un reverse-proxy

Donc comme on peut le voir, le reverse-proxy est un outil qui fait le lien entre internet (et ses utilisateurs) et un serveur (et ses applications).

Petit exemple concret: www.pofilo.fr et searx.pofilo.fr redirigent tous 2 (via DNS comme expliqué dans cet article) vers le même serveur. Le rôle du reverse-proxy (ici Traefik) est donc de rediriger les requêtes entrantes sur un serveur vers le bon service (searx ou mon site).

Quand on parle de reverse-proxys, on pense le plus souvent à Apache et nginx. Mais il y a un piège car ils sont capables d’être à la fois serveur web et reverse-proxy.

Traefik

Traefik est donc un reverse-proxy (et également un load-balancer (équilibreur de charge), point que l’on ne va pas détailler ici). Il est bien sûr Open Source (code source disponible ici (miroir ici)) et il est écrit en Go.

Les principales fonctionnalités de Traefik sont les suivantes (en partie reprises et traduites depuis le README):

  • Rechargement à chaud des configurations: pour moi une solution moins fiable qu’un reload du logiciel car il existe le piège de sauvegarder la configuration sans qu’elle ne soit terminée, ce qui peut générer une configuration invalide.
  • Supporte plusieurs algorithmes de load-balancing (équilibrage de charge).
  • Intégration native de Let’s Encrypt pour fournir du HTTPS aux différents services.
  • Interface web pour contrôler la configuration et mise à disposition d’une API Rest.
  • Fournit des métriques (Rest, Prometheus, Datadog, Statsd, InfluxDB) et des logs d’accès (JSON, CLF).
  • Go oblige, il est fourni sous un binaire unique (ou via une image Docker relativement petite).
  • Supporte l'ajout de plugins écrits en Go via un Marketplace (depuis la récente version 2.3.0 du 23 septembre dernier).

Traefik se veut également rapide et veut rendre facile le déploiement de micro-services. Cela se confirme à l’usage, c’est maintenant un plaisir d’utiliser Traefik.

Traefik s’intègre avec votre infrastructure (Docker, Swarm mode, Kubernetes, Marathon, Consul, Etcd, Rancher, Amazon ECS, …) et se configure lui-même automatiquement et dynamiquement. Faire le lien entre Traefik et l’orchestrateur devrait être la seule étape de configuration à réaliser.

Derrière cette belle phrase se cache tout de même un fond de vérité. En réalité, vous allez galérer et le détester au démarrage mais une fois qu’on maîtrise un peu (bien) la bestiole, tout se fait très simplement et rapidement. Le temps d’apprentissage reste relativement court, mais vous allez faire un paquet d’erreur au début.

Tutoriel par l’exemple

Ici, je vais reprendre l’exemple utilisé dans la documentation officielle, on n’ira pas étape par étape mais fichier par fichier (avec les explications que je peux y apporter). Je vous déconseille de les recopier tels quels (même si ça fonctionnera aux certificats TLS prêts), ce sont plus des exemples dont vous pouvez vous inspirer pour les adapter à votre sauce.

Pour les versions des images Docker, je vous laisse gérer, un article dédié à ce sujet est en cours de rédaction pour les prochains mois.

On retrouve une configuration statique (nécessite un redémarrage de Traefik en cas de modification) et une configuration dynamique (prise en compte dès la sauvegarde, à ne surtout pas éditer en production donc: une erreur dans ce fichier et votre instance de Traefik est cassée).

Architecture

xx/ # Dossier "principal"
xx/traefik/docker-compose.yml
xx/whoami/docker-compose.yml
xx/data/traefik/conf/traefik.toml # Fichier de configuration statique
xx/data/traefik/conf/dynamic/traefik.config.toml # Fichier de configuration dynamique
xx/data/traefik/certs/example.com # Dossier comprenant les certificats TLS

Fichier de configuration statique

Il s’agit ici du fichier xx/data/traefik/conf/traefik.toml.

### STATIC ###

defaultEntryPoints = ["websecure", "web"]

[entryPoints]
    [entryPoints.web]
        address = ":80"

    [entryPoints.web.http]
        [entryPoints.web.http.redirections]
            [entryPoints.web.http.redirections.entryPoint]
                to = "websecure"
                scheme = "https"

    [entryPoints.websecure]
        address = ":443"

[providers]
    [providers.file]
        # Dynamic files
        directory = "/etc/traefik/dynamic/"
        watch = true
    [providers.docker]
        endpoint = "unix:///var/run/docker.sock"
        exposedByDefault = false
        watch = true

[api]
    dashboard = true

On retrouve donc tout d’abord les entrypoints (points d’entrées) qui permettent à Traefik de récupérer tout ce qui se passe sur les ports 80 et 443 (http et https). Il y a également une redirection pour rediriger tout ce qui arrive du port 80 vers le port 443.

On a ensuite les providers qui sont des mécanismes qui permettent de découvrir les services présents sur notre infrastructure qui sont dans notre cas le fichier de configuration dynamique ainsi que Docker.

exposedByDefault = false spécifie que, par défaut, un conteneur ne sera pas exposé par Traefik. D’un point de vue sécurité, c’est pour moi une hérésie que cette variable soit à true par défaut … (source).

Enfin, on active le dashboard pour avoir l’interface web pour vérifier la configuration.

Fichier de configuration dynamique

Il s’agit ici du fichier xx/data/traefik/conf/dynamic/traefik.config.toml.

### DYNAMIC ###

[tls]
    [tls.stores]
        [tls.stores.default]
            # needed otherwise traefik generates a xx.traefik.default certificate
            [tls.stores.default.defaultCertificate] 
                certFile = "/etc/certs/example.com/fullchain.pem" 
                keyFile = "/etc/certs/example.com/privkey.pem"
    # *.example.com
    [[tls.certificates]]
        certFile = "/etc/certs/example.com/fullchain.pem"
        keyFile = "/etc/certs/example.com/privkey.pem"
        # Not necessary as it goes to default by default
        # But it will maybe change one day ! ...
        stores = ["default"]

    [tls.options]
        [tls.options.intermediate]
            minVersion = "VersionTLS12"
            cipherSuites = [
                "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
                "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305"
            ]
            sniSctrict = true

[http]
    [http.middlewares]
        [http.middlewares.secure-headers.headers]
            stsSeconds=63072000
            stsIncludeSubdomains=true
            stsPreload=true
            contentTypeNosniff=true
            referrerPolicy="no-referrer"
            customFrameOptionsValue="deny"
            contentSecurityPolicy="default-src 'self'"
            [http.middlewares.secure-headers.headers.customResponseHeaders]
                Strict-Transport-Security = "max-age=63072000"

En premier lieu, il y a la déclaration des certificats TLS (un peu étrange de devoir configurer de deux façons différents le certificat par défaut mais sinon, Traefik génère en plus un certificat en xx.traefik.default).

On retrouve ensuite les options TLS qui sont générées à l’aide de l’outil SSL Configuration Generator de Mozilla.

Les middlewares suivent dans le fichier et permettent de modifier les requêtes avant de les envoyer à un service. Ici, notre middleware permet de spécifier les headers que l’on souhaite.

Docker-compose Traefik

Il s’agit ici du fichier xx/traefik/docker-compose.yml

version: '3.7'

services:
    traefik:
        image: traefik:2.2.11
        container_name: "traefik"
        ports:
            # External:Internal
            - "80:80"
            - "443:443"
        volumes:
            - ../data/traefik/conf/dynamic/:/etc/traefik/dynamic/:ro
            - ../data/traefik/conf/traefik.toml:/etc/traefik/traefik.toml:ro
            - ../data/traefik/certs/:/etc/certs/:ro
            - /etc/timezone:/etc/timezone:ro
            - /etc/localtime:/etc/localtime:ro
            # so that Traefik can listen to the Docker events
            - /var/run/docker.sock:/var/run/docker.sock:ro
        restart: always
        networks:
            - traefik-frontend
        labels:
            # Dashboard
            - "traefik.enable=true"
            - "traefik.docker.network=traefik-frontend"
            - "traefik.http.routers.traefik.service=api@internal"
            - "traefik.http.middlewares.auth-traefik.basicauth.users=admin:$apr1$QbRRQO7i$h9Qzt3ox47ZB233g8PeIa1"
            - "traefik.http.routers.traefik.rule=Host(`traefik.example.com`)"
            - "traefik.http.routers.traefik.entrypoints=websecure"
            # TLS and security
            - "traefik.http.routers.traefik.tls=true"
            - "traefik.http.routers.traefik.tls.options=intermediate@file"
            - "traefik.http.routers.traefik.middlewares=auth-traefik, secure-headers@file"

networks:
    traefik-frontend:
        name: traefik-frontend

La particularité de Traefik, grâce à son intégration avec Docker, est qu’il a besoin d’accéder à /var/run/docker.sock. Cela lui permet, quand on rajoute ou retire un service de modifier sa configuration à chaud notamment avec les nombreux labels à spécifier:

  • traefik.enable=true: pour le conteneur soit détecté par le provider Docker.
  • traefik.docker.network=traefik-frontend: pour que le conteneur soit sur le réseau frontend utilisé par Traefik.
  • traefik.http.routers.traefik.service=api@internal: pour déclarer un routeur qui va accéder à l’interface web (option activée dans la configuration statique).
  • traefik.http.middlewares.auth-traefik.basicauth.users=admin:$apr1$QbRRQO7i$h9Qzt3ox47ZB233g8PeIa1: la déclaration d’un middleware pour rajouter un mot de passe à l’interface web (ici admin/admin) avec la commande echo $(htpasswd -nb user password)).
  • traefik.http.routers.traefik.rule=Host(\traefik.example.com`)`: l’adresse pour accéder à l’interface web.
  • traefik.http.routers.traefik.entrypoints=websecure: chaque routeur nécessite la configuration d’un entrypoint, on spécifie celui sur le port 443.
  • traefik.http.routers.traefik.tls=true: pour activer le TLS pour ce routeur.
  • traefik.http.routers.traefik.tls.options=intermediate@file: pour indiquer les options à utiliser qui sont factorisées dans le fichier dynamique.
  • traefik.http.routers.traefik.middlewares=auth-traefik, secure-headers@file: on indique les 2 middlewares à utiliser: celui pour l’authentification par mot de passe et celui pour les headers à utiliser (suffixé de @file pour préciser qu’il s’agit d’un middleware défini dans le provider de type fichier).

Ici, les labels ne servent que pour accéder à l’interface web de Traefik, qui est en réalité le service api@internal. On peut tous les supprimer si on ne souhaite pas l’activer.

Depuis la rédaction de cet article, la version 2.3.0 est sortie, je vous invite à l’utiliser directement si vous voulez vous mettre à Traefik.

Docker-compose Whoami

Il s’agit ici du fichier xx/whoami/docker-compose.yml

version: '3.7'

services:
    whoami:
        image: traefik/whoami:v1.6.0
        container_name: "whoami"
        labels:
            - "traefik.enable=true"
            - "traefik.docker.network=traefik-frontend"
            - "traefik.http.services.whoami.loadbalancer.server.port=80"
            - "traefik.http.routers.whoami.rule=Host(`whoami.example.com`)"
            - "traefik.http.routers.whoami.entrypoints=websecure"
            # TLS and security
            - "traefik.http.routers.whoami.tls=true"
            - "traefik.http.routers.whoami.tls.options=intermediate@file"
            - "traefik.http.routers.whoami.middlewares=secure-headers@file"
        networks:
            - traefik-frontend

networks:
    traefik-frontend:
        name: traefik-frontend

Concernant les labels, on retrouve donc le même principe que pour les labels utilisés pour mettre en avant l’interface web de Traefik.

On retrouve en plus la déclaration du service pour expliciter à Traefik qu’il faut qu’il communique avec le port 80 du conteneur.

Dans les 2 exemples, on peut distinguer traefik.http.routers.whoami et traefik.http.routers.traefik, whoami et traefik ont toute leur importance puisque qu’ils correspondent aux noms des routeurs: vous ferez forcément des oublis lors des copiés-collés donc méfiance !

Des logs

Il est également possible de mettre en place des logs, que ce soit pour Traefik en cas d’erreur ou warning levé par le logiciel mais aussi pour tous les accès réalisés en HTTP et HTTPS. Je vous laisse vous référer aux documentations en question:

Certificats avec Let’s Encrypt

Si vous voulez laisser Traefik gérer vos certificats, la documentation à suivre est disponible ici (il y aura des modifications à faire dans les fichiers d’exemples ci-dessus).

Pour ma part, je préfère les gérer par moi-même. La raison initiale est que j’avais déjà l’habitude de générer un certificat wildcard que je redistribue sur mes différents serveurs.

Sous Debian, il faut installer certbot avec la commande apt install certbot. Pour générer un certificat Wildcard, on peut ensuite utiliser une commande du genre certbot certonly --server https://acme-v02.api.letsencrypt.org/directory --manual --preferred-challenges dns-01 --rsa-key-size 4096 --domain example.com

Les certificats seront alors générés dans le dossier /etc/letsencrypt/live/example.com et il vous faudra copier les fichiers fullchain.pem et privkey.pem dans le dossier xx/data/traefik/certs/example.com/.

Pour ma part, j’utilise un script qui automatise ces générations et copies de fichiers.

Résultats sur l’interface web

Voici un aperçu de ce qu’on peut trouver sur l’interface web avec l’exemple montré dans cet article:

Interface web de Traefik
Interface web de Traefik

Conclusion

Traefik est un jeune outil formidable. En effet, une fois le premier apprentissage (par la pratique, la théorie ne vous apprend que très peu) effectué, Traefik est un outil très puissant où l’on peut à peu près tout faire … d’une façon ou d’une autre.

Et c’est ce dernier point son principal inconvénient. On peut tout écrire dans les fichiers Docker-compose, on peut écrire dans des fichiers de configurations (ne pas mélanger les statiques et dynamiques) que l’on peut écrire en TOML ou en YML.

Par exemple pour rediriger tout le trafic HTTP en HTTPS, il existe deux manières totalement distinctes:

  • Version 1 avec un middleware et un routeur (dans la configuration dynamique):
[http.middlewares.redirect-to-https.redirectScheme]
    scheme = "https"
...
# Global redirect to https
    [http.routers.http-all]
        entryPoints = ["web"]
        rule = "HostRegexp(`{any:.+}`)"
        middlewares = ["redirect-to-https"]
        # <container-name>-<folder-docker-compose-of-traefik>
        service = "traefik-traefik@docker" # <@docker> because the service is provided by the Docker provider
  • Version 2 apportée par une version plus récente (dans la configuration statique):
[entryPoints.web.http]
    [entryPoints.web.http.redirections]
        [entryPoints.web.http.redirections.entryPoint]
            to = "websecure"
            scheme = "https"

Donc si vous avez le temps et l’envie d’apprendre, je vous conseille Traefik qui a quelques défauts de jeunesses mais est très fiable et très robuste. En revanche, je n’ai essayé que très peu de ses concurrents et je ne saurai dire si d’autres sont meilleurs ou non. Et vous, avez-vous déjà utilisé Traefik ? Qu’en pensez-vous ?

Ce n’était pas le sujet donc je n’en ai pas parlé, mais en plus de l’HTTP, Traefik est également capable de gérer le TCP et l'UDP.

Commentaires




Ailleurs sur le Web


Cit0Day Breach Check · GitHub

Une grosse faille avec beaucoup de mails/mots de passes dans la nature avec cette brèche .. Ce script (à adapter selon les cas de chacun) permet de retrouver tous vos sites impactés pour changer immédiatement le mot de passe. Une preuve de plus de l'ind…

via Shaarli le 20 novembre 2020

Git is simply too hard |> Changelog - Liens en vrac de sebsauvage

Je ne comprends toujours pas pourquoi les gens disent que "Git est trop dur à utiliser". Il faut uniquement connaitre les bases et les quelques commandes basiques qui sont (je vois pas comment on pourrait faire vraiment plus simple). git clone git pu…

via Shaarli le 19 novembre 2020

Composition, technologies et fonctionnalités : on vous dit tout du disque dur moderne

Article très intéressant si vous êtes abonnés. J'avais pas suivi qu'il y avait tant d'innovation sur le monde du disque dur. Ca montre également qu'il ne faut pas prendre n'importe quel disque pour faire du NAS (entendre par là un disque…

via Shaarli le 17 novembre 2020

Généré avec openring


Recettes de gourmands


Pizza poulet curry

Une pizza plus estivale, mais qui sait rester gourmande !

via cooking.pofilo.fr le 31 mai 2020

Fajitas

A manger avec les mains, évidemment !

via cooking.pofilo.fr le 24 mai 2020

Pad Thai

Une recette longue mais qui reste relativement simple qui devrait ravir tous les gourmands !

via cooking.pofilo.fr le 17 mai 2020

Généré avec openring