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

Dopo un deploy, la sicurezza non si “presume”. Si verifica.

Su un VPS o su un server dedicato, i problemi arrivano quasi sempre nello stesso ordine: SSH troppo aperto, fail2ban non attivo, file sensibili leggibili da troppo utenti, pacchetti vecchi, segreti finiti nei log o in una .env lasciata in giro.

Questa checklist è pensata per i minuti subito dopo il deploy. Non sostituisce un hardening completo. Serve a scoprire gli errori più costosi prima del traffico reale.

Scenario tipico: hai appena pubblicato un’app, magari con systemd, un reverse proxy diverso dal solito, e un accesso SSH che deve restare disponibile. L’obiettivo è semplice: confermare che l’accesso sia limitato, i log siano utili, i permessi siano corretti e gli aggiornamenti non siano rimandati per abitudine.

Prerequisiti

Prima di iniziare, assicurati di avere:

  • accesso root o sudo da una seconda sessione SSH;
  • una shell con bash o simile;
  • un sistema Linux con OpenSSH e, se usato, fail2ban;
  • una copia del file di configurazione SSH da controllare;
  • il servizio applicativo già avviato, ma non ancora esposto senza verifiche.

Warning: non chiudere la sessione SSH attiva finché non hai verificato che una nuova connessione funzioni con la nuova configurazione. Il lockout resta l’errore più comune.

Note: i comandi sotto sono volutamente brevi. L’idea è usarli come blocchi di controllo, non come una procedura di provisioning completa.

Step 1: verifica la configurazione effettiva di sshd

Il file /etc/ssh/sshd_config non basta. Conta la configurazione effettivamente in uso, inclusi i valori ereditati e le direttive ripetute.

Usa sshd -T per vedere come il demone interpreterà davvero le opzioni. È il controllo più veloce per scoprire se PasswordAuthentication, PermitRootLogin o PubkeyAuthentication sono coerenti con l’hardening previsto.

sshd -T | egrep 'permitrootlogin|passwordauthentication|pubkeyauthentication|kbdinteractiveauthentication|x11forwarding|allowtcpforwarding|permitemptypasswords'

# Output:

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

Se l’output non coincide con il tuo standard, cerca prima eventuali file inclusi in /etc/ssh/sshd_config.d/. Molti problemi nascono da lì, non dal file principale.

Perché farlo subito

Una configurazione SSH errata blocca l’accesso amministrativo o lascia aperta l’autenticazione debole. Entrambi i casi costano tempo e rischio operativo.

Step 2: controlla l’accesso effettivo con una prova da seconda sessione

La verifica più utile è pratica. Apri una nuova shell e prova un login con il metodo previsto. Se usi chiavi, forza l’uso della chiave corretta.

ssh -o PreferredAuthentications=publickey -o PasswordAuthentication=no user@server.example.com

# Output:

Welcome to Debian GNU/Linux 12

Se il login fallisce, non toccare ancora il servizio. Prima confronta la configurazione applicata e i log di sshd.

journalctl -u ssh -n 20 --no-pager

# Output:

Accepted publickey for user from 203.0.113.10 port 51422 ssh2

Note: su alcune distribuzioni il servizio si chiama sshd. Su Debian e Ubuntu spesso è ssh. Il nome cambia, il controllo no.

Step 3: verifica fail2ban e la jail SSH

Fail2ban serve solo se monitora il servizio giusto e scrive le regole giuste nel backend corretto. Non basta che il demone sia attivo.

Controlla lo stato del servizio e la jail usata per SSH. Poi verifica se esistono ban attivi o se la jail è vuota perché punta al log sbagliato.

systemctl is-active fail2ban
fail2ban-client status
fail2ban-client status sshd

# Output:

active
Status
|- Number of jail:      1
`- Jail list:           sshd
Status for the jail: sshd
|- Filter
|  |- Currently failed: 0
|  `- Total failed:     3
`- Actions
   |- Currently banned:  0
   `- Total banned:     0

Se la jail sshd non appare, spesso il filtro punta a auth.log quando il sistema usa journald, o viceversa.

Un controllo utile è leggere la configurazione finale della jail:

