Cómo limitar la frecuencia de las solicitudes en HAProxy: instrucciones paso a paso


El autor explica cómo implementar en el límite de velocidad de consulta HAProxy (limitación de velocidad) con ciertas direcciones IP. El equipo de Mail.ru Cloud Solutions tradujo su artículo; esperamos que con él no tenga que gastar tanto tiempo y esfuerzo en ello como lo tuvo que gastar.

El hecho es que este es uno de los métodos más populares para proteger un servidor de ataques DoS, pero es difícil encontrar instrucciones claras en Internet sobre cómo configurarlo específicamente. Por prueba y error, el autor obligó a HAProxy a limitar la frecuencia de las solicitudes en una lista de direcciones IP, que se actualiza en tiempo real.

No se requieren conocimientos previos para configurar HAProxy, ya que todos los pasos necesarios se describen a continuación.

El código abierto y HAProxy gratuito es un equilibrador de carga y un servidor proxy muy accesibles. En los últimos años, se ha vuelto muy popular porque proporciona un alto rendimiento con un mínimo de recursos. A diferencia de los programas alternativos, la versión sin fines de lucro de HAProxy Community Edition ofrece suficientes funciones para un equilibrio de carga confiable.

Al principio, este programa es bastante difícil de entender. Sin embargo, ella tiene documentación técnica muy meticulosa y detallada . El autor dice que esta es la documentación más detallada entre todos los programas de código abierto que ha usado.
Entonces, aquí hay una guía paso a paso.

Configurar un equilibrador de carga


Para ahorrar tiempo y no distraerse con la configuración de la infraestructura, tome las imágenes Docker y Docker Compose e inicie rápidamente los componentes principales.

La primera tarea es elevar la instancia de trabajo del equilibrador de carga HAProxy con varios servidores de fondo de Apache.

Clonar el repositorio


$ git clone git@github.com:stargazer/haproxy-ratelimiter.git
$ cd haproxy-ratelimiter

Puede mirar Dockerfiley docker-compose.ymlcon los parámetros de instalación. Su discusión está más allá del alcance de este artículo, así que centrémonos en el hecho de que crearon una instancia de HAProxy en funcionamiento llamada loadbalancerdos servidores de fondo api01y api02. Para configurar HAProxy, inicialmente usaremos el archivo haproxy-basic.cfgy luego cambiaremos a haproxy-ratelimiting.cfg.

Para simplificar, el archivo de configuración se ha haproxy-basic.cfgreducido al mínimo y se ha eliminado el exceso. Veámoslo:

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 sección frontend proxyconfigura HAProxy para escuchar en el puerto 80 y reenviar todas las solicitudes al grupo de servidores en el servidor api.

CATEGORY backend apiespecifica el grupo de apiback- end con dos servidores de fondo api01y las api02direcciones correspondientes. El servidor para atender cada solicitud entrante se selecciona mediante el algoritmo de equilibrio de carga roundrobin, es decir, de hecho, los dos servidores disponibles se utilizan a su vez.

Vamos a lanzar los tres contenedores.


$ sudo docker-compose up

Ahora tenemos un contenedor loadbalancerque redirige las solicitudes a dos servidores api01y backend api02. Recibiremos una respuesta de uno de ellos si ingresamos en la barra de direcciones http://localhost/.

Es interesante actualizar la página varias veces y ver los registros docker-compose.

api01_1 | 192.168.192.3 - - [08 / Ene / 2019: 11: 38: 09 +0000] "GET / HTTP / 1.1" 200 45
api02_1 | 192.168.192.3 - - [08 / Ene / 2019: 11: 38: 10 +0000] "GET / HTTP / 1.1" 304 -
api01_1 | 192.168.192.3 - - [08 / Ene / 2019: 11: 38: 10 +0000] "GET / HTTP / 1.1" 304 -
api02_1 | 192.168.192.3 - - [08 / Ene / 2019: 11: 38: 11 +0000] "GET / HTTP / 1.1" 304 -
api01_1 | 192.168.192.3 - - [08 / Ene / 2019: 11: 38: 11 +0000] "GET / HTTP / 1.1" 304 -
api02_1 | 192.168.192.3 - - [08 / Ene / 2019: 11: 38: 11 +0000] "GET / HTTP / 1.1" 304 -

Como puede ver, dos servidores apiprocesan las solicitudes a su vez.

Ahora tenemos una instancia de HAProxy con una configuración de equilibrio de carga muy simple, y tenemos una idea de cómo funciona.

Agregar un límite en el número de solicitudes


Para establecer un límite en el número de solicitudes al equilibrador de carga, debe modificar el archivo de configuración en la instancia de HAProxy. Asegúrese de que el contenedor loadbalanceruse el archivo de configuración haproxy-ratelimiter.cfg.

Simplemente modifique el Dockerfile para reemplazar el archivo de configuración.

FROM haproxy:1.7
COPY haproxy-ratelimiter.cfg /usr/local/etc/haproxy/haproxy.cfg

Establecer límites


Todos los ajustes se registran en el archivo de configuración haproxy-ratelimiter.cfg. Estudiemoslo cuidadosamente.

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 ofrece un conjunto de primitivas de bajo nivel que proporcionan más flexibilidad y son adecuadas para una variedad de casos de uso. Sus contadores a menudo me recuerdan el registro acumulativo (sumador) en la CPU. Almacenan resultados intermedios, toman diferentes semánticas como entrada, pero al final son solo números. Para entender todo bien, tiene sentido comenzar desde el final del archivo de configuración.

Mesa Abuse


backend Abuse
stick-table type ip size 100K expire 30m store gpc0,http_req_rate(10s)


Aquí configuramos un backend ficticio llamado Abuse("abuso"). Ficticio, porque se usa solo para la tabla fija, a la que el resto de la configuración puede referirse por su nombre Abuse. Una tabla fija es una tabla almacenada en la memoria del proceso, donde para cada registro puede determinar la duración.

Nuestra mesa tiene las siguientes características:

  • type ip: Las solicitudes se guardan en la tabla mediante la dirección IP como clave. Por lo tanto, las solicitudes de la misma dirección IP se referirán al mismo registro. Esencialmente, esto significa que estamos rastreando direcciones IP y datos relacionados.
  • size 100K: La tabla contiene un máximo de 100 mil registros.
  • expire 30m: El período de retención de registros es de 30 minutos de inactividad.
  • store gpc0,http_req_rate(10s): El contador gpc0y el número de solicitudes de dirección IP de los últimos 10 segundos se almacenan con entradas . Con la ayuda, realizaremos gpc0un seguimiento de cuántas veces se detecta la dirección IP en los abusos. De hecho, un valor de contador positivo significa que la dirección IP ya está marcada como sospechosa. Llamemos a este contador abuse indicator.

En general, la tabla le Abusepermite rastrear si la dirección IP está marcada como sospechosa, así como la frecuencia actual de solicitudes de esta dirección. Por lo tanto, tenemos un historial de registros, así como información en tiempo real.

Ahora pasemos a la sección frontend proxyy veamos qué hay de nuevo allí.

Funciones y reglas de 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

Las listas de control de acceso (ACL) son declaraciones de funciones que se invocan solo cuando se establece la regla.

Echemos un vistazo más de cerca a las tres entradas de ACL. Tenga en cuenta que todos hacen referencia explícita a una tabla Abuseque usa direcciones IP como clave, por lo que cada función se aplica a la dirección IP de solicitud:

  • acl is_abuse src_http_req_rate(Abuse) ge 10: La función is_abuseregresa Truesi la frecuencia de solicitud actual es mayor o igual a diez.
  • acl inc_abuse_cnt src_inc_gpc0(Abuse) gt 0: La función inc_abuse_cntregresa Truesi el valor del incremento es gpc0mayor que cero. Como el valor inicial gpc0es cero, esta función siempre regresa True. En otras palabras, aumenta el valor abuse indicator, esencialmente indicando abuso de esta dirección IP.
  • acl abuse_cnt src_get_gpc0(Abuse) gt 0: La función abuse_cntregresa Truesi el valor es gpc0mayor que cero. En otras palabras, dice que si esta dirección IP ya ha sido detectada en abusos.

Como se mencionó anteriormente, las ACL son simplemente declaraciones, es decir, declaraciones de funciones. No se aplican a las solicitudes entrantes hasta que se active alguna regla.

Tiene sentido mirar las reglas definidas en la misma sección frontend. Las reglas se aplican a cada solicitud entrante una por una, y ejecutan las funciones desde la ACL que acabamos de definir.

Veamos qué hace cada regla:

  • tcp-request connection track-sc0 src table Abuse: Agrega una consulta a la tabla Abuse. Como la clave es la dirección IP en la tabla, esta regla se agrega a la lista de direcciones 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 esencia, presentamos dos tipos de comprobaciones: en tiempo real y en la lista negra del historial de consultas. La segunda regla rechaza todas las conexiones TCP nuevas si la dirección IP se ha notado en abusos. La tercera regla prohíbe el servicio de solicitudes HTTP para una dirección IP de la lista negra, independientemente de la frecuencia actual de solicitudes de esta dirección. La cuarta regla asegura que las solicitudes HTTP de una dirección IP sean rechazadas en el mismo momento tan pronto como se supere el umbral para la frecuencia de la solicitud. Por lo tanto, la segunda regla funciona principalmente en nuevas conexiones TCP, la tercera y la cuarta en conexiones ya establecidas, la primera es una verificación histórica y la segunda una verificación en tiempo real.

¡Probemos el filtro en acción!


Ahora podemos ensamblar y lanzar nuestros contenedores nuevamente.

$ sudo docker-compose down
$ sudo docker-compose build
$ sudo docker-compose up

El equilibrador de carga debe comenzar antes que los dos servidores de fondo.

Dirijamos nuestro navegador a http://localhost/. Si actualizamos rápidamente la página una docena de veces, superaremos el umbral de diez solicitudes en un intervalo de diez segundos, y nuestras solicitudes serán rechazadas. Si continuamos actualizando la página, las nuevas solicitudes serán rechazadas inmediatamente, incluso antes de que se establezca la conexión TCP.

Preguntas


¿Por qué el límite de diez solicitudes por cada diez segundos?


La tabla Abusedetermina http_req_rate(10s), es decir, la frecuencia de las solicitudes se mide en una ventana de diez segundos. Una función is_abusede la ACL regresa Truea una tasa de solicitud de ≥10 dentro del intervalo especificado. Por lo tanto, el abuso se considera la frecuencia de solicitudes de diez o más solicitudes en diez segundos.

En este artículo, por ejemplo, decidimos establecer un límite bajo para que sea más fácil verificar el funcionamiento del limitador.

¿Cuál es la diferencia entre las reglas de conexión http-request y tcp-request?


De la documentación :

http-request: la instrucción http-request define un conjunto de reglas que se aplican en la capa de red 7 [modelo OSI]

De la documentación :
tcp-request connection: realizar una acción en una conexión entrante dependiendo de una condición en la capa de red 4

HTTP-, TCP-?


Imagine que las solicitudes HTTP al servidor envían múltiples conexiones TCP desde la misma dirección IP. La frecuencia de las solicitudes HTTP superará rápidamente los umbrales. Es entonces cuando entra en vigor la cuarta regla, que descarta las solicitudes y pone en la lista negra la dirección IP.

Ahora es completamente posible que las conexiones HTTP desde la misma dirección IP permanezcan abiertas (ver conexión HTTP persistente ), y la frecuencia de las solicitudes HTTP ha caído por debajo de un valor umbral. La tercera regla garantiza el bloqueo continuo de las solicitudes HTTP, ya que se abuse indicatoractiva en esta IP.

Ahora suponga que después de unos minutos la misma IP intenta establecer conexiones TCP. Se descartan de inmediato, porque se aplica la segunda regla: ve la dirección IP etiquetada e inmediatamente descarta la conexión.

Conclusión


Al principio, puede ser difícil comprender la limitación de la velocidad de procesamiento de solicitudes utilizando HAProxy. Para hacer todo bien, necesita un pensamiento bastante "de bajo nivel" y una serie de acciones poco intuitivas. La documentación en esta parte es probablemente demasiado técnica y adolece de la ausencia de ejemplos básicos. Esperamos que esta guía compense la deficiencia y muestre la dirección a todos los que quieran tomar este camino.

¿Qué más leer ?

  1. Cómo se implementa la arquitectura tolerante a fallas en la plataforma Mail.ru Cloud Solutions .
  2. Los 10 mejores trucos y consejos de Kubernetes .
  3. Nuestro canal de Telegram sobre transformación digital .

All Articles