1,874 24/03/2026 07/04/2026 8 min

Hai appena messo online un nuovo server Linux e SSH è l’unico punto di accesso. In questa fase non serve un tutorial teorico. Serve una checklist corta, verificabile e ripetibile.

Lo scenario è semplice: server appena deployato, accesso root ancora attivo, chiavi non ancora controllate, fail2ban non testato e troppo spesso una regola sbagliata che ti lascia fuori. Qui trovi una mini checklist post-deploy pensata per evitare proprio questo.

Obiettivo pratico: verificare hardening SSH, permessi file, aggiornamenti, segreti e principio del least privilege senza perdere tempo in passaggi superflui.

Prerequisiti

Prima di toccare la configurazione, assicurati di avere una sessione SSH già aperta e un secondo accesso di emergenza. Se lavori su cloud, prepara anche la console web del provider.

  • Accesso sudo su una shell attiva.
  • Un utente non-root già creato.
  • Chiave SSH privata disponibile sul client.
  • Accesso alla console del provider o al pannello rescue.
  • Pacchetti base: openssh-server, fail2ban, cron o systemd timer per gli update.

Warning: non chiudere mai la sessione corrente prima di aver aperto una seconda connessione con il nuovo utente e la nuova chiave.

Step 1: fotografa lo stato reale di sshd prima di cambiare

Il primo controllo non è il file di configurazione, ma lo stato effettivo di sshd. Molte opzioni vengono sovrascritte da include, drop-in o override del sistema.

sudo sshd -T | egrep 'permitrootlogin|passwordauthentication|pubkeyauthentication|kbdinteractiveauthentication|x11forwarding|allowtcpforwarding|clientaliveinterval|clientalivecountmax'

# Output:

permitrootlogin no
passwordauthentication no
pubkeyauthentication yes
kbdinteractiveauthentication no
x11forwarding no
allowtcpforwarding no

Perché funziona: sshd -T mostra la configurazione già risolta, non solo quello che leggi nel file.

