Tentativas de login SSH nos meus logs: estou sendo invadido?
Você entra no servidor, dá uma olhada no /var/log/auth.log e encontra milhares de linhas Failed password for root vindas de endereços do mundo todo. Parece um cerco. É basicamente o clima.
A porta 22 é a porta mais previsível da internet, então scripts automáticos batem nela sem parar, em todo lugar, o dia inteiro. Eles não estão mirando você. Testam uma lista curta de nomes de usuário (root, admin, oracle, ubuntu, test) com as senhas que as pessoas mais escolhem e seguem em frente. Uma tentativa falha não custa nada a eles e só diz a você que o SSH está acessível. O seu trabalho é responder a duas perguntas: alguma tentativa deu certo, e alguma futura poderia dar?
Onde olhar
No Debian e no Ubuntu com rsyslog, o SSH grava em /var/log/auth.log. No Red Hat, Fedora, Rocky e Alma, é /var/log/secure. Instalações mais novas do Debian podem nem gravar esse arquivo e manter tudo no journal do systemd. O journal funciona em qualquer lugar com systemd; o serviço se chama ssh no Debian e no Ubuntu e sshd na família Red Hat:
sudo journalctl -u ssh --since today # Debian, Ubuntu
sudo journalctl -u sshd --since "1 hour ago" # Red Hat, Fedora, Rocky, Alma
Três contagens contam a maior parte da história:
sudo grep -c "Failed password" /var/log/auth.log
sudo grep "Accepted" /var/log/auth.log
sudo grep "Failed password" /var/log/auth.log | grep -oE "from [0-9.]+" | sort | uniq -c | sort -rn | head
A primeira mostra o tamanho do ruído. A segunda é a que importa: cada login bem-sucedido. A terceira mostra quais endereços insistem mais.
Um exemplo real: uma noite numa VPS pequena
Doze linhas de um servidor chamado web1. Os dois endereços de pessoas (o atacante que tenta admin e o dono entrando na manhã seguinte) são endereços de documentação, então nenhuma pessoa real aparece aqui. Os outros são de infraestrutura pública.
Nov 6 03:12:41 web1 sshd[24811]: Invalid user admin from 203.0.113.77 port 51022
Nov 6 03:12:43 web1 sshd[24811]: Failed password for invalid user admin from 203.0.113.77 port 51022 ssh2
Nov 6 03:12:44 web1 sshd[24811]: Connection closed by invalid user admin 203.0.113.77 port 51022 [preauth]
Nov 6 03:12:50 web1 sshd[24815]: Failed password for root from 185.220.101.1 port 40122 ssh2
Nov 6 03:12:53 web1 sshd[24815]: Failed password for root from 185.220.101.1 port 40122 ssh2
Nov 6 03:12:55 web1 sshd[24815]: Disconnected from authenticating user root 185.220.101.1 port 40122 [preauth]
Nov 6 03:14:02 web1 sshd[24830]: Invalid user oracle from 3.5.140.1 port 33412
Nov 6 03:14:04 web1 sshd[24830]: Failed password for invalid user oracle from 3.5.140.1 port 33412 ssh2
Nov 6 03:14:04 web1 sshd[24830]: Received disconnect from 3.5.140.1 port 33412:11: Bye Bye [preauth]
Nov 6 03:20:17 web1 sshd[24902]: Connection closed by 167.94.138.35 port 49212 [preauth]
Nov 6 08:41:09 web1 sshd[25510]: Accepted publickey for deploy from 198.51.100.20 port 52814 ssh2: ED25519 SHA256:k3X0exampleexampleexampleexampleexampleexa
Nov 6 08:41:09 web1 sshd[25510]: pam_unix(sshd:session): session opened for user deploy(uid=1001) by (uid=0)Passados pelo verificador de logs do iplover, os cinco endereços se separam assim:
| Endereço IP | Linhas | Categoria | Tipo de rede |
|---|---|---|---|
| 203.0.113.77 | 3 | Privados ou reservados | Endereço privado ou reservado (não verificado) |
| 185.220.101.1 | 3 | Nós de saída Tor | Nó de saída Tor |
| 3.5.140.1 | 3 | Nuvem e hospedagem | Hospedagem, nuvem ou data center |
| 167.94.138.35 | 1 | Scanners da internet | Não identificado |
| 198.51.100.20 | 1 | Privados ou reservados | Endereço privado ou reservado (não verificado) |
(Os dois endereços de documentação aparecem como reservados; no seu próprio log, os endereços reais seriam verificados.) Agora leia as linhas, porque as categorias só dizem quem bateu, não se a porta abriu.
Invalid user admin from 203.0.113.77: não existe contaadminneste servidor, então essa tentativa nunca poderia funcionar. A maior parte de um auth.log típico é assim.Failed password for root from 185.220.101.1, duas vezes: alguém tentando adivinhar a senha do root pelo Tor. A conta existe, então esta importa mais.Disconnected from authenticating user root … [preauth]significa que a sessão terminou antes do login: preauth quer dizer literalmente “antes da autenticação”.Invalid user oracle from 3.5.140.1: um script num servidor de nuvem da Amazon testando um nome de conta de banco de dados.Bye Bye [preauth]é o cliente desistindo.Connection closed by 167.94.138.35 … [preauth]: a Censys se conectou, leu o banner de versão do SSH e saiu sem testar senha. É um scanner fazendo inventário, não um ataque.Accepted publickey for deploy from 198.51.100.20: o único login bem-sucedido da noite, com chave, do dono. A linhasession openedlogo depois confirma que a sessão começou.
Veredito: um login aceito, e é do dono. Ninguém entrou. Mas o root ainda aceita senhas, então as tentativas da quarta linha poderiam dar certo um dia se a senha do root for fraca. É isso que precisa ser corrigido.
Alguém entrou mesmo?
Confira cada linha Accepted. Para cada uma, pergunte: é o meu usuário, a minha chave ou senha, e um endereço que eu reconheço (casa, escritório, VPN)? Depois compare com os registros de login:
last -a | head -20 # logins bem-sucedidos recentes, com o endereço remoto
sudo lastb | head -20 # falhas recentes (nos sistemas que as guardam)
who # quem está logado agora
Algumas distribuições mais novas substituíram last e lastb por outras ferramentas; se eles não existirem, o journal e o auth.log têm a mesma informação.
Uma linha Accepted password ou Accepted publickey que você não consegue explicar é o único resultado que muda tudo. Nesse caso, considere a conta comprometida:
- Olhe o
~/.ssh/authorized_keysde cada usuário (incluindo o root) e remova as chaves que você não reconhece. - Procure contas novas (
/etc/passwd), novas permissões desudoe novas tarefas no cron (crontab -lde cada usuário, mais/etc/cron.*). - Troque as senhas de todas as contas e renove todos os segredos guardados no servidor.
- Se o root estiver envolvido, a resposta honesta é recriar o servidor a partir de uma imagem limpa e restaurar os seus dados. Um atacante com root consegue esconder quase qualquer coisa.
Se você vir isto, faça isto
| O que você vê | O que significa | O que fazer |
|---|---|---|
Invalid user … para nomes que você não tem | Tentativas que não têm como funcionar | Nada urgente. Some quando as senhas forem desativadas. |
Failed password for root ou para outra conta real | Tentativas contra uma conta que existe | Desative o login por senha (veja abaixo), ou pelo menos o do root. |
Connection closed by … [preauth] sem nenhuma senha testada | Um scanner lendo o seu banner SSH | Nada. |
| Milhares de falhas por dia vindas de poucos endereços | Bots persistentes | Instale o fail2ban, ou libere o SSH só para os seus próprios endereços. |
Linhas Accepted … que são todas suas | Ninguém mais entrou | Nada, além de reforçar a segurança. |
Accepted … de um endereço ou usuário que você não consegue explicar | Uma provável invasão | Siga os passos acima, começando pelo authorized_keys. |
Torne a adivinhação inútil: só chaves
Uma senha pode ser adivinhada; uma chave SSH, na prática, não. Quando só chaves são aceitas, os bots podem tentar para sempre.
Primeiro, crie uma chave no seu computador e copie para o servidor. O macOS e o Linux já vêm com o cliente OpenSSH. No Windows, ele é um recurso opcional, muitas vezes já instalado; se o ssh não for reconhecido, o guia da Microsoft Introdução ao OpenSSH para Windows mostra como adicioná-lo:
ssh-keygen -t ed25519
ssh-copy-id voce@seu-servidor # no Windows, cole ~/.ssh/id_ed25519.pub no ~/.ssh/authorized_keys do servidor
Depois, no servidor, defina isto em /etc/ssh/sshd_config:
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin prohibit-password
prohibit-password só deixa o root entrar com chave; use no para manter o root totalmente fora do SSH. Antes de reiniciar, confira o que o SSH vai realmente usar. Os arquivos em /etc/ssh/sshd_config.d/ também são lidos, e algumas imagens de nuvem trazem um que reativa as senhas. Na maioria das opções, vale o primeiro valor que o sshd lê:
sudo sshd -T | grep -Ei "passwordauthentication|kbdinteractive|permitrootlogin"
Reinicie o SSH (sudo systemctl restart ssh, ou sshd na família Red Hat) e mantenha a sessão atual aberta enquanto testa um novo login numa segunda janela. Se o novo login falhar, você ainda pode consertar pela sessão antiga.
Corte o ruído: fail2ban e firewalls
As chaves tornam a adivinhação inofensiva. O fail2ban torna o log legível: ele observa falhas repetidas e bloqueia esses endereços por um tempo no firewall. No Debian e no Ubuntu, sudo apt install fail2ban, e depois crie /etc/fail2ban/jail.local:
[sshd]
enabled = true
maxretry = 5
findtime = 10m
bantime = 1h
# Sem /var/log/auth.log (Debian mais novo), leia o journal:
# backend = systemd
Reinicie com sudo systemctl restart fail2ban e confira com sudo fail2ban-client status sshd.
Se só você precisa do SSH, o filtro de ruído mais forte é uma regra de firewall que libera a porta 22 apenas para os seus próprios endereços, ou uma VPN na frente do servidor. Mudar o SSH de porta também corta bastante o ruído, mas é camuflagem, não proteção: um scanner vai encontrá-lo do mesmo jeito.
O que os endereços não conseguem dizer
Os endereços no auth.log são de onde vieram as conexões: muitas vezes uma saída do Tor, um servidor de nuvem alugado ou a máquina sequestrada de outra pessoa. Eles não identificam uma pessoa, e bloqueá-los um por um nunca vai dar conta. Denunciar ataques persistentes ao contato de abuso da rede (a consulta de IP mostra qual é) faz sentido para um servidor de nuvem que martela você por dias; para alguns logins falhos, não vale o seu tempo.
Fontes
Documentos oficiais que usamos para conferir os fatos deste guia.