# ¿Está lista tu rotación de claves de host SSH?

Una clave de host SSH debe cambiar según tu calendario, con su sustituta ya reconocida como fiable. Si los desarrolladores se enteran de la rotación por primera vez al ver el aviso rojo en su terminal, el plan ya ha fallado. Ese aviso no puede decirles si el servidor cambió de forma legítima o si alguien interceptó la conexión. Un mensaje apresurado que pida borrar una línea de `known_hosts` destruye las pruebas necesarias para decidir.

Una rotación segura consta de cuatro acciones distintas: publicar las huellas antigua y nueva mediante un canal fiable, ofrecer ambas claves el tiempo suficiente para que los clientes aprendan la nueva, retirar la antigua durante una ventana definida y demostrar que los clientes rechazan la identidad retirada. He visto equipos hacer bien todo el trabajo del servidor y, aun así, enseñar a sus desarrolladores el peor hábito posible: tratar la verificación del host como un obstáculo. El trabajo operativo consiste en hacer habitual la vía segura antes de que aparezca el aviso.

## La identidad del host no es la autenticación del usuario

Una clave de host demuestra qué servidor SSH respondió; una clave de usuario demuestra quién intenta iniciar sesión. Rotar una no afecta a la otra. La distinción parece elemental, pero muchos procedimientos de incidencias mezclan `authorized_keys`, claves SSH personales, certificados de host y archivos `/etc/ssh/ssh_host_*` bajo una instrucción imprecisa: "rotar las claves SSH".

RFC 4253 sitúa la autenticación del servidor dentro del intercambio inicial del transporte. El servidor firma el hash del intercambio con su clave de host privada y el cliente comprueba la clave pública contra una fuente fiable como `known_hosts`, una autoridad certificadora de hosts o registros SSHFP autenticados. Las contraseñas, los certificados de usuario y las claves públicas de usuario llegan después. Un desarrollador puede entregar credenciales de usuario perfectamente válidas a un impostor si el cliente ha dejado de comprobar el servidor.

Por eso el aviso de cambio de host es deliberadamente grave. Indica que se ha roto la continuidad de identidad: el nombre que escribió el desarrollador presenta ahora otra identidad. Una sustitución planificada, una máquina virtual reconstruida, un balanceador apuntando al grupo equivocado, una manipulación de DNS y una interceptación activa pueden parecer iguales en ese momento. El cliente no puede ver tu solicitud de cambio.

Anota el alcance antes de generar nada. Registra cada nombre de host y alias que se usa, cada puerto no estándar, las direcciones escritas directamente en scripts, las rutas por bastiones, los ejecutores de CI, los agentes de despliegue y cualquier `GlobalKnownHostsFile` compartido. `[host]:port` es un nombre de known-hosts distinto de `host`, y los alias pueden ocultar el destino canónico. Una rotación que cubra `git.example.net` pero omita `git.internal` parecerá intermitente aunque ambos nombres lleguen a la misma máquina.

Haz también un inventario de todos los algoritmos de clave de host que se ofrecen. Un servidor puede tener claves de host Ed25519, ECDSA y RSA a la vez. OpenSSH negocia un algoritmo con el cliente, por lo que dos desarrolladores pueden conectarse al mismo daemon y fijar claves públicas distintas. Rotar solo la clave observada en tu portátil no define el conjunto completo de identidades del servidor. Usa las entradas `HostKey` configuradas y los archivos de clave pública verificados como inventario oficial, y compáralo con lo que negocia cada grupo de clientes compatible.

## Publica ambas huellas antes de tocar el servidor

Publica las huellas actual y sustituta mientras la clave actual todavía atiende tráfico. La publicación debe recorrer un canal cuya confianza no dependa del host SSH que va a cambiar. Puede servir un repositorio de operaciones firmado, una página interna autenticada, una configuración administrada de dispositivos o una zona DNSSEC gestionada por separado. No sirve un mensaje copiado desde la misma sesión SSH que podría estar interceptada.

Genera las huellas desde archivos de clave pública en una máquina administrativa fiable, no preguntando a la red de producción qué ofrece en ese momento. OpenSSH muestra huellas SHA-256 de forma predeterminada. El comando y la forma de su salida son:

```text
$ ssh-keygen -lf ssh_host_ed25519_key.pub -E sha256
256 SHA256:<base64-fingerprint> host.example.net (ED25519)
```

Publica algo más que el resumen corto. Para cada identidad incluye nombre y puerto, algoritmo, huella SHA-256, estado (`current`, `new` o `retired`), momento del primer uso, momento de retirada y persona o sistema que aprobó el registro. Indica la zona horaria. Enumera los alias que compartan clave. Si varios nodos tras un nombre presentan adrede claves distintas, publica todo el conjunto permitido y explica el motivo.

