Article de la série « Mon ordinateur Ubuntu »
Contexte
Environnement cible : un ordinateur Ubuntu sur lequel sont installés Nginx Reverse Proxy (NPM), Portainer, Home Assistant et Stirling PDF. Tout est déployé sous forme de containers Docker avec des chemins absolus (Bind Mounts), sans volumes nommés. Stirling PDF et Home Assistant sont accessibles via des URL externes, ce qui impose une sécurisation rigoureuse.
L’objectif est d’ajouter Fail2Ban pour passer d’une sécurité passive (certificats Let’s Encrypt) à une sécurité active.
Qu’est-ce que Fail2Ban ?
Les certificats Let’s Encrypt garantissent que la connexion est chiffrée (personne ne peut lire ce qui transite). Fail2Ban agit comme un videur à l’entrée.
Contre les attaques par brute force – sa mission principale. Si quelqu’un tente de deviner un mot de passe sur une page d’authentification, Fail2Ban détecte les échecs répétés dans les logs et bannit l’IP au niveau du pare-feu (iptables).
Contre les bots de scan (DoS léger) – il bloque les robots qui scannent le serveur trop rapidement à la recherche de vulnérabilités.
Contre la recherche de fichiers sensibles – il bannit les IPs qui tentent d’accéder à des dossiers inexistants mais critiques (.env, wp-config.php, /admin).
Résumé : HTTPS garantit que le tuyau est sécurisé, mais pas que la personne au bout du tuyau est autorisée à entrer. Le HTTPS empêche l’espionnage sur le Wi-Fi public. Fail2Ban empêche l’attaquant de frapper 10 000 fois à la porte.
Vérifier le format des logs NPM (étape préalable)
Avant toute configuration, il faut s’assurer que NPM enregistre les vraies IPs publiques des visiteurs dans ses logs, et non l’IP interne du réseau Docker.
Commande de vérification – ouvre un terminal sur Ubuntu et inspecte un fichier de log :
cat /home/USER/docker/nginx-proxy-manager/data/logs/proxy-host-1_access.log | head -20
Cherche le champ [Client X.X.X.X] dans chaque ligne de log.
✓ Si tu vois des IPs publiques (ex: 205.210.x.x, 45.84.x.x) dans [Client …] : NPM transmet déjà les bonnes IPs, aucune configuration supplémentaire n’est nécessaire.
⚠ Si tu vois des IPs internes Docker (ex: 172.18.x.x) dans [Client …] : Fail2Ban sera inefficace et risque de bannir ta passerelle Docker. Ajoute les proxy_set_header dans l’onglet Advanced de chaque Proxy Host dans NPM (voir ci-dessous).
Note importante sur les IPs dans les logs NPM – le champ [Sent-to X.X.X.X] contient l’IP interne de ta machine Linux (destination du trafic) : c’est normal. Le champ [Client X.X.X.X] est la seule IP qui compte pour Fail2Ban (source du trafic entrant).
Si les IPs internes sont présentes – configuration dans NPM – dans l’interface NPM, pour chaque Proxy Host (Stirling PDF, Home Assistant…), onglet Advanced, zone Custom Nginx Configuration, ajoute :
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
Dans mon cas, les logs affichent déjà des IPs publiques. Cette configuration n’est pas nécessaire.
Installer Fail2Ban
Créer le dossier et le fichier docker-compose
mkdir ~/docker/fail2ban
cd ~/docker/fail2ban
nano docker-compose.yml
⚠ Pour que Fail2Ban puisse surveiller NPM, il doit avoir accès aux fichiers de logs générés dans le dossier /data/logs de NPM.
Contenu du docker-compose.yml :
services:
fail2ban:
image: crazymax/fail2ban:latest
container_name: fail2ban
network_mode: host
cap_add:
- NET_ADMIN
- NET_RAW
environment:
- TZ=Europe/Paris
- F2B_LOG_TARGET=/data/fail2ban.log
- F2B_LOG_LEVEL=INFO
- F2B_DB_PURGE_AGE=28d
volumes:
# Logs de Nginx a surveiller (lecture seule)
- /home/USER/docker/nginx-proxy-manager/data/logs:/data/nginx/logs:ro
# Dossier central Fail2Ban (config, db et log)
- /home/USER/docker/fail2ban:/data
# Logs systeme (lecture seule)
- /var/log:/var/log:ro
restart: always
Pourquoi network_mode: host ? En mode host, Fail2Ban partage la pile réseau de la machine Ubuntu. C’est indispensable pour qu’il voie les vraies IPs sources et puisse agir sur iptables.
Pourquoi monter les logs dans /data/nginx/logs ? L’image crazymax/fail2ban a un système de fichiers en lecture seule dans certains répertoires comme /var/log/nginx. Monter les logs dans /data/nginx/logs (qui est le volume /data accessible en écriture) évite cette erreur au démarrage.
Structure des dossiers de configuration
L’image crazymax/fail2ban attend ses fichiers de configuration dans /data/ (dans le conteneur), ce qui correspond à /home/USER/docker/fail2ban/ sur Ubuntu.
Avant de déployer, crée la structure manuellement :
mkdir -p /home/USER/docker/fail2ban/action.d
mkdir -p /home/USER/docker/fail2ban/filter.d
mkdir -p /home/USER/docker/fail2ban/jail.d
Structure finale sur Ubuntu :
/home/USER/docker/fail2ban/
├── action.d/
│ └── docker-action.conf
├── filter.d/
│ └── nginx-404.conf
├── jail.d/
│ └── jail.local
└── fail2ban.log (cree automatiquement au demarrage)
⚠ Ne crée pas de sous-dossier ‘config/’ ou ‘data/’ supplémentaire. Les dossiers action.d, filter.d et jail.d doivent être directement dans /home/USER/docker/fail2ban/.
Note sur les permissions – si les dossiers ont été créés par Docker (au premier démarrage du conteneur), ils appartiennent à root. Utilise sudo pour créer les fichiers, ou reprends la propriété du dossier :
sudo chown -R USER:USER/home/USER/docker/fail2ban/
Configuration de Fail2Ban
Fichier action.d/docker-action.conf
Ce fichier définit les règles iptables appliquées lors d’un bannissement. Il crée une chaîne dédiée f2b-npm-docker dans la table FORWARD.
Crée le fichier /home/USER/docker/fail2ban/action.d/docker-action.conf :
sudo nano /home/USER/docker/fail2ban/action.d/docker-action.conf
Contenu :
[Definition]
actionstart = iptables -N f2b-npm-docker
iptables -A f2b-npm-docker -j RETURN
iptables -I FORWARD -p tcp -m multiport --dports 0:65535 -j f2b-npm-docker
actionstop = iptables -D FORWARD -p tcp -m multiport --dports 0:65535 -j f2b-npm-docker
iptables -F f2b-npm-docker
iptables -X f2b-npm-docker
actioncheck = iptables -n -L FORWARD | grep -q 'f2b-npm-docker[ \t]'
actionban = iptables -I f2b-npm-docker -s <ip> -j DROP
actionunban = iptables -D f2b-npm-docker -s <ip> -j DROP
Fichier jail.d/jail.local
Ce fichier définit les règles de bannissement et lie les filtres aux logs.
Crée le fichier /home/USER/docker/fail2ban/jail.d/jail.local :
sudo nano /home/USER/docker/fail2ban/jail.d/jail.local
Contenu :
[DEFAULT]
# Temps de bannissement (1h)
bantime = 1h
# Fenetre d'observation (10 min)
findtime = 10m
# Nombre d'erreurs avant bannissement
maxretry = 5
# Action : utilise le fichier docker-action.conf
action = docker-action
# --- JAIL POUR AUTHENTIFICATION NGINX ---
[nginx-http-auth]
enabled = true
filter = nginx-http-auth
# Surveille les logs d'erreur de NPM
logpath = /data/nginx/logs/proxy-host-*_error.log
# --- JAIL ANTI-BOTS (erreurs 404/401/403) ---
[nginx-404]
enabled = true
port = http,https
filter = nginx-404
logpath = /data/nginx/logs/proxy-host-*_access.log
# Plus severe : 30 erreurs en 1 minute = banni 24h
maxretry = 30
findtime = 1m
bantime = 24h
Fichier filter.d/nginx-404.conf
Ce filtre indique à Fail2Ban quoi chercher dans les logs d’accès. Le format des logs NPM est différent du format Nginx standard – le regex doit en tenir compte.
Format réel d’une ligne de log NPM :
[11/May/2026:08:30:08 +0000] – 401 401 – GET https pdf.domaine.com « / » [Client 205.210.31.135] …Crée le fichier /home/USER/docker/fail2ban/filter.d/nginx-404.conf :
sudo nano /home/USER/docker/fail2ban/filter.d/nginx-404.conf
Contenu :
[Definition]
# Regex adapte au format de log NPM (champ [Client X.X.X.X])
failregex = ^\[.*\] - \d+ \d+ - \w+ https? \S+ "\S+" \[Client <HOST>\] .*(404|401|403)
ignoreregex =
Le marqueur <HOST> est la variable Fail2Ban qui capture l’adresse IP – elle correspond au contenu du champ [Client …] dans les logs NPM.
ignoreregex = avec rien après le signe égal signifie « aucune ligne à ignorer ». Cette ligne doit être présente même vide, sinon Fail2Ban peut générer une erreur au démarrage.
Déploiement dans Portainer
- Dans Portainer, clique sur ton environnement « local », puis va dans l’onglet « Stacks » à gauche.
- Clique sur le bouton « + Add stack ».
- Donne-lui un nom : fail2ban.
- Dans la zone « Web editor », copie et colle le contenu du docker-compose.yml.
- Clique sur « Deploy the stack ». Docker télécharge l’image (1 à 2 minutes selon la connexion).
Une fois la stack déployée, vérifie que le fichier fail2ban.log a bien été créé dans /home/USER/docker/fail2ban/. S’il n’apparaît pas, c’est souvent une question de permissions (le conteneur doit avoir le droit d’écrire dans ce dossier).
Vérification
Étape 1 – Vérifier que les logs sont accessibles
Ouvre un terminal sur Ubuntu et tape :
docker exec -it fail2ban ls /data/nginx/logs
✓ Si tu vois la liste de tes fichiers .log (en particulier proxy-host-*_access.log et proxy-host-*_error.log), le pont entre Fail2Ban et les logs NPM fonctionne.
Étape 2 – Vérifier les jails actives
Dans Portainer, ouvre la console (Exec) du conteneur fail2ban et tape :
fail2ban-client status
✓ Tu dois voir apparaître deux jails : nginx-http-auth et nginx-404. C’est bien le cas pour moi :

