SSH hardening avanzato: proteggere l'accesso ai VPS oltre le best practice di base
Il 14 maggio 2025 mi ha chiamato il responsabile IT di un gruppo veneto del settore logistica integrata per la ristorazione - 94 dipendenti distribuiti fra sede centrale di Treviso e tre magazzini regionali, circa 13 milioni di euro di fatturato annuo, un parco di 18 server Linux in produzione fra Hetzner Falkenstein e un piccolo data center privato. La chiamata arrivava tre giorni dopo la scoperta di un'intrusione: uno degli sviluppatori esterni dell'azienda aveva lasciato sei mesi prima senza passaggio di consegne formale, le sue chiavi SSH erano rimaste in ~/.ssh/authorized_keys di tre dei server senza che nessuno se ne accorgesse, e il 10 maggio si era collegato a uno dei server di staging "per recuperare un file di test" - o così aveva detto via email all'ex-CTO quando è stato scoperto dai log. L'audit forense che avevo condotto nei giorni successivi ha rivelato che oltre al server di staging l'ex-sviluppatore aveva fatto login anche su due server di produzione e aveva scaricato via SCP circa 2,3 GB di documenti includendo backup incompleti del database clienti. Le sue intenzioni restano dubbie - lui ha negato qualunque uso malevolo dei dati - ma l'esposizione era reale.
Il responsabile IT aveva già eseguito l'emergenza operativa: rimozione delle chiavi dell'ex-sviluppatore da tutti i server, audit completo dei authorized_keys su ogni macchina, rotazione delle credenziali database. Ma la conversazione strategica che mi aveva chiamato a fare era diversa: come facciamo a impedire strutturalmente che questa cosa si ripeta? L'azienda aveva già applicato quelle che chiamiamo le "best practice SSH standard" - porta non-22 (2222), PermitRootLogin no, PasswordAuthentication no, solo autenticazione a chiave. Ma tutte queste misure non avevano fatto nulla per prevenire l'incidente, perché l'ex-sviluppatore era un utente legittimo al momento di aggiungere le chiavi, e quelle chiavi erano rimaste attive ben oltre la cessazione del suo ruolo. Serviva un modello di autorizzazione SSH più sofisticato, che andasse strutturalmente oltre la semplice gestione di chiavi statiche in authorized_keys.
In nove giornate di lavoro distribuite in tre settimane, ho implementato su tutti i 18 server del cliente un'architettura SSH hardened a più livelli di difesa: certificati SSH con Certificate Authority interna con TTL brevi al posto delle chiavi raw, autenticazione 2FA tramite TOTP per utenti interattivi, restrizione rigorosa di AllowedUsers per ciascun host in funzione del ruolo, port knocking come layer ulteriore su server esposti a internet, logging centralizzato degli accessi con alert real-time per eventi anomali. Al termine dell'intervento, il processo di rimozione di un utente dal sistema è diventato un'operazione centralizzata e istantanea: basta rimuovere il suo certificato dalla CA o disabilitare l'utente nel sistema di autenticazione centrale, e nell'arco di 6 ore (al massimo TTL del certificato attivo) l'utente perde accesso a tutti i server simultaneamente - senza bisogno di toccare authorized_keys su ciascuna macchina. Il costo consulenziale dell'intervento è stato 8.200 euro. Il valore difensivo in termini di semplificazione operativa e riduzione del rischio residuo è stato misurato dal responsabile IT come uno dei migliori investimenti infrastrutturali degli ultimi tre anni.
Questo articolo è il playbook operativo con cui implemento SSH hardening avanzato in contesti PMI italiane, basato sull'esperienza di circa 25 progetti simili negli ultimi sei anni. Il principio guida è uno: le best practice SSH standard sono necessarie ma insufficienti per organizzazioni con parco server multiplo e turnover di personale non zero. Chi gestisce più di 5-8 server Linux in produzione ha bisogno di un modello di autorizzazione centralizzata, non di chiavi distribuite su ogni macchina.
Perché authorized_keys con chiavi statiche è insostenibile in aziende con più di pochi server
Il modello di autenticazione SSH tradizionale basato su ~/.ssh/authorized_keys è perfetto per scenari molto semplici - un singolo server, uno o due amministratori fissi nel tempo, zero rotazione di personale. In qualunque scenario più realistico per una PMI italiana, il modello produce nel tempo una serie di problemi strutturali che si accumulano silenziosamente fino a esplodere.
Primo problema: duplicazione delle chiavi su ogni server. Se l'azienda ha 18 server e 6 amministratori che hanno accesso a sottoinsiemi variabili di server, gestire manualmente i 108 file authorized_keys (con possibili sovrapposizioni) è organizzativamente impraticabile. Cambi di chiavi, aggiunte di nuovi amministratori, revoche, tutto diventa un incubo operativo. Secondo problema: assenza di audit centralizzato. Non esiste un registro unico di "chi ha accesso a cosa" - per sapere chi può connettersi al server X bisogna leggere il suo authorized_keys specifico. Audit e compliance diventano esercizi lunghi e inevitabilmente incompleti. Terzo problema: revoca inaffidabile. Quando un amministratore lascia l'azienda, bisogna ricordarsi di andare su ogni server a rimuovere la sua chiave. Se qualcuno dimentica anche solo un server, l'utente mantiene accesso privilegiato che non dovrebbe avere - esattamente lo scenario del cliente veneto. Quarto problema: le chiavi SSH raw non scadono mai. Una chiave aggiunta nel 2019 è ancora perfettamente valida nel 2026 se non è stata rimossa, senza nessun meccanismo di scadenza automatica.
La soluzione architetturale moderna per superare tutti questi problemi è SSH Certificate Authority, una feature nativa di OpenSSH documentata da oltre dieci anni ma adottata sistematicamente da poche organizzazioni italiane. L'idea è semplice e potente: invece di distribuire chiavi SSH pubbliche individuali su ciascun server, l'organizzazione crea una CA SSH interna (una chiave-coppia crittografica usata per firmare certificati), configura ciascun server per fidarsi di quella CA, e a ogni amministratore emette un certificato firmato dalla CA che include il suo user, i server a cui può accedere, e una scadenza breve (tipicamente 1-8 ore). Il certificato funziona come le chiavi tradizionali ma con tre vantaggi decisivi. Primo, si configura sui server una sola volta (la trust della CA pubblica), e da quel momento qualunque nuovo utente con certificato valido è autorizzato - senza toccare più i server. Secondo, il certificato ha scadenza automatica breve, quindi se un utente lascia l'azienda e la CA smette di emettere nuovi certificati per lui, l'accesso si estingue da solo entro poche ore. Terzo, il certificato è firmato dalla CA con logging centralizzato, quindi si sa esattamente chi ha ricevuto quali permessi e quando. La documentazione ufficiale OpenSSH sui certificati è disponibile nella manpage ssh-keygen(1) nella sezione CERTIFICATES, ed è il riferimento canonico per implementazione operativa.
Se gestisci un parco server Linux con più di cinque macchine in produzione e non hai ancora implementato SSH CA centralizzata, nel mio profilo professionale trovi il dettaglio degli interventi di hardening SSH e autorizzazione centralizzata che ho condotto in contesti PMI italiane, sempre con approccio calibrato sulla capacità operativa reale del team.
Certificate Authority SSH: l'architettura che sostituisce authorized_keys su scala
L'architettura concreta che ho implementato sul cliente veneto è strutturata così. Primo componente: una macchina bastion host dedicata (un VPS Hetzner CX21 separato, con hardening massimo e accesso limitato a due amministratori senior) ospita la CA SSH interna. Due chiavi-coppia distinte: una user CA che firma certificati per gli utenti amministratori, una host CA che firma certificati per i server stessi (così gli utenti possono verificare di connettersi al server legittimo invece che a un man-in-the-middle, feature che le chiavi host tradizionali non offrono nativamente). La chiave privata della CA è conservata offline su una YubiKey hardware con PIN protection - non mai sul disco del bastion host, per evitare esposizione in caso di compromissione del bastion.
Secondo componente: uno script di certificate signing request distribuito agli amministratori. Quando un amministratore deve accedere, esegue localmente uno script che genera una chiave-coppia temporanea e invia una CSR al bastion host via HTTPS autenticato con 2FA. Il bastion, dopo verifica identità tramite 2FA e autorizzazione policy, firma la CSR con TTL breve (4 ore di default) e i principal permissions appropriati, restituendo il certificato. L'amministratore carica il certificato in SSH agent e lo usa per connettersi ai server a cui è autorizzato. Dopo le 4 ore il certificato scade automaticamente e serve rinnovarlo.
Terzo componente: configurazione dei server. Ogni server ha nel /etc/ssh/sshd_config la direttiva TrustedUserCAKeys /etc/ssh/ca_user_key.pub che indica la chiave pubblica della user CA da fidarsi. Non ci sono più authorized_keys di utenti individuali - qualunque utente con certificato valido firmato dalla CA è accettato. Configurazioni specifiche per ruolo tramite Match User o Match Group limitano cosa ogni tipologia di utente può fare (es. gli sviluppatori non possono eseguire sudo, solo gli amministratori DB possono accedere al server DB master).
Il pattern di emissione dei certificati supporta ruoli e principal granulari. Esempio concreto di comando di firma sul bastion:
ssh-keygen -s /root/user_ca_key \
-I "marco_giovedi_14h" \
-n "marco-admin,marco-dba" \
-V +4h \
/tmp/csr_marco.pubIl certificato emesso può essere usato per loggarsi come utente marco-admin sui server che ammettono quel principal, come marco-dba sui server database, scade fra 4 ore, ha identifier univoco che permette revoca granulare. Il pattern di gestione delle credenziali si aggancia al modello strutturato di gestione dei file .env in produzione per Laravel e Symfony che ho descritto in un articolo dedicato, dove la CA SSH è uno dei componenti di un sistema di secrets management complessivo.
2FA con TOTP per utenti interattivi: la seconda chiave per i casi davvero sensibili
Oltre al meccanismo di certificati, per accessi a server particolarmente critici aggiungo un secondo fattore di autenticazione basato su TOTP (Time-based One-Time Password). Il pacchetto libpam-google-authenticator su Debian/Ubuntu permette di aggiungere 2FA a SSH tramite modulo PAM in modo trasparente. Il pattern di configurazione è: sui server identificati come critici, dopo l'autenticazione via certificato il sistema richiede inserimento di un codice TOTP generato dall'app di autenticazione dell'utente (Aegis, Google Authenticator, 1Password).
Il risultato è che anche se un attaccante ottenesse accesso al certificato SSH di un amministratore (improbabile ma non impossibile con endpoint compromesso), non potrebbe connettersi ai server critici senza anche il secondo fattore custodito sul dispositivo mobile dell'amministratore. Il trade-off è che 2FA aggiunge frizione al workflow quotidiano - non lo applico su tutti i server, solo sui 4-6 server veramente critici identificati con il responsabile IT del cliente. Per server di sviluppo, staging, o macchine amministrative di routine, il certificato SSH da solo è sufficiente.
AllowedUsers restrittivo, idle timeout, banner legal
Tre ulteriori misure di hardening operativo che applico sistematicamente su tutti i server di produzione sono relativamente banali ma efficaci nella difesa in profondità.
AllowedUsers/AllowedGroups. La direttiva AllowUsers di sshd limita esplicitamente quali utenti possono loggarsi tramite SSH, anche se altri utenti locali esistono nel sistema. Su ogni server, definisco esplicitamente l'elenco di utenti autorizzati a connettersi via SSH (tipicamente root disabilitato, sysadmin per amministratori generali, deploy per automazioni CI/CD, eventuali altri utenti specifici). Combinato con AllowGroups sshusers, questo pattern chiude strutturalmente il rischio che un utente di database o di applicazione (che non dovrebbe avere SSH) riceva accesso SSH accidentale.
Idle timeout. Le direttive ClientAliveInterval 300 e ClientAliveCountMax 2 disconnettono automaticamente sessioni SSH inattive dopo 10 minuti di inattività. Questo previene lo scenario in cui un amministratore dimentica una sessione SSH aperta su un laptop non presidiato e un attaccante con accesso fisico alla macchina la riutilizza.
Banner legal. La direttiva Banner /etc/ssh/banner mostra un messaggio all'utente prima dell'autenticazione. Il banner deve indicare che l'accesso è limitato a personale autorizzato, che l'uso è monitorato, che accessi non autorizzati sono perseguibili legalmente. Non è una protezione tecnica ma è un requisito legale per avere rilevanza probatoria in caso di procedura giudiziaria per accesso abusivo a sistema informatico ex art. 615-ter del codice penale italiano.
Monitoraggio centralizzato e alert real-time
L'ultimo pilastro dell'architettura è il monitoring centralizzato degli accessi SSH con alert real-time su eventi anomali. Il pattern che applico include tre livelli. Primo livello: logging centralizzato via rsyslog + Loki di tutti gli eventi sshd (auth success, auth failure, disconnect, session end) da tutti i server verso uno storage centrale. Grafana con dashboard dedicata mostra in tempo reale il numero di sessioni attive, il rate di tentativi falliti per ora, la distribuzione geografica degli IP sorgente, i top user per numero di accessi.
Secondo livello: alert real-time via Slack/email su pattern anomali. Alert automatici scattano per: tentativo di accesso da IP geograficamente anomalo rispetto al pattern storico dell'utente (un amministratore che si connette abitualmente da Italia improvvisamente da Russia), burst di tentativi falliti (più di 10 fallimenti in 60 secondi), accesso in orario anomalo (tipicamente tra le 2 e le 5 AM quando il team dorme), uso di un certificato SSH ritornato in un fileset pubblico del codice (controllato tramite pattern di revocation list periodica).
Terzo livello: integrazione con SIEM per correlation con altri eventi di sicurezza. Se un server riceve un accesso SSH nello stesso minuto in cui il firewall ha bloccato una richiesta malevola sul web, il SIEM correla i due eventi e genera un alert di severità più alta. Per PMI senza SIEM dedicato, il logging centralizzato + alert Slack coprono il 80% del beneficio operativo.
Il pattern complessivo si integra con i principi di amministrazione di sistema Linux su VPS per PMI senza DevOps dedicato che ho descritto in un articolo dedicato, dove l'automazione del deploy di configurazioni SSH hardened via Ansible è parte del pattern operativo standard.
Port knocking come layer ulteriore su bastion exposti a internet
Su bastion host direttamente esposti su internet - come il server CA del cliente veneto - aggiungo come strato ulteriore il port knocking: il server SSH non è raggiungibile dalla rete pubblica di default; risponde solo dopo una sequenza specifica di "knock" su porte predefinite. Il pacchetto knockd su Debian/Ubuntu implementa il meccanismo. L'amministratore autorizzato esegue una breve sequenza di connessioni TCP su porte sconosciute all'esterno (es. 7654, 8432, 3412 in ordine), il server knockd sul bastion rileva la sequenza esatta, apre temporaneamente iptables per permettere SSH dall'IP sorgente per 30 secondi, l'amministratore stabilisce la sessione SSH, quindi iptables richiude la porta.
Port knocking non è una difesa crittograficamente robusta - un attaccante che osserva il traffico può replicare la sequenza - ma rende invisibile il servizio SSH ai scanner automatizzati di internet che cercano target sulla porta 22 (o porte SSH comuni). Gli attacchi brute-force di bot su SSH globali si riducono a zero nel log - perché non trovano neanche il servizio attivo. È una pura security through obscurity che si aggiunge alla difesa crittografica vera, ma riduce significativamente il rumore di attacchi automatizzati e concentra il focus del team su threat reali.
Il risultato finale dell'intervento sul cliente veneto a sei mesi dal go-live è stato il seguente. Rimozione centralizzata degli accessi per dipendenti che lasciano l'azienda ridotta da "intervento manuale su ciascun server per ogni utente" a "revoca unica sulla CA, propagazione automatica entro 4 ore". Audit completo degli accessi SSH centralizzato su Grafana con visibilità real-time di chi è connesso dove. Zero incidenti di accesso non autorizzato nei sei mesi di operatività. Tentativi di brute-force SSH sui bastion esposti scesi da migliaia al giorno a zero (grazie al port knocking). Tempo medio di onboarding di un nuovo amministratore per accesso SSH: sceso da 2-3 ore (aggiungere chiave su 18 server più configurazione locale) a 15 minuti (aggiunta nell'auth server centrale, l'amministratore richiede certificato da solo al primo accesso). Costo consulenziale: 8.200 euro una tantum, zero costi ricorrenti aggiuntivi.
Se gestisci un parco server Linux con più di cinque macchine in produzione, hai turnover di personale tecnico non zero, e non hai ancora implementato SSH Certificate Authority centralizzata, ti trovi quasi certamente nella condizione di avere chiavi SSH legacy attive che sopravvivono a ruoli cessati - esattamente la condizione che ha causato l'incidente del cliente veneto. L'implementazione di SSH CA e degli altri layer descritti in questo articolo è un intervento una tantum che produce beneficio strutturale per la vita dell'infrastruttura. Se vuoi confrontarti sul tuo caso specifico con una valutazione tecnica del tuo attuale modello di autorizzazione SSH e una proposta di evoluzione calibrata sulla dimensione del tuo parco server, contattami per una consulenza preliminare: in una sessione di analisi guidata produciamo insieme un inventario del tuo attuale modello di accesso, una identificazione dei punti di esposizione più critici, e una roadmap di evoluzione verso CA centralizzata implementabile senza interruzioni operative del tuo ambiente esistente.