Skip to content

Traefik avec CrowdSec

Architecture

CrowdSec analyse les logs Traefik, détecte les comportements malveillants et applique des décisions via un plugin Traefik natif (crowdsec-bouncer-traefik-plugin).

Utilisateur → Traefik (plugin CrowdSec) → Application
                  ├── IP sans décision    → accès direct
                  ├── Décision captcha    → page Turnstile → accès après résolution
                  └── Décision ban        → HTTP 403
Type de scénario Remédiation Condition
HTTP (crawl, probing, etc.) Captcha Turnstile ≤ 3 décisions en 24h
HTTP récidiviste Ban (403) > 3 décisions en 24h
Non-HTTP (SSH brute force, etc.) Ban (403) Toujours

Après résolution du captcha, grâce de 30 minutes avant qu'un nouveau captcha soit présenté.


Prérequis

Acquisition des logs (acquis.yaml)

Créer /opt/crowdsec/crowdsec-config/acquis.yaml :

filenames:
  - /var/log/traefik/*
labels:
  type: traefik

Whitelist API CrowdSec (config.yaml)

Ce fichier est généré automatiquement au premier démarrage de CrowdSec. Après le premier lancement, éditer /opt/crowdsec/crowdsec-config/config.yaml :

api:
  server:
    trusted_ips:
      - 127.0.0.1
      - 172.0.0.0/4   # Réseau Docker
      - ::1
prometheus:
  enabled: true
  listen_addr: 0.0.0.0
  listen_port: 6060

Puis redémarrer : docker compose restart crowdsec

Fichiers secrets

Placer ces fichiers dans /opt/traefik/conf/ :

Fichier Contenu Comment l'obtenir
crowdsec-api-key.txt Clé API bouncer docker exec crowdsec cscli bouncers add crowdsecBouncer -o raw
turnstile-site-key.txt Site Key Turnstile dash.cloudflare.com > Turnstile — domaine albt.org
turnstile-secret-key.txt Secret Key Turnstile Même page Cloudflare
# Écrire les clés (sans retour à la ligne)
echo -n 'CLE_BOUNCER'   > /opt/traefik/conf/crowdsec-api-key.txt
echo -n 'SITE_KEY'      > /opt/traefik/conf/turnstile-site-key.txt
echo -n 'SECRET_KEY'    > /opt/traefik/conf/turnstile-secret-key.txt

Déploiement Docker Compose

services:
  crowdsec:
    image: crowdsecurity/crowdsec
    container_name: crowdsec
    environment:
      GID: "${GID-1000}"
      COLLECTIONS: "crowdsecurity/linux crowdsecurity/traefik"
    volumes:
      - /opt/crowdsec/crowdsec-db:/var/lib/crowdsec/data/
      - /opt/crowdsec/crowdsec-config:/etc/crowdsec/
      - /opt/traefik/log:/var/log/traefik/:ro
      - $INFRASTRUCTURE_FOLDER_LOCATION/variable-crowdsec-profiles.yaml:/etc/crowdsec/profiles.yaml:ro
    networks:
      - frontend
    restart: unless-stopped

  traefik:
    image: traefik
    container_name: traefik
    restart: always
    networks:
      - frontend
    ports:
      - "80:80"
      - "443:443"
      - "8081:8080"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /opt/traefik/certs:/etc/traefik/certs/
      - /opt/traefik/conf:/etc/traefik/conf/
      - /opt/traefik/log:/var/log/traefik/
      - /opt/traefik/plugins-storage:/plugins-storage
      - $INFRASTRUCTURE_FOLDER_LOCATION/variable-traefik.yaml:/etc/traefik/traefik.yaml:ro
      - $INFRASTRUCTURE_FOLDER_LOCATION/variable-crowdsec-captcha.html:/captcha.html:ro
      - $INFRASTRUCTURE_FOLDER_LOCATION/variable-crowdsec-middleware.yaml:/etc/traefik/conf/middleware-crowdsec.yaml:ro
    environment:
      - CF_DNS_API_TOKEN=$CF_DNS_API_TOKEN

/opt/traefik/plugins-storage : nécessaire car Traefik tourne en non-root et doit pouvoir télécharger les plugins.


Prérequis Réseau : IPv6 Natif Docker

Par défaut, Docker utilise un proxy (Userland Proxy) pour gérer le trafic IPv6 entrant. Ce proxy pose un problème majeur de sécurité : il masque la véritable adresse IPv6 publique du client en la remplaçant par l'adresse IP de la passerelle du réseau Docker (ex: fd01::1).

Conséquence : si CrowdSec détecte une menace, il bannira l'adresse de la passerelle, bloquant ainsi tous les utilisateurs IPv6.

Pour résoudre ce problème et permettre à Traefik de voir les vraies adresses IPv6, il est impératif d'activer le routage IPv6 natif via ip6tables dans le démon Docker de l'hôte.

Éditez le fichier /etc/docker/daemon.json sur le serveur hôte :

{
    "ipv6": true,
    "ip6tables": true,
    "fixed-cidr-v6": "fd00::/80",
    "experimental": true
}
(Attention : Conservez vos autres configurations existantes, comme celles pour nvidia-container-runtime).

Redémarrez ensuite le service Docker (sudo systemctl restart docker) et recréez vos réseaux (docker compose down && docker compose up -d).


Configuration Traefik

Configuration statique (variable-traefik.yaml)

Important : La directive forwardedHeaders.trustedIPs est cruciale. Elle indique à Traefik de faire confiance aux proxys locaux (comme le bridge Docker) pour lire la véritable adresse IP du client dans les en-têtes HTTP.

global:
  checkNewVersion: false
  sendAnonymousUsage: false
api:
  dashboard: true
  insecure: true
entryPoints:
  web:
    address: :80
    http:
      middlewares:
        - crowdsec-bouncer@file
      redirections:
        entryPoint:
          to: websecure
          scheme: https
          permanent: true
  websecure:
    address: :443
    forwardedHeaders:
      trustedIPs:
        - "127.0.0.1/32"
        - "::1/128"
        - "fd00::/8"
        - "172.16.0.0/12"
        - "192.168.0.0/16"
        - "10.0.0.0/8"
    http:
      middlewares:
        - crowdsec-bouncer@file
certificatesResolvers:
  cloudflare:
    acme:
      email: "votre.mail@mail.org"
      storage: /etc/traefik/certs/cloudflare-acme.json
      caServer: 'https://acme-v02.api.letsencrypt.org/directory'
      keyType: EC256
      dnsChallenge:
        provider: cloudflare
        resolvers:
          - "1.1.1.1:53"
          - "8.8.8.8:53"
serversTransport:
  insecureSkipVerify: true
providers:
  docker:
    exposedByDefault: false
    endpoint: 'unix:///var/run/docker.sock'
    watch: true
  file:
    directory: /etc/traefik/conf/
    watch: true
log:
  level: "INFO"
  filePath: "/var/log/traefik/traefik.log"
accessLog:
  filePath: "/var/log/traefik/access.log"
experimental:
  plugins:
    bouncer:
      moduleName: github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin
      version: v1.5.0

Configuration dynamique (variable-crowdsec-middleware.yaml)

Middleware chargé par le file provider (/etc/traefik/conf/). Configure le plugin bouncer en mode live avec Cloudflare Turnstile comme captcha provider. Les clés sont lues depuis les fichiers secrets dans /etc/traefik/conf/.

Profil CrowdSec (variable-crowdsec-profiles.yaml)

Monté en :ro sur /etc/crowdsec/profiles.yaml (surcharge le fichier du volume principal).

  • Scénarios contenant http → décision captcha
  • Après 3 captchas en 24h → bascule vers ban
  • Scénarios non-HTTP → ban direct

Template captcha (variable-crowdsec-captcha.html)

Page HTML affichée à l'utilisateur quand une décision captcha est active pour son IP.


Commandes utiles

Toutes les commandes s'exécutent via docker exec crowdsec cscli ...

Décisions

cscli decisions list                              # Voir les décisions actives
cscli decisions add --ip <IP> -d 10m --type captcha  # Forcer un captcha (test)
cscli decisions add --ip <IP> -d 10m --type ban      # Forcer un ban (test)
cscli decisions delete --ip <IP>                  # Supprimer une décision
cscli decisions delete --all                      # Tout supprimer

Alertes

cscli alerts list                  # Historique des alertes
cscli alerts list --ip <IP>        # Alertes pour une IP
cscli alerts inspect <ID>          # Détails d'une alerte

Maintenance

cscli hub update                   # Mettre à jour le hub
cscli hub upgrade                  # Mettre à jour les scénarios installés
cscli scenarios list               # Scénarios actifs
cscli parsers list                 # Parsers actifs
cscli bouncers list                # État des bouncers connectés

Régénérer la clé API du bouncer

docker exec crowdsec cscli bouncers add crowdsecBouncer -o raw
echo -n 'NOUVELLE_CLE' > /opt/traefik/conf/crowdsec-api-key.txt
docker compose restart traefik