Comment gérer de nombreux serveurs SSH

Donnez aux serveurs des noms qui indiquent le rôle et l'environnement, comme web-prod-1. Gardez un fichier de configuration ssh par projet dans ~/.ssh/config.d/, et appliquez les réglages communs avec des motifs comme Host web-*. Utilisez une clé distincte par usage avec IdentitiesOnly yes, gardez la liste des serveurs dans un seul fichier d'inventaire, et passez à un outil comme Ansible dès que vous répétez le même changement sur de nombreux serveurs.

Mis à jour le . Les exemples de configuration ont été passés à ssh -G avec OpenSSH 10.3 sur macOS et 10.0 sur Debian 13.

Utiliser des noms qui portent de l'information

Choisissez un schéma et tenez-vous-y, par exemple rôle, environnement et numéro :

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

Un nom comme celui-ci vous dit ce que vous allez toucher, et il fonctionne avec les motifs. Un nom comme server3 ou une simple adresse IP ne fait ni l'un ni l'autre. Ajoutez un préfixe de projet ou de client si vous vous occupez de plusieurs.

Utiliser des motifs plutôt que des lignes répétées

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

Rappelez-vous que la première valeur l'emporte et que tous les blocs correspondants s'appliquent : l'ordre compte donc. Voir le guide de la configuration ssh. Vérifiez n'importe quel hôte avec ssh -G web-prod-1.

Si vos noms se résolvent dans le DNS, vous pouvez supprimer les lignes propres à chaque hôte :

Host web-*
  HostName %h.example.com

Un fichier par projet

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

Chaque fichier contient les hôtes d'un projet : vous pouvez le partager avec un collègue, le garder dans le dépôt de ce projet, ou le supprimer à la fin du projet. Gardez Include en haut du fichier principal, et les valeurs par défaut Host * en dessous.

Groupes

ssh n'a pas de groupes à proprement parler. Les motifs sont la solution simple, et c'est le nommage qui fait le travail. Les tags, depuis OpenSSH 9.4, regroupent des hôtes dont les noms ne s'alignent pas :

Host legacy-api billing-1
  Tag prod

Match tagged prod
  User deploy

Pour lancer une commande en lecture seule sur un groupe, une simple boucle shell suffit à petite échelle :

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 tient ssh à l'écart de l'entrée standard, si bien que la même boucle fonctionne aussi quand les noms viennent d'un fichier. BatchMode fait échouer au lieu d'attendre à une invite, et ConnectTimeout évite qu'un serveur mort retarde tous les autres. Pour lancer en parallèle et déployer prudemment, voyez comment exécuter une commande sur plusieurs serveurs.

Une clé par usage

Utiliser une clé par usage, comme le travail, un client ou la production, signifie que perdre l'une d'elles n'ouvre pas tout. Cela signifie aussi que vous pouvez retirer l'accès d'un client en retirant une seule clé.

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

Sans IdentitiesOnly yes, ssh propose aussi les clés de votre agent, ce qui mène à Too many authentication failures. Ne mettez pas IdentityFile dans Host * si vous voulez cette séparation : cette valeur est ajoutée à la liste et ne la remplace pas, donc l'autre clé est essayée aussi.

Une clé par serveur représente en général plus de travail qu'elle n'en vaut. Si la liste des personnes et des serveurs change sans cesse, regardez plutôt les certificats SSH.

known_hosts haché : un compromis

HashKnownHosts yes stocke les noms d'hôte de ~/.ssh/known_hosts sous forme de hachages, si bien que celui qui vole le fichier n'obtient pas de liste toute prête de vos serveurs. Debian et Ubuntu l'activent dans /etc/ssh/ssh_config. OpenSSH en amont le laisse désactivé.

  • Ce que vous gagnez : un fichier volé ne liste pas vos serveurs en clair. Les hachages peuvent quand même être testés contre une hypothèse : cela ralentit un attaquant sans l'arrêter.
  • Ce que vous perdez : vous ne pouvez plus lire ni chercher le fichier à l'œil, et la complétion du shell qui s'appuie dessus cesse de fonctionner.

Les outils fonctionnent toujours avec les entrées hachées :

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 conserve l'original, avec les noms en clair, sous le nom known_hosts.old. Supprimez ce fichier si la confidentialité était la raison de cette opération. Notez que votre configuration ssh, l'historique de votre shell et votre inventaire contiennent aussi ces noms : le hachage seul cache donc moins qu'il n'y paraît.

Tenir un inventaire

Au-delà d'une douzaine de serveurs, gardez une liste unique de ce qui existe : nom, adresse, rôle, environnement, responsable. Un fichier texte dans un dépôt Git privé suffit. Ne mettez ni mots de passe ni clés dedans. C'est cette liste que vous consultez quand quelqu'un part, ou quand vous demandez quels serveurs sont encore utilisés.

Un inventaire Ansible est un bon format pour cela, même si vous n'utilisez pas encore Ansible :

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

[db]
db-prod-1 ansible_user=postgres

Si vous avez déjà une liste de ce type, vous pouvez générer les fichiers de config.d à partir d'elle, au lieu de tenir deux listes à la main. Les mêmes fichiers servent aussi à une app qui importe les hôtes depuis ~/.ssh/config, y compris les fichiers cités par Include.

Quand un outil comme Ansible convient

ssh est bon pour atteindre un serveur. Il ne garde pas les serveurs identiques. Passez à Ansible ou à un outil similaire quand vous faites le même changement sur plus de quelques serveurs, quand vous devez répéter une installation, ou quand vous avez besoin d'une trace de ce qui a changé.

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

Ansible utilise par défaut la commande ssh : votre configuration, vos hôtes de rebond et vos clés continuent donc de fonctionner. La cible n'a besoin que de SSH et de Python. Vérifiez ce que fera un playbook avec --check avant de le lancer en production, et lisez comment éviter les erreurs sur les serveurs de production.

Questions

Comment organiser beaucoup d'hôtes dans ~/.ssh/config ?

Donnez aux hôtes des noms qui indiquent le rôle et l'environnement, placez les réglages communs dans des motifs comme Host web-*, et gardez un fichier par projet dans ~/.ssh/config.d/, chargé avec Include.

Comment lancer une commande sur beaucoup de serveurs SSH ?

Pour quelques serveurs, une boucle shell avec ssh -n -o BatchMode=yes suffit. Pour les changements répétés, utilisez Ansible, qui passe par votre configuration ssh existante.

Faut-il une seule clé SSH pour tous les serveurs ?

Utilisez une clé par usage, comme le travail, un client ou la production, avec IdentitiesOnly yes. Une clé par serveur demande en général plus d'efforts qu'elle n'en vaut.

Sources

Autres guides : Configuration et nombreux serveurs

Tous les guides