grep -R "^*\(logpath\|backend\|enabled\)" /etc/fail2ban/jail* /etc/fail2ban/jail.d/* 2>/dev/null

# Output:

backend = systemd
enabled = true
logpath = %(sshd_log)s

Warning: se fail2ban banna il tuo IP di test, devi avere un canale alternativo per rientrare. Una console del provider o un IP secondario aiutano molto.

Step 4: controlla permessi e ownership dei file sensibili

Molti incidenti nascono da permessi troppo larghi. Il classico errore è una chiave privata leggibile da altri utenti, oppure una .env lasciata in una directory web pubblica.

Parti dalle chiavi SSH e dalle directory home. Poi passa ai file dell’applicazione e ai secret file usati da systemd.

ls -ld ~/.ssh
ls -l ~/.ssh
stat -c '%a %U %G %n' ~/.ssh ~/.ssh/authorized_keys ~/.ssh/id_ed25519
find /var/www -maxdepth 3 \( -name .env -o -name '*.key' -o -name '*secret*' \) -ls

# Output:

700 user user /home/user/.ssh
600 user user /home/user/.ssh/authorized_keys
600 user user /home/user/.ssh/id_ed25519

Per i file di configurazione dell’app, applica il principio del minimo privilegio. Se il web server deve solo leggere, il file non deve essere modificabile da quel processo.

chown root:www-data /var/www/app/.env
chmod 640 /var/www/app/.env
chmod 750 /var/www/app
chmod 640 /etc/systemd/system/app.service.d/override.conf

# Output:

nessun output se i comandi riescono

Note: non usare chmod 777 come scorciatoia. Risolve il sintomo e amplifica il problema.

Step 5: verifica i segreti fuori dal codice e fuori dai log

I segreti non devono stare nel repository, nei log di shell, né nei dump di variabili d’ambiente stampati durante il deploy.

Se usi file .env, controlla che non siano versionati e che il processo li legga solo tramite un utente dedicato. Se usi systemd, verifica che EnvironmentFile punti al file giusto e con i permessi corretti.

git -C /var/www/app ls-files | grep -E '(^|/)\.env$|secret|key'
systemctl cat app.service | sed -n '/EnvironmentFile/p'
cat /proc/$(pidof app)/environ | tr '\0' '\n' | egrep 'SECRET|TOKEN|KEY|PASSWORD'

# Output:

nessuna riga che mostri segreti in chiaro

Se trovi una variabile sensibile in /proc/<pid>/environ, ricorda che il problema non è solo quello che vedi. È tutto ciò che può leggerla con gli stessi privilegi.

Step 6: conferma aggiornamenti di sicurezza e reboot policy

Un server appena deployato non dovrebbe restare fermo su pacchetti vecchi per settimane. Controlla gli aggiornamenti disponibili e verifica se esistono patch di sicurezza rimaste indietro.

apt update
apt list --upgradable
unattended-upgrades --dry-run --debug 2>/dev/null | head -n 40

# Output:

Listing...
openssl/stable-security 3.0.13-1~deb12u1 amd64 [upgradable from: 3.0.12-1]
libssl3/stable-security 3.0.13-1~deb12u1 amd64 [upgradable from: 3.0.12-1]

Su sistemi con kernel aggiornato, verifica anche se è richiesto un reboot. Molti dimenticano questo punto e credono di essere coperti, mentre il kernel vecchio resta in memoria.

if [ -f /var/run/reboot-required ]; then cat /var/run/reboot-required; fi

# Output:

/var/run/reboot-required

Warning: se il server è in manutenzione o con carico basso, pianifica il reboot subito dopo aver chiuso la checklist. Non aspettare il primo alert.

Step 7: applica least privilege ai servizi che hai appena deployato

Il principio del minimo privilegio non riguarda solo utenti e password. Riguarda anche systemd, accesso al filesystem, capability e rete.

Un servizio web non dovrebbe avere più permessi del necessario. Se può leggere una directory, non deve scriverla. Se ascolta su una porta alta, non ha bisogno di capability extra. Se non deve uscire in rete, blocca l’uscita dove puoi.

systemctl show app --property=User,Group,NoNewPrivileges,ProtectSystem,ProtectHome,PrivateTmp,CapabilityBoundingSet,AmbientCapabilities

# Output:

User=appuser
Group=appuser
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
CapabilityBoundingSet=
AmbientCapabilities=

Se i valori sono vuoti o permissivi, il servizio sta chiedendo troppo. Meglio correggerlo adesso che dopo un audit o un incidente.

Un override minimale può bastare:

[Service]
User=appuser
Group=appuser
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
ReadWritePaths=/var/www/app/storage /var/log/app

# Output:

limitazione coerente con le sole directory scrivibili

Verifica finale

La verifica finale deve dirti se il server è pronto a reggere traffico reale senza esporre più del necessario.

  • Connettiti via SSH con il metodo atteso.
  • Conferma sshd -T e confronta i parametri critici.
  • Controlla che fail2ban abbia la jail sshd attiva.
  • Verifica permessi di .ssh, file applicativi e secret file.
  • Controlla aggiornamenti disponibili e reboot richiesto.
  • Conferma che il servizio giri con un utente dedicato e con restrizioni systemd.

Se vuoi un test rapido da fare a fine checklist, usa questo mini-blocco:

sshd -T | egrep 'permitrootlogin|passwordauthentication|pubkeyauthentication'
fail2ban-client status sshd
stat -c '%a %U %G %n' ~/.ssh ~/.ssh/authorized_keys
systemctl show app --property=User,NoNewPrivileges,ProtectSystem,ProtectHome

# Output:

permitrootlogin no
passwordauthentication no
pubkeyauthentication yes
Status for the jail: sshd
700 user user /home/user/.ssh
600 user user /home/user/.ssh/authorized_keys
User=appuser
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes

Troubleshooting

1) Failed publickey for user from 203.0.113.10 port 51422 ssh2

Causa: la chiave non corrisponde all’utente, oppure authorized_keys ha permessi o ownership errati.

Fix:

chown -R user:user ~/.ssh
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

# Output:

permessi corretti per il login con chiave

2) ERROR No file(s) found for glob /var/log/auth.log

Causa: fail2ban cerca il log tradizionale, ma il sistema usa journald o un backend diverso.

Fix:

grep -R "backend\|logpath" /etc/fail2ban
systemctl restart fail2ban
fail2ban-client status sshd

# Output:

Status for the jail: sshd

3) Authentication refused: bad ownership or modes for directory /home/user/.ssh

Causa: la directory .ssh è scrivibile da gruppo o altri, e OpenSSH blocca il login per sicurezza.

Fix:

chmod 700 ~/.ssh
chmod go-w ~
ls -ld ~ ~/.ssh

# Output:

home directory e .ssh non più scrivibili da gruppo o altri

Conclusione

Una checklist post-deploy utile non cerca la perfezione. Cerca gli errori che aprono la porta o fermano il rientro in produzione.

Se questa serie di controlli passa, hai già ridotto molto il rischio operativo. Il prossimo passo concreto è automatizzare gli stessi test in una pipeline o in un job di post-deploy, così non dipendono dalla memoria di chi rilascia.