Traefik

De Wiki doc


Traefik est un logiciel permettant de faire un serveur mandataire inversé (reverse proxy) et pouvant faire de la répartition de charge (load balancer). Il est développé en Go et a la particularité de faire de la découverte automatique (toute les deux secondes par défaut) de fichiers de configurations (pas de redémarrage du service). Ceci a pour intérêt de permettre une actualisation du mandataire en agissant uniquement sur des fichiers et non le service lui-même. On peut alors imaginer une actualisation de la configuration du serveur par simple copier/coller de fichiers depuis un partage sans privilèges root par exemple.

ATTENTION

L'application Traefik évolue très rapidement. Le présent document traite de la version 2 de Traefik, les fichiers de configurations n'ont rien à voir.

Schéma fonctionnel

Traefik traite les requêtes des clients en les faisant passer dans divers fonctions selon le schéma suivant:

<img src="https://doc.ycharbi.fr/fichiers/services/mandataire/traefik/images/Schéma_logique_Traefik.svg" alt="Schéma logique Traefik" style="display: block; width: 60%; height:auto; margin-left:auto; margin-right:auto;"/>

  • EntryPoint : Permet la déclaration des ports d'entrés de Traefik. Habituellement 80 et 443
  • Routers : Permet la déclaration de l'URL pour contacter le service
  • Middlewares : Permet la modification du comportement de l'appel. Ex: Ajout d'une authentification
  • Service : Permet la déclaration du serveur qui héberge le service


Installation

Traefik n'est pas présent dans les dépôts Debian Buster. Il va donc falloir le télécharger depuis son dépôt Git.

wget https://github.com/containous/traefik/releases/download/v2.2.1/traefik_v2.2.1_linux_amd64.tar.gz -P /tmp/
mkdir -p /opt/traefik/{config/vhosts,tls,passwd,logs}
tar xzvf /tmp/traefik_v2.2.1_linux_amd64.tar.gz -C /opt/traefik/

Création de l'utilisateur du service

useradd --system --home-dir /opt/traefik --user-group --shell /usr/sbin/nologin traefik
chown -R traefik:traefik /opt/traefik
chmod -R g+w /opt/traefik

Création du service Systemd

vim /etc/systemd/system/traefik.service
[Unit]
Description=Traefik, circulez y'a rien à voir !

[Service]
RestartSec=2s
Type=simple
User=traefik
Group=traefik
WorkingDirectory=/opt/traefik
ExecStart=/opt/traefik/traefik --configFile=/opt/traefik/config/traefik.toml
Restart=always
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target

Chargement du service au démarrage du système

systemctl daemon-reload
systemctl enable traefik.service
systemctl start traefik.service
systemctl status traefik.service

Configuration de base

Le fichier de configuration de Traefik est au format TOML, mais peux être également au format JSON. Il y a donc deux noms de fichier recommandés suivant le langage utilisé :

  • traefik.toml
  • traefik.yml

Ce fichier est attendu dans l'une de ces arborescences :

  • /etc/traefik/
  • $HOME/.config
  • . (Dans la même arborescence que le binaire)

Nous pouvons également définir à Traefik un fichier de configuration spécifique :

traefik --configFile=foo/bar/myconfigfile.toml

ATTENTION

Dans ce fichier, il est attendu une configuration statique. Une modification de celui-ci nécessitera une relance de Traefik pour qu'elles soient prisent en compte. Sont également concernés les fichiers étrangers aux différents hôtes virtuels comme par exemple middleware.toml et tls.toml que nous verrons plus bas. Seules les fichiers contenants les directives routers et services ne nécessites pas de redémarrage du service.

Voici un exemple:

vim /opt/traefik/config/traefik.toml
# Configuration globale de Traefik
[global]
  sendAnonymousUsage = false # Évite l'envoi d'informations à l'éditeur

# Configuration des journaux de Traefik
[log]
  level = "DEBUG" # Niveau de journalisation [DEBUG|PANIC|FATAL|ERROR|WARN|INFO]
  format = "common" # Format des journaux [common|json]
  filePath = "/opt/traefik/logs/traefik.log" # Chemin d'accès au fichier de journalisation

