Ajout documentation complète Traefik
- Créer documentation détaillée configuration Traefik - Documenter réseaux Docker (traefik-public et traefik-private) - Expliquer gestion certificats SSL/TLS automatique - Ajouter exemples configuration services - Inclure diagrammes architecture réseau
This commit is contained in:
parent
c35a240ec3
commit
f5b5e7ee74
2 changed files with 1054 additions and 18 deletions
|
|
@ -4,14 +4,532 @@ sidebar_position: 4
|
|||
|
||||
# Traefik - Reverse Proxy moderne
|
||||
|
||||
:::info
|
||||
Cette page sera complétée prochainement avec des détails sur la configuration Traefik.
|
||||
:::
|
||||
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.
|
||||
|
||||
## À venir
|
||||
## Architecture globale
|
||||
|
||||
- Configuration détaillée des deux instances Traefik (publique et privée)
|
||||
- Gestion des certificats SSL avec Let's Encrypt
|
||||
- Intégration avec CrowdSec pour la sécurité
|
||||
- Configuration des middlewares
|
||||
- Routing et gestion des domaines
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -4,14 +4,532 @@ sidebar_position: 4
|
|||
|
||||
# Traefik - Modern Reverse Proxy
|
||||
|
||||
:::info
|
||||
This page will be completed soon with detailed Traefik configuration.
|
||||
:::
|
||||
Traefik is the reverse proxy at the heart of the homelab's Docker infrastructure. It handles routing of all HTTP/HTTPS requests to the appropriate containers, with automatic SSL certificate management and security integration via CrowdSec.
|
||||
|
||||
## Coming soon
|
||||
## Global Architecture
|
||||
|
||||
- Detailed configuration of two Traefik instances (public and private)
|
||||
- SSL certificate management with Let's Encrypt
|
||||
- CrowdSec integration for security
|
||||
- Middleware configuration
|
||||
- Routing and domain management
|
||||
The infrastructure uses **two distinct Traefik instances**, each on its own network interface:
|
||||
|
||||
- **traefik-public** (192.168.1.2): Public services accessible from the Internet (*.tellserv.fr)
|
||||
- **traefik-private** (192.168.1.3): Local services reserved for the internal network (*.local.tellserv.fr)
|
||||
|
||||
This separation provides:
|
||||
- **Network isolation**: Private services are never exposed publicly
|
||||
- **Enhanced security**: Differentiated security policies based on exposure
|
||||
- **Simplified management**: Each instance has its own configuration and rules
|
||||
|
||||
### Network Prerequisites
|
||||
|
||||
The VM hosting Traefik has **two NICs (Network Interface Cards)**:
|
||||
- **NIC 1** (192.168.1.2): Public interface for traefik-public
|
||||
- **NIC 2** (192.168.1.3): Local interface for traefik-private
|
||||
|
||||
## Common Elements to Both Instances
|
||||
|
||||
### Image and Version
|
||||
|
||||
Both instances use the official **Traefik v3** image:
|
||||
|
||||
```yaml
|
||||
image: traefik:v3
|
||||
```
|
||||
|
||||
### Docker Network
|
||||
|
||||
Both instances connect to the external Docker network `traefik_network` which enables communication with all containers to be proxied:
|
||||
|
||||
```yaml
|
||||
networks:
|
||||
- traefik_network
|
||||
```
|
||||
|
||||
This network must be created beforehand with:
|
||||
|
||||
```bash
|
||||
docker network create traefik_network
|
||||
```
|
||||
|
||||
### SSL Certificate Management
|
||||
|
||||
Both instances use **Let's Encrypt** with the **Cloudflare** DNS challenge to automatically generate wildcard SSL certificates.
|
||||
|
||||
**DNS Challenge Advantages:**
|
||||
- Wildcard certificates (*.tellserv.fr, *.local.tellserv.fr)
|
||||
- No need for public HTTP exposure for validation
|
||||
- Works even for internal services
|
||||
|
||||
**Common Configuration:**
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
- TZ=Europe/Paris
|
||||
- CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN}
|
||||
```
|
||||
|
||||
The Cloudflare API token must have the following permissions:
|
||||
- Zone / DNS / Edit
|
||||
- Zone / Zone / Read
|
||||
|
||||
### Docker Provider
|
||||
|
||||
Both instances use the Docker provider for automatic service discovery:
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
```
|
||||
|
||||
Traefik monitors containers and automatically creates routes based on Docker labels.
|
||||
|
||||
### Dynamic Configuration
|
||||
|
||||
Each instance loads dynamic configuration files from a dedicated directory:
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
- ./dynamic-public:/etc/traefik/dynamic:ro # For traefik-public
|
||||
- ./dynamic-private:/etc/traefik/dynamic:ro # For traefik-private
|
||||
```
|
||||
|
||||
These files allow defining middlewares, routers, and services without restarting Traefik.
|
||||
|
||||
### Traefik Dashboard
|
||||
|
||||
Both instances expose their dashboard via a dedicated subdomain:
|
||||
- **traefik-public**: `traefik-public.local.tellserv.fr`
|
||||
- **traefik-private**: `traefik-private.local.tellserv.fr`
|
||||
|
||||
The dashboard allows real-time visualization of:
|
||||
- Active routers and their rules
|
||||
- Detected services
|
||||
- Applied middlewares
|
||||
- Backend health status
|
||||
|
||||
### Restart Policy
|
||||
|
||||
```yaml
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
Containers automatically restart unless manually stopped.
|
||||
|
||||
### Docker Host Access
|
||||
|
||||
```yaml
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
```
|
||||
|
||||
This configuration allows Traefik containers to access the Docker host via the name `host.docker.internal`, useful for proxying services running directly on the host.
|
||||
|
||||
## traefik-public Instance
|
||||
|
||||
### Role and Usage
|
||||
|
||||
The **traefik-public** instance manages all services **accessible from the Internet**:
|
||||
- Public web applications
|
||||
- Exposed APIs
|
||||
- Authenticated services accessible from outside
|
||||
|
||||
### Network Binding
|
||||
|
||||
```yaml
|
||||
ports:
|
||||
- "192.168.1.2:80:80"
|
||||
- "192.168.1.2:443:443"
|
||||
```
|
||||
|
||||
Traefik listens only on IP **192.168.1.2**, corresponding to the VM's first NIC.
|
||||
|
||||
### Entry Points
|
||||
|
||||
**Port 80 (HTTP)**:
|
||||
```yaml
|
||||
web:
|
||||
address: ":80"
|
||||
http:
|
||||
redirections:
|
||||
entryPoint:
|
||||
to: websecure
|
||||
scheme: https
|
||||
```
|
||||
|
||||
Automatic HTTP to HTTPS redirection for all requests.
|
||||
|
||||
**Port 443 (HTTPS)**:
|
||||
```yaml
|
||||
websecure:
|
||||
address: ":443"
|
||||
http:
|
||||
middlewares:
|
||||
- crowdsec-bouncer@file
|
||||
- secheaders@file
|
||||
- ratelimit@file
|
||||
transport:
|
||||
respondingTimeouts:
|
||||
idleTimeout: 300s
|
||||
```
|
||||
|
||||
Three middlewares applied by default to all public services:
|
||||
1. **crowdsec-bouncer**: Blocking malicious IPs detected by CrowdSec
|
||||
2. **secheaders**: HTTP security headers (HSTS, CSP, etc.)
|
||||
3. **ratelimit**: Request rate limiting
|
||||
|
||||
### Public Middlewares
|
||||
|
||||
File: `dynamic-public/middlewares.yml`
|
||||
|
||||
**ratelimit**:
|
||||
```yaml
|
||||
ratelimit:
|
||||
rateLimit:
|
||||
average: 100
|
||||
burst: 50
|
||||
period: 1s
|
||||
```
|
||||
Allows an average of 100 requests/second with bursts up to 50 additional requests.
|
||||
|
||||
**secheaders**:
|
||||
```yaml
|
||||
secheaders:
|
||||
headers:
|
||||
stsSeconds: 31536000
|
||||
forceSTSHeader: true
|
||||
```
|
||||
Forces HSTS (HTTP Strict Transport Security) for 1 year, requiring browsers to always use HTTPS.
|
||||
|
||||
**evasive**:
|
||||
```yaml
|
||||
evasive:
|
||||
rateLimit:
|
||||
average: 3
|
||||
burst: 5
|
||||
period: 1s
|
||||
```
|
||||
Strict rate limiting for sensitive endpoints (3 req/s average, 5 burst).
|
||||
|
||||
### CrowdSec Integration
|
||||
|
||||
**CrowdSec** is a community-based intrusion detection and prevention system. Traefik-public integrates the **CrowdSec bouncer** to automatically block malicious IPs.
|
||||
|
||||
**CrowdSec Middleware** (applied to websecure):
|
||||
```yaml
|
||||
middlewares:
|
||||
- crowdsec-bouncer@file
|
||||
```
|
||||
|
||||
The bouncer queries the local CrowdSec API to check if the source IP is banned. On match, the request is blocked with HTTP 403.
|
||||
|
||||
### SSL Certificates
|
||||
|
||||
Certificate storage:
|
||||
```yaml
|
||||
volumes:
|
||||
- ./letsencrypt-public:/letsencrypt
|
||||
```
|
||||
|
||||
ACME configuration in `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"
|
||||
```
|
||||
|
||||
Certificates are automatically renewed 30 days before expiration.
|
||||
|
||||
### Logging
|
||||
|
||||
```yaml
|
||||
log:
|
||||
level: DEBUG
|
||||
filePath: "/var/log/traefik/traefik.log"
|
||||
accessLog:
|
||||
filePath: "/var/log/traefik/access.log"
|
||||
format: json
|
||||
```
|
||||
|
||||
Logs stored in `/var/log/traefik/` on the Docker host:
|
||||
- **traefik.log**: Traefik system logs (startup, errors, reloads)
|
||||
- **access.log**: Access logs in JSON format (HTTP requests)
|
||||
|
||||
### Docker Provider Configuration
|
||||
|
||||
```yaml
|
||||
providers:
|
||||
docker:
|
||||
endpoint: "unix:///var/run/docker.sock"
|
||||
exposedByDefault: false
|
||||
network: traefik_network
|
||||
```
|
||||
|
||||
- **exposedByDefault: false**: Containers are not automatically exposed, Traefik labels must be explicitly added
|
||||
- **network: traefik_network**: Traefik will use this network to communicate with containers
|
||||
|
||||
### Docker Labels Example
|
||||
|
||||
To expose a 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
|
||||
```
|
||||
|
||||
## traefik-private Instance
|
||||
|
||||
### Role and Usage
|
||||
|
||||
The **traefik-private** instance manages all services **reserved for the local network**:
|
||||
- Administration interfaces (Proxmox, Cockpit)
|
||||
- Internal dashboards
|
||||
- Monitoring services
|
||||
- Development tools
|
||||
|
||||
### Network Binding
|
||||
|
||||
```yaml
|
||||
ports:
|
||||
- "192.168.1.3:80:80"
|
||||
- "192.168.1.3:443:443"
|
||||
```
|
||||
|
||||
Traefik listens only on IP **192.168.1.3**, corresponding to the VM's second NIC.
|
||||
|
||||
### Entry Points
|
||||
|
||||
**Port 80 (HTTP)**:
|
||||
```yaml
|
||||
weblocal:
|
||||
address: ":80"
|
||||
http:
|
||||
redirections:
|
||||
entryPoint:
|
||||
to: local
|
||||
scheme: https
|
||||
```
|
||||
|
||||
Automatic HTTP to HTTPS redirection (to `local` entrypoint).
|
||||
|
||||
**Port 443 (HTTPS)**:
|
||||
```yaml
|
||||
local:
|
||||
address: ":443"
|
||||
http:
|
||||
middlewares:
|
||||
- localonly@file
|
||||
```
|
||||
|
||||
A single middleware applied by default: **localonly** which restricts access to local IPs.
|
||||
|
||||
### Private Middlewares
|
||||
|
||||
File: `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"
|
||||
```
|
||||
|
||||
Whitelist of allowed IPs:
|
||||
- **127.0.0.1/32**: Localhost
|
||||
- **192.168.1.0/24**: Main LAN network
|
||||
- **100.64.0.0/10**: Tailscale network (VPN)
|
||||
- **172.18.0.0/16**: Internal Docker network
|
||||
|
||||
Any request from an IP outside these ranges is rejected with HTTP 403.
|
||||
|
||||
**ratelimit**, **secheaders**, **evasive**:
|
||||
|
||||
Identical to traefik-public, available to be applied as needed to specific services.
|
||||
|
||||
### SSL Certificates
|
||||
|
||||
Certificate storage:
|
||||
```yaml
|
||||
volumes:
|
||||
- ./letsencrypt-private:/letsencrypt
|
||||
```
|
||||
|
||||
ACME configuration in `traefik-private.yml`:
|
||||
```yaml
|
||||
certificatesResolvers:
|
||||
cloudflare:
|
||||
acme:
|
||||
email: your-email@example.com
|
||||
storage: /letsencrypt/cloudflare_acme.json
|
||||
keyType: EC256
|
||||
dnsChallenge:
|
||||
provider: cloudflare
|
||||
```
|
||||
|
||||
Although services are local, they benefit from **valid SSL certificates** thanks to DNS challenge.
|
||||
|
||||
### Logging
|
||||
|
||||
```yaml
|
||||
log:
|
||||
level: DEBUG
|
||||
filePath: "/var/log/traefik-local/traefik.log"
|
||||
accessLog:
|
||||
filePath: "/var/log/traefik-local/access.log"
|
||||
format: json
|
||||
```
|
||||
|
||||
Logs stored in `/var/log/traefik-local/` on the Docker host.
|
||||
|
||||
### Docker Provider Configuration
|
||||
|
||||
Identical to traefik-public:
|
||||
```yaml
|
||||
providers:
|
||||
docker:
|
||||
endpoint: "unix:///var/run/docker.sock"
|
||||
exposedByDefault: false
|
||||
network: traefik_network
|
||||
```
|
||||
|
||||
### Exposed Services
|
||||
|
||||
Static configuration files in `dynamic-private/`:
|
||||
|
||||
**cockpit.yml**: Proxying Cockpit (system administration web interface)
|
||||
**proxmox.yml**: Proxying Proxmox interface
|
||||
|
||||
These files define routers and services for applications not running in Docker.
|
||||
|
||||
### Docker Labels Example
|
||||
|
||||
To expose a 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
|
||||
```
|
||||
|
||||
## Security and Best Practices
|
||||
|
||||
### Public/Private Separation
|
||||
|
||||
- **Never expose** administration services via traefik-public
|
||||
- **Always verify** the entrypoint used in Docker labels
|
||||
- **Prefer** traefik-private for anything that doesn't need to be public
|
||||
|
||||
### Security Middlewares
|
||||
|
||||
- **CrowdSec**: Active only on traefik-public, blocks automated attacks
|
||||
- **localonly**: Applied by default on traefik-private
|
||||
- **ratelimit**: Basic anti-DDoS protection
|
||||
- **secheaders**: Browser-side security enhancement
|
||||
|
||||
### Certificate Management
|
||||
|
||||
- **Automatic rotation**: Let's Encrypt renews certificates every 90 days
|
||||
- **Backup**: Regularly backup `cloudflare_acme.json` files
|
||||
- **Monitoring**: Check logs to detect renewal failures
|
||||
|
||||
### Logging and Monitoring
|
||||
|
||||
- **Accessible logs**: Mounted as volumes on the host for analysis
|
||||
- **JSON format**: Facilitates parsing and integration with monitoring tools
|
||||
- **DEBUG level**: Useful for troubleshooting, can be reduced in production
|
||||
|
||||
## Configuration Limitations
|
||||
|
||||
While functional and secure, this architecture has certain limitations to be aware of:
|
||||
|
||||
### Shared Docker Network
|
||||
|
||||
**Issue**: Both Traefik instances (public and private) use the **same Docker network** (`traefik_network`). This means all containers connected to this network can potentially communicate with each other, whether exposed publicly or locally.
|
||||
|
||||
**Impact**:
|
||||
- A container exposed via traefik-public can technically access a container exposed via traefik-private
|
||||
- Network isolation is incomplete, relying only on IP bindings (192.168.1.2 vs 192.168.1.3)
|
||||
|
||||
**Possible Improvement**:
|
||||
- Create two distinct Docker networks: `traefik_public_network` and `traefik_private_network`
|
||||
- Connect each Traefik instance only to its dedicated network
|
||||
- Ensure complete network isolation at the Docker level
|
||||
|
||||
### Lack of VLAN Segmentation
|
||||
|
||||
**Issue**: Both VM NICs share the same physical network (192.168.1.0/24) without VLAN segmentation.
|
||||
|
||||
**Impact**:
|
||||
- The NIC for traefik-private (192.168.1.3) technically has Internet access via the network gateway, when it doesn't need it
|
||||
- No network isolation at L2/L3 level between public and private interfaces
|
||||
- In case of compromise, an attacker could potentially pivot between both networks
|
||||
|
||||
**Possible Improvement**:
|
||||
- **Public VLAN**: Place the traefik-public NIC (192.168.1.2) in a VLAN with Internet access
|
||||
- **Private VLAN**: Place the traefik-private NIC (192.168.1.3) in an isolated VLAN without Internet access
|
||||
- Configure strict firewall rules between VLANs
|
||||
- This segmentation would significantly strengthen isolation and limit attack surface
|
||||
|
||||
### Docker Socket Access
|
||||
|
||||
**Issue**: Both Traefik instances have **direct and complete** access to the Docker socket (`/var/run/docker.sock`). The Docker socket is Docker's administration API, giving full control over the host.
|
||||
|
||||
**Security Impact**:
|
||||
- A compromised Traefik container could control all host containers
|
||||
- Possibility of privilege escalation (launching containers in privileged mode, mounting sensitive volumes, etc.)
|
||||
- Read-only access (`ro`) limits damage, but still allows extracting sensitive information (environment variables, secrets, etc.)
|
||||
|
||||
**Possible Improvement**:
|
||||
- Use a **Docker socket proxy** like [Tecnativa/docker-socket-proxy](https://github.com/Tecnativa/docker-socket-proxy)
|
||||
- This proxy allows fine-grained filtering of allowed operations (e.g., only read containers and their labels)
|
||||
- Reduce attack surface by limiting API access to endpoints strictly necessary for Traefik
|
||||
|
||||
Configuration example:
|
||||
```yaml
|
||||
docker-socket-proxy:
|
||||
image: tecnativa/docker-socket-proxy
|
||||
environment:
|
||||
CONTAINERS: 1 # Allow container reading
|
||||
NETWORKS: 1 # Allow network reading
|
||||
SERVICES: 0 # Deny access to Swarm services
|
||||
TASKS: 0 # Deny access to Swarm tasks
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
```
|
||||
|
||||
These improvements are not critical for a homelab but would be **strongly recommended in production environments**.
|
||||
|
||||
## Resources
|
||||
|
||||
- [Official Traefik v3 Documentation](https://doc.traefik.io/traefik/)
|
||||
- [Docker Provider](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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue