Aller au contenu
Sécurité medium

Fail2ban : bannir automatiquement les attaques par force brute

15 min de lecture

Fail2ban lit vos journaux, repère les échecs d'authentification répétés et bannit l'adresse IP fautive dans le pare-feu. Un serveur SSH exposé sur Internet encaisse des milliers de tentatives automatisées par jour ; Fail2ban les coupe après trois essais, sans que vous ayez à surveiller quoi que ce soit.

Ce guide installe Fail2ban, crée une prison SSH, prouve le bannissement par une vraie attaque, puis traite le piège qui fait perdre le plus de temps : une prison qui tourne, qui semble configurée, et qui ne bannit jamais personne.

  • Comprendre le trio prison, filtre et action qui structure Fail2ban
  • Configurer une prison SSH dans jail.local, sans jamais toucher à jail.conf
  • Vérifier qu'un bannissement bloque réellement la connexion, et pas seulement dans l'affichage
  • Diagnostiquer une prison qui ne bannit rien, avec fail2ban-regex
  • Piéger les récidivistes avec la prison recidive
  • Choisir entre Fail2ban, CrowdSec et un pare-feu applicatif
  • Un serveur Linux avec un accès root ou sudo. Le lab de référence est une Debian 12.
  • Un service qui journalise ses échecs d'authentification, typiquement SSH.
  • Des notions de pare-feu : Fail2ban ne bloque rien lui-même, il pilote iptables, nftables ou UFW.

Tout Fail2ban tient dans trois notions. Les confondre est la première source d'erreur.

Un filtre est une collection d'expressions régulières qui décrivent à quoi ressemble un échec dans un fichier de log. Fail2ban en livre 94 par défaut : SSH, Nginx, Apache, Postfix, Dovecot et bien d'autres.

Une action est ce qui se passe quand le seuil est franchi. Par défaut, ajouter une règle de rejet dans le pare-feu, mais cela peut aussi être un courriel ou un appel d'API.

Une prison (jail) assemble les deux : sur ce fichier de log, avec ce filtre, si tu vois N échecs en T secondes, applique cette action pendant D secondes.

RéglageRôleValeur courante
maxretryéchecs tolérés avant sanction3 à 5
findtimefenêtre d'observation10 minutes
bantimedurée du bannissement10 minutes à plusieurs heures
ignoreipadresses jamais banniesvotre IP fixe, le réseau local
Fenêtre de terminal
sudo apt update && sudo apt install -y fail2ban

Vérifiez la version installée, elle a son importance :

Fenêtre de terminal
fail2ban-server --version
Fail2Ban v1.0.2

Ne modifiez jamais /etc/fail2ban/jail.conf. Ce fichier appartient au paquet, et la prochaine mise à jour écrasera vos changements sans prévenir. Fail2ban lit en plus un fichier jail.local, dont les valeurs priment sur celles de jail.conf. C'est là, et nulle part ailleurs, que vous écrivez.

/etc/fail2ban/jail.local
[DEFAULT]
# Durée du bannissement
bantime = 10m
# Fenêtre d'observation
findtime = 10m
# Nombre d'échecs toléré dans la fenêtre
maxretry = 3
# Ne jamais bannir ces adresses
ignoreip = 127.0.0.1/8 ::1 192.168.1.0/24
[sshd]
enabled = true
port = ssh
logpath = /var/log/auth.log

La section [DEFAULT] s'applique à toutes les prisons ; chaque section nommée peut la surcharger. Ici, une seule prison est active, [sshd], qui réutilise le filtre sshd livré avec Fail2ban.

Rechargez, puis vérifiez que la prison est bien montée :