# Configuration des journaux d'accès
[accessLog]
  filePath = "/opt/traefik/logs/access.log" # Chemin d'accès au fichier de journalisation

# Configuration des fourniseurs
[providers]
  [providers.file] # Fournisseur de type "fichier"
    directory = "/opt/traefik/config/vhosts/" # Chemin d'accès du répertoire où sont stockés les fichiers des services
    watch = true # Permet le chargement à chaud des modifications de fichiers de configuration des hôtes virtuels

# Configuration de l'API
[api]
  dashboard = true # Permet d'activer le tableau de bord de Traefik
  debug = false # Permet de désactiver le mode développeur de l'API
  insecure = true # Permet d'activer l'accès à l'API via le port 8080 par défaut

# Configuration des points d'entrées de Traefik
[entryPoints]
  [entryPoints.http] # Permet de déclarer un point d'entrée nommé "http"
    address = ":80" # Permet de déclarer le port d'entrée

ATTENTION

Pour les filePath des sections [log] et [accessLog], il faudra prévoir une rotation du fichier journal (voir Logrotate) car Traefik ne gère pas cette fonction. Ceci est indispensable pour ne pas voir votre partition se remplir jusqu'à déni de service.

Comme nous avons édité le fichier de configuration principal du serveur, il faut recharger le service

systemctl restart traefik.service

Fonctionnalités

L'outil permet un éventail de fonctionnalités souvent rencontrées dans des logiciels de ce type. Elles sont décrites dans un ou plusieurs fichiers. Un hôte étant définit par des sections, il est possible d'en combiner plusieurs en un seul fichier ou d'en faire un par fichier. Ceci est configuré dans le paramètre directory de la directive [providers] vu précédemment. Comme précisé par le paramètre watch de cette même directive, ces fichiers sont appliqués au serveur à chaque enregistrements.

Parmi les fonctionnalités que nous avons explorées, nous pouvons noter la possibilité de faire:

  • un mandataire inverse HTTP/HTTPS
  • l'ajout d'une authentification HTTP
  • un répartiteur de charge


Afin de simplifier la compréhension des fichiers d'exemples qui suivront, nous allons partir du principe que nous voulons mettre à disposition de visiteurs sur Internet, un site WEB simple en HTML. Ce site est hébergé sur deux serveurs, ce qui nous permettra de configurer une répartition de charge. Nous le configurerons tout d'abord en HTTP, lui appliquerons une authentification, mettrons en place la répartition de charge et le passerons en HTTPS.

HTTP

Dans cette section, nous allons aborder la mise en place d'une redirection simple en HTTP comme on le ferrai avec Haproxy.

<img src="https://doc.ycharbi.fr/fichiers/services/mandataire/traefik/images/Schéma_exemple_Traefik_http.svg" alt="Schéma logique Traefik" style="display: block; width: 60%; height:auto; margin-left:auto; margin-right:auto;"/>

Nous allons créer un fichier représentant le nom du service:

vim /opt/traefik/config/vhosts/ma_page.toml
# Service de type http
[http]
  [http.routers] # Déclaration des routers
     [http.routers.vers_ma_page] # Création d'un routeur nommé "vers_ma_page"
      rule = "Host(`mapage.jmador.yo`)" # URL avec laquelle sera joint notre site web
      service = "ma_page" # Nom du service (déclaré ci-dessous)

  [http.services] # Déclaration des services
    [http.services.ma_page.loadBalancer] # Création d'un service nommé "ma_page"
      [[http.services.ma_page.loadBalancer.servers]] # Déclaration d'un serveur 
        url = "http://[2001:db8:0:180::a0]" # URL vers mon serveur MaPage

Vous pouvez noter le mot clé loadBalancer à la ligne créant le service ([http.services.ma_page.loadBalancer]). En fait, Traefik fait constament de la répartition de charge, même avec un seul hôte (ce qui revient à toujours envoyer les requêtes sur la même machine). La configuration d'une réelle répartition de charge consiste donc à ajouter un hôte, d'où la simplicité de mise en œuvre. Nous verrons ceci plus bas.

