O Fail2ban é instalado em primeiro lugar e por aí costuma ficar: o pacote está lá, o serviço corre, o sshd parece protegido. Meio ano depois descobre-se que a jail lê um ficheiro que neste sistema não existe, e que tudo o resto — o correio, o painel, o formulário de acesso do sítio — nunca esteve protegido.
Vejamos o que convém mesmo ativar num VPS vulgar com um sítio, que números pôr ali e como garantir que uma jail funciona em vez de apenas constar na configuração.
Verifique primeiro se o sshd apanha alguma coisa
Um único comando:
sudo fail2ban-client status sshd
Na saída estão as linhas Currently failed, Total failed e Total banned. Se Total failed for zero num servidor exposto há pelo menos um dia, a jail não funciona. Confronte com a realidade:
sudo lastb | wc -l
Milhares de tentativas falhadas no lastb perante zeros no Fail2ban significam uma coisa só: o filtro não está a olhar para onde deve.
A causa mais frequente é o Debian 12. Já não instala o rsyslog por omissão, o ficheiro /var/log/auth.log simplesmente não existe, e a jail sshd que vem de origem está configurada para ler esse ficheiro. Não aparece mensagem de erro nenhuma: o serviço arranca, o estado é mostrado, os contadores ficam a zero. Resolve-se passando para o diário do systemd:
[sshd]
enabled = true
backend = systemd
A outra hipótese é repor o rsyslog como pacote, se um auth.log em texto lhe fizer falta para outras ferramentas. O Ubuntu 24.04 comporta-se da mesma maneira.
Onde escrever as definições
Em /etc/fail2ban/jail.conf não se mexe: é sobrescrito na atualização do pacote, e qualquer alteração feita ali um dia desaparecerá em silêncio. As suas definições vão para /etc/fail2ban/jail.local; esse ficheiro é lido em último lugar e prevalece sobre o comum.
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 203.0.113.25
bantime = 1h
findtime = 10m
maxretry = 5
backend = systemd
ignoreip com o seu próprio endereço não é um luxo mas um seguro: bloquear-se a si próprio por um erro de escrita acontece mais depressa do que parece. Se não tem endereço fixo, mantenha uma entrada de reserva além do SSH: a consola ou o VNC do alojamento.
A jail que muda o panorama: recidive
Uma jail vulgar tem memória curta: cinco tentativas em dez minutos, uma hora de bloqueio, e uma hora depois recomeça tudo. Um robô convive muito bem com isso e voltará amanhã e depois. A recidive fecha exatamente essa brecha: não lê o registo do sistema mas o do próprio Fail2ban, ou seja, bloqueia quem o Fail2ban já bloqueou.
[recidive]
enabled = true
bantime = 1w
findtime = 1d
maxretry = 5
Lê-se assim: «bloqueado cinco vezes num dia, fora durante uma semana». A jail precisa do ficheiro /var/log/fail2ban.log; se passou o registo do próprio Fail2ban para syslog, a recidive também vai precisar de backend = systemd.
O efeito prático desta única jail costuma notar-se mais do que o ajuste fino de todas as outras: os habituais desaparecem e nos registos fica apenas ruído de fundo recente.
Que mais ativar se o servidor aloja um sítio
Por utilidade decrescente:
nginx-http-authouapache-auth: força bruta contra a autenticação básica. Necessário se uma área de administração ou um ambiente de testes está protegido por palavra-passe do servidor web;nginx-botsearch: a varredura de caminhos conhecidos:/wp-login.php,/phpmyadmin,/.env. Não é uma intrusão mas reconhecimento, e é precisamente ele que precede tudo o resto;nginx-limit-req: só funciona se no próprio Nginx estiver definida alimit_req_zone; sem ela a jail está ativa e é inútil;postfix-sasledovecot: indispensáveis se gere correio próprio. A busca de palavras-passe de caixas de correio é contínua e habitualmente não é vigiada por ninguém.
[nginx-botsearch]
enabled = true
logpath = /var/log/nginx/access.log
O formulário de acesso do próprio sítio é história à parte. A aplicação não tem registo próprio de acessos falhados, e no access.log uma tentativa falhada parece um POST vulgar com código 200 ou 302, indistinguível de uma bem-sucedida por qualquer filtro genérico. Há dois caminhos: fazer a aplicação escrever as falhas no syslog (a maioria dos gestores de conteúdos tem uma extensão para isso) ou limitar a frequência de pedidos à página de acesso. O segundo é um limitador, não um detetor, e é melhor sabê-lo de antemão.
Os números
bantime = 10m é a definição por omissão por causa da qual o Fail2ban é muitas vezes tido por inútil: dez minutos chegam a um robô para voltar. Uma hora no primeiro bloqueio mais a recidive por uma semana funciona muito melhor do que bloqueios permanentes, que com o tempo se transformam numa lista de regras interminável.
maxretry = 3 para SSH é uma forma segura de se bloquear a si próprio. Cinco tentativas em dez minutos cortam a força bruta igualmente bem.
Uma busca lenta — uma tentativa de cinco em cinco minutos — nunca cairá dentro da janela findtime. Não é motivo para esticar a janela para um dia: obteria falsos positivos contra os seus próprios colaboradores. Contra a busca lenta não atua um limiar, mas a autenticação por palavra-passe desativada.
Garanta que o bloqueio chega aos pacotes
O Fail2ban limita-se a chamar um comando externo. Se banaction não corresponder ao que filtra realmente o tráfego, o registo encher-se-á de alegres linhas Ban 198.51.100.7 enquanto os pacotes continuam a chegar. O Debian 12 usa nftables por omissão, e com o UFW ativo o correto é banaction = ufw. Para verificar:
sudo nft list ruleset | grep -c f2b
sudo iptables -S | grep f2b
Pelo menos um dos dois deve mostrar regras. E o próprio filtro pode ser testado sem esperar por um ataque:
sudo fail2ban-regex systemd-journal /etc/fail2ban/filter.d/sshd.conf
No fim da saída indica-se quantas linhas corresponderam. Zero significa que o filtro e o registo não se encontram, e continuar a configurar não faz sentido.
O que o Fail2ban não faz
Não protege da força bruta distribuída: mil endereços com uma tentativa cada não atingirão limiar nenhum com definição nenhuma. Não corrige a causa: um endereço bloqueado não melhora uma palavra-passe fraca. E nada sabe de reputação: um endereço que ontem atacava servidores alheios está limpo aos seus olhos até se virar para o seu. Daí a combinação prática: chaves em vez de palavras-passe, o Fail2ban como limitador de ruído e uma lista de bloqueio partilhada (CrowdSec) como conhecimento da experiência alheia.
Um problema à parte é que tudo isto tem de ser visto de vez em quando. Ninguém escreve fail2ban-client status jail a jail durante semanas, e um aumento do número de bloqueios nota-se quando algo já se partiu. Nas páginas de demonstração abaixo os mesmos dados cabem numa só página: a lista de jails, quem está bloqueado neste momento e de onde vêm as tentativas.