Perché esporre SSH su Internet è un errore architetturale sottovalutato

Esporre un server Linux su Internet significa affacciarsi in un ambiente ostile dominato da scansioni automatiche e tentativi di accesso continui. Analizziamo perché il problema non è SSH in sé, ma l’idea stessa di rendere pubblico un servizio di amministrazione.

Chiunque esponga un server Linux sulla rete Internet, anche solo per ospitare un sito personale o qualche servizio di supporto, entra immediatamente in un ambiente ostile. Non è una questione di “se” qualcuno proverà a collegarsi, ma di quando e con quale frequenza. I log di autenticazione lo dimostrano con brutalità: tentativi di connessione continui, automatizzati, ripetuti, che non mirano a un obiettivo specifico ma sfruttano la pura statistica.

Pensate a Shodan: permette di scovare webcam, NAS e dispositivi remoti proprio per via del fatto che le interfacce di accesso sono disponibili pubblicamente sulla rete Internet. Gli utenti possono interrogare il database con filtri avanzati come IP, porta, servizio (i.e. port:80 apache), Paese o versione software

Il problema principale, però, non è il rumore in sé. Il vero rischio nasce quando una configurazione server è scambiata come “sicura per davvero”. In questo articolo affrontiamo la questione in modo tecnico e realistico: non come bloccare singoli attacchi, ma come ridisegnare l’accesso al server affinché l’attacco non sia nemmeno possibile nella maggior parte dei casi.

Il primo errore concettuale: pensare che SSH sia il problema

L’accesso tramite SSH non è intrinsecamente insicuro. Al contrario, OpenSSH è uno dei componenti più analizzati, robusti e continuamente oggetto di auditing. Se configurato correttamente, non rientra nel modello di minaccia realistico di un attaccante generico.

I bot che martellano la porta 22 non stanno “forzando SSH”: stanno provando combinazioni note di utenti e password. Nel momento in cui l’autenticazione mediante password viene disabilitata e si usano chiavi moderne (ed25519), quel vettore di attacco cessa di esistere.

Il punto critico, quindi, non è “rafforzare SSH fino all’infinito”, ma chiedersi: ha senso che un’interfaccia di amministrazione sia raggiungibile da Internet ovvero attraverso l’indirizzo IP pubblico del server?

Il punto di partenza: osservare cosa arriva al server

Su un sistema basato ad esempio su Ubuntu, la prima evidenza concreta arriva dai log di autenticazione. Il file /var/log/auth.log mostra immediatamente che l’accesso SSH è sondato in modo costante da parte di sconosciuti, spesso bot. Un’analisi iniziale può essere fatta direttamente dalla shell:

sudo grep "Failed password" /var/log/auth.log

oppure, per individuare reset e tentativi anomali:

sudo grep "Connection reset" /var/log/auth.log

Quello che conta non è il singolo evento, ma la frequenza. Se gli stessi pattern si ripetono ogni giorno, non si è davanti a un attacco mirato, ma a rumore di fondo ostile. Ed è proprio questo rumore che spinge a prendere decisioni architetturali, non semplicemente reattive.

Hardening SSH: consentire il “minimo indispensabile”

Il primo passo concreto per mettere in sicurezza l’accesso remoto consiste nell’abbandonare completamente l’autenticazione a password e passare a chiavi crittografiche moderne. Non è un semplice “rafforzamento”: è l’eliminazione di un’intera classe di attacchi.

Sul sistema client da cui ci si collega al server, è opportuno generare una chiave ed25519, oggi considerata lo standard di riferimento per SSH grazie alla sua sicurezza, velocità e dimensione ridotta. Il comando seguente crea una coppia di chiavi con un fattore di derivazione elevato, rendendo molto costoso qualsiasi tentativo di attacco offline sulla passphrase:

ssh-keygen -t ed25519 -a 64 -f ~/.ssh/id_ed25519

Una volta generata la chiave, questa deve essere installata sull’account remoto autorizzato. Finché l’accesso SSH è ancora disponibile, il metodo più semplice e sicuro è utilizzare ssh-copy-id, che si occupa automaticamente dei permessi e della posizione corretta del file authorized_keys:

ssh-copy-id -i ~/.ssh/id_ed25519.pub user@SERVER_IP

A questo punto il server è già pronto per funzionare senza alcuna password, ma è fondamentale rendere questa scelta obbligatoria anche lato daemon.

Configurazione di sshd: forzare il modello di sicurezza corretto

La configurazione del server SSH avviene tramite il file /etc/ssh/sshd_config. È essenziale modificare questo file mantenendo una sessione SSH attiva e testando le modifiche in parallelo, per evitare lockout accidentali.

sudo nano /etc/ssh/sshd_config

All’interno del file, le direttive chiave da impostare sono le seguenti:

PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
PermitEmptyPasswords no

Queste opzioni impongono un modello di accesso estremamente chiaro: nessun login diretto come root, nessuna password, nessuna autenticazione interattiva. Solo chi possiede una chiave valida può autenticarsi.

Per ridurre ulteriormente la superficie d’attacco, è consigliabile limitare il comportamento del daemon e il numero di tentativi concessi:

MaxAuthTries 3
LoginGraceTime 20
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no

In ambienti controllati, può essere utile restringere esplicitamente gli utenti autorizzati all’accesso SSH:

AllowUsers user

In una fase successiva, quando l’accesso sarà confinato a una VPN, è anche possibile vincolare il demone a un indirizzo di ascolto specifico (ad esempio l’interfaccia VPN), evitando che SSH ascolti su interfacce pubbliche:

#ListenAddress 10.0.0.1

Una volta completate le modifiche, la configurazione deve essere validata prima del reload del servizio:

sudo sshd -t && sudo systemctl reload ssh

Se il controllo sintattico non produce errori e la connessione attiva resta funzionante, l’accesso SSH può ora essere considerato correttamente irrobustito e pronto per essere ulteriormente protetto a livello di firewall e architettura di rete.

Il vero cambio di approccio: SSH non è un servizio pubblico

Una delle conclusioni più solide emerse dall’esperienza pratica è semplice: SSH non dovrebbe essere raggiungibile da Internet. Non perché sia debole, ma perché non serve che sia esposto sulla WAN. L’accesso amministrativo non è un’attività pubblica. È quindi logico spostarlo dietro una rete privata cifrata. In questo scenario, la VPN diventa perimetro di riferimento e con WireGuard l’approccio è estremamente leggero e pulito.

Si può quindi procedere con l’installazione di WireGuard e la generazione delle chiavi lato server:

sudo apt install wireguard

umask 077
wg genkey | tee /etc/wireguard/server.key | wg pubkey > /etc/wireguard/server.pub

Il file /etc/wireguard/wg0.conf definisce l’interfaccia privata:

[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = <chiave-privata-server>

Quando si introduce una VPN come WireGuard, il server non si limita più a essere un semplice endpoint, ma assume il ruolo di nodo di instradamento tra reti diverse. In pratica, deve essere in grado di ricevere pacchetti su un’interfaccia (quella VPN) e inoltrarli verso un’altra (LAN o Internet), e viceversa. Si può quindi attivare il forwarding IP con WireGuard in questo modo:

echo net.ipv4.ip_forward=1 | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl -p /etc/sysctl.d/99-wireguard.conf

Il comando sudo systemctl enable --now wg-quick@wg0 permette di avviare l’interfaccia di WireGuard. Conclusa questa procedura, il server è raggiungibile solo dai peer autorizzati. A questo punto, SSH può essere considerato un servizio interno alla VPN.

Firewall host-based: come rendere impossibile l’errore umano

Qui entra in gioco UFW. Il firewall non serve a “bloccare attacchi”, ma a rendere impossibili configurazioni sbagliate. La politica corretta è:

sudo ufw default deny incoming
sudo ufw default allow outgoing

Un firewall host-based non dovrebbe mai essere usato come lista infinita di eccezioni, ma come filtro negativo: tutto è bloccato, salvo ciò che è esplicitamente necessario.

Si autorizzano, ad esempio, solo i servizi realmente pubblici:

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

E l’accesso SSH viene limitato esclusivamente alla subnet VPN:

sudo ufw allow from 10.8.0.0/24 to any port 22 proto tcp

A questo punto, la porta SSH non è più raggiungibile da Internet, anche se il daemon risulta in ascolto.

Docker: il punto in cui molte configurazioni falliscono

Un errore estremamente comune riguarda Docker. Il noto software di containerizzazione manipola iptables e può esporre servizi bypassando il firewall host-based. Un approccio corretto volto al rafforzamento, dal punto di vista della sicurezza, della configurazione lato server passa dalla chain DOCKER-USER (catena di filtraggio di iptables pensata specificamente per dare all’amministratore un punto di controllo stabile e prioritario), che viene valutata prima delle regole automatiche di Docker.

Un approccio sicuro consiste nel permettere solo il traffico sulle porte 80/443 verso i container:

iptables -I DOCKER-USER -p tcp --dport 80 -j RETURN
iptables -I DOCKER-USER -p tcp --dport 443 -j RETURN
iptables -I DOCKER-USER -m conntrack --ctstate NEW -j DROP

In alternativa, i servizi amministrativi non devono essere pubblicati su 0.0.0.0, ma legati all’IP della VPN o accessibili solo via reverse proxy interno.

Fail2ban: igiene operativa, non una soluzione miracolosa

In un’architettura ben progettata, Fail2ban non rappresenta la prima linea di difesa, né tantomeno una “protezione magica”. La sua funzione reale è molto più pragmatica: ridurre il rumore operativo e rendere i log nuovamente leggibili.

Fail2ban è uno strumento di sicurezza reattiva per sistemi Linux che monitora i log dei servizi alla ricerca di comportamenti sospetti, come tentativi di accesso ripetuti o richieste anomale e applica automaticamente contromisure temporanee.

Quando servizi come SSH o un Web server pubblico sono inevitabilmente sondati da bot automatici, il problema principale non è il rischio immediato di compromissione, ma la quantità di eventi inutili che finiscono per mascherare segnali potenzialmente più interessanti. È esattamente in questo contesto che Fail2ban trova la sua collocazione naturale.

Configurato su servizi esposti, come SSH (quando ancora accessibile pubblicamente) o Nginx, Fail2ban limita il flooding dei log causato da tentativi ripetuti, introduce una penalizzazione temporale per comportamenti aggressivi, rende evidenti pattern ricorrenti che altrimenti si perderebbero nel rumore di fondo.

Un esempio minimale di jail per SSH (configurazione più semplice e funzionale possibile di Fail2ban per il servizio SSH) può essere il seguente:

[sshd]
enabled = true
logpath = /var/log/auth.log
maxretry = 4
findtime = 3600
bantime = 3600

Questa configurazione (sudo nano /etc/fail2ban/jail.local) non “protegge” un SSH mal configurato, ma migliora drasticamente la qualità dell’operatività quotidiana: meno eventi inutili, meno consumo di risorse, maggiore chiarezza.

In un sistema progettato correttamente, Fail2ban tende a diventare quasi invisibile. I ban sono rari, i log sono silenziosi e l’attenzione può tornare sui problemi reali. Ed è proprio questo silenzio operativo a indicare che l’architettura sta funzionando come previsto.

Conclusioni

Mettere in sicurezza un server esposto a Internet non significa inseguire ogni nuovo strumento o reagire a ogni tentativo di connessione sospetta. La sicurezza efficace nasce da scelte architetturali consapevoli, non da soluzioni improvvisate o stratificate senza una logica chiara.

Un server ben progettato espone solo ciò che è indispensabile, separa accesso utente e accesso amministrativo, usa una VPN come primo perimetro, considera firewall e container come elementi da integrare, non da sommare, si concentra sull’osservazione dei pattern nel tempo invece di reagire al singolo evento.

L’hardening di SSH elimina intere classi di attacchi noti, ma è solo il primo passo. Il vero salto di qualità avviene quando l’accesso amministrativo è separato dai servizi pubblici e confinato dietro una VPN, quando il firewall adotta una politica deny-by-default e quando i container non possono più aggirare le regole di rete del sistema host.

Ti consigliamo anche

Link copiato negli appunti