Traefik: LE reverse proxy

Je vous présente dans cette article, comment est installé et configuré Traefik dans notre infra pour orchestrer nos applications docker.

Traefik: LE reverse proxy
Photo by Bruno Wolff / Unsplash

Pour la petite histoire...

Il y a quelques années, en arrivant chez Lucine, j'ai été confronté à une infrastructure en VM VSphere chez OVH.

Pour déployer les applications, nous utilisions des scripts Ansible…
Bon, ça coûtait un bras et c’était pénible de déployer la moindre mise à jour (même si quelques scripts exécutés via SSH étaient de la partie).
Mais j'avais un collègue DSI ouvert d'esprit, avec qui j’ai découvert le reverse proxy Traefik, merci Vincent si tu te reconnais.

Depuis, nous avons dit adieu aux VM, vSphere et OVH (et à Lucine), mais j'ai gardé Traefik en reverse proxy pour tous mes environnements et applications !

Vous l'aurez compris, une fois bien configuré, Traefik gère comme un charme toutes vos applications, avec les certificats SSL automatiques fournis par Let's Encrypt, load balancing et cie...


Docker / Docker compose

Comme beaucoup d'autres comparses, il y a une autre techno que j'apprécie tout particulièrement j'ai nommé Docker.

Depuis quelques années maintenant, je n'ai plus une seule application qui ne soit pas dockerisée. Quelle joie quand on a connu le self-hosting multi-technologies sur VPS, avec Apache, Tomcat, PHP, MySQL... Mais je m'égare, Docker sera le sujet d'autres articles (ou pas, il y en a tellement...).


Revenons à Traefik.

Configuration de base

J'héberge pour ma part mes applications sur un serveur dédié, tournant sous Debian, avec le pare-feu UFW activé. Il ne laisse passé que les ports web (80 et 443), tout deux écouter par Traefik.
J'ai aussi installé un fail2ban, tiens ça aussi il faudra revenir dessus...

Évidement, il faut également un nom de domain, ce sera domain.com pour l'exemple, fourni par OVH.

Enfin il nous faut également un réseau docker (bridge) pour faire le ... pont ... entre le votre carte réseau et les cartes virtuelles de vos containers.
J'ai pris l'habitude de l'appeler web, mais vous pouvez l'appeler comme bon vous semble, à condition d'utiliser ce nom dans le reste de vos configurations.

docker network create web

Organisation des applications

Je déploie toutes mes applications via Docker Compose. Pour ce faire, j'ai opté pour une organisation toute simple :
un dossier /app à la racine de mon disque.
Pour chaque application, un sous-dossier contenant au minimum :

  • le docker-compose.yml
  • un .env

Et parfois un dossier config pour des configuration spécifique ne passant pas par variables d'environnement et data pour stocker les données non volatiles.

Et c'est parti pour Traefik.

Je crée donc une dossier /app/traefik pour accueillir le service.

💡
Et oui Traefik est une application Docker comme les autres 😄

Et on y met nos fichier docker-compose.yml et .env

# docker-compose.yml
name: traefik
services:
    traefik:
        image: traefik:v2.11.5
        container_name: traefik
        hostname: traefik
        restart: unless-stopped
        command:
            # Setup logger
            - --log.level=DEBUG
            - --log.filePath=/var/log/traefik/traefik.log
            - --accesslog=true
            - --accesslog.filepath=/var/log/traefik/access.log
            # Enable the Trafik dashboard
            - --api.dashboard=true
            # Tell Traefik to discover containers using the Docker API
            - --providers.docker=true
            - --providers.docker.network=web
            - --providers.docker.endpoint=unix:///var/run/docker.sock
            - --providers.docker.exposedbydefault=false
            # Set up LetsEncrypt
            - --certificatesresolvers.le.acme.dnschallenge=true
            - --certificatesresolvers.le.acme.dnschallenge.provider=ovh
            - --certificatesresolvers.le.acme.dnschallenge.delayBeforeCheck=10
            - --certificatesresolvers.le.acme.email=admin@domain.com
            - --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json
            # Set up an insecure listener that redirects all traffic to TLS entrypoint
            - --entrypoints.web.address=:80
            - --entrypoints.web.http.redirections.entryPoint.to=websecure
            - --entrypoints.web.http.redirections.entryPoint.scheme=https
            - --entrypoints.web.http.redirections.entrypoint.permanent=true
            # Set up the TLS configuration for websecure
            - --entrypoints.websecure.address=:443
            - --entrypoints.websecure.http.tls=true
            - --entrypoints.websecure.http.tls.certResolver=le

        environment:
          - TZ=Europe/Paris
          - OVH_ENDPOINT=ovh-eu
          - OVH_APPLICATION_KEY=${APP_OVH_APPLICATION_KEY}
          - OVH_APPLICATION_SECRET=${APP_OVH_APPLICATION_SECRET}
          - OVH_CONSUMER_KEY=${APP_OVH_CONSUMER_KEY}

        volumes:
            - /var/run/docker.sock:/var/run/docker.sock
            - ./data/letsencrypt:/letsencrypt
            - ./log/traefik:/var/log/traefik
        ports:
            - '80:80'
            - '443:443'
        networks:
            - web
        labels:
            - traefik.enable=true
            # ADMIN AUTH
            - traefik.http.middlewares.admin-auth.basicauth.users=${APP_PASSWORD}
            # HTTP  ROUTER
            - traefik.http.routers.${APP_NAME}.rule=Host(`${APP_DNS}`)
            - traefik.http.routers.${APP_NAME}.entrypoints=websecure
            - traefik.http.routers.${APP_NAME}.middlewares=admin-auth@docker
            - traefik.http.routers.${APP_NAME}.service=api@internal