Un registro útil se parece a este:

```text
Host: build.example.net:22
Algorithm: ssh-ed25519
Current: SHA256:<old-fingerprint>
New: SHA256:<new-fingerprint>
Overlap begins: 2026-08-10 15:00 UTC
Old key removed: 2026-08-17 15:00 UTC
Old key marked revoked: 2026-08-17 15:30 UTC
Owner: Platform operations
```

Las fechas son ilustrativas, pero el orden no. Publicar después del cambio convierte un trabajo previsto en una decisión de confianza improvisada. Publicar solo el valor nuevo impide compararlo con una fijación existente. Mantén visible el registro antiguo como retirado después del cambio para poder reconocer máquinas obsoletas y reutilizaciones inesperadas.

Las huellas son identificadores públicos, no secretos. La clave privada permanece protegida en el servidor, pero la clave pública y la huella deben distribuirse lo suficiente para que la verificación no dependa de encontrar a un único administrador durante la ventana. Trata la aprobación de la publicación como un cambio de seguridad: dos archivos fuente que producen la misma huella son una prueba más sólida que una captura pegada en un chat.

## El solapamiento permite aprender la sustituta con seguridad

Ofrece las claves de host antigua y nueva juntas antes de retirar la antigua. Durante ese periodo, un cliente establecido autentica el servidor con la clave antigua ya fiable y puede aprender la adicional mediante la extensión `hostkeys@openssh.com` de OpenSSH. La continuidad procede de la clave antigua, por lo que la nueva no llega como una afirmación sin autenticar.

El manual `ssh_config` de OpenSSH describe `UpdateHostKeys` específicamente como ayuda para una rotación gradual. También enumera límites relevantes. El cliente solo acepta claves adicionales tras autenticarse con una clave simple ya fiable o aceptada expresamente, mediante un archivo known-hosts del usuario. No aprende claves por esta vía si el servidor se autenticó con un certificado de host o únicamente con un archivo global. La opción también puede quedar desactivada por defecto si el usuario sustituye `UserKnownHostsFile` o activa `VerifyHostKeyDNS`.

No des por hecho el valor predeterminado. Inspecciona la configuración efectiva en hosts representativos:

```text
$ ssh -G build.example.net | grep -E '^(updatehostkeys|userknownhostsfile|stricthostkeychecking) '
stricthostkeychecking ask
updatehostkeys true
userknownhostsfile ~/.ssh/known_hosts ~/.ssh/known_hosts2
```

En el servidor, conserva archivos privados separados y declara ambos. Los nombres son ejemplos; usa rutas acordes con tu sistema y tu gestión de configuración:

```text
HostKey /etc/ssh/ssh_host_ed25519_key_old
HostKey /etc/ssh/ssh_host_ed25519_key_new
```

Ejecuta `sshd -t` antes de recargar. El manual de `sshd` indica que este modo comprueba la validez de la configuración y la integridad de las claves, por lo que detecta archivos ilegibles y ajustes mal formados antes de que el daemon los relea. Recarga en vez de finalizar sesiones activas, salvo que el gestor del servicio indique otro método. Conecta después desde un cliente limpio que solo confíe en la clave antigua e inspecciona su archivo aislado tras una sesión autenticada correcta.

Configurar varias claves no garantiza que todos los clientes negocien la misma. Influyen la preferencia de algoritmos, la antigüedad del cliente, un `HostKeyAlgorithms` personalizado, los certificados y los almacenes globales. Mantén el solapamiento al menos durante un ciclo de conexión normal de cada clase de cliente, no durante un número arbitrario de horas. Una flota de portátiles que se conecta cada semana necesita una ventana distinta de los ejecutores de CI que se conectan cada pocos minutos.

Mide la adopción sin fingir que puedes ver todos los archivos `known_hosts` personales. Los clientes administrados pueden informar de la presencia de la clave nueva en su archivo distribuido. En clientes no administrados, registra conexiones correctas por versión cuando la política de privacidad lo permita, publica un comando de verificación claro y conserva el solapamiento durante el uso ordinario. La ausencia de quejas no demuestra la propagación.

## La ventana necesita estados y condiciones de parada

Una ventana de cambio debe definir estados observables, responsables y condiciones de reversión. "Rotar a las 15:00" no es un plan: no dice nada del solapamiento, de la preparación de clientes ni del instante en que la identidad antigua deja de ser aceptable. Coloca la transición de confianza en la misma cronología que el despliegue.

Usa cinco controles:

1. Publicación completa: revisores independientes reproducen todas las huellas desde los archivos aprobados.
2. Solapamiento activo: el servidor ofrece ambas identidades, supera `sshd -t` y los clientes limpios se autentican con la fijación antigua mientras aprenden la nueva.
3. Preparación cumplida: los almacenes administrados contienen la sustituta, las pruebas cubren sistemas y rutas compatibles, y soporte tiene las huellas exactas.
4. Cambio completo: el servidor ya no ofrece la clave privada antigua, las sesiones nuevas funcionan con comprobación estricta y ningún nodo inesperado sigue presentándola.
5. Revocación probada: un endpoint de prueba con la identidad antigua es rechazado y la huella retirada sigue publicada como revocada.

Da a una persona autoridad para detener el cambio. Detente si un nodo presenta una clave sin publicar, una ruta llega a un nodo fuera del inventario, un cliente compatible no puede aprender o recibir la sustituta, o revertir exigiría restaurar una clave que el equipo de respuesta considera comprometida. Una rotación rutinaria puede volver a la clave antigua durante el solapamiento. Una respuesta a un compromiso no puede tratar la clave comprometida como retorno seguro.

Separa la reversión del servicio de la reversión de confianza. Puedes restaurar un paquete anterior sin recuperar la identidad retirada. Mantén disponibles una configuración conocida, acceso a la clave sustituta y acceso por consola o proveedor para reparar SSH sin debilitar la verificación. Si SSH es la única forma de reparar SSH, existe un punto único de fallo oculto.

Incluye las sesiones multiplexadas de larga duración. La conexión compartida de OpenSSH puede mantener un maestro activo mientras nuevos shells reutilizan el transporte existente. Esos shells no realizan otro intercambio de clave y no prueban la identidad posterior. Cierra los maestros de prueba con `ssh -O exit host` cuando corresponda y exige nuevas conexiones TCP a las sondas.

Fija la retirada con precisión y mantén abierta la comunicación después. Quien vuelva de vacaciones tendrá fijaciones obsoletas. El resultado esperado es un fallo documentado y una vía verificada de actualización, no una excepción. Soporte debe comparar primero las huellas, localizar la entrada con `ssh-keygen -F` y sustituirla desde el registro aprobado.

## Ensaya el aviso con un archivo de confianza aislado

Prueba la rotación sin tocar el `~/.ssh/known_hosts` real de nadie. Un archivo aislado hace reproducible cada estado y evita que una prueba dependa de claves aprendidas meses atrás. Usa un endpoint de ensayo capaz de reproducir la secuencia de producción o un `sshd` temporal en otro puerto bajo las mismas restricciones.

Crea primero `known_hosts.test` a partir de la clave pública antigua aprobada. Construye la línea desde el artefacto revisado, no desde un escaneo en vivo. Conecta al endpoint fijando el nombre lógico mediante `HostKeyAlias`:

```text
$ ssh -F /dev/null \
  -o HostKeyAlias=build.example.net \
  -o UserKnownHostsFile=./known_hosts.test \
  -o GlobalKnownHostsFile=/dev/null \
  -o StrictHostKeyChecking=yes \
  -o UpdateHostKeys=yes \
  -p 2222 test-host.example.net true
```

La primera ejecución debe funcionar mientras se ofrezcan ambas claves. Inspecciona el archivo con `ssh-keygen -F build.example.net -f ./known_hosts.test` y confirma que contiene la sustituta aprobada. Calcula la huella de cada campo de clave devuelto y compárala con la publicación. Un código cero demuestra conectividad, no el conjunto de identidades.

Configura después el endpoint para ofrecer solo la nueva y abre una conexión nueva. Debe funcionar porque el cliente aprendió la sustituta durante el solapamiento autenticado. Restaura una copia con solo la fijación antigua y repite contra el endpoint nuevo. Con `StrictHostKeyChecking=yes`, debe fallar. El caso negativo prueba que los clientes que perdieron el solapamiento se detienen en vez de aceptar en silencio.

Apunta por último a un endpoint con una clave ajena. Conserva el texto del fallo y su estado de salida para el procedimiento. No suavices la prueba hasta que siempre pase; estás protegiendo el rechazo. Comprueba que los scripts propaguen la salida distinta de cero y no la oculten en reintentos.

Nunca uses `StrictHostKeyChecking=no` para arreglar el ensayo. El manual actual dice que permite continuar con claves cambiadas bajo ciertas restricciones, mientras `accept-new` todavía las rechaza. `accept-new` por sí solo tampoco resuelve la rotación porque una sustituta para un nombre conocido cuenta como cambio. Suministra material de confianza autenticado o usa el solapamiento.

