config_nging_gatsby-vignette

⏳  Temps de lecture :  ~6 min.
✍🏻  Édité le :  mercredi 24 mars 2021
Niveau :   🧩 🧩 🧩 🧩 🧩

Pour ce premier article d'une série dédiée à la mise en place d'un serveur à l'aide de conteneurs Docker, il est naturel d'aborder la question par ce qui constitue son épine dorsale : le reverse proxy.

Introduction

Quel différence entre un bon vieux Apache / Nginx me dirait vous ? Après tout, ça fonctionne, et vous avez passé tant de tant à triturer vos configurations pour ne pas tout casser en adoptant un des derniers projet open-source à la mode. If it works, don't fix it, n'est-ce pas ?

Déjà, nous verrons dans cet article que loin de remiser les vénérables, Træfik les complémente.
Chaque outil se concentrera désormais sur ce qu'il maitrise le mieux : un reverse proxy automatisé et centralisé d'une part (Træfik) et nos serveurs HTTP de l'autre (Apache, Nginx, Node + Express, Django…).

Aussi, ce serait négliger le plus grand attrait de Træfik à mes yeux : son intégration symbiotique avec la plate-forme de conteneurs Docker.

Ses cas d'usages sont nombreux, mais d'emblée nous pouvons en identifier les principaux :

  • Fédération de vos services sur un ou des domaines / sous-domaines.
  • Génération automatique de vos certificats SSL au fil de l'eau.
  • Équilibrage de la charge sur différents instances dans une grappe de services.
  • Authentification basique.
  • Gestion de middlewares.
  • Règles de filtrage : ré-écritures par motifs, redirections…
  • Moniteur de santé de vos services.

Nous nous intéressons surtout ici aux deux premières fonctions, puis, dans un futur article, nous explorerons les possibilités du middleware d'authentification Authelia, qui fonctionne aussi en symbiose avec Træfik.

À noter que Træfik peut traiter du traffic TCP, tel HTTP/S (ports 80 et 443), ou encore WebSockets, mais aussi de l'UDP, etc.
Seul l'HTTP sera étudié ici, ce qui couvre la majorité des usages.

Les utilités du Reverse Proxy

Petit rappel graphique de l'utilité d'un tel outil dans votre pile technologique.

Traefik — Schéma de fonctionnement

Complémentarité avec Nginx etc.

Même si Træfik enlève un gros poids en terme de responsabilités qui incombait autrefois au combo reverse proxy / serveur HTTP avec Nginx, il est souvent utile d'en avoir une petite instance aux côtés des piles Docker que l'on déploie.

Nginx / Apache permettent tout un tas de choses qui ne sont pas aussi bien documentées voire absentes du viseur de Træfik telles que :

  • L'injection d'entêtes HTTP.
  • La politique de mise en cache.
  • Le service de contenu statique.

À voir dans le futur dans quel mesure il phagocytera ces spécificités, mais pour l'heure, de l'ancienneté de Nginx, découle plus de de ressources en ligne afin d'optimiser le déploiement de services tiers (Nextcloud, Seafile, Odoo…).
Par exemple, au moment où j'écris ces lignes, la version payante Træfik Enterprise dispose d'un module « http-cache » que je n'ai pas testé.

Cela étant dit, il me semble préférable de bien séparer les responsabilités pour le moment, le temps de mieux connaître l'outil, encore jeune et pas parfaitement stable (cf. la V2 qui fait un bond).

Création et Configuration de Træfik

Base du fichier de la pile Docker Compose

Tout d'abord, dans un dossier vierge, on crée un fichier docker-compose.yml.

Ensuite, voyons qu'elle est la version la plus récente de Træfik.
La bonne pratique est de fixer les versions afin d'éviter les mauvaises surprises à l'avenir.

Avec ceci, on veille à ce que le service se (re)lance tout le temps par le biais du démon de Docker.

version: '3'
  
