Une des pièces centrales de l’infrastructure des services de Picasoft est Traefik.

Comme on l’a vu dans les pages précédentes, exposer un conteneur par un mapping sur le port de l’hôte a ses limites.

Dans cette page, on va voir en illustration le problème avec les ports, et comment le résoudre grâce à Traefik. =)

Sur chaque machine, Picasoft héberge plusieurs services web accessibles via une URL. Lors de la consultation sur un navigateur web, cette URL est traduite en adresse IP, et permet de trouver la machine qui correspond à l’URL.

Chaque service tourne dans un conteneur Docker. En prenant pour exemple Mattermost et Etherpad, la situation est la suivante :

Note:

Le navigateur contacte naturellement le port 443 car l’URL est en HTTPS, dans cet exemple. 443 est le port standard pour le protocole HTTPS, la version sécurisée de HTTP.

Question:

Comment est-ce-que la machine sait quel conteneur correspond à quelle URL ?

En réalité, elle n’a aucun moyen de le savoir.

Question:

Comment faire ?

Une technique traditionnelle, lorsque plusieurs services tournent sur une même machine et doivent être accessibles via Internet, est d’utiliser les ports pour la destination. Ainsi, chaque conteneur écoute sur un port particulier de la machine. Par exemple, on aurait la situation suivante :

Ainsi, chaque conteneur écoute sur un port spécifique :

  • team.picasoft.net:4000 envoie sur le port 4000 de la machine, sur lequel le conteneur Mattermost écoute
  • pad.picasoft.net:5000 envoie sur le port 5000 de la machine, sur lequel le conteneur Mattermost écoute

Question:

Quel est le problème avec cette configuration ?

C’est une solution simple, néanmoins elle nécessite d’utiliser les ports dans l’URL, et ce n’est vraiment pas pratique pour les utilisateurices. Les sites web que l’on consulte tous les jours n’ont pas de numéro de port dans leur URL, en général.

Important:

En plus, le service n’est pas sécurisé de base via HTTPS avec cette solution. On ne rentre pas dans les détails ici, mais HTTPS permet de chiffrer les communications entre un client et un serveur. Pour faire du HTTPS, il faut obtenir un certificat, qui atteste qu’on est bien le propriétaire du site et qui « contient » la clé de chiffrement, puis indiquer aux applications d’utiliser ces certificats… Un gros boulot qu’il faudrait refaire sur l’ensemble des services!

L’autre technique traditionnelle, c’est d’utiliser un reverse proxy.

  • Un proxy est un serveur vers lequel sont redirigées toutes les requêtes qui sortent d’un ordinateur (par exemple, dans une entreprise, une université…)
  • Un reverse proxy est un serveur vers lequel sont redirigées toutes les requêtes qui entrent vers un ordinateur.

L’idée est donc qu’un reverse proxy écoute sur les port 80 et 443 (qui n’ont pas besoin d’être spécifiés dans l’URL), et qu’il redirige automatiquement vers le bon conteneur, grâce à l’URL de la requête. La situation sera la suivante, avec Traefik :

Question:

Comment associer une URL à un conteneur ?

Il y a plusieurs solutions. nginx peut tout à faire être utilisé comme reverse proxy. L’inconvénient, c’est qu’il va falloir lui fournir une configuration statique qui associe une URL à un conteneur, et cette configuration devra être modifiée à chaque nouveau service. Il faudra aussi gérer les certificats pour chaque service, par exemple.

C’est pourquoi nous utilisons Traefik, un outil clé en main pour Docker. Traefik fonctionne comme un reverse proxy traditionnel, mais ajoute quelques fonctionnalités bien pratiques :

  • L’association entre URL et conteneur se fait grâce aux labels Docker, ce qui évite de modifier la configuration du reverse-proxy à l’ajout d’un nouveau service.
  • Traefik découvre automatiquement les nouveaux services en “surveillant” Docker, regarde les labels, et crée des routes automatiquement
  • Traefik gère automatiquement la création et le renouvellement des certificats des services
  • Comme tout reverse-proxy, on peut lui demander d’ajouter des headers de sécurité dans les requêtes HTTP

En somme, pour ajouter un nouveau service accessible depuis internet via HTTPS, il suffit d’ajouter quelques labels sur le conteneur, et à son lancement, il est accessible automagiquement, sans configuration supplémentaire.

Lien:

Pour les curieux.ses, une explication de la configuration est aussi disponible sur la page de migration vers Traefik v2.

On ne présente pas ici la manière de configurer Traefik, qui évolue au fil du temps, mais les choix qui ont été faits et les points de vigilance.

Traefik permet de sécuriser les services web en établissant une connexion TLS (HTTPS) entre les clients et lui. Il communique ensuite avec les conteneurs sur le réseau privé Docker, en clair (HTTP). Il est donc fondamental qu’il soit bien sécurisé. En particulier, voici quelques options de configuration qui ont été ajoutées :

  • Forcer à rediriger les requêtes HTTP vers HTTPS.
  • Utilisation de Let’s Encrypt pour créer et renouveller automatiquement les certificats des services.
  • Restriction de la version de TLS minimale (actuellement, 1.2).
  • Restriction des algorithmes de chiffrement utilisés (ciphers), pour éviter les plus faibles tout en garantissant une compatibilité avec la plupart des appareils.
  • Injection de headers de sécurité dans toutes les requêtes et réponses, permettant de limiter les risques d’attaques classiques (XSS, MITM, etc).

Traefik est aussi configuré pour compresser l’ensemble des flux qui transitent par son biais, sauf les flux de streaming (e.g. vidéo…).

Traefik se trouve dans le réseau Docker appelé proxy. Il inspecte tous les conteneurs de ce réseau et décide s’il doit router vers ces conteneurs, en fonction de leurs labels.

Attention:

Tout conteneur qui ne se trouve pas dans le réseau proxy ne sera jamais pris en compte par Traefik.

  1. Traefik est un service critique pour la sécurité de nos services : c’est un des seuls services qui est accessible en première ligne depuis Internet. De tous nos services, c’est un de ceux qui peut être le plus plausiblement compromis.
  2. Pour avoir la liste des autres conteneurs Docker, Traefik se connecte normalement via la socket d’administration Docker /var/run/docker.sock. C’est une permission accordée que très exceptionnellement à un conteneur, car elle donne tous les droits sur Docker, et donc les droits root par extension.

Pour éviter de donner trop de permissions à Traefik, on utilise un intermédiaire entre Traefik et la socket d’administration. Cet intermédiaire n’est pas accessible depuis le monde extérieur, donc difficile à compromettre. Il ne va autoriser que les requêtes légitimes de Traefik (lister les conteneurs) et interdire toute autre tentative de connexion (créer ou supprimer un conteneur, un volume, etc).

C’est parti pour un peu plus de pratique sur la page suivante !

  • technique/tech_team/traefik.txt
  • de ppom