Configurer fail2ban et bloquer automatiquement les attaques brute force
Comment configurer proprement fail2ban via jail.local, faire fonctionner le jail sshd sur les quatre distributions LTS actuelles, contrôler les bannissements et les lever. Avec les erreurs qui vous ralentissent le plus souvent.
Tout serveur doté d'une adresse IPv4 publique reçoit des tentatives de connexion sur le port 22 quelques minutes seulement après sa première mise en service. Il ne s'agit pas d'une attaque ciblée, mais du bruit de fond permanent d'Internet. fail2ban lit les lignes de log de ces échecs et fait bloquer l'adresse source par le pare-feu pendant une durée définie. L'outil est packagé dans toutes les distributions traitées ici et se met en place en quelques minutes. Les pièges se trouvent ailleurs : sur Debian 12, fail2ban démarre volontiers avec une erreur de configuration, sur certains systèmes il semble bannir sans qu'aucun paquet ne soit réellement rejeté, et une ligne mal pensée dans jail.local vous coupe votre propre accès.
Cet article couvre précisément ces points, pour Debian 13, Debian 12, Ubuntu 24.04 LTS et Ubuntu 22.04 LTS.
Pourquoi jail.local et jamais jail.conf
Le fichier /etc/fail2ban/jail.conf appartient au paquet. Il peut être écrasé à chaque mise à jour, et dpkg vous posera alors la question dans le meilleur des cas, mais pas du tout lors d'une mise à jour automatique sans surveillance. Vos adaptations vivent donc dans /etc/fail2ban/jail.local. Ce fichier n'existe pas après l'installation, vous le créez vous-même, et il ne doit contenir que les valeurs que vous modifiez réellement.
L'ordre dans lequel fail2ban lit ces fichiers est important. La page de manuel jail.conf(5) le précise explicitement :
jail.confjail.d/*.confpar ordre alphabétiquejail.localjail.d/*.localpar ordre alphabétique
Les fichiers lus en dernier l'emportent. Concrètement : votre jail.local écrase aussi les valeurs par défaut de /etc/fail2ban/jail.d/defaults-debian.conf que fournit le paquet de la distribution. Beaucoup de tutoriels affirment le contraire et recommandent pour cette raison un fichier séparé dans jail.d/. Ce n'est pas nécessaire. jail.d/ n'a de sens que si vous voulez déployer vos jails un par un via un outil de gestion de configuration.
Ce que votre distribution impose déjà
La plus grande différence entre les quatre systèmes ne tient pas à fail2ban lui-même, mais à ce seul fichier que le paquet dépose dans /etc/fail2ban/jail.d/defaults-debian.conf. Commencez par le consulter :
cat /etc/fail2ban/jail.d/defaults-debian.conf
| Système | fail2ban | Contenu de defaults-debian.conf |
|---|---|---|
| Debian 13 | 1.1.0 | banaction = nftables, banaction_allports = nftables[type=allports], et pour [sshd] en plus backend = systemd, journalmatch = _SYSTEMD_UNIT=ssh.service + _COMM=sshd et enabled = true |
| Debian 12 | 1.0.2 | uniquement [sshd] et enabled = true |
| Ubuntu 24.04 | 1.0.2 | banaction = nftables, banaction_allports = nftables[type=allports], backend = systemd, plus [sshd] avec enabled = true |
| Ubuntu 22.04 | 0.11.2 | uniquement [sshd] et enabled = true |
Presque tout le reste en découle. Debian 13 et Ubuntu 24.04 lisent le journal systemd d'origine et bannissent via nftables. Debian 12 et Ubuntu 22.04 retombent sur les valeurs par défaut de jail.conf, donc banaction = iptables-multiport et backend = auto. Or auto ne signifie surtout pas « choisis la bonne méthode ». Le commentaire dans jail.conf indique que auto essaie successivement pyinotify puis polling. Ce sont deux mécanismes basés sur des fichiers. La valeur auto ne choisit jamais le journal. Sans le fichier /var/log/auth.log, le jail sshd tourne donc dans le vide.
Installation et première vérification
apt update
apt install -y fail2ban
Sur Debian 12 et Ubuntu 22.04, un paquet s'ajoute si vous voulez exploiter le journal plutôt qu'un fichier de log :
apt install -y python3-systemd
Sur Debian 13, python3-systemd est une dépendance stricte du paquet et se trouve donc déjà là. Sur Debian 12, il est seulement recommandé, ce qui constitue la source d'erreur la plus fréquente sur ce système. Vérifiez ensuite la version et l'état :
apt-cache policy fail2ban
fail2ban-client --version
systemctl status fail2ban --no-pager
Si active (running) s'affiche déjà ici, le plus gros est fait. Si vous lisez failed, passez directement à la section sur les messages d'erreur.
Construire votre propre jail.local
Créez /etc/fail2ban/jail.local. Cette version fonctionne sur les quatre systèmes, à condition que python3-systemd soit installé :
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 203.0.113.7
bantime = 1h
findtime = 10m
maxretry = 5
bantime.increment = true
bantime.factor = 2
bantime.maxtime = 7d
banaction = nftables
banaction_allports = nftables[type=allports]
[sshd]
enabled = true
backend = systemd
port = ssh
maxretry = 4
bantime = 2h
La signification des valeurs, en clair : findtime est la fenêtre de temps glissante, maxretry le nombre d'échecs tolérés à l'intérieur, bantime la durée du bannissement. Ici, quatre échecs en dix minutes conduisent à deux heures de blocage. Les durées comprennent les suffixes m, h, d et w, un nombre seul s'exprime en secondes. Un bantime de -1 bannit définitivement.
Le point intéressant, c'est bantime.increment. Avec cette option, fail2ban double la durée de blocage à chaque récidive de la même adresse IP, dans la limite de bantime.maxtime. Une faute de frappe isolée d'un vrai utilisateur coûte deux heures, un bot obstiné se retrouve après quelques tours avec une semaine. Cette fonction existe depuis fail2ban 0.11, elle est donc aussi disponible sur Ubuntu 22.04. Pour qu'elle survive aux redémarrages, la base de données doit conserver l'historique, ce que pilote dbpurgeage dans /etc/fail2ban/fail2ban.conf, réglé sur un jour par défaut. Si vous mettez bantime.maxtime = 7d, augmentez également dbpurgeage dans un fichier fail2ban.local.
Deux pièges dans ce bloc. D'abord port = ssh : cette valeur se résout en port 22 via /etc/services. Si votre service SSH écoute sur 2222, il faut écrire port = 2222, sinon vous créez une règle de pare-feu pour un port où personne ne frappe. Ensuite ignoreip : indiquez-y l'adresse IP fixe de votre bureau, mais jamais tout un réseau d'opérateur. Et ne comptez pas uniquement là-dessus. La valeur ignoreself est de toute façon à true par défaut et protège les adresses IP du serveur lui-même.
Différences selon le système
Sur Debian 12, vous avez deux voies équivalentes. Soit vous restez sur backend = systemd et installez python3-systemd, soit vous installez rsyslog, omettez backend et travaillez avec /var/log/auth.log. Par ailleurs, sur Debian 12, vous devriez définir banaction en connaissance de cause. Le paquet recommande nftables ou iptables, et apt n'installe en règle générale que nftables. La valeur par défaut de jail.conf est pourtant iptables-multiport. Sur une installation minimale, fail2ban appelle alors un binaire qui n'est tout simplement pas là.
Sur Ubuntu 22.04, rsyslog fait partie de l'installation serveur standard, /var/log/auth.log existe, et le jail fonctionne sans intervention. Si vous ne voulez rien changer sur ce système, laissez simplement backend et banaction de côté dans votre jail.local. fail2ban 0.11.2 connaît certes l'action nftables, mais la voie par iptables-multiport est la plus balisée sur ce système.
Sur Debian 13 et Ubuntu 24.04, votre jail.local coïncide avec les valeurs livrées par le paquet. Cela ne nuit pas, cela rend simplement la configuration explicite et donc lisible.
Appliquer la configuration et vérifier qu'elle agit vraiment
Recharger la configuration sans perdre les bannissements en cours :
fail2ban-client reload
Sur Debian 12 et Ubuntu 24.04, fail2ban 1.0.2 affiche au démarrage et au rechargement l'avertissement 'allowipv6' not defined in 'Definition'. Il est inoffensif, le service continue de fonctionner normalement.
Un systemctl restart fail2ban est rarement nécessaire et remet à zéro les bannissements en cours. Viennent ensuite les commandes d'état :
fail2ban-client ping
fail2ban-client status
fail2ban-client status sshd
La dernière sortie est la plus parlante. Elle affiche Currently failed, Total failed, le nombre de bannissements et la liste des adresses bloquées. C'est exactement là que la plupart des tutoriels s'arrêtent, et exactement là que le problème commence : un jail qui démarre proprement et annonce « 0 banned » peut être totalement aveugle. Un jail sans le moindre résultat ressemble en tout point à un jail qui lit la mauvaise source de logs.
Trois vérifications rendent cette différence visible. Premièrement : le filtre voit-il seulement les lignes ?
fail2ban-regex systemd-journal /etc/fail2ban/filter.d/sshd.conf
Avec un backend basé sur des fichiers, utilisez plutôt :
fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf
À la fin apparaît une ligne du type Lines: 4211 lines, 0 ignored, 137 matched, 4074 missed. Si vous y lisez 0 matched alors que le log contient visiblement des échecs de connexion, le filtre ou la sélection dans le journal ne convient pas.
Deuxièmement : quel filtre de journal le jail utilise-t-il réellement ?
fail2ban-client get sshd journalmatch
La réponse varie selon le système, et cette différence compte. Debian 13 renvoie _SYSTEMD_UNIT=ssh.service + _COMM=sshd, Ubuntu 24.04 en revanche _SYSTEMD_UNIT=sshd.service + _COMM=sshd. L'unité s'appelle donc sshd.service sur Ubuntu et ssh.service sur Debian. Sur Debian 12 et Ubuntu 22.04, la commande renvoie No journal match filter set dans l'état livré, parce que le backend systemd n'y est pas présélectionné et que le jail lit un fichier de log. Ce n'est pas une erreur, c'est la confirmation qu'il n'existe aucun filtre de journal. Sur ces systèmes, il n'en devient actif qu'avec backend = systemd dans votre jail.local.
Les conditions sont séparées par + et combinées par un OU logique. _SYSTEMD_UNIT=ssh.service + _COMM=sshd correspond donc à tout ce qui provient de l'unité ssh.service ou à tout processus nommé sshd. Pour la contre-épreuve dans le journal, reprenez exactement le nom d'unité issu de la sortie ci-dessus :
journalctl -u ssh --no-pager -n 50
journalctl _COMM=sshd --no-pager -n 50
Sur Ubuntu 24.04, la première ligne devient par conséquent journalctl -u sshd --no-pager -n 50. Qui se trompe de nom ici obtient une sortie vide, puis cherche du mauvais côté.
Si des lignes Failed password for invalid user apparaissent ici alors que fail2ban ne compte rien, la sélection dans le journal est en cause. Cela devient pertinent si vous faites tourner OpenSSH depuis les backports ou si vous passez à l'activation par socket. Depuis OpenSSH 9.8, le processus fils s'appelle sshd-session et non plus sshd, et avec l'activation par socket l'unité ne s'appelle plus ssh.service. Vous complétez alors votre jail.local (sur Ubuntu 24.04 avec _SYSTEMD_UNIT=sshd.service) :
[sshd]
journalmatch = _SYSTEMD_UNIT=ssh.service + _COMM=sshd + _COMM=sshd-session
Troisièmement, l'épreuve décisive. Bannissez à la main une adresse du réseau de documentation et regardez si elle arrive bien dans le pare-feu :
fail2ban-client set sshd banip 203.0.113.10
La commande répond 1, soit une adresse bannie. L'outil avec lequel vous constatez ce bannissement dépend de la banaction effective, et celle-ci est présélectionnée différemment selon la distribution :
| Système | banaction d'origine | Contrôle du bannissement actif |
|---|---|---|
| Debian 13 | nftables | nft list table inet f2b-table |
| Ubuntu 24.04 | nftables | nft list table inet f2b-table |
| Debian 12 | iptables-multiport | iptables -n -L f2b-sshd |
| Ubuntu 22.04 | iptables-multiport | iptables -n -L f2b-sshd |
Le plus ancien Debian 11 se comporte ici comme Debian 12. L'origine de la différence se lit dans /etc/fail2ban/jail.d/defaults-debian.conf : seuls Debian 13 et Ubuntu 24.04 y apportent un banaction = nftables, les autres restent sur la valeur par défaut iptables-multiport de jail.conf. Si vous ne voulez pas gérer cette distinction serveur par serveur, mettez banaction = nftables dans votre propre jail.local, comme dans l'exemple plus haut, puis vérifiez partout avec nft.
Avec l'action nftables, il existe une table f2b-table dans la famille inet, contenant une chaîne f2b-chain de priorité filter - 1 et un set nommé addr-set-sshd. C'est là que 203.0.113.10 doit figurer comme élément. Une chaîne f2b-sshd n'existe pas sur ces systèmes, iptables -n -L f2b-sshd y répond No chain/target/match by that name. Inversement, nft list table inet f2b-table renvoie Error: No such file or directory sur Debian 12 et Ubuntu 22.04 tant que iptables-multiport y travaille. Ces deux messages ne prouvent pas une configuration cassée, seulement que la commande ne correspond pas à l'action configurée.
Avec l'action iptables, 203.0.113.10 doit se trouver dans la chaîne f2b-sshd. Ce n'est qu'une fois l'adresse IP visible dans le pare-feu que la chaîne est complète, du log jusqu'au paquet rejeté. N'oubliez pas de faire le ménage ensuite.
Lever un bannissement
Libérer une seule adresse d'un jail précis :
fail2ban-client set sshd unbanip 203.0.113.10
Une adresse dans tous les jails d'un coup :
fail2ban-client unban 203.0.113.10
Et en cas d'urgence, tout remettre à zéro :
fail2ban-client unban --all
Cette dernière commande est celle qu'il vous faut si vous vous êtes bloqué vous-même et que vous êtes assis devant une console de secours sur le serveur. Elle lève tous les bannissements sans redémarrer le service. Si vous n'accédez plus du tout au serveur, il ne reste que la console dans l'espace client. Sur les serveurs root KVM et les serveurs dédiés de KernelHost, vous y atteignez une session VNC qui fonctionne indépendamment de SSH. C'est aussi la raison pour laquelle vous ne devriez pas définir bantime = -1 sur un serveur dépourvu d'un second accès.
Un bannissement que vous levez revient immédiatement si les échecs sont encore comptés dans la fenêtre de temps. C'est pourquoi unban supprime aussi les échecs associés. Une adresse qui doit rester libre en permanence a sa place dans ignoreip, suivi de fail2ban-client reload.
Interaction avec UFW et nftables
C'est ici que naît le dégât le plus difficile à repérer, car fail2ban continue joyeusement d'annoncer des bannissements pendant que les paquets passent.
UFW est une surcouche d'iptables. Au démarrage ou au rechargement, il réécrit la table de filtrage via iptables-restore. Les chaînes créées par fail2ban disparaissent alors purement et simplement, et fail2ban ne les recrée pas de lui-même. Après chaque ufw enable, ufw disable ou ufw reload, la règle est donc : systemctl restart fail2ban. En attendant, fail2ban-client status sshd affiche une longue liste d'adresses IP bannies dont pas une seule n'est réellement bloquée.
Il est plus propre de laisser fail2ban bannir directement via UFW. L'action se trouve dans /etc/fail2ban/action.d/ufw.conf et est présente dans les quatre distributions. Dans jail.local :
[DEFAULT]
banaction = ufw
banaction_allports = ufw
Les bannissements atterrissent alors sous forme de règles dans UFW lui-même et survivent à son rechargement. Contrôle :
ufw status numbered
Les adresses bannies y apparaissent comme DENY IN tout en haut de la liste. Il est important que les règles de blocage se situent avant votre autorisation pour le port 22, sinon c'est l'autorisation qui s'applique en premier. L'action livrée les place donc en tête avec insert.
Qui n'utilise pas UFW et maintient à la place son propre jeu de règles dans /etc/nftables.conf rencontre un problème voisin. Un flush ruleset au début de ce fichier, comme le propose le modèle standard, efface aussi f2b-table au rechargement. Soit vous renoncez au flush global et videz uniquement votre propre table, soit vous accrochez le redémarrage de fail2ban au service nftables. L'avantage de la table propre à fail2ban, c'est qu'avec la priorité filter - 1 elle agit avant la table de filtrage normale, et donc aussi avant les règles issues d'iptables-nft.
Ce qu'il ne faut pas mélanger : banaction = nftables et un jeu de règles iptables maison, en espérant que iptables -L montre les bannissements. Les deux voies fonctionnent en parallèle, mais chaque outil n'affiche que son propre jeu de règles. Vérifiez toujours avec l'outil qui correspond à la banaction configurée.
Les messages d'erreur mot pour mot, et ce qui se cache derrière
Selon le système, vous trouvez les logs à deux endroits :
journalctl -u fail2ban --no-pager -n 100
tail -n 100 /var/log/fail2ban.log
Failed during configuration: Have not found any log file for sshd jail
Le message signature de Debian 12. Le jail tourne avec backend = auto, cherche /var/log/auth.log, et le fichier n'existe pas parce qu'aucun rsyslog n'est installé et que tout atterrit dans le journal. Le service ne démarre même pas, systemctl status fail2ban affiche Failed with result 'exit-code'. Deux solutions : soit apt install -y rsyslog puis un redémarrage, soit la ligne backend = systemd sous [sshd] dans jail.local. La seconde voie est la plus moderne, mais elle réclame le paquet suivant.
Backend 'systemd' failed to initialize due to No module named 'systemd'
Vous avez défini backend = systemd, mais la liaison Python vers le journal manque. Sur Debian 12, python3-systemd est seulement recommandé et n'est pas tiré lors des installations sans paquets recommandés. Remède :
apt install -y python3-systemd
systemctl restart fail2ban
Failed to access socket path: /var/run/fail2ban/fail2ban.sock. Is fail2ban running?
Le client n'atteint pas le serveur. Dans l'immense majorité des cas, le service ne tourne tout simplement pas, parce qu'il a échoué sur une erreur de configuration. Lisez d'abord systemctl status fail2ban, puis le journal. Plus rarement, un fichier socket orphelin subsiste après un arrêt brutal, et le démarrage du serveur signale alors Server already running. Dans ce cas, supprimez le fichier socket et redémarrez.
NOK: ('sshd',)
La réponse à fail2ban-client status sshd quand ce jail n'existe pas à l'exécution. Soit enabled = true manque, soit vous avez fait une faute de frappe dans le nom, soit une erreur de syntaxe plus haut dans jail.local a avalé la section. Les jails réellement actifs s'affichent avec fail2ban-client status. Ce que fail2ban a assemblé à partir de tous les fichiers apparaît dans le dump de configuration :
fail2ban-client -d
Error banning et iptables not found
Le jail compte correctement, mais le bannissement échoue au niveau de l'action. Typique des installations Debian 12 minimales, où seul nftables est présent alors que la valeur par défaut est iptables-multiport. Soit vous ajoutez apt install -y iptables, soit vous passez à banaction = nftables dans jail.local. La seconde voie est celle que Debian 13 et Ubuntu 24.04 empruntent déjà d'eux-mêmes.
Quand plus rien n'aide : le retour en arrière ordonné
Comme toutes vos adaptations se trouvent dans jail.local, le chemin du retour est court. Mettez le fichier de côté, redémarrez le service, et vous voilà revenu aux valeurs livrées par le paquet :
mv /etc/fail2ban/jail.local /root/jail.local.bak
systemctl restart fail2ban
fail2ban-client status
Si tout fonctionne ainsi, l'erreur est dans votre configuration, et vous réinsérez les blocs un par un. Si cela ne fonctionne toujours pas, la cause est dans l'environnement, donc dans la source de logs absente ou dans l'outil de pare-feu manquant. C'est précisément pour cela que jail.conf doit rester intact : ce retour en arrière en dix secondes n'est possible que tant que le fichier du paquet est dans son état d'origine.
Pour finir, la mise en perspective. fail2ban réduit le bruit dans les logs et arrête les tentatives de connexion lentes et répétées. Une attaque sérieuse menée depuis un grand botnet n'utilise chaque adresse qu'une seule fois et passe à côté de toute limitation de débit. Contre le brute force sur SSH, la mesure la plus efficace reste la désactivation de l'authentification par mot de passe au profit des clés. Et contre les attaques volumétriques, seul un filtrage dans le réseau aide, chez nous via 3,2 Tbps de filtrage Arbor en temps réel directement dans le datacenter de Frankfurt am Main et jusqu'à 17 Tbps de capacité de filtrage globale dans les offres Professional. fail2ban est la couche en dessous, sur l'hôte, et il y remplit sa mission de façon fiable dès que trois choses sont réunies : la bonne source de logs, la bonne action de pare-feu et une jail.local que vous pouvez, en cas de doute, retirer d'un seul geste.
Questions fréquentes
Dois-je vraiment laisser jail.conf intact ?
Pourquoi fail2ban ne démarre-t-il pas sur Debian 12 après l'installation ?
Que signifie exactement backend = auto ?
Comment lever un bannissement ?
Pourquoi fail2ban bannit-il alors que l'attaquant passe quand même ?
À quoi vois-je que fail2ban fonctionne vraiment ?
fail2ban remplace-t-il une protection DDoS ?
2026 KernelHost GmbH. Tous droits réservés. Ce guide est protégé par le droit d'auteur. Sa republication sur d'autres sites web, même partielle ou sous une forme modifiée, n'est pas autorisée sans notre accord écrit. Les citations accompagnées de la source et d'un lien sont expressément les bienvenues.