networks:
    web:
        external: true

Puis il nous faut les quelques variable d'environnement utilisé dans le composé.

#.env file
APP_NAME=traefik
APP_DNS=traefik.domain.com
APP_PASSWORD=htpassword-type-username-and-password
APP_OVH_APPLICATION_KEY=my-secret-ovh-app-key
APP_OVH_APPLICATION_SECRET=my-secret-ovh-app-secret
APP_OVH_CONSUMER_KEY=my-ovh-consumer-key

Quelques explications:

  • On active les logs qui seront dispo dans le dossier ./log/traefik
  • Le dashboard (lecture seule) est activé pour monitorer ce qu'il se passe sur le reverse proxy.
    Il est sécurisé par un couple user/mdp du type htpassword.
    https://httpd.apache.org/docs/2.4/fr/programs/htpasswd.html
    https://www.web2generators.com/apache-tools/htpasswd-generator
  • Letsencrypt utilise l'API d'OVH pour confirmer automatiquement la propriété du nom de domaine (ça fonctionne également avec d'autre provider comme GANDI).
  • Tout le trafique non sécurisé (http, port 80) est redirigé vers https et les certificats Letsencrypt sont activés par défaut pour toutes les applications.

Les labels docker

C'est LE truc magique qui va vous permettre d'orchestrer vos applications en toute simplicité.
Ici on créer un router appelé traefik (cf .env) qui va écouter la DNS traefik.domain.com (cf .env)

Traefik détecte automatiquement le port du service, bien que l'on puisse le spécifier (dans le cas ou par exemple votre container expose plusieurs ports).

# docker-compose.yml
name: traefik
services:
    traefik:
        image: traefik:v2.11.5
#...
    labels:
#...
      - traefik.http.routers.${APP_NAME}.service=${APP_NAME}
      - traefik.http.services.${APP_NAME}.loadbalancer.server.port=8080
#...

Ces labels vont vous permettre de configurer Traefik, service à service.

Ont y reviendra au fur et a mesure des futurs articles...

Et d'ici là je vous invite évidement à RTFM
https://doc.traefik.io/traefik/getting-started/quick-start/

Démarrage du reverse proxy

Pas de surprise, c'est du docker-compose

docker compose up -d

A partir de là, rendez-vous sur votre DNS et vous devriez voir votre dashboard Trafik

Ajoutons donc un service !

Traefik propose une micro app containerisé très utile pour faire des test.
C'est l'image docker / container whoami.

Si on suit le principe énoncé plus haut, on crée un dossier dans /app, et on y ajoute un docker-compose.yml et un fichier .env (au besoin)

mkdir -p /app/whoami
cd /app/whoami
touch docker-compose.yml .env
# docker-compose.yml
name: whoami
services:
  whoami:
    image: traefik/whoami
    container_name: whoami
    hostname: whoami
    networks:
      - web
    labels:
      - traefik.enable=true
      - traefik.http.routers.${APP_NAME}.rule=Host(`${APP_DNS}`)
      - traefik.http.routers.${APP_NAME}.entrypoints=websecure

networks:
  web:
    external: true
# .env
APP_DNS=whoami.domain.com
APP_NAME=whoami

Conclusion

Et voilà, dans les grandes lignes... Je reviendrai sur différents points au fil des futurs articles.
Mais cette architecture permet d'héberger plutôt facilement pas mal de services (il faut que le serveur tienne la route quand même, je ne ferais pas ça sur un Raspberry Pi 😄).
Vous pouvez maintenant héberger facilement :

  • Nextcloud pour faire un cloud ? (on ajoutera quelques middlewares au reverse proxy pour encore plus de sécurité)
  • Matrix Synapse et Element pour faire un service de communication sécurisé, privé et chiffré de bout en bout ?
  • Bitwarden (vaultwarden) pour fournir un coffre-fort numérique ?
  • Gitlab CE pour héberger votre code-base ? Ou juste un Gitlab Runner pour déployer vos applications ?
  • Sonarqube pour la qualité
  • Ou toutes autre application dockerisé !

En parlant de Gitlab, avec un bonne CI/CD il devient encore plus facile de déployer et mettre à jour vos apps 😎. On en reparle bientôt.

À vos claviers et bon déploiement.