## Revocar debe causar rechazo, no sugerir limpieza

Retirar la clave privada antigua del servidor previsto solo prueba que ese servidor dejó de ofrecerla. Revocar significa que los clientes rechazarán su clave pública si aparece de nuevo, incluso en un nodo olvidado o en manos de quien robó la privada. Borrar la línea antigua de `known_hosts` hace lo contrario: elimina la memoria que reconoce la identidad retirada.

Los archivos known-hosts de OpenSSH admiten `@revoked`. Una clave coincidente marcada así nunca se acepta y genera un aviso. Las flotas administradas pueden distribuir una entrada global; entornos pequeños pueden mantener un archivo dedicado. Usa exactamente la clave pública aprobada y limita con cuidado el patrón:

```text
@revoked build.example.net ssh-ed25519 <old-public-key-data>
```

Una Key Revocation List resulta útil cuando el conjunto crece o hay certificados. Genera una KRL de prueba desde la clave antigua y consúltala antes de desplegar:

```text
$ ssh-keygen -k -f revoked-hostkeys.krl ssh_host_ed25519_key_old.pub
$ ssh-keygen -Q -f revoked-hostkeys.krl ssh_host_ed25519_key_old.pub
ssh_host_ed25519_key_old.pub (<comment>): REVOKED
```

La consulta devuelve un estado distinto de cero para una clave revocada, así que la automatización debe interpretarlo de forma expresa. Configura `RevokedHostKeys` hacia la KRL, conecta a un endpoint con la identidad antigua y exige rechazo. Después exige éxito contra el nuevo. Ambas mitades importan: un archivo ilegible puede denegar todas las conexiones.

`ssh-keygen -R host` sirve para editar archivos con nombres cifrados, pero no revoca. Elimina todas las entradas del nombre, incluida una sustituta válida, y deja que la conexión siguiente establezca confianza de nuevo. Úsalo solo tras comparar las entradas con el registro y añade después la nueva clave aprobada. Un script que ejecuta `-R` seguido de `ssh-keyscan` sin autenticar automatiza la renuncia a la confianza.

Conserva el material público revocado. Puedes destruir una clave privada retirada no comprometida según la política, pero guarda su pública, huella, aprobación y prueba. Esos datos permiten reconocer una imagen antigua que vuelva meses después.

## La automatización debe cerrarse con seguridad sin volverse frágil

Los trabajos no interactivos necesitan una fuente de confianza mantenida, no comprobaciones desactivadas. Los ejecutores de CI, agentes de despliegue y agentes de programación autónomos suelen encontrar primero la rotación porque abren conexiones breves todo el día. Si una imagen incorpora una sola clave y nadie mantiene su renovación, el parche habitual es `StrictHostKeyChecking=no`. Convierte un error de disponibilidad en un fallo de autenticación.

Elige un modelo por clase: incorpora ambas claves durante el solapamiento, monta un archivo global administrado, devuelve líneas aprobadas con `KnownHostsCommand`, usa SSHFP validado con DNSSEC o confía en una CA de host. El manual dice que `KnownHostsCommand` devuelve el formato habitual y se suma a los archivos. También termina la conexión si el comando falla, un valor correcto que exige disponibilidad del servicio de confianza.

Declara el comportamiento batch:

```text
Host build.example.net
    BatchMode yes
    StrictHostKeyChecking yes
    UserKnownHostsFile /etc/company/ssh_known_hosts
    UpdateHostKeys no
```

`UpdateHostKeys no` es intencionado aquí. La gestión de configuración posee el archivo, así que permitir cambios de workers efímeros crea estado que no se conserva ni audita. En un archivo personal persistente, `UpdateHostKeys yes` puede ser mejor. La propiedad de la distribución decide cuál es correcto.

Prueba alias, bastiones, IP directas y puertos distintos como identidades separadas. `ProxyJump` todavía autentica el destino y el salto tiene su propia fijación. Un contenedor puede montar un archivo de solo lectura; un agente aislado puede usar otro binario o ignorar el directorio del usuario. Ejecuta `ssh -G target` dentro del entorno real.

Para SSH dirigido por agentes, separa credenciales y confianza del host. Sallyport ejecuta acciones mediante `sp-ssh` mientras las claves SSH permanecen en su bóveda cifrada, por lo que el agente no recibe los secretos. Eso protege la custodia; la rotación aún necesita una fuente autenticada y un rechazo probado.

Un fallo automatizado debe indicar huellas esperada y observada sin imprimir material privado. No reintentes con ajustes más débiles. Detén el trabajo, conserva stderr y la configuración efectiva, y remite el conflicto al responsable.

