Come gestire molti server SSH

Dai ai server nomi che mostrano ruolo e ambiente, come web-prod-1. Tieni un file di configurazione ssh per progetto in ~/.ssh/config.d/ e applica le impostazioni comuni con pattern come Host web-*. Usa una chiave separata per ogni scopo con IdentitiesOnly yes, tieni l'elenco dei server in un solo file di inventario e usa uno strumento come Ansible quando ripeti la stessa modifica su molti server.

Aggiornata il . Gli esempi di configurazione sono stati verificati con ssh -G su OpenSSH 10.3 su macOS e 10.0 su Debian 13.

Usa nomi che portano informazione

Scegli uno schema e rispettalo, per esempio ruolo, ambiente e numero:

web-prod-1
web-prod-2
db-staging-1
acme-web-prod-1

Un nome così ti dice su cosa stai per intervenire e funziona con i pattern. Un nome come server3 o un semplice indirizzo IP non fa né l'una né l'altra cosa. Aggiungi un prefisso di progetto o di cliente se ne segui più di uno.

Usa i pattern, non righe ripetute

Host *-prod-*
  User deploy
  IdentityFile ~/.ssh/prod_ed25519
  IdentitiesOnly yes

Host web-prod-1
  HostName 203.0.113.11

Host web-prod-2
  HostName 203.0.113.12

Ricorda che vince il primo valore e che si applicano tutti i blocchi corrispondenti, quindi l'ordine conta. Vedi la guida al file di configurazione ssh. Controlla qualsiasi host con ssh -G web-prod-1.

Se i tuoi nomi si risolvono nel DNS, puoi eliminare le righe per singolo host:

Host web-*
  HostName %h.example.com

Un file per progetto

# top of ~/.ssh/config
Include ~/.ssh/config.d/*
~/.ssh/config.d/
  acme
  internal
  lab

Ogni file contiene gli host di un progetto, così puoi condividerlo con un collega, tenerlo nel repository di quel progetto o eliminarlo quando il progetto finisce. Tieni Include in cima al file principale e i default di Host * sotto.

Gruppi

ssh non ha gruppi in senso stretto. I pattern sono il modo semplice, quindi sono i nomi a fare il lavoro. I tag, da OpenSSH 9.4, raggruppano host i cui nomi non seguono uno schema comune:

Host legacy-api billing-1
  Tag prod

Match tagged prod
  User deploy

Per eseguire un comando in sola lettura su un gruppo, un semplice ciclo di shell va bene su piccola scala:

for host in web-prod-1 web-prod-2; do
  printf '%s: ' "$host"
  ssh -n -o BatchMode=yes -o ConnectTimeout=5 "$host" uptime
done

-n tiene ssh lontano dall'input standard, così lo stesso ciclo funziona anche quando i nomi arrivano da un file. BatchMode fa fallire invece di attendere a un prompt e ConnectTimeout evita che un server morto blocchi tutti gli altri. Per l'esecuzione in parallelo e i rilasci prudenti, vedi come eseguire un comando su più server.

Una chiave per scopo

Usare una chiave per scopo, come lavoro, un cliente o la produzione, significa che perderne una non apre tutto. Significa anche che puoi togliere l'accesso a un cliente rimuovendo una sola chiave.

Host acme-*
  IdentityFile ~/.ssh/acme_ed25519
  IdentitiesOnly yes

Senza IdentitiesOnly yes, ssh offre anche le chiavi presenti nel tuo agent, il che porta a Too many authentication failures. Non mettere IdentityFile in Host * se vuoi questa separazione: quel valore viene aggiunto all'elenco e non lo sostituisce, quindi viene provata anche l'altra chiave.

Una chiave per server di solito costa più fatica di quanto valga. Se l'elenco di persone e server cambia di continuo, guarda invece i certificati SSH.

known_hosts con hash: un compromesso

HashKnownHosts yes salva i nomi host in ~/.ssh/known_hosts come hash, così chi ruba il file non ottiene un elenco pronto dei tuoi server. Debian e Ubuntu lo attivano in /etc/ssh/ssh_config. OpenSSH originale lo lascia disattivato.

  • Cosa guadagni: un file rubato non elenca i tuoi server in chiaro. Gli hash si possono comunque confrontare con un'ipotesi, quindi questo rallenta un attaccante e non lo ferma.
  • Cosa perdi: non puoi leggere o cercare il file a occhio, e il completamento della shell basato su di esso smette di funzionare.

Gli strumenti funzionano comunque con le voci con hash:

ssh-keygen -F web-prod-1      # find a host
ssh-keygen -R web-prod-1      # remove a host
ssh-keygen -H                 # hash an existing file

ssh-keygen -H conserva l'originale, con i nomi in chiaro, come known_hosts.old. Cancella quel file se lo hai fatto per riservatezza. Nota che anche la tua configurazione ssh, la cronologia della shell e l'inventario contengono i nomi, quindi l'hash da solo nasconde meno di quanto sembri.

Tieni un inventario

Oltre la dozzina di server, tieni un solo elenco che dica cosa esiste: nome, indirizzo, ruolo, ambiente, responsabile. Basta un file di testo in un repository Git privato. Non metterci password né chiavi. L'elenco è ciò che controlli quando qualcuno se ne va, o quando ti chiedi quali server sono ancora in uso.

Un inventario Ansible è un buon formato per questo anche se non usi ancora Ansible:

[web]
web-prod-1
web-prod-2

[db]
db-prod-1 ansible_user=postgres

Se hai già un elenco così, puoi generare da esso i file di config.d, invece di mantenere due elenchi a mano. Gli stessi file servono anche a un'app che importa gli host da ~/.ssh/config, compresi i file indicati da Include.

Quando conviene uno strumento come Ansible

ssh è ottimo per raggiungere un server. Non mantiene i server uguali tra loro. Passa ad Ansible o a uno strumento simile quando fai la stessa modifica su più di pochi server, quando devi ripetere una configurazione o quando ti serve una traccia di cosa è cambiato.

ansible all -i inventory.ini -m ping
ansible web -i inventory.ini -a 'uptime'

Ansible usa di default il comando ssh, quindi la tua configurazione, i jump host e le chiavi continuano a funzionare. Il bersaglio ha bisogno solo di SSH e Python. Controlla cosa farà un playbook con --check prima di eseguirlo in produzione, e leggi come evitare errori sui server di produzione.

Domande

Come organizzo molti host in ~/.ssh/config?

Dai agli host nomi che indicano ruolo e ambiente, metti le impostazioni comuni in pattern come Host web-* e tieni un file per progetto in ~/.ssh/config.d/, caricato con Include.

Come eseguo un comando su molti server SSH?

Per pochi server basta un ciclo della shell con ssh -n -o BatchMode=yes. Per le modifiche che ripeti, usa Ansible, che passa dalla tua configurazione ssh.

Conviene una sola chiave SSH per tutti i server?

Usa una chiave per scopo, come lavoro, un cliente o produzione, con IdentitiesOnly yes. Una chiave per server di solito costa più fatica di quanto valga.

Fonti

Altro in Configurazione e tanti server

Tutte le guide