m4nifest0-tech

Cerca nel sito

Cybersecurity

Hardening di SSH su Debian: checklist pratica

Chiavi ed25519, configurazione con drop-in, root e password disabilitati, fail2ban e un metodo di test per non chiudersi fuori da un server Debian.
Pubblicato il Aggiornato il 8 min di letturaLivello: Base

SSH è nella maggior parte delle infrastrutture il principale canale di amministrazione: chi lo raggiunge con credenziali valide ottiene il controllo del sistema. Questa guida descrive una configurazione sobria per Debian 12 e 13: autenticazione solo con chiave, root escluso, accesso limitato a un gruppo di amministratori, tentativi limitati e ban automatico dei client insistenti. Ogni passaggio è verificabile e l'ordine è pensato per non perdere l'accesso al server.

Regola preliminare: non chiudersi fuori

Ogni modifica a sshd può rendere il server irraggiungibile. La regola è una sola: finché la nuova configurazione non è stata provata da una seconda connessione, la sessione già aperta non va chiusa.

Attenzione

Tenere aperta almeno una sessione SSH per tutta la durata delle modifiche e provare ogni cambiamento da un secondo terminale. systemctl reload ssh non interrompe le sessioni esistenti, quindi quella aperta resta disponibile per annullare un errore. Se il server è una VM o un container su Proxmox, la console dell'hypervisor (per un LXC, pct enter dal nodo) è un accesso di emergenza indipendente da SSH.

Chiavi ed25519

Generare la coppia di chiavi

Sul client si genera una coppia ed25519. La passphrase protegge la chiave privata se il file viene copiato; l'opzione -a aumenta i round della funzione di derivazione che la protegge.

Sul client
ssh-keygen -t ed25519 -a 100 -C "admin@portatile"

La chiave privata (~/.ssh/id_ed25519) non lascia mai il client. Il file .pub è l'unico da distribuire.

Installare la chiave pubblica sul server

Sul client
ssh-copy-id -i ~/.ssh/id_ed25519.pub [email protected]

Il comando aggiunge la chiave al file ~/.ssh/authorized_keys dell'utente sul server e richiede ancora l'autenticazione con password, quindi va eseguito prima di disattivarla. Subito dopo si verifica il login a chiave in una nuova sessione.

Configurazione tramite drop-in

Da Debian 12 il file principale /etc/ssh/sshd_config include i file *.conf della directory /etc/ssh/sshd_config.d/. Conviene lasciare intatto il file principale, gestito dal pacchetto, e scrivere le modifiche in un file dedicato: gli aggiornamenti non producono conflitti.

Una regola di OpenSSH è decisiva: per ogni parola chiave vale il primo valore letto. L'Include sta in testa al file principale e i file della directory sono letti in ordine lessicale, quindi un prefisso numerico basso prevale sugli altri. Alcune immagini preconfigurate aggiungono propri file, come 50-cloud-init.conf, con valori diversi: 10-hardening.conf li precede e vince.

/etc/ssh/sshd_config.d/10-hardening.conf (sul server)
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey
MaxAuthTries 3
AllowGroups ssh-users

Il significato delle direttive:

  • PermitRootLogin no impedisce il login diretto come root. L'amministrazione passa da un utente nominale e da sudo, che lascia traccia di chi ha eseguito cosa. Il valore predefinito di Debian, prohibit-password, consente ancora root con chiave.
  • PasswordAuthentication no e KbdInteractiveAuthentication no vanno impostate entrambe. Con UsePAM yes, impostazione predefinita su Debian, il metodo keyboard-interactive può chiedere la password di sistema tramite PAM e aggirare la prima direttiva.
  • AuthenticationMethods publickey dichiara in modo esplicito l'unico metodo ammesso.
  • MaxAuthTries 3 riduce i tentativi per connessione (il valore predefinito è 6).
  • AllowGroups ssh-users limita l'accesso ai membri del gruppo. AllowUsers fa lo stesso con i nomi utente; usare una sola delle due direttive mantiene la regola leggibile.
Sul server
sudo groupadd ssh-users
sudo usermod -aG ssh-users utente

Importante

Il gruppo va creato e popolato prima di ricaricare il servizio. Con AllowGroups riferito a un gruppo vuoto o inesistente nessuno potrebbe più accedere.

Nota

Ogni chiave offerta dall'agente SSH conta come un tentativo. Con molte chiavi caricate, MaxAuthTries 3 può produrre l'errore «Too many authentication failures» prima ancora di provare quella giusta. Sul client si risolve con IdentitiesOnly yes e IdentityFile in ~/.ssh/config, così viene offerta solo la chiave prevista.

Verifica e ricarica

La sintassi si controlla prima di ricaricare. Il comando non stampa nulla se la configurazione è valida.

Sul server
sudo sshd -t
sudo systemctl reload ssh