Ejecuta la misma prueba desde un worker desechable sin estado previo. Entrégale exactamente el paquete administrado, invoca el comando real y archiva `ssh -G`. Así detectas un falso éxito causado por una sesión interactiva que llenó un archivo no declarado.

Prueba el fallo quitando la sustituta de una copia del paquete después del cambio. El trabajo debe parar antes de enviar un comando remoto y preservar el estado SSH. Distribuye la sustituta y repite. Si el sistema convierte todo fallo SSH en timeout, corrige esa falta antes de la ventana para distinguir identidad rechazada, pérdida de red y autenticación de usuario.

Busca configuraciones que amplíen la confianza. Un comodín puede hacer válida una clave para muchos nombres y las claves compartidas vuelven indistinguibles varias máquinas. Puede ser intencional, pero el registro debe declarar el alcance. Prefiere líneas específicas para identidades distintas y prueba el nombre exacto del trabajo.

Fija también el binario y el contrato de configuración de los trabajos autónomos. Una imagen base puede cambiar valores predeterminados a la vez que cambia la identidad. Captura versión, algoritmos efectivos, resumen del archivo y nombre lógico antes y después. Así sabrás si el trabajo vio otro servidor, carecía de la clave o no pudo negociar.

## DNS y los certificados cambian el trabajo de distribución

SSHFP y los certificados pueden reducir las fijaciones por host, pero no eliminan la planificación. Trasladan el ancla estable. Úsalos cuando puedas operar esa ancla mejor que miles de entradas independientes.

RFC 4255 define SSHFP y exige datos DNS autenticados antes de confiar en una huella. En la práctica, DNSSEC debe validarse desde el cliente hasta el registro. Un registro sin firma ayuda a comparar, pero no establece identidad frente a quien altere DNS. `VerifyHostKeyDNS yes` confía implícitamente solo en coincidencias seguras.

Publica ambos SSHFP durante el solapamiento, espera a cachés y resolvers, y elimina el antiguo con la clave privada. Genera registros con `ssh-keygen -r hostname -f public_key_file` desde archivos aprobados. Verifica el registro y su estado por la misma ruta del cliente. Un TTL bajo no arregla DNSSEC roto ni datos erróneos.

Los certificados permiten confiar en una entrada como `@cert-authority *.example.net`. Una nueva clave certificada con principales y vigencia correctos se acepta bajo la CA existente. Es más limpio en flotas dinámicas, pero la CA tiene mucho poder. Protege su privada, limita emisiones, registra seriales y principales, y ensaya su revocación.

No implantes una CA solo para evitar una rotación incómoda. Añade emisión, caducidad, nombres y cambio de CA. Una flota pequeña puede operar fijaciones explícitas; una grande y efímera suele beneficiarse de certificados.

Documenta dónde comienza la confianza. `UpdateHostKeys` no se aplica tras autenticación por certificado, y un archivo global no aprende adiciones mediante el archivo del usuario. Mezclar modelos sin precedencia produce éxito en un portátil y fallo en CI.

## Cierra con pruebas que sobrevivan a la ventana

Una rotación completa deja constancia de qué cambió, quién lo aprobó, qué aceptaron los clientes y si se rechazó lo antiguo. Guarda claves públicas, huellas reproducidas, resultado de `sshd -t`, configuración efectiva, revisión publicada, matriz de clientes, tiempos, artefacto de revocación y prueba negativa. Nunca guardes la privada en el ticket.

Registra el estado real después. Consulta cada nodo por cada ruta y compara con el conjunto aprobado, recordando que la recolección de red es una observación. Compárala con la publicación autenticada. Revisa imágenes de autoescalado y nodos apagados; las claves antiguas suelen volver con capacidad de sustitución.

Activity journal y Sessions journal de Sallyport pueden conservar un registro resistente a manipulaciones de las acciones SSH de un agente, proyectado desde un log cifrado encadenado por hashes. `sp audit verify` comprueba la cadena offline sobre el texto cifrado sin clave de bóveda, lo que añade pruebas sin sustituir los ensayos de identidad.

Programa seguimiento según el comportamiento de la flota. Busca clientes que fallen por la huella retirada, nodos inesperados y scripts con opciones de bypass. Retira la configuración temporal. Mantén la revocación mientras la privada pueda sobrevivir en copias, imágenes o manos no autorizadas.

El aviso de host debe seguir siendo raro y alarmante. Una buena rotación no lo suprime. Organiza la transición antes para que los clientes previstos no tengan que ignorarlo y prueba que todavía detiene la conexión si regresa la identidad retirada.
