Ceux qui me connaissent le savent très bien : Lire les logs serveurs, c’est ma très grande passion.
C’est pour ça que j’ai créé la FFLSLS ( Fédération Française de lecture sportive de logs serveurs) : pour faire reconnaitre la pratique en haut-niveau comme discipline olympique.

(Il faut dire que je suis un peu trop disponible en ce moment, donc si vous cherchez quelquun pour consolder une IT, n'hésitez pas à me contacter, je suis dispo)

À lire mes logs serveurs, ces derniers mois, le trafic automatisé a explosé, et pas que pour des usages légitimes : tentatives de spam-comment, recherches forcenées de failles sécu, d’artefact d’installation, etc…

Mon serveur perso est une toute petite VM hébergée avec amour sur Toulouse par Tetaneutral, un hébergeur de services internet et FAI associatif, dont je suis membre depuis 15 ans. AS197422 pour les intimes.

J’ai la main sur ma VM, et j’ai des responsabilités par rapport aux autres membres de l’association.

Seulement je ne voulais pas complètement reprendre de zéro ma VM, changer de serveur web, etc : j’ai énormément de tâches de fond qui tourne dessus, parfois des trucs que j’ai mis en place depuis 15 ans, et qui marchent sans soucis, malgré les montées en version et petites retouches de code suivant l’état de l’art.

Pour faire le ménage, j’ai installé fail2ban sur mon serveur Apache.

Capture d'un extrait de log combiné du serveur Apache et fail2ban qui vient de bannir une adresse IP

Pourquoi fail2ban ?

Donc en gros, pour le moindre effort :

Parce qu’il est déjà installé, et filtre déjà les comportements abusifs sur ssh en paramétrant à la volée un outil de filtrage, en général iptable ou plus récemment nftable, un module du kernel Linux qui bloque au plus bas niveau les adresses IP qui se sont pris un carton rouge après 3 tentatives de connexions infructueuses. Pour détecter ces comportements déviants, il ne fait que regarder des logs serveurs et applique à chaque ligne des regex. Ces adresses IP sont autorisées à ré-accéder au service au boût d'un temps d'emprisonnement calculé par filtre, multiplié par le nombre de fois qu'il a été ermis en prison.

Ce n’est pas une mesure de protection contre toutes les failles, mais un ralentisseur qui réduit les probabilités d’exploitations. Son but est de violemment ralentir les tentatives d’attaques en brute-force sur les accès ssh des serveurs. Ces attaques restent possibles, mais au lieu de tomber un mot de passe en disons 6 mois en connaissant un compte utilisateur valide (encore faut que la chauve-souris connaisse mon handle dans l'immeuble), il faudra environ 2,5 éternités pour y arriver.
En moyenne.

Et comme fail2ban tourne asynchroniquement par rapport aux services qu’ils surveille, tout problème ou erreur d’exécution n’impacte pas le service surveillé lui-même. Sauf s’il lance plein de règles de filtrage et qu'il bloque tout le monde, mais on parle alors d’une autre lessive.

Par contre, il prend de la RAM et de la puissance CPU. Mais l’analyse en action sur ma VM avec mes paramètres persos montre que cet impact est négligeable : environ 30 Mo de RAM, et même pas 6% de charge CPU. Un thread PHP consomme le double de RAM, donc avoir un service fail2ban pour filtrer prend bien moins de ressources que le CMS pour répondre aux 80 requêtes par seconde en 404/403 lors d’un scan non-sollicité de vulnérabilité.

Oui, 80 requêtes attaquantes par seconde, c'est la moyenne des attaques, et j'en prends une toute les 5 minutes les soirs d'été en heures creuses. Le flood peut durer entre 20 secondes et 6 minutes. Merci pour tous les patterns d'attaques, mais j'ai rien demandé.

Et on ouvre la console

Si vous avez déjà fail2ban installé sur votre serveur, vous avez très probablement déjà des filtres Apache et NGinX disponibles dans /etc/fail2ban/filter.d, mais seul sshd est en monitoring par défaut. Oui, c'est surprenant pour du Linux, mais la distrib va pas surbloquer bêtement. On va activer ceux d'Apache car c'est le service web que j'utilise.

D’abord, j’ai créé un /etc/fail2ban/jail.local, en me basant sur une excellente documentation, en n'oubliant pas de l'adapter à votre distrib et sas particularités.

# detect potential search for exploits and php vulnerabilities
[apache-noscript]
enabled  = true
port     = http,https
filter   = apache-noscript
logpath  = /var/log/apache*/*error.log
maxretry = 6
banaction_allports = nftables[type=allports]

# detect Apache overflow attempts
[apache-overflows]
enabled  = true
port     = http,https
filter   = apache-overflows
logpath  = /var/log/apache*/*error.log
maxretry = 2
banaction_allports = nftables[type=allports]

# detect failures to find a home directory on a server
[apache-nohome]
enabled  = true
port     = http,https
filter   = apache-nohome
logpath  = /var/log/apache*/*error.log
maxretry = 2
banaction_allports = nftables[type=allports]

# detect failures to execute non-existing scripts that
# are associated with several popular web services
# e.g. webmail, phpMyAdmin, WordPress
[apache-botsearch]
enabled  = true
port     = http,https
filter   = apache-botsearch
logpath  = /var/log/apache*/*error.log
maxretry = 2
banaction_allports = nftables[type=allports]

# MODULE PERSO (c'est expliqué plus bas)
[apache-dascritch]
enabled  = true
port     = http,https
filter   = apache-dascritch
logpath  = /var/log/apache*/access.log
maxretry = 2
banaction_allports = nftables[type=allports]

N'ayez pas peur sur les multiples déclarations d'un même fichier de log, il n'est ouvert que par un seul pipe.

Les filtres proposés se déclenchent sur des événements d'erreurs, en général un répertoire non accessible ou un script inexistant. Sauf que sur les frameworks modernes, l'URL du script est cachée, et toute erreur du type 400, 403, 404, sont prises en charge par le framework utilisé par le site web au cas où dans son système d'adressage, cela corresponde à une ressource réelle. Donc impossible de mettre en cache une vue 404 qui soit servi directement par le serveur web, à moins de faire une règle lourdasse à maintenir pour indiquer tout son plan de nommage dans la partie service web. Donc cela implique à chaque ressource incorrecte, charger le framework qui était en cache, résoudre sa route, peu-être faire des requêtes SQL, chercher en cache la vue du template, etc… Le prix n'est pas nul. Et c'est justement cette gestion d'erreur par l'applicatif qui pose souci et qui fait que les multiples requêtes sans sens font exploser les consommation du serveur.

Y'a des méthodes qu'on peut appliquer, comme cantonner les adressages dynamiques du framework à partir d'un path précis, genre /cms/, et donc on peut envisager de rendre la page d'erreur en statique, mais cela implique de modifier le contenu des sites web, de créer des redirections, et cela fait perdre de la plasticité sur d'autres fichiers, puisque certains CMS génèrent, par exemple des manifest.json différents selon que vous soyez authentifiés comme éditeur ou non.

Donc, j'avais besoin d'écrire un script qui se base non plus sur les logs d'erreurs mais les logs d'accès à mon serveur web, et qui éjecte les requêtes qui ne semblent pas appropriées sur mon serveur.

J’ai écrit un premier filtre perso dans /etc/fail2ban/filter.d appelé apache-dascritch.conf, pour reprendre la convention de nommage des filtres, et vu qu'il ne semble pas avoir de convention particulière pour les contributions spécifiques hors distributions.

[Definition]
failregex = \  "POST /wp.*           ← J'ai pas WordPress, donc on triggue
            \  "GET /wp.*
            \  "GET /config               ← ben voyons
            \  "GET /\.env.*               ← y'en a qui nettoient mal leur mise en prod
            \  "GET /\.git.*
            \  "POST /graphql          ←  Apparemment très très attaquable
            \  "GET /@fs.*               ← Node.js, tentative de chargement d'une bibliothèque en direct, vous ne passerez pas
            \  "GET /cgi-bin               ← Les CGI-Bin ne sont pas utilisés, du moins pas accessibles via un répertoire public
            \  "GET //          ←  parfaitement valable, et j'ai vu sur un forum que ça permet d'esquiver les filtres de protection. Perdu !
            \  "\\x16\\x03\\x01"       ← artefact de https sur le port 80, jamais vu hors d'une attaque
            \  "CONNECT               ← verbe HTTP que je n'utilise pas sur ce serveur
            \  "SSTP_DUPLEX_POST      ← verbe HTTP inexistant, sauf chez Microsoft
            \  "GET /vpntunnel               ← recherche de VPN ouverts

ignoreregex =

(Note : je ne présente que quelques règles, il y en a un peu moins d'une vingtaine. Trop en dévoiler peut donner des informations pour réussir certaines attaques. Je ne parle pas trop aux robots : ça les instruits)

Vous l'avez compris, chaque ligne est une regex, avec un atome <HOST> qui désigne l'adresse IP (IPv4 ou IPv6) et juste après un espace, la requête HTTP à matcher. Au début de ma regex, le \ <HOST> permet de bien signifier l’espace délimiteur dans les logs traditionnels Apache. Y'a qu'un seul espace après le backslash, c'est la balise <pre> qui me fait des misères.

Je reste sur une suite de règles très très réduites afin d’en garder une maîtrise, de ne pas faire monter le CPU dans les tours et d’éviter des faux-positifs complètement abusés. La simplicité est la beauté.

À noter que je surveille les logs d'accès de TOUS les serveurs webs de la VM, donc les noms de domaines, de sous-domaines et les accès IP directs. Cela veut dire qu'il faut éviter que des outils de monitoring et de contrôles, même déjà filtrés par IP, ne soient PAS dans les regex de détection, mais défiltrés par les regex d'exceptions. Il faut aussi connaître l'histoire des noms de domaine, et si ceux-ci avaient d'anciens spiders des moteurs de recherche ! Dans le doute, cela vaut le coup de jouer ces regex sur 15 jours de logs, mais cela risque de ne pas suffire !

De votre côté, faites le tour de votre serveur, regardez quel serveur web est utilisé (Apache, NGinX, Apollo, IIS, etc…), quel langage est utilisé, quel framework est déployé et quels outils API de monitoring vous avez peut-être. On n'est pas à l'abri d'un antédiluvien PHPMyAdmin qui sert en urgence (et qui aurait dû être supprimé depuis 15 ans).
En plus de connaitre parfaitement vos plans de nommages, de routage et son historique, faites aussi attention aux simplifications hâtives ! Bloquer sur toute URL commençant par /. vous fera bloquer tout agent logiciel qui vérifie ce que vous déclarez en /.well-known, donc ce n'est pas une bonne idée.

Je pourrais aussi filtrer sur les user-agents, mais ceux-ci étant purement déclaratifs, ces règles deviennent trop vite obsolètes.

Protch le moustique

Ici, à peine 2 minutes après l’écriture de mon premier filtre, un moustique attrapé en plein vol. L’action dure moins de 1 500 millisecondes, j'ai même pas eu le temps de bailler (oui, je sais, j’ai une sortie de logs maison, notamment pour avoir un timestamp ISO-8601 unifié)

==> /var/log/apache2/access.log <==
2026-08-10 11:36:47 (dascritch.net) 136.108.66.99 "GET /backend/settings.py HTTP/2.0" 404 15928 "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.3; +https://openai.com/gptbot)" "-"
2026-08-10 11:36:47 (dascritch.net) 136.108.66.99 "GET /instance/config.py HTTP/2.0" 404 15927 "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.3; +https://openai.com/gptbot)" "-"
2026-08-10 11:36:47 (dascritch.net) 136.108.66.99 "GET /.env.development HTTP/2.0" 404 15925 "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.3; +https://openai.com/gptbot)" "-"

==> /var/log/fail2ban.log <==
2026-08-10 11:36:48,110 fail2ban.filter         [127499]: INFO    [apache-dascritch] Found 136.108.66.99 - 2026-08-10 11:36:47

==> /var/log/apache2/access.log <==
2026-08-10 11:36:47 (dascritch.net) 136.108.66.99 "GET /.env.staging HTTP/2.0" 404 15921 "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.3; +https://openai.com/gptbot)" "-"

==> /var/log/fail2ban.log <==
2026-08-10 11:36:48,124 fail2ban.filter         [127499]: INFO    [apache-dascritch] Found 136.108.66.99 - 2026-08-10 11:36:47

==> /var/log/apache2/access.log <==
2026-08-10 11:36:47 (dascritch.net) 136.108.66.99 "GET /config.py HTTP/2.0" 404 15918 "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.3; +https://openai.com/gptbot)" "-"
2026-08-10 11:36:48 (dascritch.net) 136.108.66.99 "GET /.env.test HTTP/2.0" 404 15941 "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.3; +https://openai.com/gptbot)" "-"

==> /var/log/fail2ban.log <==
2026-08-10 11:36:48,326 fail2ban.filter         [127499]: INFO    [apache-dascritch] Found 136.108.66.99 - 2026-08-10 11:36:48

==> /var/log/apache2/access.log <==
2026-08-10 11:36:48 (dascritch.net) 136.108.66.99 "GET /dotclear/rss.php HTTP/2.0" 302 388 "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36" "-"
2026-08-10 11:36:48 (dascritch.net) 136.108.66.99 "GET /app/.env HTTP/2.0" 404 15917 "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.3; +https://openai.com/gptbot)" "-"
2026-08-10 11:36:48 (dascritch.net) 136.108.66.99 "GET /sendgrid.env HTTP/2.0" 404 15921 "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.3; +https://openai.com/gptbot)" "-"
2026-08-10 11:36:48 (dascritch.net) 136.108.66.99 "GET /src/.env HTTP/2.0" 404 15917 "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.3; +https://openai.com/gptbot)" "-"
2026-08-10 11:36:48 (dascritch.net) 136.108.66.99 "GET /config.env HTTP/2.0" 404 15919 "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.3; +https://openai.com/gptbot)" "-"
2026-08-10 11:36:48 (dascritch.net) 136.108.66.99 "GET /frontend/.env HTTP/2.0" 404 15922 "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.3; +https://openai.com/gptbot)" "-"
2026-08-10 11:36:48 (dascritch.net) 66.249.77.201 "GET /vrac/.blog1/2cents/701-FIBD07-Thomas-brouillard.jpg HTTP/1.1" 200 90278 "Googlebot-Image/1.0" "-"
2026-08-10 11:36:48 (dascritch.net) 136.108.66.99 "GET /server/.env HTTP/2.0" 404 15920 "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.3; +https://openai.com/gptbot)" "-"

==> /var/log/fail2ban.log <==
2026-08-10 11:36:48,726 fail2ban.actions        [127499]: NOTICE  [apache-dascritch] Ban 136.108.66.99

==> /var/log/apache2/access.log <==
2026-08-10 11:36:48 (dascritch.net) 136.108.66.99 "GET /production/.env HTTP/2.0" 404 15933 "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.3; +https://openai.com/gptbot)" "-"
2026-08-10 11:36:48 (dascritch.net) 136.108.66.99 "GET /docker/.env HTTP/2.0" 404 15920 "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.3; +https://openai.com/gptbot)" "-"
2026-08-10 11:36:48 (dascritch.net) 136.108.66.99 "GET /dev/.env HTTP/2.0" 404 15935 "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.3; +https://openai.com/gptbot)" "-"

Pour vérifier l'efficacité du filtre qu'on a créé :

dieutoutpuissant@vmsecrete/etc/fail2ban $ sudo fail2ban-client status apache-dascritch
Status for the jail: apache-dascritch
|- Filter
|  |- Currently failed: 1
|  |- Total failed:     68
|  `- File list:        /var/log/apache2/access.log
`- Actions
   |- Currently banned: 21
   |- Total banned:     22
   `- Banned IP list:   136.108.66.99 34.60.235.212 136.70.138.98 104.23.221.112 104.23.221.113 104.23.221.28 158.158.35.55 185.177.72.100 185.177.72.53 195.178.110.31 20.104.100.201 20.251.112.238 45.148.10.15 45.148.10.246 45.148.10.62 51.103.131.31 72.146.2.52 82.158.131.223 4.204.200.143 20.203.138.185 157.245.167.36 45.154.98.108

(Attention à son langage : failed veut dire qu'il a des IP qui viennent de franchir la ligne jaune, et qui seront bannies si elles recommencent. C'est donc tout à fait normal que la valeur ne soit pas à zéro)

En 48 heures, il a été redoutable

Allégorie d'un admin-sys tentant de repousser une vague d'attaque zombies, © Paul Robertson

fail2ban évite les DoS dûs aux requêtes abusives d’une seule source, pas les DDoS concertés, tout comme il ne guérit pas des failles. Ce n’est pas non plus une solution définitive : les filtres devront évoluer au fur et à mesure, et il est probable que les scripts d’attaques visant les serveurs web se mettent à être moins prévisibles pour contourner les filtrages sur les patterns d’attaques les plus fréquents. Là encore, c’est de la décision, soit humaine à petit volume, soit délégué à une IA suivant le niveau de budget, de fiabilité et de confiance des serveurs à gérer. Pour l’instant, déléguer la lecture de ces logs à une IA est un poil overkill, et fortement interdite en compétition par la FFLSLS.

L’inconvénient de fail2ban est sa lenteur relative : il est en partie écrit en python. Langage que je connais bien, donc ça aide. Ça veut dire qu'il peut mettre une à deux secondes avant de dénoncer l'IP indélicate à iptables ou nftables.

Autre problème de fail2ban est qu’il faut complètement relancer le service quand on modifie un paramètre ou une règle, et qu’on doit rechecker derrière pour voir si le service n’est pas complètement interrompu à cause d’une typo dans une regex. Parce que la commande service fail2ban restart ne vous indiquera rien, il faut lancer fail2ban-client status --all pour s’assurer. Lire la doc, aussi, au cas où, ça peut aider.

Quand on a fait une erreur de config, on sent très vite revenir les tentatives d'attaques, à un niveau impressionnant.
Et dimanche 16 août, fail2ban a banni plus de 1300 adresses IP, un nombre assez effarant par rapport aux 60 dans les premières 24 heures. Soit y'a eu une campagne concertée sur ma petite VM (mais pourquoi ?), soit un attaquant particulier a décidé de passer par plusieurs VPN/machines compromises pour m'agresser, jetant les IP grillées. Donc soit on a des campagnes menées à un niveau industriel, soit une adaptation à un niveau industriel d'une unique attaque. Je pensais à une activité du week-end pour gagner du temps sur l'absence de surveillance dans les entreprises, mais le week-end suivant a été nettement plus calme (sachant que j'ai ajouté des règles, donc on attrape plus de tentatives).
Car c'est le risque : avoir blindé un minimum un vieux serveur qui comporte des noms de domaine vieux de plus de 20 ans signifie peut-être que la cible n'est pas anodine.
J'aimerais pas trop vite décevoir ceux d'en face.

Vous êtes ici à la moitié de mon texte. Vous vous dites que j'ai laissé une LLM gloser à ma place pour une telle loggorhée ? Ben non, même pas : mon texte est exclusivement bio. Mais continuons, on verra si quelqu'un remarquera ce paragraphe ;)

Q & R

J’en ai parlé à ma communauté habituelle, et ils ont eu des questions, forcément intéressantes.

(je dis forcément car j’ai la richesse d’avoir une commu de très bonne intelligence.)

Suite à ton message, j’ai fait un petit tour de mes logs Apache, et boudiou que c’est sale : c’est plein de vermine qui ne cherche qu’à s’introduire sur le serveur. 🔐 🏴‍☠️

Ce trafic malveillant a toujours existé depuis la fin des années 1990s, il s’est juste industrialisé, amplifié, par des serveurs web compromis pilotés par des c2c, puis des objets connectés très mal sécurisés (le mal de l’IoT genre machine à laver, caméra de surveillance ou télé connectée), par des injections de publicités avec les javascripts malveillants (dont pensez à bloquer les pubs), par des applications qui utilisent des bibliothèques logiciels aux conditions obscures (regardez vos apps avant de les installer avec Exodus privacy), voire même en utilisant des documents Word et PDF envoyés au petit bonheur la chance, par des développeurs qui téléchargent tout et n’importe quoi dans leur environnement de travail (et Docker n’est absolument pas suffisant pour sécuriser).
Et maintenant avec les agents logiciels type LLM, l’industrialisation des attaques a pris une nouvelle échelle, c'est flagrant depuis le printemps dernier.

Tu mets pas un rate-limit ?

Non, car sur ce serveur, j’ai entre autre posté une page technique avec près de 2 000 images individuelles. Nous avons aussi le site de CPU, l'émission Carré Petit Utile, qui a beaucoup de petits fichiers. Par exemple le mode de distribution média DASH/HLS qui fractionne le sonore d'’émission d’une heure en une centaine de petits fichiers, afin de rendre très rapide les saut de chapitre sur les smartphones. On a aussi le flux podcast. iTunes Apple Podcast s’amuse parfois à en télécharger les 250 illustrations dans les 5 secondes.
Le nombre de faux positifs serait désastreux, malgré la mise en place de HTTP/2 et HTTP/3.

Tiens, il faudrait que je parle des effets de publication d’une nouvelle émission sur Mastodon : Flood immédiat. Des opérateurs de site web de presse m’ont confirmé que c’était parfois un frein objectif à la présence sur le fédiverse.

Pourquoi rester sur Apache ?

Ça n'est pas le sujet mais Apache répond à ce dont j'ai besoin, est toujours maintenu et reçoit des mises à jour de sécurité, et j'avais pas envie de me coltiner la conversion de plein de règles .htaccess. Je sais que c'est pas le mieux en optimisation, mais ça répond sans soucis. Les règles que je donne s'appliquent avec un minimum d'adaptation aussi sur NGinX.

Tu penses qu'on pourrait l'appliquer sur un serveur tiers, genre un Zabbix qui monitore une flotte de serveurs ?

Sûrement, très sûrement, mais cela demande beaucoup d'adaptions et de scripting. Genre surveiller le /var/log/fail2ban.log de chaque serveur, isoler des messages Ban et Unban, réinjecter dans un log à surveiller dans le fail2ban. Par contre, j'ai pas cherché à regarder si des règles s'appliquent aussi pour débannir immédiatement des IP.

Ça serait con que toute une entreprise se retrouve bannie de tout son cloud à cause d'un dev qui a lancé sans prévenir un outil de scans de vulnérabilités depuis le réseau interne.
Oui, j'ai déjà vu, j'ai ri sous cape.

Pourquoi pas Anubis ?

Anubis résout un autre problème, celui du trafic de robots d’IA qui viennent repomper du contenu à revendre en tant que perroquets scolastiques, tout en en créant tout plein d’autres problèmes.

Vis à vis des logiciels clients de podcasts ou clouds persos qui récupèrent les flux, c’est extrêmement compliqué à paramétrer : soit je bloque, soit je laisse les IA pomper 150 Go de sonores.

De toutes façons, les moteurs d'IA commencent à s'embarquer dans les navigateurs web en webextension pour justement se faire passer pour un utilisateur humain, et Anubis commence à se faire contourner, hélas.

Regarde pour migrer sous reaction (de ppom, lire son article) au lieu de fail2ban ? C’est quand même une plaie à maintenir, ce vieux bousin…

Tout à fait. Mais je me suis mis comme objectif d’installer le minimum de programme supplémentaire. fail2ban est déjà là pour filtrer les connexions ssh, l’étendre pouvait être fait en moins d’une heure,

Regarde crowdsec

J’adore l’idée d’une liste crowd-sourcée, un peu comme Akismet, un filtre antispam collaboratif par WordPress. Je reste sur ma problématique de ne toucher qu’au minimum à la distribution de base et installer peu de choses, mais clairement, je vais regarder pour d’autres projets que je soutiens.

À noter que dans l'installation par défaut, fail2ban peut participer et utiliser les listes partagées sur badips.com via leur API, mais à l'heure où j'écris, le serveur badips.com semble down, peut-être depuis des années. Le service semble depuis voir été remplacé par ipthreat.net, lui aussi intégré dans les options de fail2ban.

Peut-être aussi que des questions se sont posées, genre waterhole-poisonning par reporting abusif, attaques en DDoS créant des DoS locaux, et j'en passe.

Pourquoi pas Cloudflare ?

C’est une très bonne question.

  1. j’ai pas les sous, ou plutôt, je ne veux pas y mettre un budget, ni un compte gratuit ;
  2. je ne connais pas les comportements, surtout vis à vis des softs de podcasts, puisque j’héberge 150 Go de sonores ;
  3. je souhaite que mon système soit en autonomie avec la dépendance externe la plus minimale possible ;
  4. je viens à peine de me dégafamiser sur le dernier point (le proxy podcast qui était chez Feedburner), c’est pas pour retourner sur un SPOF externe sous la juridication d'une puissance militaire hostile puissament équipée. Don't feed your ennemies.

Et puis, je préfère valoriser mes skills de Président Fondateur du FFLSLS

Qui te dit que, contrairement aux attaques par ssh, tu n’es pas en train de bloquer des proxies, des VPN voire des points de sorties de Tor ?

C’est effectivement un risque, car des bots passent par ce type de service, ou des VPN gris (des VPN professionnels proposant des adresses résidentielles de la ville et du FAI de votre choix, c'est facile en ayant zombifié des millions de télés connectées), mais ce n'est pas la très grande majorité. D’où la durée limitée des bans et pas permanents. Sans compter que j'ai plusieurs fois vu des publicités servies par de grands sites, qui comportaient une charge javascript permettant d'attaquer d'autres serveurs, d'où l'intérêt des CSP/CORS dans les navigateurs web pour tenter de limiter cet… usage indirect des ressources.
À priori, si un point de sorti est grillé, les clients de VPN, les spectacteurs malheureux d'une pub-malwarisée, ou les utilisateurs de Tor peuvent demander un autre circuit pour ressortir. Ceux derrière un proxy d’entreprise peuvent toujours prendre leur smartphone personnel.

Quelle durée pour les bans ? 2 ans ?

Non, seulement 6 mois après 3 tentatives. Ne soyons pas vaches.

il existe aussi des mod security [sur Apache] qui filtre ça avec des règles connu et maintenu (qui est static), ça te fait une sorte de WAF home made.

Exact. Mais la beauté de fail2ban est qu’il est asynchrone par rapport aux process Apache, donc il ne l’impacte pas en termes de performances, et il bannit aussi les IP qui tentent les attaques sur des ports comme ssh, mysql, pgsql, mongodb, postfix, gitlab, roundcube, traefik, counter-strike et j’en passe. Une tentative d'attaque d'une IP sur un port indique déjà des intentions pas très pacifiques.
Je tiens à préciser que les adresses bannies pour attaques web sont toujours différentes de attaques ssh, ce qui peut s'expliquer par le nombre de serveurs de relais disponibles ré-utilisables directement comme reverse-proxies.
Coucou les caméras de sécurités mal sécurisées !

La consommation de CPU pour gérer les règles c’est infernal. Suis très fier de valoriser du vieux matos. Mais là franchement fail2ban j’en ai touché les limites avec cette histoire.

Pour l'instant, je touche du bois, mes indicateurs m'ont montré un gain très net en RAM et CPU, donc j'en suis très satisfait. J’en changerai sûrement, le but est de faire le moindre effort possible, le moindre impact possible sur le serveur, le moindre ajout possible de logiciel.

Pourquoi ne pas utiliser l’IA ?

Parce que c’est un moteur statistique, donc techniquement c’est de l’IA.

Je rappelle que je voulais modifier le moins possible mon serveur. Ajouter du logiciel, c'est augmenter fatalement la surface d'attaque.

Après, utiliser l'IA pour découvrir et isoler par exemple de nouveaux patterns d'attaque, et pouvoir proposer des mises à jour des règles de bannissement dans un repo git, avec un traçage d'historique et des tests automatisés de non-surblocage, ça ferait un beau projet.

Pas mal, ça gère les attaques de petit-gris ?

Je suppose que l'on peut faire de la regex unicode dans les paramètres de filtrage de fail2ban, mais n'ayant pas encore vu de tentatives, difficile d'évaluer sans test reproductible.

La discussion continuera sûrement ↓ dans les commentaires ↓ donc n'hésitez pas.

Bilan

  • Utiliser l'existant et le déjà installé
  • Pouvoir l'étendre et ajouter des paramètres par rapport à mon cas d'usage
  • Déployer un minlmum d'effort
  • Filtrer efficacement les nuisances
  • Réduire les charges CPU, RAM et réseaux de la VM
  • Documenter mon expérience
  • Soulager mes nerfs envers ces démonstration de scanners de vulnérabilités non-sollicitées

Personnellement, c'est un easy-win et ça va me resservir pour des serveurs d'associations.

Et maintenant ?

Parmi les autres possibilités parues pendant la rédaction du billet, cet article de SebSauvage

Il me reste à protéger le serveur minitel de la radio, trop librement accessible par le port 3615.