Bloqueando tentativas de invasão no SSH com Fail2ban
Tabela de Conteúdo
Todo servidor com SSH exposto na internet recebe tentativas de login o dia inteiro, geralmente bots testando usuários e senhas comuns. O fail2ban monitora os logs de autenticação e bane automaticamente quem errar demais.
Instalando
$ sudo apt install fail2ban
Em distribuições baseadas em RHEL:
$ sudo dnf install fail2ban
Configurando o jail do SSH
O fail2ban vem com um arquivo de configuração padrão, jail.conf, mas ele é sobrescrito em atualizações. Por isso as customizações vão em jail.local:
$ sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
Edite a seção [sshd] de jail.local:
[sshd]
enabled = true
port = ssh
maxretry = 5
findtime = 10m
bantime = 1h
maxretryé o número de tentativas erradas antes do banfindtimeé a janela de tempo em que essas tentativas são contadasbantimeé quanto tempo o IP fica banido
Se a porta do SSH não for a padrão, ajuste port de acordo.
Evitando se banir sozinho
Antes de reiniciar o serviço, garanta que seu próprio IP não caia no bloqueio. Adicione um ignoreip na seção [DEFAULT] de jail.local:
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 203.0.0.5
Coloque ali seu IP fixo ou a faixa da VPN que você usa pra administrar o servidor. Sem isso é fácil se trancar de fora depois de errar a senha algumas vezes testando a própria configuração.
Iniciando o serviço
$ sudo systemctl enable --now fail2ban
$ sudo systemctl status fail2ban
Verificando bans ativos
$ sudo fail2ban-client status sshd
O comando mostra quantos IPs já foram banidos e a lista dos que estão banidos no momento. Pra dar uma ideia real do volume, essa é a saída de uma das minhas VPS:
Status for the jail: sshd
|- Filter
| |- Currently failed: 4
| |- Total failed: 483296
| `- File list: /var/log/auth.log
`- Actions
|- Currently banned: 3
|- Total banned: 63964
`- Banned IP list: 195.178.110.26 124.193.81.23 176.53.159.197
Mais de 480 mil tentativas de login falhas e 63 mil bans desde que o jail está ativo deixar o SSH exposto sem isso é convite pra brute force o tempo todo.
Vale entender de onde vem esse número: o arquivo de log (/var/log/fail2ban.log) gira e mantém só os registros recentes — o log mais antigo que ainda sobra nessa VPS é de poucos dias atrás. Quem sustenta o “Total failed”/“Total banned” ao longo do tempo é o banco interno do fail2ban (/var/lib/fail2ban/fail2ban.sqlite3), que não é afetado pela rotação do log de texto.
Desbloqueando um IP
Se algum IP legítimo acabar banido:
$ sudo fail2ban-client set sshd unbanip 203.0.113.10
Testando o filter sem esperar um ataque de verdade
Antes de confiar que o jail vai funcionar, dá pra testar o filter contra o log real sem esperar alguém tentar invadir de fato:
$ sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf
O comando mostra quantas linhas do log bateram com o regex do filter. Se o número vier zero mesmo com tentativas de login falhas no log, o problema está no filter ou no caminho do log — não no jail em si.
Na mesma VPS, a parte que importa da saída é o resumo no final:
Results
=======
Failregex: 19513 total
Ignoreregex: 0 total
Lines: 25951 lines, 10212 ignored, 9301 matched, 6438 missed
[processed in 1.46 sec]
9301 das 25951 linhas do log bateram com o filter. O resto (ignored/missed) é log de outra coisa — sessão aberta/fechada, cron, etc. — não tentativa de autenticação, então não é motivo de preocupação. Zero em matched é que seria sinal de problema.
Diagnosticando quando o fail2ban não bane ninguém
O primeiro lugar pra olhar é o log do próprio fail2ban:
$ sudo tail -f /var/log/fail2ban.log
Um erro comum: o backend errado pra onde o log do SSH realmente está. Em distribuições que não gravam mais em /var/log/auth.log (só no journal do systemd), o backend auto às vezes não pega as linhas certas. Force explicitamente:
[sshd]
backend = systemd
Depois de qualquer mudança em jail.local, recarregue:
$ sudo fail2ban-client reload
Banindo mais forte quem reincide
Um ban de 1 hora não impede que o mesmo IP volte a tentar depois. Pra quem já foi banido várias vezes, vale aumentar a punição. Duas formas de fazer isso:
bantime.increment, mais simples, dobra o tempo de ban a cada reincidência do mesmo IP no mesmo jail:
[DEFAULT]
bantime.increment = true
bantime.factor = 2
bantime.maxtime = 1w
O jail recidive, mais flexível, olha o histórico de bans em todos os jails e aplica uma punição própria pra quem se repete:
[recidive]
enabled = true
logpath = /var/log/fail2ban.log
banaction = %(banaction_allports)s
bantime = 1w
findtime = 1d
maxretry = 5
Ativar o recidive valeu na hora: no mesmo dia em que liguei nessa VPS, ele já pegou IPs que o sshd tinha acabado de banir e voltaram a tentar depois do ban expirar:
fail2ban.actions [2303383]: NOTICE [sshd] Ban 176.53.159.197
fail2ban.actions [2303383]: NOTICE [recidive] Ban 176.53.159.197
fail2ban.actions [2303383]: NOTICE [sshd] Ban 195.178.110.26
fail2ban.actions [2303383]: NOTICE [recidive] Ban 195.178.110.26
$ sudo fail2ban-client status recidive
Status for the jail: recidive
|- Filter
| |- Currently failed: 47
| |- Total failed: 182
| `- File list: /var/log/fail2ban.log
`- Actions
|- Currently banned: 8
|- Total banned: 8
`- Banned IP list: 176.53.159.197 176.53.159.198 195.178.110.26 45.148.10.152 45.148.10.151 45.148.10.141 45.148.10.147 45.148.10.157
8 IPs banidos pelo recidive já no primeiro dia — todos reincidentes que o sshd já tinha banido antes.
Na prática, bantime.increment resolve a maioria dos casos com bem menos configuração. O recidive compensa quando você tem vários jails (SSH, Nginx, etc.) e quer uma política única de “reincidente” cobrindo todos eles.
Simples assim :)