services:
  traefik:
    image: traefik:v2.4.8
    restart: always
📋  copier

Configuration des Volumes

Il faut définir un point de montage pour que nos certificats SSL persistent malgré la destruction / reconstruction de notre pile Docker Compose.
On injecte ici une variable d'environnement, il est de bon ton de décoréler le stockage (un état singulier) d'une fonction (un service amnésique). Il est ainsi plus facile de migrer ou effectuer des sauvegardes.

Aussi, parce que Træfik a besoin d'un accès privilégié au démon Docker qui tourne sur l'hôte, nous les lions via son interface de connexion, le docker.sock.
C'est par ce biais que notre reverse proxy va effectuer ses tours de magie pour nous libérer de tâches ingrates, comme étendre la portée de nos certificats à de nouveaux domaines.

# …ReverseProxy/docker-compose.yml

version: '3'
  
services:
  traefik:
    image: traefik:v2.4.5
    restart: always
    volumes:
      # Persistence des certificats
      - ${SRV_VOL_BASE}/letsencrypt:/letsencrypt
      # Lien vers l'interface de connexion de Docker
      - /var/run/docker.sock:/var/run/docker.sock:ro
📋  copier

Configuration du Réseau

En tant que point d'entrée HTTP/S, Traefik est le seul conteneur à exposer les ports 80 et 443 vers le monde extérieur.
Même si nous n'utiliserons que le HTTPS, il est utile de conserver le port 80 ne serait-ce que pour rediriger les requêtes non sécurisées vers leur versions sécurisées.
Træfik en a aussi besoin pour son bon fonctionnement interne, notament pour la génération des certificats SSL.

Nous créons aussi un réseau Docker interne qui servira de relai pour les autres services qui cohabitent avec Traefik.

version: '3'
  
services:
  traefik:
    image: traefik:v2.4.5
    restart: always
    volumes:
      - ${SRV_VOL_BASE}/letsencrypt:/letsencrypt
      - /var/run/docker.sock:/var/run/docker.sock:ro
    ports:
      - 443:443
      # Port 80 utile pour redirections, certifications…
      - 80:80
    # Accès au réseau relai interne
    networks:
      - traefik
  
# Réseau relai interne
networks:
  traefik:
    driver: bridge
📋  copier

Configuration de Træfik

Nous rentrons désormais dans le cœur de notre configuration de Træfik.
Il est possible de créer un fichier séparé pour cela, traefik.toml, cependant je préfère centraliser tout dans le fichier compose via l'utisation des arguments de command et des labels.

Les Arguments de Commande

command:
      # ------------------------------------------- Configuration Docker
      - --providers.docker=true
      - --providers.docker.exposedbydefault=false
      # ------------------------------------------- Points d'entrée
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      # ------------------------------------------- Configuration SSL
      - --certificatesresolvers.ovh.acme.dnschallenge=true
      - --certificatesresolvers.ovh.acme.dnschallenge.provider=ovh
      - --certificatesresolvers.ovh.acme.email=${RESOLVER_ACME_MAIL}
      - --certificatesresolvers.ovh.acme.storage=/letsencrypt/acme.json
      # ------------------------------------------- Pré-production
      # Décommentez pour utiliser les serveurs de test de Letsencrypt.
      # - --certificatesresolvers.ovh.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
      # - --log.level=DEBUG
📋  copier
  • Les providers définissent le fournisseur de nos services, ici Docker.
  • Les entrypoints permettent de nommer les points d'entrées HTTP/S tels que les autres services docker les appeleront : web et websecure.
  • Les certificatesresolvers contiennent la configuration SSL, s'appuyant ici sur un challenge DNS avec les services d'OVH.

La configuration SSL avec OVH se trouve dans le fichier .env détaillé à la fin de ce guide.
Pour les certificatesresolvers, comme pour les entrypoints, les services l'appeleront par son nom (ovh) que nous définissons ici globalement.

