Hardening do Nginx no HestiaCP: como travar os DDoS de camada 7 e acelerar os sites com cache
Guia prático para criar um template Nginx personalizado no HestiaCP que mitiga ataques DDoS de camada de aplicação (camada 7) e, ao mesmo tempo, ativa a cache de conteúdo dinâmico para acelerar os sites.
Um ataque DDoS "clássico" visa saturar a largura de banda ou as conexões de rede do servidor (os chamados ataques de camada 3/4, a nível de rede e transporte). Mas existe outra categoria, muitas vezes mais insidiosa porque precisa de muito menos largura de banda para causar danos: os ataques de camada 7, a nível de aplicação. Aqui o alvo não é a conexão de rede em si, mas o servidor web e as aplicações que nele rodam: milhares de requisições HTTP "legítimas" na forma, mas enviadas tão rapidamente que esgotam a CPU, a memória e as conexões à base de dados, derrubando até um servidor cuja conexão à internet está longe de estar saturada.
Quem gere os próprios sites com o HestiaCP, o painel de controlo de servidor open source baseado em Nginx (sozinho, ou como proxy inverso diante do Apache), tem à disposição uma ferramenta poderosa mas pouco conhecida para se defender deste tipo de ataque: os templates Nginx personalizados. Neste guia veremos como criar um template à medida que adiciona regras de mitigação de DDoS aplicacional, e ao mesmo tempo como aproveitar a cache do Nginx para acelerar conteúdo dinâmico (PHP, aplicações Node.js, contentores Docker), reduzindo ainda mais a carga no servidor mesmo quando não há nenhum ataque em curso.
Como funcionam os templates Nginx no HestiaCP
O HestiaCP não escreve "à mão" a configuração de cada site: para cada domínio aplica um template, ou seja, um ficheiro-modelo com marcadores (placeholders) que o painel substitui pelos valores reais do domínio (nome de domínio, caminho dos ficheiros, utilizador de sistema, etc.) sempre que o domínio é criado ou "reconstruído". Os templates predefinidos encontram-se em:
/usr/local/hestia/data/templates/web/nginx/ # Nginx usado como proxy inverso diante do Apache
/usr/local/hestia/data/templates/web/nginx/php-fpm/ # Nginx com PHP-FPM (sem Apache)
Cada template é na realidade um trio de ficheiros com o mesmo nome e extensões diferentes: .tpl (configuração para HTTP), .stpl (configuração para HTTPS) e .sh (um script opcional executado na geração). Por exemplo, o template "default" é composto por default.tpl, default.stpl e default.sh.
Regra de ouro: nunca modificar os templates originais. A cada atualização do HestiaCP os ficheiros predefinidos são substituídos, e qualquer personalização feita diretamente sobre eles seria perdida. O procedimento correto é copiá-los com um novo nome e trabalhar apenas na cópia:
cd /usr/local/hestia/data/templates/web/nginx/php-fpm/
cp default.tpl hardened.tpl
cp default.stpl hardened.stpl
cp default.sh hardened.sh 2>/dev/null || true
(o exemplo usa os templates para Nginx + PHP-FPM; se o site funcionar atrás de Nginx + Apache, o mesmo procedimento aplica-se na pasta nginx/ em vez de nginx/php-fpm/).
Dentro dos ficheiros copiados encontram-se marcadores como %domain%, %docroot%, %user%, %home% e %backend_lsnr%: o HestiaCP substitui-os automaticamente pelos valores corretos para cada domínio que usar o template, por isso devem ser deixados intactos enquanto se adicionam novas diretivas em torno deles.
Primeira regra de hardening: limitar a frequência de requisições (rate limiting)
O módulo ngx_http_limit_req_module, incluído por padrão no Nginx, permite contar as requisições por endereço IP (ou qualquer outra variável) e rejeitar as que excedem um limite, devolvendo um erro HTTP 429 Too Many Requests. É a defesa mais simples e eficaz contra inundações aplicacionais: um bot que dispara centenas de requisições por segundo contra a página de login ou o motor de busca interno do site é bloqueado antes de poder consumir recursos no backend PHP.
A zona de contagem só precisa de ser declarada uma vez, globalmente, dentro do bloco http da configuração principal do Nginx (/etc/nginx/nginx.conf, ou um ficheiro separado incluído a partir dele, por exemplo /etc/nginx/conf.d/ddos-mitigation.conf):
# 10 MB de memória partilhada bastam para rastrear cerca de 160.000 IPs
limit_req_zone $binary_remote_addr zone=req_zone_general:10m rate=20r/s;
limit_req_zone $binary_remote_addr zone=req_zone_login:10m rate=5r/m;
limit_conn_zone $binary_remote_addr zone=conn_zone:10m;
Aqui são criadas duas zonas de rate limiting distintas: uma genérica (req_zone_general, 20 requisições por segundo por IP) para todo o site, e outra muito mais rigorosa (req_zone_login, 5 requisições por minuto) aplicada apenas à página de login, alvo típico tanto de ataques de força bruta como de DDoS. Foi também adicionada uma zona para limit_conn, que limita não a frequência mas o número de conexões simultâneas por IP, útil contra tentativas de esgotar o pool de conexões do backend.
No template personalizado (hardened.tpl e hardened.stpl), dentro do bloco server { ... }, aplicam-se as zonas acabadas de definir:
server {
listen %ip%:%web_port%;
server_name %domain_idn% %alias_idn%;
root %docroot%;
# Limita as conexões simultâneas por IP individual em todo o site
limit_conn conn_zone 20;
location / {
# Limite geral: permite picos curtos (burst) sem bloquear
# de imediato um utilizador que clica rapidamente
limit_req zone=req_zone_general burst=40 nodelay;
limit_req_status 429;
try_files $uri $uri/ /index.php?$args;
}
location = /wp-login.php {
limit_req zone=req_zone_login burst=5 nodelay;
limit_req_status 429;
include /home/%user%/conf/web/%domain%/nginx.hosting.conf*;
}
# ... o resto do template (gestão do PHP-FPM, páginas de erro, etc.)
# permanece igual ao template "default" original.
}
Uma breve explicação dos parâmetros usados, para quem não trabalha todos os dias com o Nginx:
rate=20r/s: a velocidade "sustentada" permitida, neste caso 20 requisições por segundo por IP.burst=40: uma espécie de "reservatório" de requisições extra que um IP pode acumular e gastar de uma só vez, para não penalizar um utilizador real que navega rapidamente entre várias páginas (cada imagem, cada ficheiro CSS ou JS de uma página gera a sua própria requisição).nodelay: sem esta opção, o Nginx colocaria em fila (atrasando) as requisições que excedem o limite; comnodelay, as requisições que excedem o burst são em vez disso rejeitadas imediatamente com um erro 429, evitando acumular conexões em espera que por sua vez consumiriam recursos.limit_req_status 429: define explicitamente o código de erro devolvido (por padrão seria 503, mas 429 é semanticamente mais correto e é melhor reconhecido por CDNs e ferramentas de monitorização).
Segunda regra de hardening: cortar as conexões lentas (anti-Slowloris)
Outra família de ataques de camada 7, conhecida como Slowloris, não envia muitas requisições, mas sim muito poucas, mantendo cada conexão aberta o máximo de tempo possível ao enviar os dados em fragmentos minúsculos: desta forma esgota o número máximo de conexões simultâneas que o servidor consegue gerir, sem nunca precisar de gerar tráfego elevado. O remédio consiste em reduzir os tempos máximos que o Nginx concede a um cliente para concluir o envio da sua requisição:
client_body_timeout 10s;
client_header_timeout 10s;
keepalive_timeout 15s;
send_timeout 10s;
# Limita também os tamanhos, para evitar uploads anómalos usados como vetor de inundação
client_max_body_size 10m;
large_client_header_buffers 4 8k;
Estes valores (muito inferiores aos 60 segundos predefinidos) devem também ser colocados dentro do bloco server { ... } do template personalizado, ou no bloco global http { ... } se se quiser aplicá-los a todos os domínios do servidor. Atenção para não exagerar: tempos demasiado agressivos podem interromper uploads legítimos de ficheiros grandes a partir de conexões lentas ou móveis; 10 segundos são geralmente um bom compromisso para sites normais.
Otimizar a cache de conteúdo dinâmico
Mitigar DDoS aplicacionais não significa apenas "bloquear os maus": significa também tornar muito barato responder a uma requisição, para que mesmo um volume elevado de tráfego (malicioso, ou simplesmente um pico súbito de popularidade) não derrube o servidor. A cache do Nginx para conteúdo dinâmico serve exatamente para isso: as páginas geradas pelo PHP (ou por uma aplicação Node.js atrás de um proxy) são guardadas temporariamente em disco e devolvidas de forma idêntica às requisições seguintes, sem ser preciso executar novamente o código da aplicação e as consultas à base de dados de cada vez.
O HestiaCP distingue dois casos, dependendo da stack escolhida para o domínio:
Nginx + PHP-FPM: cache FastCGI
Para domínios que usam Nginx com PHP-FPM (sem Apache), usa-se a cache FastCGI. Na configuração global (o mesmo ficheiro /etc/nginx/conf.d/ddos-mitigation.conf usado acima, ou nginx.conf) define-se a cache:
fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2 keys_zone=FASTCGICACHE:100m inactive=60m max_size=1g;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
No template personalizado, no bloco que trata do PHP-FPM, é preciso adicionar a inclusão de ativação depois da linha include /etc/nginx/fastcgi_params; já presente no template original:
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass %backend_lsnr%;
fastcgi_index index.php;
include /etc/nginx/fastcgi_params;
# Ativa a cache para este domínio (o ficheiro é criado pelo HestiaCP
# quando a cache FastCGI é ativada a partir do painel para o domínio)
include /home/%user%/conf/web/%domain%/nginx.fastcgi_cache.conf*;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
No entanto, nem todas as páginas devem ser colocadas em cache: um carrinho de compras, uma área pessoal após o login, ou um endpoint que gera conteúdo diferente para cada utilizador não devem servir a um visitante a página guardada para outro. Esses caminhos excluem-se definindo a variável $no_cache:
set $no_cache 0;
if ($request_uri ~* "/(carrinho|checkout|wp-admin|my-account)") {
set $no_cache 1;
}
if ($http_cookie ~* "wordpress_logged_in") {
set $no_cache 1;
}
Depois de criado o template, a cache FastCGI para um domínio específico ativa-se na página de edição do domínio no painel do HestiaCP (secção dedicada ao template Nginx/backend), selecionando o template acabado de criar. Para limpar a cache em qualquer momento (por exemplo, depois de uma atualização do site), basta usar o botão "Purge Nginx Cache" na mesma página, ou pela linha de comandos:
v-purge-nginx-cache utilizador dominio.tld
Nginx + Apache: cache de proxy
Quando, em vez disso, o domínio usa o Nginx como proxy inverso diante do Apache (uma configuração muito comum no HestiaCP para suportar .htaccess e mod_php), a cache FastCGI não está disponível: usa-se então a cache de proxy do Nginx, que funciona de forma conceptualmente idêntica mas aplica-se ao tráfego passado pelo proxy (também funciona bem para aplicações Node.js ou contentores Docker atrás do proxy). Os templates personalizados para este fim seguem uma convenção de nomes dedicada: caching-nomepersonalizado.tpl, caching-nomepersonalizado.stpl e caching-nomepersonalizado.sh, sempre criados copiando o template de partida equivalente fornecido pelo HestiaCP na pasta nginx/ (não nginx/php-fpm/), e depois selecionáveis como "template de cache" dedicado na página do domínio.
Um aviso sobre WebSockets
Tanto o template padrão como os templates de cache de proxy incluem por defeito a diretiva proxy_hide_header Upgrade;, originalmente adicionada para contornar um problema de compatibilidade com o Safari. Esta linha, no entanto, também bloqueia as conexões WebSocket (usadas, por exemplo, por chats em tempo real, notificações push do browser ou alguns painéis de administração): se a aplicação alojada precisar delas, deve ser removida do template personalizado.
Aplicar o template e verificar o seu funcionamento
Depois de guardar as alterações aos ficheiros .tpl/.stpl, é preciso selecionar o novo template para o domínio pretendido: no painel do HestiaCP, abre-se a página de edição do domínio (Web > nome do domínio > Editar), expandem-se as opções avançadas e escolhe-se o template acabado de criar (hardened, no nosso exemplo) no menu pendente do template Nginx/Proxy. Em alternativa, pela linha de comandos, pode forçar-se a reconstrução da configuração para todos os domínios de um utilizador com:
v-rebuild-user nomeutilizador
Antes de colocar tudo em produção, é boa prática validar a sintaxe e recarregar o Nginx manualmente:
nginx -t && systemctl reload nginx
Para verificar se o rate limiting funciona mesmo, pode simular-se um pequeno fluxo de requisições repetidas a partir da própria máquina (sem confundir com um verdadeiro teste de carga, que deve ser feito com cautela e apenas em sistemas de que se é proprietário):
for i in $(seq 1 80); do
curl -o /dev/null -s -w "%{http_code}\n" https://dominio.tld/
done | sort | uniq -c
Se a configuração estiver correta, as primeiras requisições (dentro do burst configurado) devolverão 200, enquanto as seguintes, depois de superado o limite, responderão 429. É o sinal de que a zona de rate limiting está a fazer o seu trabalho.
Alguns avisos antes de ativar tudo em produção
O rate limiting baseado no endereço IP tem uma limitação intrínseca a ter em conta: muitos utilizadores que navegam a partir de redes empresariais, universidades ou certas redes móveis partilham o mesmo IP público através de NAT, e limites demasiado baixos arriscam bloquear utilizadores legítimos juntamente com eventuais bots. Por isso é recomendável começar com valores prudentes (como os 20 r/s do exemplo), monitorizar os registos de erros 429 durante alguns dias, e só depois apertar os limites onde for necessário. Também é boa prática excluir do rate limiting os endereços IP das próprias ferramentas de monitorização (verificadores de disponibilidade, CDNs, eventuais serviços de backup), usando uma diretiva geo ou um simples bloco map antes da zona de limit_req, para evitar falsos alarmes.
Por fim, o rate limiting a nível aplicacional descrito neste guia constitui uma primeira linha de defesa eficaz contra DDoS de camada 7, mas não substitui uma proteção dedicada a nível de rede (camada 3/4) contra ataques volumétricos muito grandes: para esses casos continuam a ser indispensáveis serviços de mitigação a montante do servidor, como uma CDN com proteção anti-DDoS ou a filtragem oferecida por alguns fornecedores de hosting e de conectividade.
Fontes oficiais
Partilhar
Qual é a sua reação?
Gosto
0
Não gosto
0
Adoro
0
Divertido
0
Irritado
0
Triste
0
Uau
0