--- sidebar_position: 4 tags: [traefik, docker, reverse-proxy, ssl, crowdsec, homelab] last_update: date: 2025-11-30 --- # Traefik - Reverse Proxy moderne Traefik est le reverse proxy au cœur de l'infrastructure Docker du homelab. Il gère le routage de toutes les requêtes HTTP/HTTPS vers les conteneurs appropriés, avec gestion automatique des certificats SSL et intégration de la sécurité via CrowdSec. ## Architecture globale L'infrastructure utilise **deux instances Traefik distinctes**, chacune sur sa propre interface réseau : - **traefik-public** (192.168.1.2) : Services publics accessibles sur Internet (*.tellserv.fr) - **traefik-private** (192.168.1.3) : Services locaux réservés au réseau interne (*.local.tellserv.fr) Cette séparation permet : - **Isolation réseau** : Les services privés ne sont jamais exposés publiquement - **Sécurité renforcée** : Politiques de sécurité différenciées selon l'exposition - **Gestion simplifiée** : Chaque instance a sa propre configuration et ses propres règles ### Prérequis réseau La VM hébergeant Traefik dispose de **deux NICs (Network Interface Cards)** : - **NIC 1** (192.168.1.2) : Interface publique pour traefik-public - **NIC 2** (192.168.1.3) : Interface locale pour traefik-private ## Éléments communs aux deux instances ### Image et version Les deux instances utilisent l'image officielle **Traefik v3** : ```yaml image: traefik:v3 ``` ### Réseau Docker Les deux instances se connectent au réseau Docker externe `traefik_network` qui permet la communication avec tous les conteneurs à proxifier : ```yaml networks: - traefik_network ``` Ce réseau doit être créé au préalable avec : ```bash docker network create traefik_network ``` ### Gestion des certificats SSL Les deux instances utilisent **Let's Encrypt** avec le challenge DNS **Cloudflare** pour générer automatiquement des certificats SSL wildcard. **Avantages du challenge DNS :** - Certificats wildcard (*.tellserv.fr, *.local.tellserv.fr) - Pas besoin d'exposition HTTP publique pour la validation - Fonctionne même pour les services internes **Configuration commune :** ```yaml environment: - TZ=Europe/Paris - CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN} ``` Le token API Cloudflare doit avoir les permissions : - Zone / DNS / Edit - Zone / Zone / Read ### Provider Docker Les deux instances utilisent le provider Docker pour la découverte automatique des services : ```yaml volumes: - /var/run/docker.sock:/var/run/docker.sock:ro ``` Traefik surveille les conteneurs et crée automatiquement les routes basées sur les labels Docker. ### Configuration dynamique Chaque instance charge des fichiers de configuration dynamique depuis un répertoire dédié : ```yaml volumes: - ./dynamic-public:/etc/traefik/dynamic:ro # Pour traefik-public - ./dynamic-private:/etc/traefik/dynamic:ro # Pour traefik-private ``` Ces fichiers permettent de définir des middlewares, des routers et des services sans redémarrer Traefik. ### Dashboard Traefik Les deux instances exposent leur dashboard via un sous-domaine dédié : - **traefik-public** : `traefik-public.local.tellserv.fr` - **traefik-private** : `traefik-private.local.tellserv.fr` Le dashboard permet de visualiser en temps réel : - Les routers actifs et leurs règles - Les services détectés - Les middlewares appliqués - L'état de santé des backends ### Politique de redémarrage ```yaml restart: unless-stopped ``` Les conteneurs redémarrent automatiquement sauf s'ils ont été arrêtés manuellement. ### Accès à l'hôte Docker ```yaml extra_hosts: - "host.docker.internal:host-gateway" ``` Cette configuration permet aux conteneurs Traefik d'accéder à l'hôte Docker via le nom `host.docker.internal`, utile pour proxifier des services tournant directement sur l'hôte. ## Instance traefik-public ### Rôle et utilisation L'instance **traefik-public** gère tous les services **accessibles depuis Internet** : - Applications web publiques - APIs exposées - Services authentifiés mais accessibles de l'extérieur ### Binding réseau ```yaml ports: - "192.168.1.2:80:80" - "192.168.1.2:443:443" ``` Traefik écoute uniquement sur l'IP **192.168.1.2**, correspondant à la première NIC de la VM. ### Entry points **Port 80 (HTTP)** : ```yaml web: address: ":80" http: redirections: entryPoint: to: websecure scheme: https ``` Redirection automatique de HTTP vers HTTPS pour toutes les requêtes. **Port 443 (HTTPS)** : ```yaml websecure: address: ":443" http: middlewares: - crowdsec-bouncer@file - secheaders@file - ratelimit@file transport: respondingTimeouts: idleTimeout: 300s ``` Trois middlewares appliqués par défaut sur tous les services publics : 1. **crowdsec-bouncer** : Blocage des IPs malveillantes détectées par CrowdSec 2. **secheaders** : Headers de sécurité HTTP (HSTS, CSP, etc.) 3. **ratelimit** : Limitation du nombre de requêtes ### Middlewares publics Fichier : `dynamic-public/middlewares.yml` **ratelimit** : ```yaml ratelimit: rateLimit: average: 100 burst: 50 period: 1s ``` Autorise en moyenne 100 requêtes/seconde avec des pics jusqu'à 50 requêtes supplémentaires. **secheaders** : ```yaml secheaders: headers: stsSeconds: 31536000 forceSTSHeader: true ``` Force HSTS (HTTP Strict Transport Security) pendant 1 an, obligeant les navigateurs à toujours utiliser HTTPS. **evasive** : ```yaml evasive: rateLimit: average: 3 burst: 5 period: 1s ``` Rate limiting strict pour les endpoints sensibles (3 req/s en moyenne, 5 en burst). ### Intégration CrowdSec **CrowdSec** est un système de détection et prévention d'intrusions communautaire. Traefik-public intègre le **bouncer CrowdSec** pour bloquer automatiquement les IPs malveillantes. **Middleware CrowdSec** (appliqué sur websecure) : ```yaml middlewares: - crowdsec-bouncer@file ``` Le bouncer interroge l'API CrowdSec locale pour vérifier si l'IP source est bannie. En cas de match, la requête est bloquée avec un code HTTP 403. ### Certificats SSL Stockage des certificats : ```yaml volumes: - ./letsencrypt-public:/letsencrypt ``` Configuration ACME dans `traefik-public.yml` : ```yaml certificatesResolvers: cloudflare: acme: email: your-email@example.com storage: /letsencrypt/cloudflare_acme.json keyType: EC256 dnsChallenge: provider: cloudflare resolvers: - "1.1.1.1:53" - "8.8.8.8:53" ``` Les certificats sont automatiquement renouvelés 30 jours avant expiration. ### Logging ```yaml log: level: DEBUG filePath: "/var/log/traefik/traefik.log" accessLog: filePath: "/var/log/traefik/access.log" format: json ``` Logs stockés dans `/var/log/traefik/` sur l'hôte Docker : - **traefik.log** : Logs système de Traefik (démarrage, erreurs, recharges) - **access.log** : Logs d'accès au format JSON (requêtes HTTP) ### Configuration provider Docker ```yaml providers: docker: endpoint: "unix:///var/run/docker.sock" exposedByDefault: false network: traefik_network ``` - **exposedByDefault: false** : Les conteneurs ne sont pas automatiquement exposés, il faut explicitement ajouter des labels Traefik - **network: traefik_network** : Traefik utilisera ce réseau pour communiquer avec les conteneurs ### Exemple de labels Docker Pour exposer un service via traefik-public : ```yaml services: myapp: image: myapp:latest labels: - "traefik.enable=true" - "traefik.http.routers.myapp.rule=Host(`app.tellserv.fr`)" - "traefik.http.routers.myapp.entrypoints=websecure" - "traefik.http.routers.myapp.tls.certresolver=cloudflare" - "traefik.http.services.myapp.loadbalancer.server.port=80" networks: - traefik_network ``` ## Instance traefik-private ### Rôle et utilisation L'instance **traefik-private** gère tous les services **réservés au réseau local** : - Interfaces d'administration (Proxmox, Cockpit) - Dashboards internes - Services de monitoring - Outils de développement ### Binding réseau ```yaml ports: - "192.168.1.3:80:80" - "192.168.1.3:443:443" ``` Traefik écoute uniquement sur l'IP **192.168.1.3**, correspondant à la seconde NIC de la VM. ### Entry points **Port 80 (HTTP)** : ```yaml weblocal: address: ":80" http: redirections: entryPoint: to: local scheme: https ``` Redirection automatique de HTTP vers HTTPS (entrypoint `local`). **Port 443 (HTTPS)** : ```yaml local: address: ":443" http: middlewares: - localonly@file ``` Un seul middleware appliqué par défaut : **localonly** qui restreint l'accès aux IPs locales. ### Middlewares privés Fichier : `dynamic-private/middlewares.yml` **localonly** : ```yaml localonly: ipWhiteList: sourceRange: - "127.0.0.1/32" - "192.168.1.0/24" - "100.64.0.0/10" - "172.18.0.0/16" ``` Liste blanche d'IPs autorisées : - **127.0.0.1/32** : Localhost - **192.168.1.0/24** : Réseau LAN principal - **100.64.0.0/10** : Réseau Tailscale (VPN) - **172.18.0.0/16** : Réseau Docker interne Toute requête provenant d'une IP hors de ces plages est rejetée avec un code HTTP 403. **ratelimit**, **secheaders**, **evasive** : Identiques à traefik-public, disponibles pour être appliqués au besoin sur des services spécifiques. ### Certificats SSL Stockage des certificats : ```yaml volumes: - ./letsencrypt-private:/letsencrypt ``` Configuration ACME dans `traefik-private.yml` : ```yaml certificatesResolvers: cloudflare: acme: email: your-email@example.com storage: /letsencrypt/cloudflare_acme.json keyType: EC256 dnsChallenge: provider: cloudflare ``` Bien que les services soient locaux, ils bénéficient de **certificats SSL valides** grâce au challenge DNS. ### Logging ```yaml log: level: DEBUG filePath: "/var/log/traefik-local/traefik.log" accessLog: filePath: "/var/log/traefik-local/access.log" format: json ``` Logs stockés dans `/var/log/traefik-local/` sur l'hôte Docker. ### Configuration provider Docker Identique à traefik-public : ```yaml providers: docker: endpoint: "unix:///var/run/docker.sock" exposedByDefault: false network: traefik_network ``` ### Services exposés Fichiers de configuration statiques dans `dynamic-private/` : **cockpit.yml** : Proxification de Cockpit (interface web d'administration système) **proxmox.yml** : Proxification de l'interface Proxmox Ces fichiers définissent des routers et services pour des applications ne tournant pas dans Docker. ### Exemple de labels Docker Pour exposer un service via traefik-private : ```yaml services: monitoring: image: grafana/grafana:latest labels: - "traefik.enable=true" - "traefik.http.routers.monitoring.rule=Host(`grafana.local.tellserv.fr`)" - "traefik.http.routers.monitoring.entrypoints=local" - "traefik.http.routers.monitoring.tls.certresolver=cloudflare" - "traefik.http.services.monitoring.loadbalancer.server.port=3000" networks: - traefik_network ``` ## Sécurité et bonnes pratiques ### Séparation public/privé - **Ne jamais exposer** de services d'administration via traefik-public - **Toujours vérifier** l'entrypoint utilisé dans les labels Docker - **Privilégier** traefik-private pour tout ce qui n'a pas besoin d'être public ### Middlewares de sécurité - **CrowdSec** : Actif uniquement sur traefik-public, bloque les attaques automatisées - **localonly** : Appliqué par défaut sur traefik-private - **ratelimit** : Protection anti-DDoS basique - **secheaders** : Renforcement de la sécurité côté navigateur ### Gestion des certificats - **Rotation automatique** : Let's Encrypt renouvelle les certificats tous les 90 jours - **Backup** : Sauvegarder régulièrement les fichiers `cloudflare_acme.json` - **Monitoring** : Vérifier les logs pour détecter les échecs de renouvellement ### Logging et monitoring - **Logs accessibles** : Montés en volumes sur l'hôte pour analyse - **Format JSON** : Facilite le parsing et l'intégration avec des outils de monitoring - **Niveau DEBUG** : Utile pour le troubleshooting, peut être réduit en production ## Limites de cette configuration Bien que fonctionnelle et sécurisée, cette architecture présente certaines limites à connaître : ### Réseau Docker partagé **Problème** : Les deux instances Traefik (public et private) utilisent le **même réseau Docker** (`traefik_network`). Cela signifie que tous les conteneurs connectés à ce réseau peuvent potentiellement communiquer entre eux, qu'ils soient exposés publiquement ou localement. **Impact** : - Un conteneur exposé via traefik-public peut techniquement accéder à un conteneur exposé via traefik-private - Le cloisonnement réseau n'est pas complet, il repose uniquement sur les IP bindings (192.168.1.2 vs 192.168.1.3) **Amélioration possible** : - Créer deux réseaux Docker distincts : `traefik_public_network` et `traefik_private_network` - Connecter chaque instance Traefik uniquement à son réseau dédié - Garantir une isolation réseau complète au niveau Docker ### Absence de segmentation VLAN **Problème** : Les deux NICs de la VM partagent le même réseau physique (192.168.1.0/24) sans segmentation VLAN. **Impact** : - Le NIC pour traefik-private (192.168.1.3) a techniquement accès à Internet via la passerelle réseau, alors qu'il n'en a pas besoin - Pas de cloisonnement réseau au niveau L2/L3 entre les interfaces publique et privée - En cas de compromission, un attaquant pourrait potentiellement pivoter entre les deux réseaux **Amélioration possible** : - **VLAN public** : Placer le NIC de traefik-public (192.168.1.2) dans un VLAN avec accès Internet - **VLAN privé** : Placer le NIC de traefik-private (192.168.1.3) dans un VLAN isolé, sans accès Internet - Configurer des règles de firewall strictes entre les VLANs - Cette segmentation renforcerait considérablement le cloisonnement et limiterait la surface d'attaque ### Accès au socket Docker **Problème** : Les deux instances Traefik ont un accès **direct et complet** au socket Docker (`/var/run/docker.sock`). Le socket Docker est l'API d'administration de Docker, donnant un contrôle total sur l'hôte. **Impact sécurité** : - Un conteneur Traefik compromis pourrait contrôler tous les conteneurs de l'hôte - Possibilité d'élévation de privilèges (lancer un conteneur en mode privileged, monter des volumes sensibles, etc.) - Accès en lecture seule (`ro`) limite les dégâts, mais permet toujours d'extraire des informations sensibles (variables d'environnement, secrets, etc.) **Amélioration possible** : - Utiliser un **proxy au socket Docker** comme [Tecnativa/docker-socket-proxy](https://github.com/Tecnativa/docker-socket-proxy) - Ce proxy permet de filtrer finement les opérations autorisées (ex: seulement lire les conteneurs et leurs labels) - Réduire la surface d'attaque en limitant l'accès API aux endpoints strictement nécessaires à Traefik Exemple de configuration : ```yaml docker-socket-proxy: image: tecnativa/docker-socket-proxy environment: CONTAINERS: 1 # Autoriser lecture des conteneurs NETWORKS: 1 # Autoriser lecture des réseaux SERVICES: 0 # Interdire accès aux services Swarm TASKS: 0 # Interdire accès aux tâches Swarm volumes: - /var/run/docker.sock:/var/run/docker.sock:ro ``` Ces améliorations ne sont pas critiques pour un homelab, mais seraient **fortement recommandées en environnement de production**. ## Ressources - [Documentation officielle Traefik v3](https://doc.traefik.io/traefik/) - [Provider Docker](https://doc.traefik.io/traefik/providers/docker/) - [Middlewares](https://doc.traefik.io/traefik/middlewares/overview/) - [Let's Encrypt](https://doc.traefik.io/traefik/https/acme/) - [CrowdSec Bouncer](https://docs.crowdsec.net/u/bouncers/traefik/) - [Docker Socket Proxy](https://github.com/Tecnativa/docker-socket-proxy)