Tentativi di accesso SSH nei log: mi stanno hackerando?
Entri nel tuo server, dai un'occhiata a /var/log/auth.log e trovi migliaia di righe di Failed password for root da indirizzi di mezzo mondo. Sembra un assedio. È soprattutto meteo.
La porta 22 è la porta più prevedibile di internet, quindi gli script automatici ci bussano di continuo, ovunque, tutto il giorno. Non prendono di mira te. Provano una breve lista di nomi utente (root, admin, oracle, ubuntu, test) con le password scelte più spesso, e passano oltre. Un tentativo fallito non costa loro nulla e a te dice solo che SSH è raggiungibile. Il tuo compito è rispondere a due domande: qualche tentativo è riuscito, e qualcuno potrebbe riuscire in futuro?
Dove guardare
Su Debian e Ubuntu con rsyslog, SSH scrive in /var/log/auth.log. Su Red Hat, Fedora, Rocky e Alma il file è /var/log/secure. Le installazioni Debian più recenti potrebbero non scrivere affatto quel file e tenere tutto nel journal di systemd. Il journal funziona ovunque ci sia systemd; il servizio si chiama ssh su Debian e Ubuntu e sshd nella famiglia Red Hat:
sudo journalctl -u ssh --since today # Debian, Ubuntu
sudo journalctl -u sshd --since "1 hour ago" # Red Hat, Fedora, Rocky, Alma
Tre conteggi raccontano quasi tutta la storia:
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
Il primo misura il rumore. Il secondo è quello che conta: ogni login riuscito. Il terzo mostra quali indirizzi insistono di più.
Un esempio concreto: una notte su una piccola VPS
Dodici righe da un server chiamato web1. I due indirizzi di persone (chi tenta con admin e il proprietario che si collega la mattina dopo) sono indirizzi di documentazione, così qui non compare nessuna persona reale. Gli altri sono infrastruttura pubblica.
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)Passati nel log checker di iplover, i cinque indirizzi si dividono così:
| Indirizzo IP | Righe | Categoria | Tipo di rete |
|---|---|---|---|
| 203.0.113.77 | 3 | Privati o riservati | Indirizzo privato o riservato (non controllato) |
| 185.220.101.1 | 3 | Nodi di uscita Tor | Nodo di uscita Tor |
| 3.5.140.1 | 3 | Cloud e hosting | Hosting, cloud o data center |
| 167.94.138.35 | 1 | Scanner di internet | Non identificato |
| 198.51.100.20 | 1 | Privati o riservati | Indirizzo privato o riservato (non controllato) |
(I due indirizzi di documentazione risultano riservati; nel tuo log gli indirizzi veri verrebbero controllati.) Ora leggi le righe, perché le categorie dicono solo chi ha bussato, non se la porta si è aperta.
Invalid user admin from 203.0.113.77: su questo server non esiste un accountadmin, quindi questo tentativo non poteva mai funzionare. La maggior parte di un auth.log tipico è fatta di righe così.Failed password for root from 185.220.101.1, due volte: qualcuno che prova la password di root passando da Tor. L'account esiste, quindi questo conta di più.Disconnected from authenticating user root … [preauth]significa che la sessione è finita prima del login: preauth vuol dire proprio “prima dell'autenticazione”.Invalid user oracle from 3.5.140.1: uno script su un server cloud di Amazon che prova il nome di un account di database.Bye Bye [preauth]è il client che si arrende.Connection closed by 167.94.138.35 … [preauth]: Censys si è collegato, ha letto il banner con la versione di SSH ed è andato via senza provare password. È uno scanner che fa l'inventario, non un attacco.Accepted publickey for deploy from 198.51.100.20: l'unico login riuscito della notte, con una chiave, da parte del proprietario. La rigasession openedsubito dopo conferma che la sessione è partita.
Verdetto: un solo login accettato, ed è del proprietario. Non è entrato nessuno. Però root accetta ancora le password, quindi i tentativi della quarta riga potrebbero un giorno riuscire se la password di root è debole. È questo che va sistemato.
Qualcuno è entrato davvero?
Controlla ogni riga Accepted. Per ognuna chiediti: è il mio nome utente, la mia chiave o password, e un indirizzo che riconosco (casa, ufficio, VPN)? Poi confronta con i registri degli accessi:
last -a | head -20 # ultimi login riusciti, con l'indirizzo remoto
sudo lastb | head -20 # ultimi login falliti (sui sistemi che li conservano)
who # chi è collegato in questo momento
Alcune distribuzioni recenti hanno sostituito last e lastb con altri strumenti; se mancano, il journal e auth.log contengono le stesse informazioni.
Una riga Accepted password o Accepted publickey che non sai spiegare è l'unico risultato che cambia tutto. In quel caso, considera l'account compromesso:
- Guarda
~/.ssh/authorized_keysdi ogni utente (root compreso) e rimuovi le chiavi che non riconosci. - Cerca account nuovi (
/etc/passwd), nuovi permessisudoe nuovi cron job (crontab -lper ogni utente, più/etc/cron.*). - Cambia le password di tutti gli account e rinnova i segreti conservati sul server.
- Se è coinvolto root, la risposta onesta è ricostruire il server da un'immagine pulita e ripristinare i dati. Un attaccante con root può nascondere quasi tutto.
Se vedi questo, fai questo
| Cosa vedi | Cosa significa | Cosa fare |
|---|---|---|
Invalid user … per nomi che non hai | Tentativi che non possono funzionare | Niente di urgente. Spariscono quando disattivi le password. |
Failed password for root o per un altro account reale | Tentativi su un account che esiste | Disattiva i login con password (vedi sotto), o almeno quello di root. |
Connection closed by … [preauth] senza password tentate | Uno scanner che legge il banner di SSH | Niente. |
| Migliaia di fallimenti al giorno da pochi indirizzi | Bot insistenti | Aggiungi fail2ban, o consenti SSH solo dai tuoi indirizzi. |
Righe Accepted … tutte tue | Non è entrato nessun altro | Niente, a parte rafforzare la configurazione. |
Accepted … da un indirizzo o un utente che non sai spiegare | Probabile intrusione | Segui i passi qui sopra, partendo da authorized_keys. |
Rendi inutili i tentativi: solo chiavi
Una password si può indovinare; una chiave SSH, in pratica, no. Quando sono accettate solo le chiavi, i bot possono provare all'infinito.
Per prima cosa crea una chiave sul tuo computer e copiala sul server. macOS e Linux hanno il client OpenSSH integrato. Su Windows è una funzionalità facoltativa, spesso già installata; se ssh non viene riconosciuto, la guida di Microsoft Introduzione a OpenSSH per Windows spiega come aggiungerlo:
ssh-keygen -t ed25519
ssh-copy-id tu@tuo-server # su Windows, incolla ~/.ssh/id_ed25519.pub in ~/.ssh/authorized_keys sul server
Poi, sul server, imposta queste righe in /etc/ssh/sshd_config:
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin prohibit-password
prohibit-password fa entrare root solo con una chiave; usa no per tenere root fuori da SSH del tutto. Prima di riavviare, controlla cosa userà davvero SSH. Vengono letti anche i file in /etc/ssh/sshd_config.d/, e alcune immagini cloud ne includono uno che riattiva le password. Per la maggior parte delle impostazioni vince il primo valore letto da sshd:
sudo sshd -T | grep -Ei "passwordauthentication|kbdinteractive|permitrootlogin"
Riavvia SSH (sudo systemctl restart ssh, oppure sshd nella famiglia Red Hat) e tieni aperta la sessione attuale mentre provi un nuovo login in una seconda finestra. Se il nuovo login fallisce, puoi ancora sistemare le cose da quella vecchia.
Riduci il rumore: fail2ban e firewall
Le chiavi rendono innocui i tentativi. fail2ban rende leggibile il log: controlla i fallimenti ripetuti e blocca per un po' quegli indirizzi nel firewall. Su Debian e Ubuntu, sudo apt install fail2ban, poi crea /etc/fail2ban/jail.local:
[sshd]
enabled = true
maxretry = 5
findtime = 10m
bantime = 1h
# Senza /var/log/auth.log (Debian recenti), leggi il journal:
# backend = systemd
Riavvialo con sudo systemctl restart fail2ban e controllalo con sudo fail2ban-client status sshd.
Se SSH serve solo a te, il filtro più efficace è una regola del firewall che consente la porta 22 solo dai tuoi indirizzi, oppure una VPN davanti al server. Spostare SSH su un'altra porta riduce molto il rumore, ma è mimetizzazione, non protezione: uno scanner la troverà comunque.
Cosa gli indirizzi non possono dirti
Gli indirizzi in auth.log sono la provenienza delle connessioni: spesso un nodo Tor, un server cloud preso in affitto o il computer compromesso di qualcun altro. Non identificano una persona, e bloccarli uno a uno non tiene il passo. Segnalare attacchi insistenti al contatto abusi della rete (lo mostra il lookup IP) ha senso per un server cloud che ti martella per giorni; per qualche login fallito non vale il tuo tempo.
Fonti
Documenti ufficiali usati per verificare i fatti di questa guida.