Con la prima sessione ancora aperta, da un secondo terminale si prova un normale login a chiave (ssh [email protected]) e poi si verifica che la password sia davvero rifiutata:

Sul client, in una seconda sessione
ssh -o PubkeyAuthentication=no -o PreferredAuthentications=password [email protected]
[email protected]: Permission denied (publickey).

I valori effettivi, dopo l'unione di tutti i file, si leggono con sshd -T. È il controllo più affidabile: se un valore non coincide con quello scritto nel drop-in, un altro file lo sta sovrascrivendo.

Sul server
sudo sshd -T | grep -Ei '^(permitrootlogin|passwordauthentication|kbdinteractiveauthentication|authenticationmethods|maxauthtries|allowgroups) '
permitrootlogin no
maxauthtries 3
passwordauthentication no
kbdinteractiveauthentication no
authenticationmethods publickey
allowgroups ssh-users

L'ordine delle righe può variare tra le versioni di OpenSSH.

fail2ban

fail2ban legge i log di autenticazione e blocca per un certo tempo gli indirizzi che falliscono ripetutamente. Con la sola autenticazione a chiave non aggiunge protezione crittografica: riduce il rumore nei log e il carico dovuto ai tentativi inutili. Resta utile come difesa in profondità.

Sul server
sudo apt install fail2ban
/etc/fail2ban/jail.d/sshd.conf (sul server)
[sshd]
enabled = true
backend = systemd
maxretry = 5
findtime = 10m
bantime = 1h
ignoreip = 127.0.0.1/8 ::1 203.0.113.10

backend = systemd fa leggere i messaggi dal journal, utile quando non esiste /var/log/auth.log. In ignoreip conviene inserire l'indirizzo da cui si amministra, per non bannare sé stessi durante i test.

Sul server
sudo systemctl restart fail2ban
sudo fail2ban-client status sshd
Status for the jail: sshd
|- Filter
|  |- Currently failed: 0
|  |- Total failed:     0
|  `- Journal matches:  _SYSTEMD_UNIT=sshd.service + _COMM=sshd
`- Actions
   |- Currently banned: 0
   |- Total banned:     0
   `- Banned IP list:

La forma esatta dell'output dipende dalla versione. Per provare la jail si tentano, da un host di prova diverso da quello indicato in ignoreip, alcuni login con un nome utente inesistente: ogni riga «Invalid user» nel journal deve far aumentare il contatore dei fallimenti.

Suggerimento

Le versioni recenti di OpenSSH separano il processo per connessione (sshd-session) dal listener. Se il contatore resta a zero, la causa più probabile è un filtro non aggiornato: si controlla con sudo fail2ban-regex systemd-journal /etc/fail2ban/filter.d/sshd.conf. Per rimuovere un blocco indesiderato: sudo fail2ban-client set sshd unbanip 203.0.113.50.

Cosa non serve

  • Cambiare la porta. Non è una misura di sicurezza: riduce il rumore dei bot, non l'esposizione, e complica regole di firewall, strumenti e documentazione. Per ridurre davvero l'esposizione conviene raggiungere SSH solo dalla rete di gestione, come descritto in segmentazione della rete con VLAN, oppure tramite una VPN WireGuard.
  • Rimodellare a mano gli algoritmi crittografici. I valori predefiniti di OpenSSH su Debian sono ragionevoli. Modificare Ciphers, MACs o KexAlgorithms ha senso solo per requisiti di conformità e rischia incompatibilità con i client.

Passi successivi: secondo fattore e certificati

Il passo successivo è un secondo fattore: un codice TOTP richiesto tramite PAM e combinato con la chiave grazie a AuthenticationMethods publickey,keyboard-interactive. Richiede di riattivare KbdInteractiveAuthentication e configurare PAM, quindi va provato in laboratorio, con la sessione di riserva aperta.

L'alternativa, adatta a più server, sono i certificati SSH: una chiave di autorità (CA) firma le chiavi degli utenti con una scadenza breve e i server si fidano della CA tramite TrustedUserCAKeys, senza distribuire elenchi di chiavi pubbliche. Il drop-in si può applicare a più host con un playbook, come nel flusso descritto in provisioning di VM con cloud-init e Ansible.

Checklist finale

  • Chiave ed25519 generata con passphrase, installata con ssh-copy-id e provata in una nuova sessione
  • Drop-in 10-hardening.conf creato, file principale intatto
  • Root, password e keyboard-interactive disabilitati
  • Gruppo ssh-users creato e popolato, AllowGroups attivo
  • MaxAuthTries ridotto e IdentitiesOnly yes sul client
  • sshd -t senza errori, systemctl reload ssh eseguito con una sessione ancora aperta
  • Valori effettivi confermati con sshd -T e login con password rifiutato
  • fail2ban attivo con jail sshd e contatori verificati
  • SSH raggiungibile solo dalla rete di gestione o da VPN
  • Valutati secondo fattore o certificati SSH

Articoli correlati