Fenêtre de terminal
sudo systemctl restart fail2ban
sudo fail2ban-client status sshd
Status for the jail: sshd
|- Filter
| |- Currently failed: 0
| |- Total failed: 0
| `- File list: /var/log/auth.log
`- Actions
|- Currently banned: 0
|- Total banned: 0
`- Banned IP list:

Une prison qui affiche Running ne prouve rien. La seule vérification qui vaille est de se faire bannir soi-même, depuis une autre machine, en gardant une session ouverte pour se rattraper.

Depuis une machine de test, enchaînez quatre connexions avec un mauvais mot de passe :

Fenêtre de terminal
for i in 1 2 3 4; do
sshpass -p mauvais ssh -o PreferredAuthentications=password \
-o PubkeyAuthentication=no victime@192.168.1.50 exit
done

Puis, sur le serveur :

Fenêtre de terminal
sudo fail2ban-client status sshd
|- Filter
| |- Currently failed: 0
| |- Total failed: 3
`- Actions
|- Currently banned: 1
|- Total banned: 1
`- Banned IP list: 172.17.0.5

Trois échecs comptés, une adresse bannie. Notez que le quatrième essai n'apparaît pas : l'adresse était déjà bloquée avant qu'il n'arrive.

Le journal raconte la même histoire, en clair :

Fenêtre de terminal
sudo grep -E "Found|Ban" /var/log/fail2ban.log | tail -4
[sshd] Found 172.17.0.5 - 2026-07-12 20:03:22
[sshd] Found 172.17.0.5 - 2026-07-12 20:03:27
[sshd] Found 172.17.0.5 - 2026-07-12 20:03:31
[sshd] Ban 172.17.0.5

C'est la question que personne ne pose, et c'est pourtant la seule qui compte. Fail2ban peut afficher une adresse bannie sans qu'aucune règle n'ait été posée, par exemple si le pare-feu n'est pas celui qu'il croit.

Regardez la chaîne créée dans le pare-feu :

Fenêtre de terminal
sudo iptables -L f2b-sshd -n
Chain f2b-sshd (1 references)
target prot opt source destination
REJECT 0 -- 172.17.0.5 0.0.0.0/0 reject-with icmp-port-unreachable
RETURN 0 -- 0.0.0.0/0 0.0.0.0/0

Et vérifiez le comportement depuis la machine bannie :

ssh: connect to host 192.168.1.50 port 22: Connection refused

Détection, règle de pare-feu, connexion refusée. La chaîne est complète.

Fenêtre de terminal
sudo fail2ban-client set sshd unbanip 172.17.0.5

La règle disparaît immédiatement du pare-feu et la connexion redevient possible. Sans cette commande, il faut attendre l'expiration du bantime.

Étape 4 - Le piège : une prison qui ne bannit jamais rien

Section intitulée « Étape 4 - Le piège : une prison qui ne bannit jamais rien »

C'est l'échec le plus fréquent, et le plus déroutant. Le service tourne, la prison est active, l'attaque est bien en cours, et le compteur reste obstinément à zéro.

Fail2ban fournit l'outil de diagnostic qui donne la réponse en une commande. Il rejoue le filtre sur le fichier de log et dit exactement ce qu'il a compris :

Fenêtre de terminal
sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf

Voici la sortie obtenue en lab, sur un serveur pourtant réellement attaqué :

Failregex: 4 total
| 14) [4] ^<F-NOFAIL>pam_[a-z]+\(sshd:auth\):\s+authentication failure;</F-NOFAIL>...
Lines: 4 lines, 4 ignored, 0 matched, 0 missed

Quatre lignes lues, quatre ignorées, aucune retenue. La clé est la balise <F-NOFAIL> : Fail2ban reconnaît la ligne pam_unix ... authentication failure, mais la classe délibérément comme « pas un échec ». Il attend la ligne suivante, celle que sshd écrit lui-même : Failed password for ... from ....

Dans notre lab, cette ligne n'arrivait jamais dans auth.log, parce que sshd avait été lancé avec l'option -e, qui envoie ses journaux sur la sortie d'erreur au lieu de syslog. PAM écrivait bien dans le fichier, mais pas sshd.

La règle à retenir tient en une phrase : si le compteur Total failed reste à zéro pendant une attaque, le problème est dans le log, pas dans la prison.

Un attaquant patient revient après l'expiration du bannissement. La prison recidive existe précisément pour lui : elle surveille le journal de Fail2ban lui-même, et bannit longuement ceux qui se font bannir de façon répétée.

[recidive]
enabled = true
logpath = /var/log/fail2ban.log
banaction = %(banaction_allports)s
bantime = 1w
findtime = 1d
maxretry = 3

Deux détails la rendent redoutable. Elle lit le journal de Fail2ban, donc toute prison alimente son compteur. Et banaction_allports bloque l'adresse sur tous les ports, pas seulement celui du service attaqué : celui qui insiste sur SSH perd aussi l'accès au serveur web.

Fail2ban est réactif, jamais préventif. Il agit après coup, sur des échecs déjà survenus. Il ne remplace donc ni l'authentification par clé, ni le durcissement de SSH, ni un pare-feu bien réglé. Un serveur dont SSH n'accepte que les clés n'a aucune tentative de mot de passe à bannir, ce qui est infiniment plus solide.

Il ne voit que ce qui est journalisé. Une attaque qui ne produit pas de ligne de log lui est invisible, par construction.

Il protège mal contre le distribué. Trois essais par adresse, sur dix mille adresses, ne déclenchent jamais rien. C'est la limite fondamentale du modèle, et c'est ce qui a donné naissance aux approches collaboratives.

La question est légitime, et la réponse n'est pas « le plus récent gagne ».

Fail2ban est présent dans tous les dépôts, se configure en un fichier, et ne dépend d'aucun service tiers. Sa force est sa simplicité et son universalité : il sait lire n'importe quel log dès qu'on lui écrit une expression régulière. C'est le bon choix sur un serveur isolé, un homelab, une machine qui n'a pas vocation à parler au monde.

CrowdSec ajoute ce qui manque à Fail2ban : une liste noire communautaire. Une adresse qui attaque quelqu'un d'autre est bloquée chez vous avant même de vous avoir touché. C'est la réponse au problème des attaques distribuées, au prix d'une architecture plus lourde et d'une dépendance à un service en ligne.

SysWarden va plus loin encore, avec un pare-feu applicatif, des listes de blocage et du géoblocage.

Le projet Fail2ban lui-même mérite un mot d'honnêteté : sa dernière version publiée date d'avril 2024, ce qui a fait dire à beaucoup qu'il était abandonné. Le dépôt raconte autre chose : des commits réguliers jusqu'en juin 2026, plus de 18 000 étoiles, et un développement actif. Le projet ne publie pas souvent, il ne meurt pas pour autant.

Avant le tableau, un réflexe : les trois quarts des problèmes de Fail2ban se diagnostiquent avec fail2ban-regex, qui rejoue le filtre sur le log et montre ce qui a été compris.

SymptômeCauseCorrectif
Total failed: 0 pendant une attaquele service ne journalise pas dans logpathvérifier le fichier, ou passer en backend = systemd
0 matched, N ignoredla ligne est marquée NOFAILc'est une ligne PAM, il manque la ligne du service
0 matched, N missedle filtre ne reconnaît pas le formatvérifier le format de date et la version du filtre
IP bannie mais connexion possibleaucune règle posée dans le pare-feuiptables -L f2b-<prison> -n
Vous êtes banni de votre serveurignoreip incompletaccès console, fail2ban-client set <prison> unbanip <ip>
Les bannissements disparaissent au redémarragepas de persistanceactiver la base : dbfile dans fail2ban.local
Rien ne démarreerreur de syntaxe dans jail.localfail2ban-client -t teste la configuration
  • Fail2ban assemble un filtre (à quoi ressemble un échec), une prison (le seuil) et une action (le pare-feu).
  • Ne touchez jamais à jail.conf : la mise à jour du paquet l'écrase. Tout se passe dans jail.local.
  • Renseignez ignoreip en premier, sinon vous vous bannirez vous-même.
  • Une prison active ne prouve rien. Vérifiez le bannissement en vous faisant bannir, et contrôlez la règle de pare-feu.
  • Si le compteur reste à zéro pendant une attaque, le problème est dans le log, pas dans la prison. fail2ban-regex le dit en une commande.
  • La balise <F-NOFAIL> explique les lignes vues mais ignorées : Fail2ban attend la ligne du service, pas celle de PAM.
  • La prison recidive bannit une semaine, sur tous les ports, ceux qui reviennent.
  • Fail2ban est réactif : il ne remplace ni les clés SSH, ni le durcissement. Il est aussi impuissant face aux attaques distribuées, ce à quoi CrowdSec répond.
  • Le projet ne publie plus depuis avril 2024 mais reste développé : commits réguliers en 2026.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn