Como limitar a frequência de solicitações no HAProxy: instruções passo a passo


O autor explica como implementar no limite de velocidade da consulta HAProxy ( limite de taxa) com um determinado endereço IP. A equipe do Mail.ru Cloud Solutions traduziu seu artigo - esperamos que, com ele, você não precise gastar tanto tempo e esforço com ele quanto gastou.

O fato é que esse é um dos métodos mais populares de proteger um servidor contra ataques de negação de serviço, mas é difícil encontrar instruções claras na Internet sobre como configurá-lo especificamente. Por tentativa e erro, o autor forçou o HAProxy a limitar a frequência de solicitações em uma lista de endereços IP, que são atualizados em tempo real.

Nenhum conhecimento prévio é necessário para configurar o HAProxy, pois todas as etapas necessárias são descritas abaixo.

O HAProxy de código aberto e gratuito é um balanceador de carga e servidor proxy altamente acessíveis. Nos últimos anos, tornou-se muito popular porque oferece alto desempenho com um mínimo de recursos. Diferentemente de programas alternativos, a versão sem fins lucrativos do HAProxy Community Edition oferece recursos suficientes para balanceamento de carga confiável.

Este programa é inicialmente muito difícil de entender. No entanto, ela possui documentação técnica muito meticulosa e detalhada . O autor diz que esta é a documentação mais detalhada entre todos os programas de código aberto que ele já usou.
Então, aqui está um guia passo a passo.

Configurando um Balanceador de Carga


Para economizar tempo e não se distrair com a configuração da infraestrutura, pegue as imagens do Docker e do Docker Compose e inicie rapidamente os componentes principais.

A primeira tarefa é aumentar a instância de trabalho do balanceador de carga HAProxy com vários servidores de backend Apache.

Clonar o repositório


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

Você pode ver Dockerfilee docker-compose.ymlcom os parâmetros de instalação. A discussão está além do escopo deste artigo, então vamos insistir no fato de que eles criaram uma instância HAProxy trabalhando chamado loadbalancerdois servidores de back-end api01e api02. Para configurar o HAProxy, inicialmente usaremos o arquivo haproxy-basic.cfge depois mudaremos para haproxy-ratelimiting.cfg.

Por simplicidade, o arquivo de configuração foi haproxy-basic.cfgreduzido ao mínimo e limpo de excesso. Vejamos:

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

A seção frontend proxydefine o HAProxy para escutar na porta 80 e encaminhar todas as solicitações para o pool de servidores apino back-end.

CATEGORIA backend apiespecifica backend piscina apicom dois servidores de back-end api01e api02e endereços correspondentes. O servidor para atender a cada solicitação recebida é selecionado pelo algoritmo de balanceamento de carga roundrobin, ou seja, os dois servidores disponíveis são usados ​​alternadamente.

Vamos lançar todos os três de nossos contêineres


$ sudo docker-compose up

Agora temos um contêiner loadbalancerque redireciona solicitações para dois servidores api01e back - end api02. Obteremos uma resposta de um deles se inserirmos na barra de endereço http://localhost/.

É interessante atualizar a página várias vezes e ver os logs 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 -

Como você pode ver, dois servidores apiprocessam solicitações por vez.

Agora temos uma instância HAProxy com uma configuração de balanceamento de carga muito simples e temos alguma idéia de como ela funciona.

Adicione um limite ao número de solicitações


Para definir um limite no número de solicitações para o balanceador de carga, é necessário modificar o arquivo de configuração na instância HAProxy. Verifique se o contêiner loadbalancerusa o arquivo de configuração haproxy-ratelimiter.cfg.

Basta modificar o Dockerfile para substituir o arquivo de configuração.

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

Definindo limites


Todas as configurações são registradas no arquivo de configuração haproxy-ratelimiter.cfg. Vamos estudá-lo com cuidado.

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)

O HAProxy oferece um conjunto de primitivas de baixo nível que fornecem mais flexibilidade e são adequadas para uma variedade de casos de uso. Seus contadores geralmente me lembram o registro acumulativo (somador) na CPU. Eles armazenam resultados intermediários, usam semânticas diferentes como entrada, mas no final são apenas números. Para entender tudo bem, faz sentido começar do final do arquivo de configuração.

Mesa Abuse


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


Aqui, montamos um back-end fictício chamado Abuse("abuso"). Fictício, porque é usado apenas para stick-table, ao qual o restante da configuração pode se referir por nome Abuse. Uma tabela de palitos é uma tabela armazenada na memória do processo, onde para cada registro você pode determinar a vida útil.

Nossa tabela possui as seguintes características:

  • type ip: As solicitações são salvas na tabela pelo endereço IP como uma chave. Portanto, solicitações do mesmo endereço IP se referirão ao mesmo registro. Essencialmente, isso significa que estamos rastreando endereços IP e dados relacionados.
  • size 100K: A tabela contém um máximo de 100 mil registros.
  • expire 30m: O período de retenção de registros é de 30 minutos de inatividade.
  • store gpc0,http_req_rate(10s): O contador gpc0e o número de solicitações de endereço IP dos últimos 10 segundos são armazenados com entradas . Com a ajuda, gpc0rastrearemos quantas vezes o endereço IP é observado em abusos. De fato, um valor de contador positivo significa que o endereço IP já está marcado como suspeito. Vamos ligar para este contador abuse indicator.

Em geral, a tabela Abusepermite rastrear se o endereço IP está marcado como suspeito, bem como a frequência atual de solicitações desse endereço. Portanto, temos um histórico de registros, bem como informações em tempo real.

