L'auteur explique comment implémenter dans HAProxy une limite de vitesse de requête (limitation de débit) avec certaines adresses IP. L'équipe Mail.ru Cloud Solutions a traduit son article - nous espérons que vous n'aurez pas à y consacrer autant de temps et d'efforts que vous ne l'avez dû.Le fait est que c'est l'une des méthodes les plus populaires pour protéger un serveur contre les attaques DoS, mais il est difficile de trouver des instructions claires sur Internet pour le configurer spécifiquement. Par essais et erreurs, l'auteur a contraint HAProxy à limiter la fréquence des requêtes sur une liste d'adresses IP, qui est mise à jour en temps réel.Aucune connaissance préalable n'est requise pour configurer HAProxy, car toutes les étapes nécessaires sont décrites ci-dessous.Open source et gratuit HAProxy est un équilibreur de charge et un serveur proxy hautement accessibles. Ces dernières années, il est devenu très populaire car il offre des performances élevées avec un minimum de ressources. Contrairement aux programmes alternatifs, la version à but non lucratif de HAProxy Community Edition offre suffisamment de fonctionnalités pour un équilibrage de charge fiable.Ce programme est au début assez difficile à comprendre. Cependant, elle dispose d'une documentation technique très méticuleuse et détaillée . L'auteur dit que c'est la documentation la plus détaillée parmi tous les programmes open source qu'il ait jamais utilisés.Voici donc un guide étape par étape.Configuration d'un équilibreur de charge
Pour gagner du temps et ne pas vous laisser distraire par la configuration de l'infrastructure, prenez les images Docker et Docker Compose et lancez rapidement les principaux composants.La première tâche consiste à augmenter l'instance de travail de l'équilibreur de charge HAProxy avec plusieurs serveurs principaux Apache.Clonez le référentiel
$ git clone git@github.com:stargazer/haproxy-ratelimiter.git
$ cd haproxy-ratelimiter
Vous pouvez consulter Dockerfile
et docker-compose.yml
avec les paramètres d'installation. Leur discussion dépasse le cadre de cet article, nous allons donc nous attarder sur le fait qu'ils ont créé une instance HAProxy fonctionnelle appelée loadbalancer
deux serveurs principaux api01
et api02
. Pour configurer HAProxy, nous allons d'abord utiliser le fichier haproxy-basic.cfg
, puis basculer vers haproxy-ratelimiting.cfg
.Par souci de simplicité, le fichier de configuration a été haproxy-basic.cfg
réduit au strict minimum et débarrassé de tout excès. Regardons ça:defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
frontend proxy
bind *:80
use_backend api
backend api
balance roundrobin
server api01 api01:80
server api02 api02:80
La section frontend proxy
définit HAProxy pour écouter sur le port 80 et transmettre toutes les demandes au pool de serveurs api
sur le backend.CATÉGORIE backend api
spécifie le pool principal api
avec deux serveurs principaux api01
et api02
et les adresses correspondantes. Le serveur pour traiter chaque demande entrante est sélectionné par l'algorithme d'équilibrage de charge roundrobin
, c'est-à-dire que les deux serveurs disponibles sont utilisés tour à tour.Lançons les trois de nos conteneurs
$ sudo docker-compose up
Nous avons maintenant un conteneur loadbalancer
qui redirige les demandes vers deux serveurs principaux api01
et serveurs api02
. Nous obtiendrons une réponse de l'un d'eux si nous entrons dans la barre d'adresse http://localhost/
.Il est intéressant de rafraîchir la page plusieurs fois et de consulter les journaux docker-compose
.api01_1 | 192.168.192.3 - - [08 / Jan / 2019: 11: 38: 09 +0000] "GET / HTTP / 1.1" 200 45
api02_1 | 192.168.192.3 - - [08 / Jan / 2019: 11: 38: 10 +0000] "GET / HTTP / 1.1" 304 -
api01_1 | 192.168.192.3 - - [08 / Jan / 2019: 11: 38: 10 +0000] "GET / HTTP / 1.1" 304 -
api02_1 | 192.168.192.3 - - [08 / Jan / 2019: 11: 38: 11 +0000] "GET / HTTP / 1.1" 304 -
api01_1 | 192.168.192.3 - - [08 / Jan / 2019: 11: 38: 11 +0000] "GET / HTTP / 1.1" 304 -
api02_1 | 192.168.192.3 - - [08 / Jan / 2019: 11: 38: 11 +0000] "GET / HTTP / 1.1" 304 -
Comme vous pouvez le voir, deux serveurs api
traitent les demandes à tour de rôle.Nous avons maintenant une instance HAProxy avec une configuration d'équilibrage de charge très simple, et nous avons une idée de son fonctionnement.Ajouter une limite au nombre de demandes
Pour définir une limite sur le nombre de demandes à l'équilibreur de charge, vous devez modifier le fichier de configuration dans l'instance HAProxy. Assurez-vous que le conteneur loadbalancer
utilise le fichier de configuration haproxy-ratelimiter.cfg
.Modifiez simplement le Dockerfile pour remplacer le fichier de configuration.FROM haproxy:1.7
COPY haproxy-ratelimiter.cfg /usr/local/etc/haproxy/haproxy.cfg
Fixer des limites
Tous les paramètres sont enregistrés dans le fichier de configuration haproxy-ratelimiter.cfg
. Étudions-le attentivement.defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
frontend proxy
bind *:80
# ACL function declarations
acl is_abuse src_http_req_rate(Abuse) ge 10
acl inc_abuse_cnt src_inc_gpc0(Abuse) gt 0
acl abuse_cnt src_get_gpc0(Abuse) gt 0
# Rules
tcp-request connection track-sc0 src table Abuse
tcp-request connection reject if abuse_cnt
http-request deny if abuse_cnt
http-request deny if is_abuse inc_abuse_cnt
use_backend api
backend api
balance roundrobin
server api01 api01:80
server api02 api02:80
backend Abuse
stick-table type ip size 100K expire 30m store gpc0,http_req_rate(10s)
HAProxy propose un ensemble de primitives de bas niveau qui offrent plus de flexibilité et conviennent à une variété de cas d'utilisation. Ses compteurs me rappellent souvent le registre cumulatif (additionneur) du CPU. Ils stockent des résultats intermédiaires, prennent différentes sémantiques en entrée, mais au final ce ne sont que des nombres. Pour bien comprendre tout, il est logique de commencer à la toute fin du fichier de configuration.Table Abuse
backend Abuse
stick-table type ip size 100K expire 30m store gpc0,http_req_rate(10s)
Ici, nous avons mis en place un backend factice appelé Abuse
(«abus»). Fictif, car il n'est utilisé que pour stick-table, auquel le reste de la configuration peut faire référence par son nom Abuse
. Un stick-table est une table stockée dans la mémoire de processus, où pour chaque enregistrement, vous pouvez déterminer la durée de vie.Notre table présente les caractéristiques suivantes:type ip
: Les demandes sont enregistrées dans le tableau par adresse IP sous forme de clé. Ainsi, les demandes provenant de la même adresse IP feront référence au même enregistrement. Essentiellement, cela signifie que nous suivons les adresses IP et les données connexes.
size 100K
: Le tableau contient un maximum de 100 000 enregistrements.
expire 30m
: La période de conservation des enregistrements est de 30 minutes d'inactivité.
store gpc0,http_req_rate(10s)
: Le compteur gpc0
et le nombre de demandes d'adresses IP pour les 10 dernières secondes sont stockés avec des entrées . Avec l'aide, gpc0
nous suivrons combien de fois l'adresse IP est remarquée dans les abus. En fait, une valeur de compteur positive signifie que l'adresse IP est déjà marquée comme suspecte. Appelons ce compteur abuse indicator
.
En général, le tableau Abuse
vous permet de suivre si l'adresse IP est marquée comme suspecte, ainsi que la fréquence actuelle des demandes de cette adresse. Par conséquent, nous avons un historique des enregistrements, ainsi que des informations en temps réel.Passons maintenant à la section frontend proxy
et voyons les nouveautés.Fonctions et règles ACL
frontend proxy
bind *:80
# ACL function declarations
acl is_abuse src_http_req_rate(Abuse) ge 10
acl inc_abuse_cnt src_inc_gpc0(Abuse) gt 0
acl abuse_cnt src_get_gpc0(Abuse) gt 0
# Rules
tcp-request connection track-sc0 src table Abuse
tcp-request connection reject if abuse_cnt
http-request deny if abuse_cnt
http-request deny if is_abuse inc_abuse_cnt
use_backend api
Les listes de contrôle d'accès (ACL) sont des déclarations de fonctions qui sont appelées uniquement lorsque la règle est définie.Examinons de plus près les trois entrées ACL. Gardez à l'esprit qu'ils font tous explicitement référence à une table Abuse
qui utilise des adresses IP comme clé, de sorte que chaque fonction s'applique à l'adresse IP de la demande:acl is_abuse src_http_req_rate(Abuse) ge 10
: La fonction is_abuse
retourne True
si la fréquence de requête actuelle est supérieure ou égale à dix.
acl inc_abuse_cnt src_inc_gpc0(Abuse) gt 0
: La fonction inc_abuse_cnt
renvoie True
si la valeur d'incrément est gpc0
supérieure à zéro. Puisque la valeur initiale gpc0
est zéro, cette fonction retourne toujours True
. En d'autres termes, il augmente la valeur abuse indicator
, signalant essentiellement un abus de cette adresse IP.
acl abuse_cnt src_get_gpc0(Abuse) gt 0
: La fonction abuse_cnt
renvoie True
si la valeur est gpc0
supérieure à zéro. En d'autres termes, il dit si cette adresse IP a déjà été repérée dans des abus.
Comme mentionné précédemment, les ACL sont simplement des déclarations, c'est-à-dire des déclarations de fonctions. Ils ne s'appliquent pas aux demandes entrantes tant qu'une règle n'est pas déclenchée.Il est logique de regarder les règles définies dans la même section frontend
. Les règles sont appliquées à chaque demande entrante une par une - et exécutent les fonctions de l'ACL que nous venons de définir.Voyons ce que fait chaque règle:tcp-request connection track-sc0 src table Abuse
: Ajoute une requête à la table Abuse
. Étant donné que la clé est l'adresse IP dans le tableau, cette règle s'ajoute à la liste des adresses IP.
tcp-request connection reject if abuse_cnt
: TCP-, IP- , abuse. , TCP- IP-.
http-request deny if abuse_cnt
: , IP- . IP-, abuse.
http-request deny if is_abuse inc_abuse_cnt
: , is_abuse
inc_abuse_cnt
True
. , , IP- , IP- .
En substance, nous introduisons deux types de contrôles: en temps réel et dans la liste noire de l'historique des requêtes. La deuxième règle rejette toutes les nouvelles connexions TCP si l'adresse IP a été remarquée lors d'abus. La troisième règle interdit le service des requêtes HTTP pour une adresse IP de la liste noire, quelle que soit la fréquence actuelle des requêtes de cette adresse. La quatrième règle garantit que les demandes HTTP provenant d'une adresse IP sont rejetées au moment même dès que le seuil de fréquence des demandes est dépassé. Ainsi, la deuxième règle fonctionne principalement sur les nouvelles connexions TCP, la troisième et la quatrième sur les connexions déjà établies, la première étant une vérification historique et la seconde une vérification en temps réel.Essayons le filtre en action!
Nous pouvons maintenant assembler et lancer à nouveau nos conteneurs.$ sudo docker-compose down
$ sudo docker-compose build
$ sudo docker-compose up
L'équilibreur de charge doit démarrer avant les deux serveurs principaux.Dirigeons notre navigateur vers http://localhost/
. Si nous actualisons rapidement la page une douzaine de fois, nous dépasserons le seuil de dix demandes dans un intervalle de dix secondes - et nos demandes seront rejetées. Si nous continuons à actualiser la page, les nouvelles demandes seront rejetées immédiatement - avant même que la connexion TCP ne soit établie.Des questions
Pourquoi la limite de dix demandes par dix secondes?
Le tableau Abuse
détermine http_req_rate(10s)
, c'est-à-dire que la fréquence des demandes est mesurée dans une fenêtre de dix secondes. Une fonction is_abuse
de l'ACL revient True
à un taux de demande ≥10 dans l'intervalle spécifié. Ainsi, l'abus est considéré comme la fréquence des demandes de dix demandes ou plus en dix secondes.Dans cet article, par exemple, nous avons décidé de définir une limite basse pour faciliter le contrôle du fonctionnement du limiteur.Quelle est la différence entre les règles de connexion http-request et tcp-request?
De la documentation :http-request: l'instruction http-request définit un ensemble de règles qui s'appliquent à la couche réseau 7 [modèle OSI]
De la documentation :connexion tcp-request: exécution d'une action sur une connexion entrante en fonction d'une condition au niveau de la couche réseau 4
HTTP-, TCP-?
Imaginez que les requêtes HTTP au serveur envoient plusieurs connexions TCP à partir de la même adresse IP. La fréquence des requêtes HTTP dépassera rapidement les seuils. C'est alors que la quatrième règle entre en vigueur, qui rejette les demandes et met l'adresse IP sur liste noire.Maintenant, il est tout à fait possible que les connexions HTTP à partir de la même adresse IP restent ouvertes (voir connexion HTTP persistante ), et la fréquence des demandes HTTP est tombée en dessous de la valeur seuil. La troisième règle garantit le blocage continu des requêtes HTTP, car il est abuse indicator
déclenché sur cette IP.Supposons maintenant qu'après quelques minutes, la même IP tente d'établir des connexions TCP. Ils sont abandonnés immédiatement, car la deuxième règle s'applique: il voit l'adresse IP étiquetée et abandonne immédiatement la connexion.Conclusion
Au début, il peut être difficile de comprendre la limitation de la vitesse de traitement des demandes à l'aide de HAProxy. Pour tout faire correctement, vous avez besoin d'une réflexion assez "de bas niveau" et d'un certain nombre d'actions non intuitives. La documentation de cette partie est probablement trop technique et souffre de l'absence d'exemples de base. Nous espérons que ce guide comblera la lacune et montrera la direction à tous ceux qui souhaitent emprunter cette voie.Quoi d'autre à lire :- Comment l'architecture tolérante aux pannes est implémentée dans la plateforme Mail.ru Cloud Solutions .
- Les 10 meilleurs trucs et astuces de Kubernetes .
- Notre chaîne Telegram sur la transformation numérique .