Fail2Ban sur Debian reste l’une des protections les plus rentables quand un serveur expose SSH ou d’autres services sensibles. Je vais aller droit au but: installation, configuration propre, réglages utiles pour éviter les faux positifs, puis vérification concrète que les bannissements fonctionnent vraiment. L’objectif n’est pas d’empiler des options, mais de mettre en place une défense simple à maintenir et suffisamment robuste pour un contexte DevOps.
Les réglages qui font réellement la différence avant de verrouiller SSH
- Installer le paquet Debian puis activer le service avec systemd.
- Éviter de modifier les fichiers de base et préférer une configuration locale dans `jail.local` ou `jail.d`.
- Partir sur `maxretry = 5`, `findtime = 10m` et un `bantime` de 10 minutes à 1 heure selon l’exposition.
- Vérifier si vos logs SSH viennent de `/var/log/auth.log` ou du journal systemd, car le backend doit suivre la vraie source.
- Tester le jail avec `fail2ban-client` avant de considérer la protection comme opérationnelle.
- Aligner Fail2Ban avec le pare-feu déjà utilisé, au lieu de mélanger plusieurs couches de blocage sans besoin.

Ce que Fail2Ban change sur un serveur Debian
Fail2Ban observe les journaux, repère des séquences d’échecs répétées, puis applique un blocage temporaire à l’adresse source. Dit autrement, il ne remplace pas SSH ni le pare-feu: il ajoute une couche réactive qui coupe court aux essais automatisés les plus bruyants. C’est précisément ce qui en fait un bon outil de base sur une machine Debian exposée à Internet.
Dans sa logique, trois briques comptent vraiment. Le filtre identifie les événements suspects dans les logs, le jail regroupe les règles d’un service donné, et l’action décrit comment bannir l’adresse IP, en général via le pare-feu. Quand ces trois éléments sont alignés, la protection devient lisible et prévisible.
Je garde aussi un point de vue très pragmatique: Fail2Ban n’est utile que si le service journalise correctement et si le bannissement est appliqué au bon niveau. Dès que l’une de ces conditions manque, la configuration semble “active” sur le papier mais ne protège plus grand-chose. C’est pour cela que je commence toujours par une installation propre, avant de toucher au moindre paramètre.
Cette base en tête, passons à l’installation et à la mise en route du service sur Debian.
Installer le paquet et activer le service
Sur Debian, je pars systématiquement du paquet officiel. Cela évite les bricolages inutiles et garantit que le service est intégré au système d’initialisation attendu. Je passe toujours par `fail2ban-client` pour vérifier l’état du démon, car c’est l’interface prévue pour dialoguer avec lui.
sudo apt update
sudo apt install fail2ban
sudo systemctl enable --now fail2ban
sudo fail2ban-client ping
sudo fail2ban-client version
Si tout va bien, `ping` doit répondre avec un équivalent de `pong`. Si ce n’est pas le cas, je regarde immédiatement le journal du service plutôt que de modifier la configuration à l’aveugle:
sudo journalctl -u fail2ban -n 50 --no-pager
Ce contrôle initial paraît banal, mais il évite beaucoup de temps perdu. Un démon qui ne démarre pas, un paquet mal installé ou une erreur de syntaxe dans un fichier de configuration se détectent vite à ce stade. Une fois le service vivant, le vrai travail commence: créer une configuration locale qui ne sera pas écrasée à la prochaine mise à jour.
Préparer une configuration locale propre
Le point décisif est simple: je ne modifie pas les fichiers par défaut directement. Je préfère un fichier local, lisible, isolé, que je peux relire rapidement six mois plus tard sans me demander quelle ligne vient du paquet et quelle ligne vient de moi. En pratique, je pars souvent sur `/etc/fail2ban/jail.d/sshd.local`, parce qu’un fichier par service reste plus facile à maintenir qu’un gros bloc central.
/etc/fail2ban/jail.d/sshd.local
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 203.0.113.10 198.51.100.0/24
findtime = 10m
maxretry = 5
bantime = 1h
backend = systemd
[sshd]
enabled = true
port = ssh
Les adresses d’exemple doivent évidemment être remplacées par vos vraies IP d’administration, votre plage VPN ou l’adresse du bastion. J’ajoute toujours mes chemins d’accès légitimes dans `ignoreip`, parce qu’un auto-bannissement sur un serveur de production est une erreur très bête, mais fréquente. Et si votre machine continue à écrire les échecs SSH dans `/var/log/auth.log`, gardez `backend = auto` ou adaptez la source de logs en conséquence: l’important est que Fail2Ban voie les événements réels, pas une hypothèse de configuration.
Cette configuration de base suffit souvent à lancer un premier jail SSH. Pour qu’elle soit vraiment utile, il faut maintenant régler les paramètres avec un peu de nuance.
Régler le jail SSH sans se bloquer soi-même
Je pars rarement sur des valeurs agressives dès le départ. Pour SSH, un seuil de 5 tentatives sur 10 minutes, avec un bannissement d’une heure, donne un bon équilibre entre sécurité et confort d’exploitation. Cela casse les scripts de bruteforce sans punir exagérément un utilisateur qui a simplement tapé son mot de passe trop vite.| Paramètre | Valeur de départ | Rôle concret |
|---|---|---|
ignoreip |
IP d’admin, VPN, bastion | Évite de bloquer les accès de confiance. |
maxretry |
5 | Nombre d’échecs tolérés avant bannissement. |
findtime |
10 minutes | Fenêtre pendant laquelle Fail2Ban compte les erreurs. |
bantime |
1 heure | Durée du blocage temporaire. |
port |
ssh ou port personnalisé |
Doit correspondre au port réellement écouté par SSH. |
backend |
systemd ou auto
|
Source utilisée pour lire les logs. |
Deux détails font souvent toute la différence. D’abord, le port surveillé doit être le bon si vous avez déplacé SSH sur un port non standard. Ensuite, le `ignoreip` doit inclure votre chemin d’administration complet, pas seulement votre IP fixe du bureau. Si vous passez par un VPN ou un bastion, je considère ces adresses comme prioritaires à exclure du bannissement.
Quand je dois durcir davantage, je préfère augmenter d’abord `bantime` avant de toucher `maxretry`. C’est plus lisible et moins agressif pour les utilisateurs légitimes, tout en cassant mieux le rythme des attaques automatisées. Une fois ces réglages en place, il faut vérifier que le service applique vraiment ce que vous venez d’écrire.
Vérifier que la protection fonctionne vraiment
Une configuration ne vaut rien tant qu’elle n’a pas été testée. Je commence par l’état global, puis je vérifie le jail SSH lui-même, et seulement ensuite je provoque un échec de connexion depuis une machine de test ou une deuxième session. Ce séquencement évite les faux diagnostics.
sudo fail2ban-client status
sudo fail2ban-client status sshd
sudo fail2ban-client set sshd unbanip 192.0.2.15
sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf
Le premier contrôle doit me montrer que le démon tourne et que le jail est chargé. Le second me donne le nombre d’IPs bannies et les statistiques du jail `sshd`. Le dernier est utile surtout si vos logs sont basés sur des fichiers classiques; il permet de valider le filtre lui-même, mais pas de remplacer le test du service en conditions réelles.
- Lancer `fail2ban-client status` pour confirmer que le service répond.
- Vérifier `fail2ban-client status sshd` pour s’assurer que le jail est actif.
- Provoquer 5 échecs de connexion SSH depuis une IP non incluse dans `ignoreip`.
- Observer le journal du service avec `journalctl -u fail2ban -f` pendant le test.
- Unban immédiatement si vous avez testé depuis la mauvaise adresse.
Je garde aussi en tête un point important: un filtre peut être correct, mais inopérant si le backend de logs ne pointe pas vers la bonne source. C’est particulièrement vrai quand une machine Debian utilise davantage le journal systemd que les fichiers d’authentification classiques. Une fois la validation faite, il reste à faire cohabiter Fail2Ban avec le pare-feu réellement utilisé sur le serveur.
Aligner le bannissement avec le pare-feu déjà en place
Fail2Ban n’est pas un firewall autonome; il pilote le pare-feu qui existe déjà sur la machine. Sur Debian, je cherche donc la cohérence, pas l’empilement inutile. Si la machine est déjà gérée proprement en `nftables`, je reste dans cet univers; si l’infrastructure s’appuie sur UFW, je ne rajoute pas une couche parallèle juste pour le principe.
| Situation | Ce que je fais | Pourquoi |
|---|---|---|
| Pile réseau centrée sur `nftables` | Je garde une action compatible avec cette pile. | Je limite les doublons et je lis les règles plus vite. |
| Serveur administré avec UFW | J’utilise l’action de bannissement alignée sur UFW. | Je laisse un seul outil écrire les règles visibles. |
| Ancienne base `iptables` | Je conserve la logique existante si elle est déjà stable. | Je n’introduis pas de migration de pare-feu juste pour Fail2Ban. |
Le principe est simple: une seule chaîne de décision pour bloquer l’IP, et Fail2Ban comme moteur de détection. Dès que plusieurs couches essaient de faire la même chose sans documentation claire, le débogage devient pénible. On finit alors avec des bans invisibles, des règles oubliées et une fausse impression de sécurité. Les erreurs les plus courantes viennent souvent de là.
Les erreurs que je vois le plus souvent
Dans la pratique, les problèmes reviennent toujours à peu près dans le même ordre. Le paquet est bien installé, le service démarre, mais rien n’est vraiment bloqué, ou alors la bonne IP finit par être bannie au mauvais moment. Un diagnostic simple permet presque toujours de trouver la cause en quelques minutes.
| Symptôme | Cause probable | Correction utile |
|---|---|---|
| Le service tourne, mais aucun ban n’apparaît | Backend de logs incohérent ou jail non activé | Vérifier le backend, puis `status sshd`. |
| Fail2Ban ne démarre pas au boot | Erreur de syntaxe dans le fichier local | Relire le fichier et redémarrer le service après correction. |
| Votre propre IP est bannie | `ignoreip` incomplet | Ajouter l’IP, la plage VPN et le bastion d’administration. |
| SSH sur un port custom n’est pas protégé | Le jail surveille encore le port par défaut | Déclarer le bon port dans la configuration. |
| Le filtre semble correct, mais rien ne remonte | Les logs ne sont pas lus là où vous pensez | Contrôler `auth.log`, `journalctl` et le backend choisi. |
Quand je tombe sur un échec de démarrage, je vérifie d’abord les crochets de section, les espaces parasites et le fichier réellement chargé. Quand le jail existe mais ne bannit rien, je soupçonne presque toujours le chemin des logs ou un `ignoreip` trop large. Une fois ces trois points clarifiés, Fail2Ban devient très simple à exploiter.
Ce que je mets en place sur un Debian exposé
Sur un serveur accessible publiquement, je combine toujours Fail2Ban avec une hygiène SSH stricte. Les clés publiques remplacent les mots de passe quand c’est possible, l’accès root direct reste désactivé, et je limite les comptes qui ont le droit de se connecter. Fail2Ban n’est alors plus un pansement, mais une couche de réduction de bruit très efficace.
- Je garde l’authentification par mot de passe désactivée dès que le contexte le permet.
- J’ajoute systématiquement les adresses d’administration dans `ignoreip`.
- Je surveille les bannissements via `fail2ban-client` ou le journal système, pas seulement à l’installation.
- Je teste la configuration après chaque changement de pare-feu ou mise à jour majeure du serveur.
- Quand l’attaque est durable, j’envisage `bantime.increment` avec prudence, seulement après observation.
J’aime aussi garder un réglage mesuré plutôt qu’une configuration spectaculaire. Un bannissement temporaire bien choisi, des logs cohérents et un pare-feu aligné valent mieux qu’une politique trop dure que l’équipe n’ose plus maintenir. Sur Debian, cette discipline suffit souvent à transformer Fail2Ban en protection discrète, fiable et facile à faire évoluer.