Il est également possible de faire des règles (paramètre rule) avec plus de conditions en utilisant des structures conditionnelles telles que || ou &&.

Authentification

Pour l’authentification, nous avons besoin d'un "middlewares" qui intercepte le lien pour demander un utilisateur et un mot de passe :

<img src="https://doc.ycharbi.fr/fichiers/services/mandataire/traefik/images/Schéma_exemple_Traefik_http_auth.svg" alt="Schéma logique Traefik" style="display: block; width: 60%; height:auto; margin-left:auto; margin-right:auto;"/>

Pour ce faire, nous allons créer un fichier htpasswd (utilitaire présent dans le paquet apache2-utils):

htpasswd -c /opt/traefik/passwd/groupe1 ycharbi

ATTENTION

Si vous voulez ajouter un utilisateur, utilisez la commande suivante : htpasswd /opt/traefik/passwd/groupe1 nmorin. L'usage du paramètre -c ne devant être utilisé que pour la création du fichier, sous peine de l'écraser. De plus, il faut impérativement relancer le service pour prendre en compte tout ajout/modification/suppression.

Un middleware est unique au sein de Traefik, bien qu'il soit possible de mettre la section dédiée directement dans l'hôte concerné, il est plus judicieux de leur créer un fichier propre afin de pouvoir piocher dedans les groupes d'utilisateurs pour les authentifications:

vim /opt/traefik/config/vhosts/middlewares.toml
# Service de type http
[http]
  [http.middlewares] # Déclaration des middlewares
    [http.middlewares.mon_auth.basicAuth] # Création d'un middleware nommé "mon_auth" de type "basicAuth"
      usersFile = "/opt/traefik/passwd/groupe1" # Définit l'arborescence du fichier htpasswd

Puis nous allons reprendre le fichier /opt/traefik/config/vhosts/ma_page.toml:

[http]
  [http.routers]
     [http.routers.vers_ma_page]
      rule = "Host(`mapage.jmador.yo`)"
      service = "ma_page"
      middlewares = ["mon_auth"] # Définit les middlewares utilisés pour ce routeur

  [http.services]
    [http.services.ma_page.loadBalancer]
      [[http.services.ma_page.loadBalancer.servers]]
        url = "http://[2001:db8:0:180::a0]"

Répartition de charges

Nous allons voir la configuration de la répartition de charge. Plusieurs serveurs pourront êtres utilisés pour répondre de façon alternés aux clients. Attention, nous n'aborderons pas ici la notion de gestion de faillite (failover). Un serveur injoignable sera quand même utilisé dans la répartition et renvéra des erreurs aux clients.

<img src="https://doc.ycharbi.fr/fichiers/services/mandataire/traefik/images/Schéma_exemple_Traefik_http_auth_rep.svg" alt="Schéma logique Traefik" style="display: block; width: 60%; height:auto; margin-left:auto; margin-right:auto;"/>

L'ajout d'un serveur dans le système de répartition de charge est très simple. Il faut seulement déclarer un autre serveur dans le service. La répartition de charge utilise l'algorithme d'ordonnancement Round-robin.

Reprenons notre fichier /opt/traefik/config/vhosts/ma_page.toml :

[http]
  [http.routers]
     [http.routers.vers_ma_page]
      rule = "Host(`mapage.jmador.yo`)"
      service = "ma_page"
      middlewares = ["mon_auth"]

  [http.services]
    [http.services.ma_page.loadBalancer]
      [[http.services.ma_page.loadBalancer.servers]]
        url = "http://[2001:db8:0:180::a0]"
      [[http.services.ma_page.loadBalancer.servers]] # Déclaration d'un autre serveur 
        url = "http://[2001:db8:0:180::a1]" # URL vers mon serveur 2 MaPage

Avec persistance de sessions

Traefik nous offre la possibilité de gérer la persistance de sessions sur un serveur de manière très simple. Il faut simplement ajouter la valeur .sticky.cookie lors de la déclaration de notre service.

INFORMATION

