Vai al contenuto
GLGiuseppe Lanza Scrivimi

SSH sicuro su Linux: chiavi, configurazione e accessi controllati

Proteggere l’accesso SSH a un server Ubuntu: chiavi Ed25519, configurazione di sshd con un file dedicato, verifiche senza chiudersi fuori, socket activation, 2FA facoltativa e jump host con ProxyJump.

Sistemi: Ubuntu 24.04 LTS · OpenSSH 9Livello: IntermedioTempo: 40 minutiAggiornata: novembre 2026

Obiettivo e scenario

SSH è la porta d’ingresso di ogni server Linux. Una configurazione predefinita funziona, ma accetta password, permette tentativi ripetuti e non limita chi può entrare. In questa guida la rendi solida, un passo alla volta:

  • accesso solo con chiavi Ed25519, niente password;
  • niente accesso diretto di root, solo gli utenti di un gruppo dedicato;
  • configurazione in un file separato, verificabile prima di applicarla;
  • facoltativo: secondo fattore TOTP e accesso tramite un jump host.
Sessione SSH cifrata dal PC al server (ProxyJump) PC amministratore chiave privata ~/.ssh/id_ed25519 Internet Jump host (bastion) pubblico 203.0.113.20 LAN 10.0.20.1 srv-web 10.0.20.11 authorized_keys srv-db 10.0.20.12 authorized_keys ssh srv-web inoltra solo verso la porta 22 LAN server 10.0.20.0/24 La chiave privata non lascia mai il PC: sui server c’è solo la pubblica.
L’amministratore entra dal jump host, unico server esposto, e raggiunge i server interni. L’autenticazione avviene sempre con la sua chiave, direttamente sul server di destinazione.

Gli indirizzi sono di documentazione o privati: sostituiscili con i tuoi. Gli esempi usano l’utente admin; il server di partenza è un Ubuntu 24.04 con OpenSSH 9.6.

Regola d’oro: non chiuderti fuori

Per tutta la guida tieni aperta la sessione SSH che stai usando. Ogni modifica la provi da un secondo terminale. Le sessioni già aperte sopravvivono al riavvio di sshd: se la nuova configurazione ti blocca, correggi dalla prima. Se il server è una VM, tieni a portata di mano anche la console dell’hypervisor.

Chiavi Ed25519

1. Generare la coppia di chiavi sul tuo PC

La chiave si crea sul client, mai sul server. Ed25519 è veloce, ha chiavi corte ed è supportata da tutte le versioni recenti di OpenSSH:

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

-a 100 indica i cicli di derivazione usati per cifrare la chiave privata con la passphrase: rende molto più lento un attacco a forza bruta se qualcuno ti ruba il file. Metti sempre una passphrase e usa ssh-agent per non doverla digitare a ogni connessione. Ottieni due file:

FileContenutoDove va
~/.ssh/id_ed25519Chiave privata (cifrata con la passphrase)Resta solo sul tuo PC
~/.ssh/id_ed25519.pubChiave pubblicaSui server, in ~/.ssh/authorized_keys

2. Copiare la chiave pubblica sul server

Finché l’accesso con password è attivo, il modo più semplice è ssh-copy-id:

ssh-copy-id -i ~/.ssh/id_ed25519.pub admin@10.0.20.11

Il comando aggiunge la chiave in fondo a ~/.ssh/authorized_keys dell’utente remoto e sistema i permessi. Se lo fai a mano, controlla che siano questi, altrimenti sshd ignora la chiave (opzione StrictModes, attiva per impostazione predefinita):

Sul server, come utente admin
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
# la home non deve essere scrivibile da altri
chmod go-w ~

Sul PC vale lo stesso: la chiave privata deve avere permessi 600, altrimenti il client la rifiuta con il messaggio “UNPROTECTED PRIVATE KEY FILE”.

3. Provare l’accesso con la chiave

ssh -i ~/.ssh/id_ed25519 admin@10.0.20.11

Ti deve chiedere la passphrase della chiave, non la password dell’utente. Se ti chiede la password, la chiave non viene accettata: risolvi adesso, prima di disattivare le password.

4. Un gruppo per chi può entrare