Nota cross-distro: su alcune distribuzioni le direttive effettive arrivano da /etc/ssh/sshd_config.d/*.conf, non solo da /etc/ssh/sshd_config.

Step 2: blocca l’accesso root e verifica che l’utente operativo funzioni

Il punto non è “vietare root” in astratto. Il punto è confermare che l’utente amministrativo usi sudo e che la chiave funzioni prima di disabilitare il login diretto.

sudo useradd -m -s /bin/bash deployops
sudo usermod -aG sudo deployops
sudo mkdir -p /home/deployops/.ssh
sudo chmod 700 /home/deployops/.ssh
sudo cp /root/.ssh/authorized_keys /home/deployops/.ssh/authorized_keys
sudo chown -R deployops:deployops /home/deployops/.ssh
sudo chmod 600 /home/deployops/.ssh/authorized_keys

# Output:

drwx------ 2 deployops deployops 4096 ... /home/deployops/.ssh
-rw------- 1 deployops deployops  399 ... /home/deployops/.ssh/authorized_keys

Perché funziona: SSH rifiuta chiavi e directory con permessi troppo larghi, quindi il controllo dei permessi è parte della sicurezza, non un dettaglio estetico.

Note: su Debian e Ubuntu il gruppo amministrativo è spesso sudo. Su RHEL, AlmaLinux e Rocky è più comune wheel.

Test immediato da client

ssh -i ~/.ssh/id_ed25519 deployops@server.example.com 'whoami && sudo -n true && echo sudo-ok'

# Output:

deployops
sudo-ok

Perché funziona: provi sia l’autenticazione sia il passaggio a sudo senza password interattiva.

Step 3: riduci la superficie di attacco di sshd senza rompere l’accesso

Qui conviene lavorare per blocchi piccoli. Cambiare troppi parametri insieme rende difficile capire quale opzione ha causato un lockout.

sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%F)
sudoedit /etc/ssh/sshd_config

Inserisci o verifica queste direttive:

PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
X11Forwarding no
AllowTcpForwarding no
MaxAuthTries 3
ClientAliveInterval 300
ClientAliveCountMax 2

# Output:

Config saved

Perché funziona: togli i metodi di login più fragili e limiti brute force e sessioni zombie.

Warning: se usi tunnel SSH per amministrazione o deploy, AllowTcpForwarding no può romperli. In quel caso lascialo su yes e limita con match per utente o gruppo.

Prima del reload, fai il controllo sintattico.

sudo sshd -t

# Output:

nessun output

Perché funziona: sshd -t blocca errori di sintassi prima che il demone ricarichi una configurazione invalida.

Step 4: installa e testa fail2ban con un caso reale, non a occhio

fail2ban serve solo se legge i log giusti e applica la jail giusta. La verifica post-deploy deve confermare entrambi.

sudo apt-get update && sudo apt-get install -y fail2ban
sudo systemctl enable --now fail2ban
sudo fail2ban-client status

# Output:

Status
|- Number of jail:      1
`- Jail list:           sshd

Perché funziona: prima avvii il servizio, poi controlli se la jail SSH è caricata davvero.

Su RHEL, AlmaLinux e Rocky il comando equivalente usa dnf.

sudo dnf install -y fail2ban

Ora verifica la jail nel dettaglio.

sudo fail2ban-client status sshd

# Output:

Status for the jail: sshd
|- Filter
|  |- Currently failed: 0
|  `- Total failed: 0
`- Actions
   |- Currently banned: 0
   `- Total banned: 0

Perché funziona: confermi che il filtro monitora i tentativi falliti e che l’azione di ban è attiva.

Note: se non vedi la jail, controlla il backend log. Su systemd recenti spesso conviene backend = systemd.

Simula un errore di login per vedere il ban

ssh baduser@server.example.com

# Output:

Permission denied (publickey).

Perché funziona: un tentativo fallito reale genera evento nei log e permette a fail2ban di reagire.

Step 5: controlla i permessi file SSH con una scansione mirata

Molti problemi di sicurezza nascono da file troppo aperti. La parte fastidiosa è che SSH li segnala con un errore poco esplicito.

sudo find /home -maxdepth 2 -type d -name .ssh -exec stat -c '%A %U:%G %n' {} \;
sudo find /home -maxdepth 3 -name authorized_keys -exec stat -c '%A %U:%G %n' {} \;

# Output:

drwx------ deployops:deployops /home/deployops/.ssh
-rw------- deployops:deployops /home/deployops/.ssh/authorized_keys

Perché funziona: i permessi corretti impediscono a SSH di rifiutare la chiave e riducono il rischio di lettura accidentale.

Se trovi deviazioni, correggi in blocco.

sudo chmod 700 /home/deployops/.ssh
sudo chmod 600 /home/deployops/.ssh/authorized_keys
sudo chown -R deployops:deployops /home/deployops/.ssh

Note: se usi home directory montate via NFS o filesystem con ACL, verifica anche le ACL con getfacl.

Step 6: aggiorna il sistema e verifica che la finestra di manutenzione sia reale

Un server sicuro ma non aggiornato resta esposto. Il controllo post-deploy deve includere patch e reboot pending, soprattutto se hai appena creato l’immagine.

sudo apt-get update
sudo apt-get -s upgrade | sed -n '1,20p'
if [ -f /var/run/reboot-required ]; then echo reboot-required; fi

# Output:

0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.

Perché funziona: il test simulato ti dice se esistono aggiornamenti prima di pianificare la finestra di riavvio.

Su RHEL-like usa:

sudo dnf check-update
sudo needs-restarting -r

Warning: se il kernel è stato aggiornato, pianifica il reboot. Non lasciare il server in stato misto per giorni.

Step 7: tratta i segreti come segreti, non come file di progetto

Chiavi API, token e password non devono stare in repository, immagini, backup casuali o variabili esportate senza controllo. Questa verifica è piccola ma molto utile subito dopo il deploy.

sudo grep -RInE '(api[_-]?key|secret|token|password)' /etc /opt/app 2>/dev/null | head -n 20

# Output:

/opt/app/.env:3=DATABASE_PASSWORD=***

Perché funziona: trovi in fretta segreti lasciati in chiaro nei percorsi più probabili.

Se l’app usa file .env, limita i permessi.

sudo chown root:root /opt/app/.env
sudo chmod 600 /opt/app/.env

# Output:

-rw------- 1 root root 412 ... /opt/app/.env

Perché funziona: riduci la lettura accidentale da parte di altri utenti o processi non necessari.

Step 8: applica il least privilege anche ai servizi di sistema

La sicurezza non finisce su SSH. Se il servizio applicativo gira come root, stai spostando il problema più avanti. Il controllo post-deploy deve confermare l’utente effettivo del servizio.

systemctl cat app.service
systemctl show app.service -p User -p Group -p NoNewPrivileges -p PrivateTmp

# Output:

User=appuser
Group=appuser
NoNewPrivileges=yes
PrivateTmp=yes

Perché funziona: separi il processo applicativo dai privilegi di sistema e limiti il danno in caso di compromissione.

Se il servizio non ha utente dedicato, crealo prima di andare in produzione.

sudo useradd --system --home /var/lib/app --shell /usr/sbin/nologin appuser

Note: per servizi web o worker, usa account di servizio senza login e con home dedicata.

Verifica finale

Qui serve una conferma rapida, non un report lungo. Esegui questi controlli in sequenza.

  • Apri una nuova sessione SSH con chiave.
  • Verifica che root non possa entrare via SSH.
  • Controlla la jail fail2ban e lo stato dei ban.
  • Controlla i permessi di .ssh, authorized_keys e dei file .env.
  • Conferma che gli update siano applicati o pianificati.
  • Verifica che il servizio applicativo non giri come root.
ssh -o PreferredAuthentications=publickey -o PasswordAuthentication=no deployops@server.example.com 'whoami'
sudo fail2ban-client status sshd
sudo sshd -T | egrep 'permitrootlogin|passwordauthentication|pubkeyauthentication'
sudo find /home -maxdepth 3 -name authorized_keys -exec stat -c '%A %U:%G %n' {} \;

# Output:

deployops
Status for the jail: sshd
permitrootlogin no
passwordauthentication no
-rw------- deployops:deployops /home/deployops/.ssh/authorized_keys

Perché funziona: questa sequenza conferma accesso, protezione, ban e permessi con pochi comandi.

Troubleshooting

1) Permission denied (publickey).

Causa: la chiave è corretta, ma i permessi di .ssh o authorized_keys non lo sono.

Fix:

sudo chmod 700 /home/deployops/.ssh
sudo chmod 600 /home/deployops/.ssh/authorized_keys
sudo chown -R deployops:deployops /home/deployops/.ssh

2) sshd: no hostkeys available -- exiting.

Causa: il demone non trova le chiavi host, spesso dopo un hardening troppo aggressivo o un’immagine incompleta.

Fix:

sudo ssh-keygen -A
sudo systemctl restart sshd

3) ERROR Failed during configuration: Jail 'sshd' is not defined

Causa: fail2ban è attivo, ma la jail SSH non viene caricata dal file di configurazione corretto.

Fix:

sudo grep -RIn '^[[]sshd[]]' /etc/fail2ban
sudo systemctl restart fail2ban
sudo fail2ban-client status

Conclusione

Una buona sicurezza SSH dopo il deploy non richiede cinquanta interventi. Richiede una sequenza corta, testata e con output verificabile.

Se vuoi fare il passo successivo, trasforma questa checklist in uno script di bootstrap idempotente e salvalo nel tuo repository infrastrutturale. Così il controllo resta uguale su ogni nuovo server.

Note: il vero vantaggio non è “blindare tutto”. È sapere esattamente cosa hai cambiato e come tornare indietro senza panico.