Agora vamos para a seção frontend proxye veja as novidades lá.

Funções e regras da 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

Listas de controle de acesso (ACLs) são declarações de função que são chamadas somente quando a regra é definida.

Vamos dar uma olhada em todas as três entradas da ACL. Lembre-se de que todos fazem referência explícita a uma tabela Abuseque usa endereços IP como chave; portanto, cada função se aplica ao endereço IP da solicitação:

  • acl is_abuse src_http_req_rate(Abuse) ge 10: A função is_abuseretornará Truese a frequência atual da solicitação for maior ou igual a dez.
  • acl inc_abuse_cnt src_inc_gpc0(Abuse) gt 0: A função inc_abuse_cntretornará Truese o valor do incremento for gpc0maior que zero. Como o valor inicial gpc0é zero, essa função sempre retorna True. Em outras palavras, aumenta o valor abuse indicator, essencialmente sinalizando abuso a partir deste endereço IP.
  • acl abuse_cnt src_get_gpc0(Abuse) gt 0: A função abuse_cntretornará Truese o valor for gpc0maior que zero. Em outras palavras, ele diz se esse endereço IP já foi detectado em abusos.

Como mencionado anteriormente, as ACLs são simplesmente declarações, ou seja, declarações de função. Eles não se aplicam a solicitações de entrada até que alguma regra seja acionada.

Faz sentido olhar para as regras definidas na mesma seção frontend. As regras são aplicadas a cada solicitação recebida uma por uma - e executam as funções da ACL que acabamos de definir.

Vamos ver o que cada regra faz:

  • tcp-request connection track-sc0 src table Abuse: Adiciona uma consulta à tabela Abuse. Como a chave é o endereço IP na tabela, essa regra é adicionada à lista de endereços 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- .

Em essência, introduzimos dois tipos de verificações: em tempo real e na lista negra do histórico de consultas. A segunda regra rejeita todas as novas conexões TCP se o endereço IP foi observado em abusos. A terceira regra proíbe o serviço de solicitações HTTP para um endereço IP da lista negra, independentemente da frequência atual de solicitações desse endereço. A quarta regra garante que as solicitações HTTP de um endereço IP sejam rejeitadas no momento exato em que o limite de frequência de solicitações for superado. Assim, a segunda regra funciona principalmente em novas conexões TCP, a terceira e a quarta - em conexões já estabelecidas, sendo a primeira uma verificação histórica e a segunda uma verificação em tempo real.

Vamos tentar o filtro em ação!


Agora podemos montar e lançar nossos contêineres novamente.

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

O balanceador de carga deve iniciar antes dos dois servidores de back-end.

Vamos direcionar nosso navegador para http://localhost/. Se atualizarmos a página rapidamente uma dúzia de vezes, excederemos o limite de dez solicitações em um intervalo de dez segundos - e nossas solicitações serão rejeitadas. Se continuarmos a atualizar a página, novas solicitações serão rejeitadas imediatamente - mesmo antes de a conexão TCP ser estabelecida.

Questões


Por que o limite de dez solicitações por dez segundos?


A tabela Abusedetermina http_req_rate(10s), ou seja, a frequência das solicitações é medida em uma janela de dez segundos. Uma função is_abuseda ACL retorna Truea uma taxa de solicitação ≥10 dentro do intervalo especificado. Assim, o abuso é considerado a frequência de solicitações de dez ou mais solicitações em dez segundos.

Neste artigo, por exemplo, decidimos definir um limite baixo para facilitar a verificação da operação do limitador.

Qual é a diferença entre as regras de conexão http-request e tcp-request?


A partir da documentação :

solicitação http: a instrução solicitação http define um conjunto de regras que se aplicam à camada de rede 7 [modelo OSI]

A partir da documentação :
conexão tcp-request: executando uma ação em uma conexão de entrada, dependendo de uma condição na camada de rede 4

HTTP-, TCP-?


Imagine que as solicitações HTTP para o servidor enviem várias conexões TCP do mesmo endereço IP. A frequência das solicitações HTTP excederá rapidamente os limites. É então que a quarta regra entra em vigor, que descarta solicitações e coloca o endereço IP na lista negra.

Agora é perfeitamente possível que as conexões HTTP do mesmo endereço IP permaneçam abertas (consulte Conexão HTTP persistente ) e a frequência das solicitações HTTP caiu abaixo do valor limite. A terceira regra garante o bloqueio contínuo de solicitações HTTP, pois é abuse indicatoracionada neste IP.

Agora, suponha que, após alguns minutos, o mesmo IP tente estabelecer conexões TCP. Eles são descartados imediatamente, porque a segunda regra se aplica: ele vê o endereço IP rotulado e descarta a conexão imediatamente.

Conclusão


No início, pode ser difícil entender a limitação da velocidade de processamento de solicitações usando o HAProxy. Para fazer tudo certo, você precisa de um pensamento bastante "de baixo nível" e de várias ações não intuitivas. A documentação nesta parte é provavelmente muito técnica e sofre com a ausência de exemplos básicos. Esperamos que este guia corrija as deficiências e mostre a direção a todos que desejam seguir esse caminho.

O que mais se pode ler :

  1. Como a arquitetura tolerante a falhas é implementada na plataforma Mail.ru Cloud Solutions .
  2. Os 10 principais truques e dicas do Kubernetes .
  3. Nosso canal Telegram sobre transformação digital .

All Articles