La persistance de sessions permet d'allouer un client au premier serveur que le Round-robin lui aura fait joindre. La technique s'appuie sur la génération et l'envoi d'un cookie au navigateur du client (cela ne fonctionne donc pas avec curl ou wget). Celui-ci présente ce cookie au serveur mandataire qui retransmet sa requête au même serveur qu'au premier échange.

Toujours dans notre fichier /opt/traefik/config/vhosts/ma_page.toml:

# Service de type http
[http]
  [http.routers]
     [http.routers.vers_ma_page]
      rule = "Host(`mapage.jmador.yo`)"
      service = "ma_page"
      middlewares = ["mon_auth"]

  [http.services]
    [http.services.ma_page.loadBalancer.sticky.cookie] # Création d'un service nommé "ma_page" avec gestion des cookies de session
      [[http.services.ma_page.loadBalancer.servers]]
        url = "http://[2001:db8:0:180::a0]"
      [[http.services.ma_page.loadBalancer.servers]]
        url = "http://[2001:db8:0:180::a1]"

HTTPS

Dans cette section nous ajouterons le support de TLS pour faire du HTTPS. Vous trouverez la documenation officielle sur le sujet ici ainsi que l'ensemble des suites cryptographiques configurables pour le chiffrement du flux et les algorithmes d'échange de clés basés sur les courbes ellyptiques (la syntaxes des algorithmes de la RFC 8446 est utilisable dans la configuration).

Simple

Le support de HTTPS se ferra en cohabitation avec HTTP. Les clients pourrons se connecter soit en clair, soit en chiffré.

<img src="https://doc.ycharbi.fr/fichiers/services/mandataire/traefik/images/Schéma_exemple_Traefik_https_auth_rep.svg" alt="Schéma logique Traefik" style="display: block; width: 60%; height:auto; margin-left:auto; margin-right:auto;"/>

Comme vous pouvez le voir dans le schéma ci-dessus, nous avons un nouveau point d'entrée sur le port 443. Il faut donc l'ajouter dans la configuration de base de Traefik.

vim /opt/traefik/config/traefik.toml
[global]
  sendAnonymousUsage = false

# Configuration des journaux de Traefik
[log]
  level = "DEBUG"
  format = "common"
  filePath = "/opt/traefik/logs/traefik.log"

[accessLog]
  filePath = "/opt/traefik/logs/access.log"

[providers]
  [providers.file]
    directory = "/opt/traefik/config/vhosts/"
    watch = true

[api]
  dashboard = true
  debug = false
  insecure = true

[entryPoints]
  [entryPoints.http]
    address = ":80"
  [entryPoints.https] # Permet de déclarer un autre point d'entrée nommé "https"
    address = ":443" # Permet de déclarer le port d'entrée 443

Nous allons ajouter un nouveau fichier pour définir les propriétés pas défaut de notre TLS:

vim /opt/traefik/config/vhosts/tls.toml
[tls.options]
  [tls.options.default]
    # Définition des suites cryptographiques accéptés par le serveur
    cipherSuites = [ 
      "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
      "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
    ]
    curvePreferences = ["x25519", "CurveP521", "CurveP384"] # Définition des algorithmes d'échanges de clés accéptés par le serveur
    preferServerCipherSuites = true # Imposer l'utilisation des suites cryptographiques définies par le serveur

Note: Il est à précisé que GoLang ne permet pas de configurer de suites cryptographiques pour le TLS 1.3. Les développeurs du langage se justifient par le fait qu'elles sont définits dans la RFC 8446, respectés à la lettres et concidérées comme sûr donc y'a pas de problèmes... Personellement je trouve ça moyen surtout quand l'AES 128 bits est le choix par défaut... Ce n'est peut-être que temporaire, l'ajout de ce protocole datant de la version 1.12 du langage Go (on en est à la 1.13 au moment de la rédaction de ce paragraphe).

ATTENTION

N'oubliez pas que comme nous avons modifié le fichier de configuration principal ainsi qu'un fichier autre qu'un routeur ou un service, il faut relancer Traefik pour que la prise en compte de la configuration soit effective.

Dans le fichier /opt/traefik/config/vhosts/ma_page.toml :

