fail2ban tourne depuis le début sur heighliner. Il fait son boulot : un scan SSH qui se pointe sur le port 22, quelques tentatives de connexion, et l’IP est bannie localement. Mais local, c’est le problème. fail2ban ne sait pas que cette même IP est en train de scanner 40 000 autres serveurs en ce moment même. CrowdSec le sait.
Pourquoi changer, et pourquoi pas tout changer
Je ne remplace pas fail2ban immédiatement. CrowdSec a une architecture différente qui s’intègre au niveau du bouncer, et fail2ban peut coexister pendant la transition. Ce que j’ai voulu tester : est-ce que la threat intelligence communautaire apporte quelque chose de concret en homelab, où le volume de trafic est faible mais l’exposition sur Internet est réelle ?
Mon exposition à ce moment-là : NPM (LXC 102) et Traefik exposent Nextcloud, Gitea, Vaultwarden et quelques autres services via des sous-domaines de you.arewel.com. Le SSH de heighliner est sur le port 22 standard (j’allais le changer, je ne l’avais pas encore fait). Proxmox lui-même n’est pas exposé sur Internet.
Architecture CrowdSec : trois pièces
L’architecture mérite d’être comprise avant de lancer des commandes. CrowdSec est composé de trois éléments distincts.
Le Security Engine (anciennement l’agent) s’installe sur le serveur à protéger. Il lit les logs, applique des parsers et des scénarios, et détecte les comportements malveillants. Quand il détecte quelque chose, il crée une alerte et l’envoie à la Local API (LAPI).
La Local API (LAPI) est le hub local. Elle centralise les décisions (ban, captcha, etc.) et expose une API REST pour les bouncers. La LAPI est incluse dans le Security Engine par défaut pour une installation standalone.
Les bouncers sont les pièces qui appliquent les décisions de blocage. Ils sont indépendants du Security Engine et peuvent tourner sur des machines différentes. Il en existe pour nftables, Traefik, Nginx, HAProxy, et d’autres.
Le Security Engine partage aussi les IPs détectées avec la communauté CrowdSec (opt-out possible) et télécharge les blocklists communautaires, qui contiennent des IPs signalées par d’autres instances dans le monde.
Installation du Security Engine sur le nœud Proxmox
Je l’installe directement sur heighliner (Debian 12, Proxmox VE 8.3), pas dans un LXC. La raison : le Security Engine doit avoir accès aux logs du système hôte – /var/log/auth.log, les logs kernel, les logs Proxmox. Depuis un LXC, l’accès à ces logs est compliqué et fragile.
curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | bash
apt install crowdsec -y
CrowdSec détecte automatiquement les services en cours lors de l’installation et suggère des collections à installer. Sur mon nœud, il a détecté SSH et proposé crowdsecurity/sshd.
J’ai installé les collections manuellement pour avoir le contrôle :
cscli collections install crowdsecurity/linux
cscli collections install crowdsecurity/sshd
cscli collections install crowdsecurity/nginx
La collection crowdsecurity/linux couvre les comportements génériques (bruteforce, scan de ports). crowdsecurity/sshd parse /var/log/auth.log pour les tentatives SSH. crowdsecurity/nginx parse les logs d’accès Nginx pour les scans de paths, injections SQL, et tentatives XSS.
Après installation, vérifie les parsers actifs avec cscli parsers list et les scénarios avec cscli scenarios list. Si un parser important est absent, la collection correspondante n’est peut-être pas bien installée.
Redémarrage du service :
systemctl enable crowdsec
systemctl start crowdsec
systemctl status crowdsec
Vérification des métriques initiales :
cscli metrics
À ce stade, le Security Engine tourne et analyse les logs mais aucun bouncer n’est en place : les alertes sont détectées mais rien n’est bloqué.
Bouncer nftables : bloquer au niveau firewall
Le bouncer nftables applique les décisions directement dans les règles firewall du kernel, avant que le trafic n’atteigne les services. C’est le niveau de blocage le plus efficace.
apt install crowdsec-firewall-bouncer-nftables -y
Le fichier de configuration est dans /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml. Les valeurs par défaut fonctionnent pour une installation standalone. Vérifier que mode: nftables est bien défini et que api_url pointe vers http://127.0.0.1:8080.
systemctl enable crowdsec-firewall-bouncer
systemctl start crowdsec-firewall-bouncer
CrowdSec crée automatiquement une table nftables crowdsec avec des chaînes pour les IPs bannies. Vérification :
nft list table inet crowdsec
À partir de ce moment, les IPs dans la liste de décisions de la LAPI sont bloquées au niveau firewall. Les IPs de la blocklist communautaire sont aussi appliquées si on a activé les listes externes.
Activation des blocklists communautaires
C’est là que CrowdSec se différencie de fail2ban. Les blocklists communautaires contiennent des IPs signalées par d’autres instances dans le monde. L’accès aux listes de base est gratuit avec un compte CrowdSec.
cscli hub update
cscli bouncers list
Pour les blocklists communautaires via le CrowdSec Hub :
cscli cti blocklist add crowdsecurity/tor-exit-nodes
cscli cti blocklist add crowdsecurity/vpn-ips
Ces listes sont mises à jour automatiquement. Les IPs qu’elles contiennent sont intégrées aux décisions de la LAPI et donc appliquées par les bouncers.
Pour consulter les alertes en cours :
cscli alerts list
cscli alerts list --since 24h
Bouncer Traefik : middleware pour le trafic HTTP/HTTPS
Traefik expose mes services vers l’extérieur. Ajouter CrowdSec au niveau de Traefik permet de bloquer les requêtes HTTP malveillantes avant qu’elles n’atteignent les applications, même si leur IP n’est pas encore dans les listes firewall.
Le problème de compatibilité des versions
C’est le point qui m’a fait perdre le plus de temps. Il existe plusieurs bouncers Traefik pour CrowdSec :
fbonalair/traefik-crowdsec-bouncer(conteneur sidecar)- Le plugin natif Traefik
crowdsec-bouncer-traefik-plugin maxlerebourg/crowdsec-bouncer-traefik-plugin(fork actif)
Le plugin natif Traefik ne supporte pas Traefik v3 correctement dans ses versions récentes au moment où j’écris. Le fork maxlerebourg/crowdsec-bouncer-traefik-plugin a une meilleure compatibilité v3. Avant d’installer quoi que ce soit, vérifie la page GitHub du plugin pour les versions Traefik supportées.
J’utilise Traefik v3.1 dans un LXC Docker. J’ai opté pour le bouncer sidecar fbonalair/traefik-crowdsec-bouncer qui communique avec la LAPI de heighliner via le réseau des LXC.
Dans le docker-compose.yml de Traefik :
services:
crowdsec-bouncer:
image: fbonalair/traefik-crowdsec-bouncer:latest
environment:
CROWDSEC_BOUNCER_API_KEY: "${CROWDSEC_BOUNCER_KEY}"
CROWDSEC_AGENT_HOST: "192.168.30.1:8080"
GIN_MODE: release
restart: unless-stopped
L’API key du bouncer est générée sur heighliner :
cscli bouncers add traefik-bouncer
Ne stocke jamais cette clé en clair dans un fichier versionné. Utilise un fichier .env exclu du dépôt Git.
Dans la configuration Traefik, ajout du middleware :
http:
middlewares:
crowdsec-middleware:
forwardAuth:
address: "http://crowdsec-bouncer:8080/api/v1/forwardAuth"
trustForwardHeader: true
Ce middleware est ensuite appliqué aux routers exposés sur Internet. Les requêtes dont l’IP est dans les décisions de ban reçoivent une réponse 403 sans jamais atteindre l’application backend.
Ce qui a coincé : les trusted IPs
En activant le bouncer Traefik, j’ai commencé à voir des faux positifs : des requêtes légitimes bloquées. En creusant, j’ai réalisé que le bouncer voyait les IPs des proxies intermédiaires (Cloudflare, le réseau Docker interne) plutôt que les vraies IPs clientes.
Il faut configurer CROWDSEC_BOUNCER_TRUSTED_IPS avec les plages des proxies de confiance, et s’assurer que Traefik transmet bien l’en-tête X-Forwarded-For avec les vraies IPs. Dans mon cas, j’ai dû ajouter les plages Cloudflare et le réseau Docker interne.
Résultat après 72 heures
cscli alerts list --since 72h
47 IPs bloquées en 72h. Répartition observée : scans SSH (24 IPs), tentatives sur /wp-admin et /xmlrpc.php (11 IPs – je n’expose pas WordPress mais les scanners essaient quand même), scans de paths courants Apache/PHP (8 IPs), divers (4 IPs).
Les IPs de scan SSH étaient majoritairement déjà dans la blocklist communautaire CrowdSec avant même d’avoir été détectées localement. C’est l’avantage concret de la threat intelligence partagée : une IP qui a scanné 500 autres serveurs dans la journée est bloquée dès sa première tentative chez moi.
Comparaison avec fail2ban
fail2ban analyse les logs a posteriori selon des regex fixes. Il ne connaît pas les IPs malveillantes avant qu’elles aient tenté quelque chose localement. Sa configuration est simple et robuste, et il n’a pas de dépendance externe.
CrowdSec apporte deux choses que fail2ban n’a pas : la détection comportementale via des scénarios plus sophistiqués, et la threat intelligence partagée. En contrepartie, l’architecture est plus complexe (trois composants distincts, une API locale, des bouncers à déployer séparément), et la surface d’attaque est légèrement plus grande.
Pour un homelab exposé sur Internet, même partiellement, CrowdSec vaut la complexité supplémentaire. La blocklist communautaire seule justifie le déploiement : les IPs de scanners bien connus sont bloquées sans avoir jamais touché mes services.
Je garde fail2ban en parallèle pour l’instant, surtout pour la protection SSH sur le port 22, et je désactiverai son module SSH quand j’aurai confirmé que CrowdSec couvre bien ce cas d’usage sur la durée.
La prochaine étape : activer les notifications CrowdSec vers Loki (hubble.arewel.com) pour avoir les alertes dans Grafana avec le reste des métriques du homelab.
slug: crowdsec-ips-homelab
meta: CrowdSec sur Proxmox : Security Engine, bouncer nftables et Traefik, 47 IPs bloquées en 72h grâce à la threat intelligence communautaire. Guide complet 2026. (~155 chars)
tags: crowdsec, securite, ips, traefik, nftables, proxmox, homelab