No mapa de ataques há uma mancha densa sobre um país, e a regra «proibi-lo por completo» sugere-se por si mesma. Ela não dará quase nada: mudar o país de saída custa alguns minutos ao atacante, enquanto sob a proibição cairão os seus próprios visitantes e você mesmo em viagem. Pior ainda: um rastreador visível que chega de uma única nuvem mudará para endereços residenciais depois do bloqueio e deixará de se distinguir das pessoas.

Abaixo estão cinco passos que nesse mesmo servidor realmente dão resultado: cortar os scanners, limitar o ritmo, não cortar o que é necessário e, se ainda assim for preciso, bloquear corretamente. Tudo se verifica e se configura numa única sessão.

Passo 1. Ver quem realmente cria a mancha

Primeiro localize o registro de pedidos. Num servidor com vários sites, o access.log costuma estar vazio, enquanto as entradas vão para o registro do próprio host virtual:

sudo ls -lS /var/log/apache2/*access*.log /var/log/apache2/*requests*.log 2>/dev/null

Em todos os comandos seguintes substitui-se pelo arquivo que ficou no topo dessa lista. Primeiro os endereços que lideram o dia, depois o que exatamente pede aquele que está no topo:

sudo awk '{print $1}' /var/log/apache2/access.log | sort | uniq -c | sort -rn | head -20
sudo grep '^203\.0\.113\.' /var/log/apache2/access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -20

O segundo comando responde à pergunta principal. Se na saída houver dezenas de páginas diferentes do seu site, trata-se de um coletor de conteúdo: é inofensivo e basta não o contar como visitante. Se ali estiverem /wp-login.php, /.env, /phpmyadmin e outras coisas que você não tem, é um scanner, e dele trata o passo 2. Para o nginx os registros ficam em /var/log/nginx/, os campos são os mesmos.

Passo 2. Uma prisão para os pedidos daquilo que você não tem

O caminho do pedido não pode ser falsificado — ele é a própria intenção, ao contrário do endereço e do User-Agent. O filtro /etc/fail2ban/filter.d/web-probes.local:

[Definition]
failregex = ^<HOST> .* "(GET|POST|HEAD) /(wp-login\.php|wp-admin|xmlrpc\.php|\.env|\.git/|phpmyadmin|administrator/)
ignoreregex =

A prisão /etc/fail2ban/jail.d/web-probes.local:

[web-probes]
enabled  = true
port     = http,https
filter   = web-probes
logpath  = /var/log/apache2/*access.log
maxretry = 1
findtime = 1h
bantime  = 1d

Aqui o maxretry = 1 justifica-se: não existem acessos casuais a um painel de administração alheio. Aplicar e confirmar que o filtro captura linhas no seu próprio registro:

sudo fail2ban-regex /var/log/apache2/access.log /etc/fail2ban/filter.d/web-probes.local
sudo systemctl reload fail2ban && sudo fail2ban-client status web-probes

Passo 3. Limitar o ritmo

O scanner e o rastreador traem-se pela velocidade, independentemente do país de onde vêm. Trinta páginas por minuto a partir de um único endereço é um limiar que não atinge uma pessoa a percorrer um catálogo:

[http-flood]
enabled  = true
port     = http,https
filter   = apache-overflows
logpath  = /var/log/apache2/site_access.log
maxretry = 30
findtime = 60
bantime  = 1h

O detalhe decisivo é qual registro está indicado em logpath. Se o padrão abranger o registro do próprio painel de gestão ou da área de administração, você bloqueará a si mesmo: uma única página dele puxa uma dezena de pedidos de estilos e scripts, e trinta acumulam-se em dois cliques. Por isso dá-se ao painel um arquivo de registro próprio, que não corresponde ao padrão da prisão, e aos arquivos estáticos um prazo longo de cache, para que o navegador não os peça de novo a cada página. O sintoma, se isso acontecer de todo modo: o HTML chega, a apresentação não, a página fica pendurada. Remover o bloqueio:

sudo fail2ban-client set http-flood unbanip 203.0.113.10

Passo 4. Proteger aquilo que não se pode cortar

Antes de ativar os bloqueios, anote as exceções — caso contrário, o primeiro a cair sob elas será o que é necessário:

[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 203.0.113.0/24 66.249.64.0/19

O que obrigatoriamente deve estar nessa linha e por quê:

  • O seu próprio endereço ou a sub-rede do escritório. A causa mais frequente da perda de acesso ao servidor é uma regra própria, não um ataque;
  • A faixa 66.249.64.0/19. O robô de busca do Google identifica-se pelo User-Agent, mas a parte dele que executa JavaScript chega como um Chrome/Linux comum e não se apresenta. Qualquer regra do tipo «bloquear o tráfego dos centros de dados» atinge precisamente essa parte, após o que as páginas caem silenciosamente do índice. Um endereço suspeito pode ser verificado com uma consulta inversa: dig -x 66.249.66.1 +short deve devolver um nome na zona googlebot.com;
  • Os endereços das notificações de pagamento. Os webhooks dos sistemas de pagamento chegam de servidores AWS e Azure. Se fechar essas nuvens por inteiro, as confirmações de pagamento deixarão de chegar, e você saberá disso no dia em que um cliente escrever que o dinheiro foi debitado mas o acesso não está lá. As faixas tiram-se da documentação do seu provedor de pagamentos.

Passo 5. Se o bloqueio for mesmo necessário, bloqueie uma sub-rede, não um país

Um adensamento constante vindo de uma única rede, que se mantém por dias e vem acompanhado de pedidos todos iguais, é um único executor a percorrer uma lista. Esse caso bloqueia-se de forma pontual e por tempo limitado:

sudo ufw deny from 198.51.100.0/22 to any
sudo ufw status numbered

Com o segundo comando, anote o número da regra: o bloqueio terá de ser removido por ele, sudo ufw delete <número>. Combine consigo mesmo um prazo — um mês, por exemplo — e marque a remoção no calendário. As regras postas «para sempre» transformam-se, ao cabo de um ano, numa lista que ninguém se atreve a tocar, porque ninguém se lembra por que esses endereços foram para lá.

Para saber qual sub-rede bloquear, parta do endereço do primeiro passo: whois 198.51.100.17 | grep -iE 'route|cidr|netname'.

O que muda depois destes cinco passos

Os scanners são cortados pela intenção e não pela origem, e são apanhados no primeiro pedido. Os rastreadores batem no limite de ritmo. O robô de busca e as notificações de pagamento continuam a passar. O único bloqueio largo que resta é pontual, com prazo conhecido e motivo claro.

O mapa, por sua vez, não desaparece, mas o seu papel muda: ele serve não para fechar países, mas para notar o próprio adensamento e compará-lo com o que havia uma semana antes. Para isso é preciso um histórico e não um instantâneo do dia em curso. Como se apresenta um mapa assim ao lado do registro de bloqueios e da lista de portas abertas, mostram-no as páginas de demonstração abaixo.