[http]
  [http.routers]
     [http.routers.vers_ma_page]
      entryPoints = ["https"] # On force l'accès via le point d'entrée "https"
      rule = "Host(`mapage.jmador.yo`)"
      service = "ma_page"
      middlewares = ["mon_auth"]
      [http.routers.vers_ma_page.tls] # On autorise les connexion "TLS"

  [http.services]
    [http.services.ma_page.loadBalancer.sticky.cookie]
      [[http.services.ma_page.loadBalancer.servers]]
        url = "http://[2001:db8:0:180::a0]"
      [[http.services.ma_page.loadBalancer.servers]]
        url = "http://[2001:db8:0:180::a1]"

[[tls.certificates]]
  certFile = "/opt/traefik/tls/mapage.jmador.yo/mapage.jmador.yo.crt" # Chemin d'accès au certificat
  keyFile = "/opt/traefik/tls/mapage.jmador.yo/mapage.jmador.yo.key" # Chemin d'accès à la clé du certificat

Note: la génération des certificats est expliquée dans la documentation concernant OpenSSL. Prenez garde aux permissions de vos certificats. L'utilisateur "traefik" doit en être le propriétaire.

Avec redirection de HTTP vers HTTPS

Le support d'HTTPS sera obligatoire, toute connexion en HTTP redirigera automatiquement le client en HTTPS.

<img src="https://doc.ycharbi.fr/fichiers/services/mandataire/traefik/images/Schéma_exemple_Traefik_redirect_https_auth_rep.svg" alt="Schéma logique Traefik" style="display: block; width: 60%; height:auto; margin-left:auto; margin-right:auto;"/>

Comme nous pouvons le voir sur le schéma ci-dessus, il faut que l'on déclare un nouveau middlewares.

Reprenons notre fichier /opt/traefik/config/vhosts/middlewares.toml:

[http]
  [http.middlewares]
    [http.middlewares.mon_auth.basicAuth]
      usersFile = "/opt/traefik/passwd/groupe1"
    [http.middlewares.redirection_https.redirectScheme] # Création d'un middleware nommé "redirection_https" de type "redirectScheme"
      scheme = "https" # Redirection vers https
      permanent = true # Permet l'envoie d'un code HTTP 301

Puis, dans notre fichier d'hôte /opt/traefik/config/vhosts/ma_page.toml:

[http]
  [http.routers]
     [http.routers.vers_ma_page_http] # Création d'un routeur nommé "vers_ma_page_http"
      entryPoints = ["http"] # On force l'accès via le point d'entrée "http"
      rule = "Host(`mapage.jmador.yo`)" # URL avec laquelle sera joint notre site web
      service = "ma_page" # Nom du service (même si on y va pas)
      middlewares = ["redirection_https"] # Définit les middlewares utilisés pour ce routeur

     [http.routers.vers_ma_page_https] # Création d'un routeur nommé "vers_ma_page_https"
      entryPoints = ["https"]
      rule = "Host(`mapage.jmador.yo`)"
      service = "ma_page"
      middlewares = ["mon_auth"]
      [http.routers.vers_ma_page_https.tls]

  [http.services]
    [http.services.ma_page.loadBalancer.sticky.cookie]
      [[http.services.ma_page.loadBalancer.servers]]
        url = "http://[2001:db8:0:180::a0]"
      [[http.services.ma_page.loadBalancer.servers]]
        url = "http://[2001:db8:0:180::a1]"

[[tls.certificates]]
  certFile = "/opt/traefik/tls/mapage.jmador.yo/mapage.jmador.yo.crt"
  keyFile = "/opt/traefik/tls/mapage.jmador.yo/mapage.jmador.yo.key"

Supervision

Traefik permet d'exporter ses métriques au format OpenMetrics afin d'être exploités par des outils de supervision tel que Prometheus.

Pour cela il faut ajouter les paramètres suivants à la fin du fichier /opt/traefik/config/traefik.toml:

[metrics]
  [metrics.prometheus]

ATTENTION

Relancer Traefik pour la prise en compte de la configuration.

Sources