Intro
Exposer des services sur internet, même via un reverse proxy comme Nginx Proxy Manager, c’est ouvrir une porte sur le quartier le plus mal fréquenté du monde. Dans cet article, je détaille comment j’ai mis en place une double protection — Fail2ban et CrowdSec — sur mon LXC NPM, pourquoi les deux se complètent plutôt que de faire doublon, et comment je surveille tout ça au quotidien depuis Home Assistant.
Le point de départ
Mon NPM (Nginx Proxy Manager) expose plusieurs sous-domaines sur rem81.com : mon blog (domo), ma photothèque (Immich), mes statistiques de fréquentation (Matomo) et ma station météo (WeeWX). Chacun de ces services reçoit en permanence du trafic non désiré : scans automatisés, tentatives de connexion, sondage de vulnérabilités connues.
Fail2ban était déjà en place depuis un moment, avec une jail dédiée par sous-domaine. Mais je voulais une protection plus fine, capable de détecter des types d’attaques que Fail2ban ne couvre pas (injections, CVE spécifiques, scans de chemins sensibles). C’est là qu’intervient CrowdSec.
Fail2ban : le spécialiste des échecs de connexion
Fail2ban surveille des motifs précis dans les logs — typiquement les échecs de connexion à une interface d’administration (WordPress, par exemple) — et bannit l’IP via iptables après un certain nombre d’échecs dans une fenêtre de temps donnée.
Chez moi, la jail sur mon blog WordPress est la plus active de loin :
Status for the jail: npm-domo
|- Filter
| |- Currently failed: 6
| |- Total failed: 8043
`- Actions
|- Currently banned: 5
|- Total banned: 70
Plus de 8000 tentatives de connexion cumulées, 70 IP bannies au total. C’est le lot commun de tout WordPress exposé sur internet — les robots essaient en boucle des combinaisons de mots de passe sur wp-login.php.
CrowdSec : la détection comportementale communautaire
CrowdSec fonctionne différemment : il installe des collections de scénarios (des règles de détection maintenues par la communauté) qui repèrent des comportements suspects bien plus variés que « trop d’échecs de login » :
- Scan de plusieurs URL différentes en peu de temps
- Tentatives d’exploitation de CVE connues (Log4Shell, failles WordPress, Fortinet, etc.)
- Injections SQL ou XSS
- Sondage de fichiers sensibles (
.env,/wp-admin…)
L’installation se résume à deux commandes :
cscli collections install crowdsecurity/nginx
cscli collections install crowdsecurity/base-http-scenarios
Ces collections embarquent le parser (qui comprend le format des logs Nginx) et l’ensemble des scénarios de détection. Une fois qu’une IP dépasse le seuil défini par un scénario, CrowdSec crée une décision de bannissement, transmise à un bouncer — dans mon cas, un bouncer firewall qui traduit la décision en règle iptables.
Le bonus inattendu : la blocklist communautaire
En creusant les métriques Prometheus de CrowdSec, une surprise : mon bouncer bloquait bien plus d’IP que ce que mes propres logs avaient détecté. La métrique cs_active_decisions distingue en fait deux origines bien différentes :
| Origine | IP actuellement bloquées | Explication |
|---|---|---|
crowdsec | 2 | Détections issues de mes propres logs (agent local) |
CAPI | 15 470 | Blocklist communautaire partagée par tous les utilisateurs CrowdSec |
Ces 15 470 IP n’ont jamais mis les pieds sur mes serveurs — elles sont bloquées préventivement, parce qu’elles ont été signalées comme malveillantes par d’autres utilisateurs CrowdSec ailleurs dans le monde. C’est exactement la promesse de l’outil : mutualiser la détection entre tous ses utilisateurs pour se protéger d’attaques qu’on n’aurait jamais vues venir soi-même.
Pourquoi garder les deux : la preuve par les logs
La question s’est posée naturellement : Fail2ban et CrowdSec font-ils doublon ? La réponse est non, et j’ai pu le vérifier concrètement. En comparant les IP bannies par les deux outils sur mon blog au même moment :
Fail2ban avait banni : 195.178.110.223, 35.88.92.81, 64.89.163.227, 34.123.220.148, 100.58.205.11 — toutes suite à des tentatives de connexion WordPress.
CrowdSec avait banni : 160.176.150.31, une IP marocaine, pour une tentative d’injection XSS (crowdsecurity/http-xss-probing).
Aucun recoupement. Chaque outil a détecté un type d’attaque que l’autre n’aurait pas vu. C’est la meilleure démonstration que ce n’est pas de la redondance, mais deux lignes de défense complémentaires.
Un piège d’installation à connaître
Si votre Nginx Proxy Manager tourne en installation native (pas via Docker), le bouncer CrowdSec dédié à Nginx (basé sur un module Lua) ne fonctionnera pas — il nécessite OpenResty, que NPM n’utilise pas par défaut. La solution : passer par le bouncer firewall (crowdsec-firewall-bouncer-iptables), qui bloque au niveau du système plutôt qu’au niveau applicatif. Le résultat est identique — la requête n’atteint jamais sa destination — mais l’installation est plus simple et indépendante de la stack web utilisée.
apt install crowdsec-firewall-bouncer-iptables -y
Attention aussi à l’emplacement réel des logs si vous utilisez NPM en dehors de Docker : ils ne sont pas forcément dans /var/log/nginx/, mais dans /data/logs/proxy-host-X_access.log. Un petit find / -newermt "-10 minutes" permet de repérer rapidement les fichiers qui reçoivent effectivement du trafic.
Le cas particulier de Home Assistant : Nabu Casa plutôt qu’un sous-domaine
Pour l’accès distant à HA, j’ai fait un autre choix : l’abonnement Home Assistant Cloud, proposé par Nabu Casa.
Les avantages sont nombreux par rapport à une exposition classique via reverse proxy :
- Aucun port à ouvrir sur la Freebox — le flux part de chez moi vers Nabu Casa, jamais l’inverse
- Chiffrement de bout en bout : même Nabu Casa ne peut pas lire mes données
- Certificat SSL et sécurité gérés automatiquement, sans avoir à s’en occuper soi-même
- Intégration Google Assistant et Amazon Alexa en quelques clics
- Sauvegarde cloud de la configuration, restaurable dès le premier démarrage sur un nouveau matériel
- Support des webhooks et d’un WebRTC amélioré
Pour quelques euros par mois, je m’évite ainsi toute la mécanique de sécurisation qu’on vient de détailler pour mes autres sous-domaines — et accessoirement, je participe au financement de l’Open Home Foundation, la fondation à but non lucratif dont dépendent Home Assistant et ESPHome. Nabu Casa reverse une partie de ses revenus au développement de ces projets open source : un abonnement Cloud, c’est autant un service pratique qu’un petit geste de soutien à un travail formidable.
Superviser sans multiplier les interfaces
Une fois la protection en place, la vraie question devient : comment savoir que tout fonctionne, sans devoir taper des commandes cscli ou fail2ban-client à chaque fois ?
J’ai choisi d’exposer les métriques des deux outils via Prometheus :
- CrowdSec expose nativement un endpoint
/metrics(à activer dans/etc/crowdsec/config.yaml) - Fail2ban n’a pas cet export natif, mais un exporter dédié (
fail2ban_exporter) fait le pont via le socket Fail2ban
Prometheus collecte ces deux endpoints, puis alimente deux endroits :
- Grafana, qui l’utilise comme source de données pour une vue détaillée et historique
- Home Assistant, via des capteurs REST qui interrogent directement l’API Prometheus, pour avoir la sécurité de mes sites au même endroit que le reste de ma maison connectée
Exemple de capteur HA pour le nombre de bans actifs sur mon blog :
rest:
- resource: "http://<ip_prometheus>:9090/api/v1/query?query=f2b_jail_banned_current{jail=%22npm-domo%22}"
method: GET
scan_interval: 60
sensor:
- name: "Fail2ban Domo Bans Actifs"
value_template: "{{ value_json.data.result[0].value[1] | default(0) | int }}"
state_class: measurement
Un détail qui m’a fait tiquer au début : Prometheus stocke toutes ses valeurs en float, y compris des compteurs entiers comme un nombre de bans. Le | int dans le template s’occupe de la conversion à l’affichage. Et pour que ces capteurs génèrent de vraies courbes exploitables dans l’historique (au lieu d’une simple frise d’états), il faut penser à leur donner un state_class adapté — measurement pour une valeur qui monte et descend, total_increasing pour un compteur qui ne fait qu’augmenter.
Ce que ça donne au quotidien
Aujourd’hui, une simple page dans mon dashboard Home Assistant me suffit pour voir d’un coup d’œil :
- Si l’un des deux services est en panne (
f2b_up,cs_info) - Combien d’IP sont actuellement bannies, par site et par outil
- Le volume de tentatives détectées, en cumulé et sur la dernière heure
La sécurité de mes sites est désormais un indicateur comme un autre, au même titre que ma consommation électrique ou la météo du jour — plus besoin de sortir le terminal SSH pour sentir qu’un chiffre dérape.
En résumé
- Fail2ban reste pertinent pour surveiller finement les échecs de connexion applicatifs (login WordPress, etc.)
- CrowdSec couvre un spectre de menaces bien plus large (CVE, injections, scans), avec en prime une intelligence communautaire
- Les deux peuvent tourner en parallèle sans conflit ni vrai doublon
- Exposer leurs métriques via Prometheus permet de tout centraliser, y compris dans Home Assistant, sans multiplier les interfaces à surveiller
Article rédigé avec l’assistance de Claude AI (Anthropic) à partir de mes données et retours d’expérience.
