Nginx : protection contre les attaques DoS

Nginx est un serveur WEB de plus en plus prisé.
Parmi ces nombreuses fonctionnalités, il existe des parades et protection contre les attaques DoS.

Voici quelques réglages qui peuvent vous permettre d’éviter la saturation de votre serveur WEB.

Introduction

Pour rappel, il existe de nombreuses techniques et attaques DoS différentes.
Parmi ces attaques, on trouve les attaques L7 ou HTTP Flood.
Celle-ci consiste à envoyer des requêtes HTTP sur différentes pages si possible qui utilise le plus de ressources.
Cela peut conduire à saturer le serveur WEB voire le serveur dans son entier

Nginx propose plusieurs protections contre ce type d’attaque qui peuvent être activé dans la configuration du serveur WEB.
Ces protections visent à limiter le nombre de requêtes ou de connexions concurrentes pour une même adresse IP.

Pour se protéger de ces attaques, Nginx propose la directive limit_req.

Protection Nginx

limit_req_zone

Dans un premier temps, il faut créer une zone dans le contexte http à l’aide la directive limit_req_zone.
Voici la syntaxe de la directive limit_req_zone.

Syntax: 	limit_req_zone key zone=name:size rate=rate [sync];
Default: 	—
Context: 	http

Imaginons que vous souhaitez limiter le nombre de requêtes sur une adresse IP d’un client.
Voici la syntaxe que l’on peut utiliser

limit_req_zone $binary_remote_addr zone=flood:10m rate=5r/s;
  • $binary_remote_addr correspond aux adresses IP des clients qui se connectent à votre serveur WEB Nginx. Vous pouvez utiliser les variables $http_x_forwarded_for mais cela n’est pas recommandé.
  • zone est le nom de la zone que vous donnez à votre directive limit_req_zone, ici, flood
  • 10m correspond à la quantité de mémoire qui peuvent être utilisées pour stocker les adresses IP. 16,000 adresse IP utilisent environ 1 Mo, donc ici on peut en stocker 160 000.
  • enfin rate correspond au nombre de requêtes autorisées. Dans cet exemple 1 requête par seconde. On peut utiliser m pour les minutes.

limit_req

Ensuite, il faut utiliser la directive limit_req dans les blocs location ou server utilisant la zone précédemment créée.
La syntaxe est la suivante :

Syntax: 	limit_req zone=name [burst=number] [nodelay | delay=number];
Default: 	—
Context: 	http, server, location

Par exemple si on veut protéger une page login des attaques :

limit_req_zone $binary_remote_addr zone=flood:10m rate=5r/s;

location /login/ {
    limit_req zone=flood burst=12 delay=8;
    proxy_pass http://my_upstream;
}

Dans cet exemple, on charge la zone flood à 5 requêtes par seconde avec un burst à 20 et un delay de 8.
Cela signifie que l’on n’autorise que 12 requêtes.
Ainsi au delà des 8 requêtes, on impose un délai afin de ne pas dépasser la limite des 5 requêtes par seconde.
Si on ne souhaite pas imposer de délai, il faut utiliser le paramètre nodelay.

L’image suivante illustre le fonctionnement.

Les 8 premières demandes (la valeur de délai) sont envoyées sans délai par Nginx. Les 4 demandes suivantes (rafale – délai) sont retardées afin que le débit défini de 5 tr / s ne soit pas dépassé. Les 3 demandes suivantes sont rejetées car la taille totale de la rafale a été dépassée. Les demandes suivantes sont retardées.

Le paramètre delay se règle en fonction du nombre d’éléments chargés par page.
En général, pour la majorité des sites, il est autour de 12.

Enfin, lorsque le client dépasse la limite, une erreur 503 est renvoyée (paramétrable) ainsi que la génération de logs suivants.

11855#0: *5728 limiting requests, excess: 10.790 by zone "flood", client: xx.xx.xx.xx, server: forum.malekal.com, request: "GET /viewtopic.php?f=2 HTTP/1.0", host: "forum.malekal.com"

limit_conn conn_limit_per_ip

limit_conn conn_limit_per_ip est une autre directive qui peut-être déclaré dans les bloc server, location.
Celle-ci permet de limiter le nombre de connexion par IP.

Par exemple pour limiter le nombre de connexion par IP à 30 :

limit_conn conn_limit_per_ip 30

Au delà du nombre de connexions concurrentes autorisées alors Nginx retourne une erreur 503.

Fail2ban : protection avec iptables

Fail2ban est un daemon présent sur la plupart des distributions qui peut être utilisé en plus des protections Nginx.
Ce daemon peut lire les logs Nginx pour détecter les erreur 503 et au bout d’un certains nombres d’erreur générer une règle iptables pour bloquer complètement l’adresse IP.

Cela peut être utile durant des attaques afin de protéger le serveur WEB en amont.

Installer le daemon Fail2ban avec les packages de votre distributions.
Ici les chemins sont relatives à une Debian.

Par défaut, il n’existe pas de filtre pour lire le rate-limit de nginx.
Vous devez donc créer le fichier /etc/fail2ban/filter.d/nginx-req-limit.conf avec le contenu suivant :

[Definition]
failregex = limiting requests, excess:.* by zone.*client:
ignoreregex =

Ensuite dans /etc/fail2ban/jail.conf créé le contenu suivant.

[nginx-req-limit]
enabled = true
filter = nginx-req-limit
#action = iptables-multiport[name=ReqLimit, port="http,https", protocol=tcp]
action   = iptables-ipset-proto4[name=fail2ban-nginx, port="http,https", protocol=tcp, bantime=0]
logpath = /var/log/nginx/error.log
findtime = 600
bantime = 7200
maxretry = 4

Ici on définit donc le bloc nginx-req limite avec

  • le chemin du log error de nginx avec logpatch
  • L’action à mener. Par défaut, c’est iptables qui est utilisé mais on peut créer une règle ipset beaucoup plus flexibles en cas d’attaques importantes.
  • maxretry indique à partir de combien de ligne présente dans les logs, on créé le ban.
  • Enfin bantime indique le temps de « bannissement » où la règle ipset sera présente. Fail2ban va automatiquement supprimer l’adresse IP.

Cette action ipset utilise le fichier /etc/fail2ban/action.d/iptables-ipset-proto4.conf présent par défaut sur Debian.

Enfin une fois le tout paramétré, il ne reste plus qu’à tester.
Vous pouvez dans un premier temps vérifier que tout est correct avec cette commande debug.

fail2ban-regex /var/log/nginx/example.com.error.log  /etc/fail2ban/filter.d/nginx-req-limit.conf

Pour vérifier le statut, il faudra utiliser la commande suivante.

fail2ban-client status nginx-req-limit

Enfin, les logs fail2ban se trouvent dans /var/log/fail2ban.log

Démonstration en vidéo


(Visité 335 fois, 2 visites ce jour)
Noter cet article

Add Comment