Note : afin de ne pas trop solliciter les braves serveurs de Letsencrypt et et par conséquent se retrouvé bridé, il est possible d'utiliser les serveurs de pré-production (dé-commentez la ligne idoine).

Les Étiquettes de Métadonnées

labels:
      # ------------------------------------------- Global
      - traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)
      - traefik.http.routers.http-catchall.entrypoints=web
      - traefik.http.routers.http-catchall.middlewares=redirect-to-https@docker
      - traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https
…
📋  copier

Ici nous définissons une règle globale qui permet de forcer le traffic HTTP vers du HTTPS, ceci via l'utilisation d'un intermédiaire et de routeurs.

Certification SSL avec OVH

Rendez-vous d'abord sur https://eu.api.ovh.com/createToken puis créez les droits suivants :

  • GET /domain/zone/*
  • PUT /domain/zone/*
  • POST /domain/zone/*
  • DELETE /domain/zone/*

Vous êtes libre d'affiner cette configuration en remplaçant les jokers représentés par les astérisques * par vos domaines concernés ou en définissant une durée de validité plus ou moins permissive.

Ensuite, authentifiez-vous puis stockez en sécurité les informations fournies :

  • ApplicationKey
  • ApplicationSecret
  • ConsumerKey

Les fichiers de Configuration Finale

Le fichier .env qui contient les réglages du service et les identifiants du challenge DNS.
On oubliera pas d'ajouter le paramètre env_file: .env dans le fichier docker-compose.yml qui réside à ses côtés.

Note : pensez à conserver les identifiants dans un gestionnaire de mot de passe comme Bitwarden ou Keepass et conservez un modèle .env.model dans votre dépôt de sources.

# Pile Docker
# ====================================
SRV_VOL_BASE=./volumes
  
# Træfik
# ====================================
TZ=Europe/Paris
  
# Certificats SSL (Challenge DNS OVH)
# ------------------------------------
RESOLVER_ACME_MAIL=<bar@foo.com>
OVH_ENDPOINT=<ovh-eu>
OVH_APPLICATION_KEY=<key>
OVH_APPLICATION_SECRET=<secret>
OVH_CONSUMER_KEY=<consumer>
📋  copier

Le fichier docker-compose.yml au complet.

version: '3'

services:
  traefik:
    image: traefik:v2.4.5
    env_file: .env
    restart: always
    command:
      # ------------------------------------------- Configuration Docker
      - --providers.docker=true
      - --providers.docker.exposedbydefault=false
      # ------------------------------------------- Points d'entrée
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      # ------------------------------------------- Configuration SSL
      - --certificatesresolvers.ovh.acme.dnschallenge=true
      - --certificatesresolvers.ovh.acme.dnschallenge.provider=ovh
      - --certificatesresolvers.ovh.acme.email=${RESOLVER_ACME_MAIL}
      - --certificatesresolvers.ovh.acme.storage=/letsencrypt/acme.json
      # ------------------------------------------- Pré-production
      # - --certificatesresolvers.ovh.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
      # - --log.level=DEBUG
    ports:
      - 80:80
      - 443:443
    volumes:
      - ${SRV_VOL_BASE}/letsencrypt:/letsencrypt
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - traefik
    labels:
      # ------------------------------------------- Global
      - traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)
      - traefik.http.routers.http-catchall.entrypoints=web
      - traefik.http.routers.http-catchall.middlewares=redirect-to-https@docker
      - traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https

networks:
  traefik:
    driver: bridge
📋  copier

Un simple docker-compose up -d à la racine du projet et c'est parti !

Conclusion et Suite…

Avoir un reverse proxy c'est bien, lui donner de quoi s'occuper c'est mieux !

Quoi de mieux que de d'attaquer avec un autre service tout aussi prépondérant aus sein de notre pile technologique : l'annuaire d'utilisateurs (LDAP), sur lequel repose une foultitude de services courants.