Fail2ban et reverse proxy : c’est possible !

closeCet article a été publié il y a 4 ans 10 mois 6 jours, il est donc possible qu’il ne soit plus à jour. Les informations proposées sont donc peut-être expirées.

Je l’ai dit à maintes reprises, Fail2ban est un outil très intéressant quand on peut l’installer et l’utiliser, et je le recommande dès que possible. Dans le cadre de l’analyse du trafic d’un serveur web toutefois, il y a un gros souci qui survient quand on a une architecture qui va au-delà du simple serveur Apache ou Nginx. Explications.

Dernièrement, un client s’est fait bombarder son site de vente de produits « bios », avec un modèle d’attaque qui semblait simple, mais pas facile à contrer, à savoir tabasser les pages du site et les recherches de produits. Dans un cas comme celui-là, on propose soit mod_evasive, soit fail2ban, soit mod_security, chacun ayant ses forces et ses faiblesses.

Ici, le problème principal vient du fait que le trafic ne parvient pas directement au serveur : en effet, le client fait appel à un CDN, Cloudfront en l’occurrence, et donc le serveur web ne voit techniquement que le trafic des instances Cloudfront par lesquelles passent les visiteurs, qu’ils soient légitimes ou pas.

Petit schéma simplifié de l’architecture du client

Donc au niveau d’Apache, ce qu’on voit, ce sont les IPs CloudFront et pas les IPs clients, qui sont déportées dans un entête HTTP X-Forwarded-For (« XFF »). Compliqué donc pour Fail2ban d’ajouter des règles iptables sur des ips qui ne seront jamais vues par le noyau. Dans un cas alternatif, on pourrait modifier l’action par défaut pour transformer ça en ajout dynamique d’une règle « Deny from  » avec un trigger sur XFF, mais le fichier htaccess est activement utilisé déjà par le moteur du site (Prestashop), le risque de dysfonctionnement est donc trop grand.

Netfilter est un petit malin

Ici c’est un CDN, mais la réflexion reste la même quand le serveur web se trouve derrière n’importe quel reverse-proxy/load-balancer/intermédiaire, tant qu’on a l’entête XFF, on peut agir. Ici, le site est en HTTPS, mais la terminaison est effectuée sur CloudFront, et le trafic repart en HTTP derrière, ce qui peut nous servir.

En effet, en cherchant une solution, j’ai découvert que via iptables, on pouvait définir des règles en fonction de la détection de chaines de caractères. Netfilter sait faire de l’inspection de paquets ! Ça ne fonctionnera qu’en HTTP 1.X évidemment, mais c’est une excellente nouvelle pour nous. Pour procéder, il faut utiliser iptables de cette manière :

Évidemment, vous pouvez adapter la règle à vos besoins, vous avez compris que c’est le –string qui est le plus important. Je ne connais pas l’incidence de l’algorithme sur l’effectivité ou les performances par contre.

Il est donc possible de modifier les actions de fail2ban pour déclencher un drop sur la base du XFF. Pour les détails, je vous laisse consulter cet article en anglais, mais grosso modo :

  • S’assurer que l’adresse IP de l’entête XFF soit bien identifiée dans les logs du serveur web
  • Créer le filtre qui correspond à l’entête identifié dans les logs
  • Créer l’action qui correspondra à la commande iptables ad-hoc
  • Créer la jail qui fait le lien entre le filtre et l’action

Une solution qui a ses limites

Comme toute solution à un problème particulier d’ailleurs. En effet, d’une part, comme je l’ai dit ça implique que le protocole soit « en clair » ce qui est le cas d’HTTP 1.1, HTTP 1.0, mais pas HTTP 2, donc si vous avez du HTTP 2 de bout en bout, c’est cuit, il faudra procéder autrement.

Également, l’entête X-Forwarded-For n’est pas aussi fiable que l’adresse IP dans le paquet TCP. En effet, comme tout entête HTTP, il peut être abusé avant même d’être modifié par votre proxy (Cloudfront dans mon exemple), ce qui veut dire qu’il peut y avoir de fausses IPs dedans en plus de la vraie. On pourrait donc imaginer une sorte d’attaque pour bloquer certains visiteurs en ajoutant leur IP à une salve de requêtes avant même qu’ils essaient de visiter le site.

Pire, au moment de proposer cette solution, le modèle d’attaque avait changé : il n’y a plus une seule IP qui tabasse, mais une chiée plus quinze, qui font une requête à chaque fois avant de « changer ». En filtrant/comptant/classant les IPs, on constate que certaines sont très proches les unes des autres, proviennent d’hébergeurs étrangers et donc pas de vrais clients, ce qui laisse penser à une attaque distribuée via des sites compromis. Fail2ban et iptables sont donc compliqués à exploiter dans ce cas, car qualifier une plage d’ip comme provenant à coup sur d’un hébergeur non légitime et pas un vrai client potentiel est compliqué pour un robot. Idem pour la provenance géographique, voir du trafic arriver d’un hébergeur roumain sur un site de produits bios produits et vendus en France est simple à voir pour un humain, mais pour un script par contre…

De la même façon qu’on ne le verra pas avec HTTP 2.0, désormais, avec la majorité des sites reposant sur HTTPS, netfilter ne peut plus exploiter l’inspection pour détecter d’autres modèles, je pense par exemple à un blocage bas niveau de crawlers malveillants tout juste bon à saturer les sites de requêtes sans plus de ménagement; dans ce cas, seul le serveur web restera une barrière fonctionnelle, au prix d’une consommation de ressources un poil plus élevée (filtrage via mod_rewrite ou mod_setenvif).

Malgré tout, cette fonctionnalité d’iptables que je ne connaissais pas reste intéressante je pense, et je pensais qu’il était utile de la partager avec vous.

1 Commentaire
Le plus ancien
Le plus récent
Commentaires en ligne
Afficher tous les commentaires
Fabien
Fabien
25/05/2019 16:57

Merci pour la découverte de l’option d’iptables.
J’aurais nommé l’article « Fail2ban + CDN c’est possible » car avec un RP, on aurait positionné fail2ban sur celui-ci.