Cómo gestionar muchos servidores SSH

Da a los servidores nombres que muestren rol y entorno, como web-prod-1. Mantén un archivo de configuración de ssh por proyecto en ~/.ssh/config.d/ y aplica los ajustes comunes con patrones como Host web-*. Usa una clave distinta por propósito con IdentitiesOnly yes, guarda la lista de servidores en un único archivo de inventario y usa una herramienta como Ansible cuando repitas el mismo cambio en muchos servidores.

Actualizada el . Los ejemplos de configuración se pasaron por ssh -G con OpenSSH 10.3 en macOS y 10.0 en Debian 13.

Usa nombres que lleven información

Elige un esquema y respétalo, por ejemplo rol, entorno y número:

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

Un nombre así te dice qué vas a tocar y funciona con patrones. Un nombre como server3 o una dirección IP suelta no hace ninguna de las dos cosas. Añade un prefijo de proyecto o de cliente si te ocupas de más de uno.

Usa patrones, no líneas repetidas

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

Recuerda que gana el primer valor y que se aplican todos los bloques que coinciden, así que el orden importa. Mira la guía del archivo de configuración de ssh. Comprueba cualquier host con ssh -G web-prod-1.

Si tus nombres se resuelven en DNS, puedes prescindir de las líneas por host:

Host web-*
  HostName %h.example.com

Un archivo por proyecto

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

Cada archivo contiene los hosts de un proyecto, así que puedes compartirlo con un compañero, guardarlo en el repositorio de ese proyecto o borrarlo cuando el proyecto termine. Mantén Include al principio del archivo principal y los valores por defecto de Host * debajo.

Grupos

ssh no tiene grupos como tales. Los patrones son la forma sencilla, así que los nombres hacen el trabajo. Las etiquetas (tags), desde OpenSSH 9.4, agrupan hosts cuyos nombres no encajan entre sí:

Host legacy-api billing-1
  Tag prod

Match tagged prod
  User deploy

Para ejecutar un comando de solo lectura en un grupo, un simple bucle de shell basta a pequeña escala:

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 mantiene ssh lejos de la entrada estándar, así el mismo bucle también funciona cuando los nombres vienen de un archivo. BatchMode hace que falle en lugar de esperar en un prompt, y ConnectTimeout evita que un servidor caído retenga a los demás. Para ejecutar en paralelo y desplegar con cuidado, consulta cómo ejecutar un comando en varios servidores.

Una clave por propósito

Usar una clave por propósito, como trabajo, un cliente o producción, significa que perder una no abre todo. También significa que puedes retirar el acceso de un cliente quitando una sola clave.

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

Sin IdentitiesOnly yes, ssh ofrece también las claves de tu agente, lo que lleva a Too many authentication failures. No pongas IdentityFile en Host * si quieres esta separación: ese valor se añade a la lista y no la sustituye, así que también se prueba la otra clave.

Una clave por servidor suele dar más trabajo del que compensa. Si la lista de personas y servidores cambia constantemente, mira mejor los certificados SSH.

known_hosts con hash: una contrapartida

HashKnownHosts yes guarda los nombres de host de ~/.ssh/known_hosts como hashes, de modo que quien robe el archivo no obtiene una lista lista para usar de tus servidores. Debian y Ubuntu lo activan en /etc/ssh/ssh_config. El OpenSSH original lo deja desactivado.

  • Lo que ganas: un archivo robado no lista tus servidores en texto plano. Los hashes aún se pueden comprobar contra una suposición, así que esto frena a un atacante y no lo detiene.
  • Lo que pierdes: no puedes leer ni buscar en el archivo a simple vista, y el autocompletado de la shell basado en él deja de funcionar.

Las herramientas siguen funcionando con entradas 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 el original, con los nombres en claro, como known_hosts.old. Borra ese archivo si la privacidad era el motivo. Ten en cuenta que tu configuración de ssh, el historial de la shell y el inventario también contienen los nombres, así que el hash por sí solo oculta menos de lo que parece.

Mantén un inventario

Pasada una docena de servidores, mantén una lista que diga qué existe: nombre, dirección, rol, entorno, responsable. Un archivo de texto en un repositorio Git privado es suficiente. Que no contenga contraseñas ni claves. Esa lista es lo que consultas cuando alguien se va, o cuando te preguntas qué servidores siguen en uso.

Un inventario de Ansible es un buen formato para esto aunque todavía no uses Ansible:

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

[db]
db-prod-1 ansible_user=postgres

Si ya tienes una lista así, puedes generar a partir de ella los archivos de config.d, en lugar de mantener dos listas a mano. Los mismos archivos sirven también para una app que importa hosts desde ~/.ssh/config, incluidos los archivos indicados con Include.

Cuándo conviene una herramienta como Ansible

ssh es bueno para llegar a un servidor. No mantiene los servidores iguales. Pasa a Ansible o a una herramienta similar cuando hagas el mismo cambio en más de unos pocos servidores, cuando necesites repetir una configuración o cuando necesites un registro de lo que cambió.

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

Ansible usa el comando ssh por defecto, así que tu configuración, tus jump hosts y tus claves siguen funcionando. El destino solo necesita SSH y Python. Comprueba lo que hará un playbook con --check antes de ejecutarlo en producción, y lee cómo evitar errores en servidores de producción.

Preguntas

¿Cómo organizo muchos hosts en ~/.ssh/config?

Da a los hosts nombres que indiquen función y entorno, pon los ajustes comunes en patrones como Host web-* y guarda un archivo por proyecto en ~/.ssh/config.d/, cargado con Include.

¿Cómo ejecuto un comando en muchos servidores SSH?

Para pocos servidores basta un bucle de shell con ssh -n -o BatchMode=yes. Para cambios que repites, usa Ansible, que funciona sobre tu configuración de ssh.

¿Conviene una sola clave SSH para todos los servidores?

Usa una clave por propósito, como el trabajo, un cliente o producción, con IdentitiesOnly yes. Una clave por servidor suele dar más trabajo del que compensa.

Fuentes

Más en Configuración y muchos servidores

Todas las guías