Résumé des sources de logs
| Type de log | Chemin dans logpath | Ce qu’il contient |
| Erreurs d’authentification | /data/nginx/logs/proxy-host-*_error.log | Tentatives de mots de passe ratés |
| Visites (404/401/403) | /data/nginx/logs/proxy-host-*_access.log | Bots cherchant des fichiers inexistants |
| Log Fail2Ban | /data/fail2ban.log | Correspond au volume /home/USER/docker/fail2ban:/data |
Commandes utiles
Supervision
Status général des jails :
docker exec -t fail2ban fail2ban-client status
Status détaillé d’une jail (ex: nginx-404) :
docker exec -t fail2ban fail2ban-client status nginx-404
Liste de toutes les IPs bannies :
docker exec -t fail2ban fail2ban-client banned
Recherche des requêtes d’une IP dans les logs :
grep <IP-ADRESSE> /home/USER/docker/nginx-proxy-manager/data/logs/proxy-host-*_access.log
Actions manuelles
Bannir manuellement une IP :
docker exec -t fail2ban fail2ban-client set nginx-404 banip <IP>
Lever manuellement un bannissement :
docker exec -t fail2ban fail2ban-client set nginx-404 unbanip <IP>
Informations
Version de Fail2Ban :
docker exec -t fail2ban fail2ban-client version
Aide complète :
docker exec -t fail2ban fail2ban-client –help
Rendons à César
Pour concevoir ce processus d’installation et rédiger cet article, je me suis aidée de :
- https://wiki.blablalinux.be/fr/docker-compose-fail2ban
- l’IA Gemini (gemini.google.com)
- l’IA Claude (claude.ai)
100% testé et ajusté par moi.
Commentaires récents