Se você mantém servidores web expostos à internet, com certeza já viu esse padrão: ao abrir os logs de acesso, há uma enxurrada de requisições procurando por segredos, variáveis de ambiente e arquivos de configuração.
Recentemente, passei por esse cenário em um servidor rodando CloudPanel (sobre Debian 12) com Nginx. O que começou como uma simples tentativa de bloquear varreduras maliciosas evoluiu para uma sessão de troubleshooting envolvendo conflitos de portas TCP, falta de módulos Lua no Nginx e decisões de arquitetura de rede no nível do kernel.
Abaixo, compartilho o passo a passo, os erros encontrados e a solução definitiva.
1. O problema inicial: scans insistentes de arquivos .env#
Ao analisar o arquivo de access log do Nginx, identifiquei um bot realizando varreduras automatizadas em busca de credenciais:
2026-04-04 17:20:31 192.0.2.1 - - [04/Apr/2026:17:20:31 -0300] "GET /.env.production.debug HTTP/2.0" 404 1827 "-" "Mozilla/5.0..."
2026-04-04 17:20:31 192.0.2.1 - - [04/Apr/2026:17:20:31 -0300] "GET /.env.production.local HTTP/2.0" 404 1827 "-" "Mozilla/5.0..."
2026-04-04 17:20:31 192.0.2.1 - - [04/Apr/2026:17:20:31 -0300] "GET /.env.staging HTTP/2.0" 404 1827 "-" "Mozilla/5.0..."
2026-04-04 17:20:32 192.0.2.1 - - [04/Apr/2026:17:20:32 -0300] "GET /@fs/.docker.env?import HTTP/2.0" 404 1827 "-" "Mozilla/5.0..."
2026-04-04 17:20:33 192.0.2.1 - - [04/Apr/2026:17:20:33 -0300] "GET /@fs/.env.development?import HTTP/2.0" 404 1827 "-" "Mozilla/5.0..."
O bot procurava arquivos .env, configurações do Docker e até caminhos como /@fs/ (tentativa de explorar o servidor de desenvolvimento do Vite exposto).
Mesmo retornando 404 (Not Found), esse tráfego consome I/O e processamento no Nginx ao disparar chamadas de sistema (stat(), open()) no sistema de arquivos.
2. A escolha da ferramenta de mitigação e inteligência coletiva#
Avaliei três opções principais de proteção:
- ModSecurity v3: Poderoso com o conjunto OWASP CRS, mas adiciona latência ao inspecionar o corpo das requisições em tempo real.
- Fail2Ban tradicional: Solução puramente reativa e isolada. O servidor só bloqueia um atacante após sofrer vários golpes locais, sem compartilhamento de reputação global.
- CrowdSec: Abordagem moderna baseada em análise assíncrona de logs (
inotify), inteligência comunitária de IPs maliciosos (Crowdsourced Threat Intelligence) e bouncers modulares.
Como a arquitetura funciona por baixo do capô#
O CrowdSec separa a camada de detecção da camada de remediação:
[ Logs dos Serviços ] (Nginx, Apache, WordPress, etc.)
│
▼
[ Parsers & Scenarios ] ──> [ Local API (LAPI) ] <──> [ CrowdSec Central Hub ]
(Detecção Assíncrona) (Gestão de Decisões) (Reputação Colaborativa)
│
▼
[ Bouncers (Remediação) ]
│
▼
[ nftables / Netfilter ]
- Parsers e Cenários: Normalizam logs de aplicações de forma assíncrona sem prender threads do servidor web.
- Local API (LAPI): Centraliza alertas e gerencia as decisões de bloqueio localmente.
- Consenso Global: A rede comunitária compartilha sinais de IPs maliciosos confirmados mundialmente, criando uma barreira proativa antes mesmo do primeiro ataque local.
- Remediação via Bouncers: A aplicação da regra de bloqueio é delegada a bouncers dedicados.
Optei pelo CrowdSec. A ideia inicial era instalar o motor de segurança e usar o bouncer do Nginx (crowdsec-nginx-bouncer) via Lua. Foi aí que os desafios apareceram.
3. Primeiro obstáculo: conflito na porta 8080#
Após a instalação do CrowdSec no Debian, o serviço entrou em falha (status=1/FAILURE). O log do systemd entregou a causa:
journalctl -u crowdsec -n 50 --no-pager
FATAL local API server stopped with error: listening on 127.0.0.1:8080: listen tcp 127.0.0.1:8080: bind: address already in use
No CloudPanel, a porta 8080 já estava ocupada pelos serviços internos da stack (como o proxy reverso de cache Varnish ou daemons de suporte, o que pode ser inspecionado executando ss -tulpn | grep 8080).
A solução: mudando a LAPI para a porta 8090#
- Editei
/etc/crowdsec/config.yaml:
api:
server:
listen_addr: 127.0.0.1:8090
- Atualizei as credenciais do cliente local em
/etc/crowdsec/local_api_credentials.yaml:
url: http://127.0.0.1:8090
- Reiniciei o serviço:
sudo systemctl restart crowdsec
O comando ss -tulpn | grep crowdsec confirmou a LAPI rodando com sucesso na porta 8090.
4. O gargalo do módulo lua no Nginx do CloudPanel#
Com o motor ativo, instalei o crowdsec-nginx-bouncer. No entanto, no painel web do CrowdSec Console (app.crowdsec.net), aparecia o alerta:
"Inactive Remediation Components detected. Affected Security Engines may be unprotected."
Ao listar os bouncers registrados localmente:
sudo cscli bouncers list
O tipo aparecia como curl e a versão como a do utilitário de terminal (7.88.1). O Nginx nunca havia completado uma consulta na LAPI.
Ao tentar testar a rota com curl -I http://localhost/.env, o servidor respondeu:
curl: (52) Empty reply from server
E o comando nginx -t acusou erro fatal:
nginx: [emerg] open() "/etc/nginx/modules-enabled/10-mod-http-lua.conf" failed (2: No such file or directory)
Uma busca por ngx_http_lua_module.so no filesystem não retornou nenhum binário. A compilação padrão do Nginx utilizada pelo CloudPanel no Debian Bookworm não inclui o módulo dinâmico do Lua, tornando inviável rodar o bouncer embutido no processo HTTP sem recompilar o servidor web.
5. Pivotando para a solução superior: bloqueio no kernel com nftables#
Em vez de insistir em módulos Lua incompatíveis, mudei a camada de defesa: passei a bloquear os atacantes na Camada de Rede (Kernel) via NFTables.
Essa abordagem traz vantagens claras:
- O CrowdSec monitora os logs do Nginx em disco de forma assíncrona.
- Ao detectar comportamento abusivo, adiciona o IP em um conjunto (
set) do firewall. - O pacote do atacante é descartado pelo Netfilter no kernel antes mesmo de consumir memória ou sockets no Nginx.
Passo 1: Limpeza do Nginx#
sudo apt-get remove --purge crowdsec-nginx-bouncer
sudo rm -f /etc/nginx/conf.d/crowdsec_nginx.conf
Passo 2: Instalação do firewall bouncer#
sudo apt-get install nftables crowdsec-firewall-bouncer-nftables
sudo systemctl enable nftables
sudo systemctl start nftables
Gerei a chave de API para o novo bouncer:
sudo cscli bouncers add cs-firewall-bouncer
Editei /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml:
mode: nftables
api_url: http://127.0.0.1:8090/
api_key: <MINHA_API_KEY_GERADA>
log_mode: file
Iniciei o serviço:
sudo systemctl restart crowdsec-firewall-bouncer
6. O erro de ordem no nginx.conf: load_module#
Ao restaurar o Nginx, o comando nginx -t retornou outro erro clássico de sintaxe:
nginx: [emerg] "load_module" directive is specified too late in /etc/nginx/nginx.conf:13
A diretiva para carregar módulos adicionais estava posicionada após o bloco events {}. No Nginx, qualquer load_module precisa estar no topo do contexto global (main), antes de qualquer bloco:
user root; # Padrão em templates do CloudPanel; em servidores Debian puros prefira user www-data;
worker_processes auto;
pid /run/nginx.pid;
worker_rlimit_nofile 8192;
# Posicionado no topo do contexto global:
load_module modules/ngx_http_geoip_module.so;
events {
worker_connections 2000;
}
http {
...
Validei e recarreguei o Nginx:
sudo nginx -t
sudo systemctl restart nginx
7. Validação e resultados#
Ao checar a lista de bouncers ativos:
sudo cscli bouncers list
A saída confirmou a comunicação contínua:
───────────────────────────────────────────────────────────────────────────────────────────────────
Name IP Address Valid Last API pull Type
───────────────────────────────────────────────────────────────────────────────────────────────────
cs-firewall-bouncer-1775341453 127.0.0.1 ✔️ 2026-04-04T22:25:04Z crowdsec-firewall-bouncer
───────────────────────────────────────────────────────────────────────────────────────────────────
O Last API pull passou a registrar atividade na porta 8090, o alerta no painel web desapareceu e o arquivo /var/log/crowdsec.log passou a registrar o ban automático de IPs maliciosos por 4 horas diretamente no firewall.
Conclusão: principais lições e recomendações#
- Nem todo ambiente aceita bouncer de aplicação: Em servidores com Nginx customizado (como no CloudPanel), o bouncer via NFTables no kernel é muito mais estável e consome menos CPU.
- Evite colisões de porta padrão: A porta
8080costuma colidir com proxies e painéis web. Mover a LAPI do CrowdSec para a porta8090garante uma inicialização limpa. - Ordem estrita no Nginx: Módulos dinâmicos devem residir obrigatoriamente no topo do
nginx.conf, antes dos blocoseventsehttp.
Este artigo foi útil?
Deixe uma reação rápida para apoiar o conteúdo:
Este post está licenciado sob CC BY-NC.



Comentários
Participe da discussão abaixo.
0 comentários