Invece di elencare gli utenti uno per uno, crea un gruppo e consenti SSH solo ai suoi membri. Aggiungere o togliere un amministratore diventa una sola operazione:

sudo groupadd ssh-utenti
sudo usermod -aG ssh-utenti admin
id admin        # deve comparire ssh-utenti

Fallo prima di attivare AllowGroups: un utente fuori dal gruppo viene respinto anche con la chiave giusta.

La configurazione di sshd

Perché un file separato

Su Ubuntu 24.04 il file /etc/ssh/sshd_config contiene, in cima, la riga Include /etc/ssh/sshd_config.d/*.conf. Per quasi tutte le direttive sshd usa il primo valore che incontra: quello che scrivi nei file inclusi prevale quindi sul resto di sshd_config. Tra i file inclusi, vince quello che viene prima in ordine alfabetico.

Per questo il file si chiama 10-sicurezza.conf: viene letto prima, per esempio, di 50-cloud-init.conf, che sulle immagini cloud contiene spesso PasswordAuthentication yes. Il file principale resta intatto e gli aggiornamenti del pacchetto non ti chiedono di unire modifiche.

/etc/ssh/sshd_config.d/10-sicurezza.conf
# --- Autenticazione ---
PermitRootLogin no
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitEmptyPasswords no
AuthenticationMethods publickey

# --- Chi può entrare ---
AllowGroups ssh-utenti

# --- Limiti ai tentativi ---
MaxAuthTries 3
LoginGraceTime 30
MaxStartups 10:30:60

# --- Funzioni non necessarie ---
X11Forwarding no
AllowAgentForwarding no
# su un jump host questa riga va tolta: vedi più avanti
AllowTcpForwarding no

# --- Sessioni inattive e log ---
ClientAliveInterval 300
ClientAliveCountMax 2
LogLevel VERBOSE
DirettivaEffetto
PermitRootLogin noRoot non entra via SSH, nemmeno con la chiave. Entri come utente e usi sudo.
PasswordAuthentication noDisattiva le password: gli attacchi a dizionario diventano inutili.
KbdInteractiveAuthentication noDisattiva anche la richiesta “interattiva” via PAM, che altrimenti chiederebbe comunque la password. Sostituisce la vecchia ChallengeResponseAuthentication.
AuthenticationMethods publickeyRende esplicito che l’unico metodo accettato è la chiave.
AllowGroups ssh-utentiEntra solo chi appartiene al gruppo. Tutti gli altri utenti, anche di sistema, sono esclusi.
MaxAuthTries 3Dopo 3 tentativi falliti nella stessa connessione, sshd chiude.
LoginGraceTime 30Secondi a disposizione per completare l’accesso (predefinito 120).
MaxStartups 10:30:60Oltre 10 connessioni non ancora autenticate ne scarta il 30%, fino al 100% a quota 60. È il valore predefinito: lo scrivo per renderlo visibile.
X11Forwarding no, AllowAgentForwarding no, AllowTcpForwarding noDisattivano inoltri che su un server di solito non servono e che allargano la superficie d’attacco.
ClientAliveInterval 300 e ClientAliveCountMax 2Chiude le sessioni il cui client non risponde più da circa 10 minuti.
LogLevel VERBOSERegistra l’impronta della chiave usata a ogni accesso: sai sempre quale chiave è entrata.

UsePAM yes resta come nel file principale di Ubuntu: serve per sessioni, limiti e, se lo attivi, per il secondo fattore.

Il file deve finire in .conf

L’Include legge solo i file *.conf. Un 10-sicurezza.txt o un 10-sicurezza.conf.bak vengono ignorati senza alcun avviso.

Verificare e applicare senza chiudersi fuori

1. Controllo della sintassi

sudo sshd -t

Se non stampa nulla, la sintassi è corretta. Un errore indica file e riga. Non riavviare mai sshd se sshd -t segnala problemi.

2. Controllo della configurazione effettiva

sshd -T stampa i valori che sshd userà davvero, dopo aver letto tutti i file. È il modo più sicuro per scoprire se un altro file ti sta sovrascrivendo:

sudo sshd -T | grep -Ei '^(permitrootlogin|passwordauthentication|kbdinteractiveauthentication|authenticationmethods|allowgroups|maxauthtries|x11forwarding)'

Le direttive compaiono in minuscolo. Per vedere come si applicano eventuali blocchi Match a una connessione specifica:

sudo sshd -T -C user=admin,host=pc-admin,addr=10.0.99.25 | grep -i authenticationmethods

3. Applicare

sudo systemctl restart ssh

Su Ubuntu il servizio si chiama ssh, non sshd. La sessione corrente resta aperta.

4. Provare da un secondo terminale

# deve entrare con la chiave
ssh admin@10.0.20.11

# deve essere respinto: "Permission denied (publickey)"
ssh -o PubkeyAuthentication=no -o PreferredAuthentications=password admin@10.0.20.11

# deve essere respinto anche root
ssh root@10.0.20.11

Solo quando il primo comando funziona e gli altri due falliscono puoi chiudere la sessione originale. Nel registro trovi il motivo di ogni rifiuto:

sudo journalctl -u ssh -f

Ubuntu 24.04: ssh.socket e cambio di porta

Da Ubuntu 22.10 sshd parte tramite socket activation di systemd: è l’unità ssh.socket ad ascoltare sulla porta 22 e ad avviare ssh.service alla prima connessione. Puoi vederlo così:

systemctl status ssh.socket
sudo ss -tlnp | grep ':22'     # in ascolto c'è systemd, non sshd

Per quasi tutte le direttive non cambia nulla: modifichi il file e riavvii ssh. Cambia invece per Port, ListenAddress e AddressFamily, perché le porte in ascolto le apre la socket, non sshd.

Come funziona oggi

Ubuntu 24.04 include un generatore di systemd che legge Port e ListenAddress da sshd_config (file inclusi compresi) e configura di conseguenza ssh.socket. Il generatore però gira solo quando systemd ricarica la configurazione. Dopo aver cambiato la porta servono quindi due comandi, che trovi anche nel commento in cima a sshd_config:

Cambio porta su Ubuntu 24.04
# 1. apri PRIMA la nuova porta nel firewall (esempio con UFW)
sudo ufw allow 2222/tcp

# 2. aggiungi "Port 2222" al tuo file e verifica
echo 'Port 2222' | sudo tee -a /etc/ssh/sshd_config.d/10-sicurezza.conf
sudo sshd -t

# 3. rigenera la socket e riavviala
sudo systemctl daemon-reload
sudo systemctl restart ssh.socket

# 4. controlla e prova da un altro terminale
sudo ss -tlnp | grep ':2222'
ssh -p 2222 admin@10.0.20.11

Un semplice systemctl restart ssh non basta: sshd leggerebbe la nuova porta, ma la socket continuerebbe ad ascoltare sulla 22. Il comportamento è stato corretto con gli aggiornamenti di OpenSSH per la 24.04 (bug Launchpad 2069041): tieni il sistema aggiornato.

Cambiare porta non è una misura di sicurezza

Riduce il rumore nei log, perché i robot provano soprattutto la 22, ma chi ti cerca davvero trova la porta in pochi secondi. La protezione vera sono le chiavi, AllowGroups e un firewall che limita gli indirizzi di origine.

Tornare al servizio classico (facoltativo)

Se preferisci che sshd resti sempre in esecuzione e apra le porte da solo, come nelle versioni precedenti:

sudo systemctl disable --now ssh.socket
# eventuali override lasciati dagli aggiornamenti dalle versioni precedenti
sudo rm -f /etc/systemd/system/ssh.service.d/00-socket.conf
sudo rm -f /etc/systemd/system/ssh.socket.d/addresses.conf
sudo systemctl daemon-reload
sudo systemctl enable --now ssh.service
sudo ss -tlnp | grep sshd

Fallo da console o tenendo aperta la sessione: tra i due comandi nessuno ascolta sulla porta SSH, ma le sessioni esistenti restano attive.

Algoritmi crittografici (facoltativo)

OpenSSH 9.6 ha già impostazioni predefinite moderne: niente algoritmi davvero deboli attivi. Restringere ulteriormente l’elenco è utile se devi rispettare una politica aziendale o un controllo di conformità, ma va fatto con prudenza: un client vecchio, un apparato di rete o uno strumento di backup potrebbero non riuscire più a collegarsi.

Prima guarda cosa supporta il server:

ssh -Q kex
ssh -Q cipher
ssh -Q mac
sudo sshd -T | grep -Ei '^(kexalgorithms|ciphers|macs)'

Un elenco ristretto e ancora molto compatibile con i client degli ultimi anni:

/etc/ssh/sshd_config.d/20-algoritmi.conf
KexAlgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com

sntrup761x25519-sha512@openssh.com è uno scambio di chiavi ibrido resistente ai computer quantistici, già predefinito in OpenSSH 9. Le versioni più recenti (dalla 9.9) aggiungono mlkem768x25519-sha256, che però non è disponibile nel pacchetto di Ubuntu 24.04. Verifica sempre con sshd -t e prova da tutti i client che usi. Per un controllo dall’esterno c’è il pacchetto ssh-audit:

sudo apt install ssh-audit
ssh-audit 10.0.20.11

Secondo fattore TOTP (facoltativo)

Sui server più esposti, per esempio il jump host, puoi chiedere oltre alla chiave un codice a 6 cifre generato da un’app sul telefono. Si usa il modulo PAM di Google Authenticator, compatibile con qualsiasi app TOTP.

1. Installare e attivare per l’utente

sudo apt install libpam-google-authenticator

# come utente admin (non con sudo):
google-authenticator -t -d -f -r 3 -R 30 -w 3

Le opzioni: codici basati sull’ora (-t), ogni codice usabile una sola volta (-d), al massimo 3 accessi ogni 30 secondi (-r 3 -R 30), tolleranza di un codice prima e dopo (-w 3). Inquadra il QR code con l’app e conserva i codici di emergenza in un posto sicuro. Il segreto finisce in ~/.google_authenticator. L’orologio del server deve essere sincronizzato: controlla con timedatectl.

2. Configurare PAM

/etc/pam.d/sshd – estratto
# Standard Un*x authentication.
# commentata: con la chiave come primo fattore non serve la password
#@include common-auth

# secondo fattore TOTP
auth required pam_google_authenticator.so

Commentare common-auth evita che, dopo la chiave, venga chiesta anche la password. Durante il passaggio puoi scrivere pam_google_authenticator.so nullok: gli utenti che non hanno ancora configurato il TOTP entrano con la sola chiave. Toglilo appena tutti sono pronti.

3. Configurare sshd

Modifica lo stesso file 10-sicurezza.conf: per la regola del primo valore, un file 20-… non riuscirebbe a sovrascrivere i valori già impostati.

/etc/ssh/sshd_config.d/10-sicurezza.conf – righe modificate
KbdInteractiveAuthentication yes
AuthenticationMethods publickey,keyboard-interactive
sudo sshd -t && sudo systemctl restart ssh

La virgola significa “entrambi, in quest’ordine”: prima la chiave, poi il codice. Prova da un secondo terminale: dopo la passphrase della chiave deve comparire Verification code:. PasswordAuthentication resta no.

Jump host con ProxyJump

Esporre su Internet un solo server, il jump host, e raggiungere gli altri passando da lì riduce molto la superficie d’attacco. Con ProxyJump il jump host si limita a inoltrare la connessione TCP: la sessione SSH è cifrata dal tuo PC fino al server finale, e l’autenticazione con la chiave avviene direttamente su quest’ultimo. La chiave privata non passa mai dal jump host.

Lato client: ~/.ssh/config

~/.ssh/config (sul PC)
Host bastion
    HostName 203.0.113.20
    User admin
    IdentityFile ~/.ssh/id_ed25519
    IdentitiesOnly yes

Host srv-*
    User admin
    IdentityFile ~/.ssh/id_ed25519
    IdentitiesOnly yes
    ProxyJump bastion

Host srv-web
    HostName 10.0.20.11

Host srv-db
    HostName 10.0.20.12

Ora ssh srv-web passa automaticamente dal jump host. Anche scp e rsync usano questo file. Senza file di configurazione, l’equivalente al volo è:

ssh -J admin@203.0.113.20 admin@10.0.20.11

IdentitiesOnly yes fa offrire al client solo la chiave indicata: se nell’agent ne hai molte, eviti di esaurire i tentativi di MaxAuthTries.

Non usare ForwardAgent al posto di ProxyJump

Con ForwardAgent yes l’agent del tuo PC diventa utilizzabile dal jump host: chi controlla quel server, finché sei collegato, può usare le tue chiavi per entrare altrove. ProxyJump non ha bisogno dell’inoltro dell’agent.

Lato jump host

ProxyJump richiede che sul jump host sia permesso l’inoltro TCP. Nel suo 10-sicurezza.conf sostituisci AllowTcpForwarding no con un inoltro limitato alle sole porte SSH dei server interni:

/etc/ssh/sshd_config.d/10-sicurezza.conf – sul jump host
AllowTcpForwarding local
PermitOpen 10.0.20.11:22 10.0.20.12:22
PermitTTY yes

AllowTcpForwarding local consente solo l’inoltro dal client verso il server (quello che usa ProxyJump), non quello inverso. PermitOpen elenca le uniche destinazioni raggiungibili. Sui server interni, infine, permetti SSH solo dall’indirizzo LAN del jump host (10.0.20.1) e dalla rete di amministrazione.

Firewall e fail2ban

La configurazione di sshd decide come si entra; il firewall decide da dove. Le due cose si completano.

  • Limita gli indirizzi di origine: se gli amministratori arrivano da reti note (sede, VPN), consenti SSH solo da lì. Con UFW: sudo ufw allow from 10.0.99.0/24 to any port 22 proto tcp. Con nftables vedi la guida Firewall completo con nftables.
  • Rallenta i tentativi sulle porte che devono restare aperte a tutti: sudo ufw limit 22/tcp respinge un indirizzo che tenta 6 o più connessioni in 30 secondi.
  • Banna chi insiste: fail2ban legge i log di sshd e blocca per un periodo gli indirizzi che sbagliano ripetutamente. La configurazione è nella guida Proteggere i servizi con fail2ban.

Con le sole chiavi gli attacchi a dizionario non possono riuscire, ma generano rumore nei log e consumano risorse: firewall e fail2ban li fermano prima che arrivino a sshd. Se ti serve accedere da Internet senza esporre la porta 22, valuta una VPN: vedi VPN IPsec tra Windows e Ubuntu.

Problemi comuni

SintomoCausa probabileCosa fare
Permission denied (publickey) con la chiave giustaPermessi troppo larghi su home, ~/.ssh o authorized_keysApplica 700 e 600; nel log cerca “bad ownership or modes”
Respinto, nel log “not allowed because none of user’s groups are listed in AllowGroups”Utente non nel gruppo ssh-utentisudo usermod -aG ssh-utenti utente; vale dalla connessione successiva
Chiede ancora la passwordIl file non finisce in .conf, oppure un file letto prima imposta PasswordAuthentication yesControlla con sudo sshd -T | grep -i passwordauth e ls /etc/ssh/sshd_config.d/
“Too many authentication failures”Il client offre troppe chiavi dall’agent e supera MaxAuthTriesIdentitiesOnly yes e IdentityFile nel ~/.ssh/config
Nuova porta ignorata, sshd ascolta ancora sulla 22Socket activation: la porta la apre ssh.socketsudo systemctl daemon-reload e sudo systemctl restart ssh.socket
sshd -t: “Missing privilege separation directory: /run/sshd”Con la socket activation la cartella viene creata solo all’avvio del serviziosudo mkdir -p /run/sshd e ripeti il test; non è un errore di configurazione
ProxyJump fallisce con “channel 0: open failed: administratively prohibited”Inoltro TCP vietato sul jump host o destinazione non in PermitOpenSul jump host AllowTcpForwarding local e controlla PermitOpen
Dopo il TOTP chiede anche la password@include common-auth ancora attivo in /etc/pam.d/sshdCommentalo e riavvia ssh
Fuori dal server, nessuna sessione apertaConfigurazione errata applicataAccedi dalla console (hypervisor, IPMI, cloud), correggi il file, sshd -t, riavvia
Prima di andare in produzione

Prova sempre la configurazione in laboratorio, salva quella attuale prima di modificarla e tieni un accesso di emergenza (console o accesso fuori banda) quando lavori su firewall e VPN da remoto.