Debian 13: Rotación de claves SSH — revocar accesos y evitar proliferación de claves (caso #13)

¿Te fue útil?

La rotación de claves parece fácil hasta que deja de serlo. Cambias una clave, alguien no puede desplegar, el acceso de emergencia de otro equipo se rompe, y una clave de contratista “temporal” que olvidaste sigue funcionando—silenciosamente—en una máquina que nadie recuerda que existe.

Esta es la parte de las operaciones en la que o ejecutas un cambio controlado o tienes un incidente en cámara lenta con mil cortes pequeños. Debian 13 no cambia los fundamentos de SSH, pero es un buen momento para dejar de tratar las claves como monedas sueltas en el sofá.

Qué significa realmente “rotación de claves” en producción

En un laboratorio, rotar claves SSH es un elegante dos pasos: añadir la clave nueva, eliminar la antigua. En producción, es un contrato multipartito que estás renegociando bajo carga.

No solo cambias un archivo. Cambias:

  • Identidad: qué claves privadas se consideran prueba válida de quién eres.
  • Autorización: qué claves públicas se asignan a qué cuentas y con qué restricciones.
  • Alcanzabilidad: qué bastiones, runners de CI y cuentas de automatización aún pueden acceder a lo que necesitan.
  • Evidencia: si luego puedes demostrar quién tenía acceso en el momento de un incidente.

La rotación de claves bien hecha tiene tres propiedades:

  1. Reversible en minutos (durante el despliegue), pero irreversible tras finalizar la ventana de gracia (las claves viejas mueren de verdad).
  2. Auditable: puedes responder “quién puede iniciar sesión dónde” sin hacer grep a mano por todo el mundo.
  3. Alcance limitado: la ruptura de la clave de una persona no se convierte en “rotar todo en todas partes” salvo que sea necesario.

La rotación de claves mal hecha suele significar que rotas lo incorrecto (claves de cliente vs claves de host), borras primero y preguntas después, o sigues acumulando claves por miedo a eliminarlas.

Claves de host vs claves de usuario: rota lo correcto

Las claves de usuario son las que están en ~/.ssh/authorized_keys y permiten la entrada a personas (o bots). Las claves de host son las que tus clientes usan para verificar que están hablando con el servidor correcto.

Rotar claves de host es un procedimiento distinto con un radio de impacto distinto. Si tu problema es “un exempleado sigue teniendo acceso” o “se filtró una clave de contratista”, te interesan las claves de usuario y sus rutas de autorización—no las claves de host.

Una cita para tener en mente

Idea parafraseadaRichard Cook: “El éxito en sistemas complejos suele venir de la gente que se adapta constantemente, no de que el plan funcione exactamente como está escrito.”

La rotación de claves es exactamente eso: necesitas un plan, pero ganas si te adaptas rápido cuando la realidad discrepa.

Hechos e historia: por qué las claves SSH proliferan como lo hacen

La proliferación de claves no es una falla moral. Es el resultado natural de incentivos: entregar producto vence al mantenimiento, y SSH es sin fricción cuando se le permite serlo. Algunos hechos y contexto ayudan a enmarcar a qué te enfrentas.

  1. SSH reemplazó rsh/telnet mayormente por confianza y cifrado. La victoria temprana fue “no en texto claro”, no “gobernanza de identidad perfecta”. La cultura perduró.
  2. OpenSSH por defecto priorizó la conveniencia durante décadas: authorized_keys por usuario es simple, así que todos lo usaron y las flotas crecieron alrededor de ello.
  3. Los tipos de clave evolucionaron: las DSA fueron comunes y luego quedaron obsoletas; RSA permaneció en todos lados; Ed25519 se convirtió en el “por defecto moderno” por ser pequeña y rápida.
  4. El reenvío de agente SSH se diseñó para reducir la distribución de claves, pero en la práctica a menudo se convirtió en “la máquina remota ahora puede usar mi agente”, lo cual es… problemático.
  5. known_hosts es un compromiso de UX: confiar la primera vez fue práctico, pero también enseñó a la gente a ignorar avisos alarmantes cuando cambian claves de host.
  6. authorized_keys puede contener opciones (restricciones de comando, restricciones por IP de origen). La mayoría de las organizaciones no las usan y luego reinventan los mismos controles de forma deficiente en otro lugar.
  7. Credenciales de corta vida históricamente son difíciles en SSH sin herramientas adicionales; las claves de larga vida se volvieron la opción por defecto porque “simplemente funcionan”.
  8. CI/CD empeoró la proliferación: los robots necesitan acceso y los equipos suelen dejar “temporalmente” claves de despliegue en máquinas. “Temporal” es la unidad de tiempo más larga en operaciones.
  9. Existen directorios centrales (LDAP/SSSD, etc.), pero la autorización SSH a menudo se deja local porque es fácil y los outages dan miedo.

La proliferación ocurre porque es conveniente, distribuida e invisible—hasta que de repente es muy visible.

Guía de diagnóstico rápido (qué comprobar primero)

Este es el flujo de triaje cuando alguien dice “rotamos claves y ahora SSH está roto” o “revocamos claves pero el acceso sigue funcionando”.Quieres señal rápida, no un ensayo.

Primero: ¿autenticación, autorización o enrutamiento?

  1. Prueba rápida del cliente: ¿el cliente ofrece la clave esperada y qué responde el servidor?
  2. Registros del servidor: ¿qué piensa que pasó sshd?
  3. Fuente de configuración del servidor: ¿realmente usas archivos authorized_keys, o un AuthorizedKeysCommand, o ambos?

Segundo: encuentra la “fuente de la verdad” que controla

  • Si la autorización es mediante archivos locales, la verdad está distribuida: debes localizar cada authorized_keys y cualquier include/alternativa.
  • Si usas un comando/SSSD/LDAP, la verdad está centralizada: verifica que sea accesible, correcta y que el caché funcione como se espera.
  • Si usas certificados SSH, la verdad es la CA y su política de firma; la revocación puede ser basada en TTL o por ID de clave.

Tercero: confirma que realmente revocaste lo que crees que revocaste

  • Eliminar la clave vieja de un sitio no ayuda si la misma clave vive en otras diez cuentas o en un bastión.
  • Los comentarios de clave mienten. Las huellas (fingerprints) no. Siempre identifica por huella.
  • No olvides la automatización: root, usuarios de despliegue, usuarios espejo de Git, cuentas de backup, cuentas de break-glass.

Cuando estés atascado: usa logs verbosos del cliente y los logs de autenticación del servidor. Todo lo demás son sensaciones.

Construye un inventario real: cuentas, claves y rutas

Antes de revocar, haz inventario. No porque llenar papeleo sea divertido, sino porque revocar sin inventario es cómo borras la única clave funcional a un nodo de almacenamiento a las 2 a. m.

Dónde se esconden las claves en sistemas Debian

  • /home/*/.ssh/authorized_keys — la obvia.
  • /root/.ssh/authorized_keys — la dolorosa.
  • Cuentas de servicio con hogares personalizados: /var/lib/*, /srv/*.
  • Ubicaciones alternas vía AuthorizedKeysFile (puede haber múltiples rutas).
  • Obtención central de claves vía AuthorizedKeysCommand (las claves podrían no tocar nunca disco por usuario).
  • Puntos donde la gestión de configuración deja archivos (fragmentos gestionados, ficheros generados).

Decide tu modelo de revocación

Elige un modelo por entorno, no por equipo.

  • Archivos locales, gestionados: las claves viven en authorized_keys, pero son propiedad de un sistema CM (Ansible/Puppet/etc). Simple, pero aún distribuido.
  • Claves centralizadas: AuthorizedKeysCommand recupera claves de un store central (directorio, API, DB respaldada en git). Gran auditabilidad, más piezas en movimiento.
  • Certificados SSH: los usuarios se autentican con certificados de corta duración firmados por tu CA; los servidores confían en la CA. Mínima proliferación, mejor historia de revocación si manejas bien el TTL.

Si ya eres lo suficientemente grande como para decir “flota”, los certificados ganan. Si eres pequeño pero disciplinado, los archivos locales gestionados están bien. Si eres mediano y caótico, las claves centralizadas son una mejora de cordura—si lo diseñas como producción, no como un proyecto de fin de semana.

Revoca accesos limpiamente: técnicas que no crean outages

La revocación debería ser aburrida. Aburrido es la meta. El truco es escalonar cambios para poder avanzar, no retroceder desesperadamente.

Técnica 1: Añadir-antes-de-eliminar con ventana de gracia

Para usuarios humanos: añade la nueva clave, valida el inicio de sesión y luego elimina la clave vieja tras la fecha límite. Para automatización: necesitas soporte de doble clave durante el despliegue y un cambio explícito en el momento de corte.

La ventana de gracia debe ser corta. Días, no meses. Las ventanas largas son como acabar soportando dos mundos indefinidamente.

Técnica 2: Prefiere revocar por huella, no por “nombre”

La gente copia claves, renombra comentarios o pega la clave pública equivocada. Identifica siempre las claves por huella. Trata el comentario como metadatos proporcionados por el usuario—porque lo es.

Técnica 3: Usa opciones de clave para limitar el radio de impacto

Si debes mantener claves de larga duración (y muchas empresas lo hacen), al menos restringelas. Ejemplos de restricciones incluyen:

  • from="10.0.0.0/8" para limitar IPs de origen
  • command="..." para comandos forzados (con cuidado)
  • no-agent-forwarding, no-port-forwarding, no-pty

No son controles perfectos, pero convierten “una clave filtrada = shell completo desde cualquier lugar” en “una clave filtrada = acceso limitado desde lugares específicos”. Eso marca la diferencia en respuesta a incidentes.

Técnica 4: Matar el acceso a nivel de cuenta cuando sea necesario

Si un usuario es despedido o una clave se confirma comprometida, esperar la limpieza de claves es demasiado lento. Bloquea la cuenta y elimina las claves.

Los bloqueos de cuenta no sustituyen la higiene de claves, pero son un interruptor rápido.

Técnica 5: Diseña una historia de break-glass a propósito

Necesitas acceso de emergencia. Pero “todo el mundo tiene una clave root por si acaso” no es acceso de emergencia; es proliferación de claves con sentimientos.

Break-glass debe significar: un conjunto pequeño de claves/certs controlados, almacenados de forma segura, con registro, y ejercitados en simulacros. Si no lo pruebas, fallará cuando más lo necesites.

Broma #1: La proliferación de claves SSH es como un cajón de trastos: un día la necesitarás, y volverá a decepcionarte.

Evita la proliferación de claves: control centralizado, opciones y certificados SSH

La revocación es la limpieza. Evitar la proliferación es la prevención. Prevenir es más barato, rápido y menos humillante.

Centraliza la decisión, no necesariamente las claves

Muchos equipos interpretan “centralizar” como “poner todas las claves en un archivo”. Ese no es el objetivo. El objetivo es centralizar la autoridad: quién puede otorgar acceso, cómo se aprueba y cómo se audita.

Patrones recomendables:

  • La gestión de configuración genera authorized_keys desde un inventario central y lo aplica.
  • AuthorizedKeysCommand obtiene claves de un servicio de directorio y devuelve solo claves actuales y aprobadas.
  • Los certificados SSH eliminan la necesidad de distribuir claves públicas de usuario a cada servidor.

Certificados SSH: la opción madura para flotas

Los certificados de usuario SSH (OpenSSH) permiten que los servidores confíen en una CA, no en claves individuales. Los usuarios conservan las claves privadas; la CA firma un certificado de corta duración que dice “esta clave es válida para el usuario X durante N horas, con estos principals”.

Qué cambia operativamente:

  • La rotación es principalmente política de CA: emite certificados de corta duración y la “revocación” se convierte en “esperar el TTL” además de deshabilitar la emisión.
  • El onboarding es más rápido: los servidores ya confían en la CA; los nuevos usuarios no requieren tocar cada host.
  • Offboarding más limpio: deja de firmar para el usuario y espera el TTL. No hace falta rastrear la clave por toda la flota.

Existen modos de fallo: disponibilidad de la CA, errores en el flujo de firma, problemas de sincronización de tiempo. Pero son problemas que puedes diseñar para mitigar. La proliferación de claves es un lío socio-técnico que nunca desaparece completamente.

Haz que los bastiones sean aburridos y estrictos

Un bastión es donde tu política de acceso se vuelve aplicable. Si permites acceso directo por SSH desde portátiles a todo, has elegido el caos.

Valores por defecto recomendados para bastiones:

  • No permitir agent forwarding salvo que esté justificado y constreñido.
  • Registro fuerte (grabación de sesiones si el perfil de riesgo lo requiere).
  • Controles de red: los servidores solo aceptan SSH desde rangos de bastión/VPN.
  • Acceso de corta duración cuando sea posible.

Tareas prácticas con comandos, salidas y decisiones (12+)

Estas son tareas de campo que puedes ejecutar en Debian 13. Cada una incluye: un comando, qué significa la salida y qué decisión tomar a continuación. Úsalas durante rotación, respuesta a incidentes o auditorías.

Tarea 1: Confirma qué usa realmente sshd para authorized keys

cr0x@server:~$ sudo sshd -T | egrep -i 'authorizedkeys(file|command)|pubkeyauthentication|passwordauthentication'
pubkeyauthentication yes
passwordauthentication no
authorizedkeysfile .ssh/authorized_keys .ssh/authorized_keys2
authorizedkeyscommand none

Significado: Este host usa archivos por usuario; no hay comando centralizado de claves. La autenticación por contraseña está apagada (bien).

Decisión: La revocación debe tocar archivos en disco (o moverte a un modelo gestionado/central). Haz inventario de las rutas authorized_keys para cada cuenta.

Tarea 2: Verifica que sshd esté ejecutando la configuración que crees

cr0x@server:~$ systemctl status ssh
● ssh.service - OpenBSD Secure Shell server
     Loaded: loaded (/lib/systemd/system/ssh.service; enabled; preset: enabled)
     Active: active (running) since Mon 2025-12-29 11:02:14 UTC; 3h 18min ago
       Docs: man:sshd(8)
             man:sshd_config(5)
   Main PID: 1023 (sshd)
      Tasks: 1 (limit: 18956)
     Memory: 5.7M
        CPU: 1.421s
     CGroup: /system.slice/ssh.service
             └─1023 "sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups"

Significado: sshd está activo. La línea de comando muestra que el daemon es el binario esperado.

Decisión: Si los cambios no surten efecto, probablemente cambiaste el archivo incorrecto, tienes problemas de orden de includes o olvidaste recargar.

Tarea 3: Valida includes de configuración y captura errores antes de recargar

cr0x@server:~$ sudo sshd -t
cr0x@server:~$ echo $?
0

Significado: Código de salida 0 significa sintaxis de configuración válida.

Decisión: Procede con systemctl reload ssh. Si el código es distinto de cero, no recargues—arregla la configuración primero o te bloquearás al reiniciar.

Tarea 4: Muestra quién puede iniciar sesión (cuentas locales) y detecta sorpresas

cr0x@server:~$ getent passwd | awk -F: '($3>=1000 && $1!="nobody"){print $1":"$6":"$7}' | head
alice:/home/alice:/bin/bash
bob:/home/bob:/bin/bash
deploy:/srv/deploy:/bin/bash
backup:/var/lib/backup:/usr/sbin/nologin
ci-runner:/var/lib/ci-runner:/bin/bash

Significado: Tienes usuarios interactivos y cuentas de servicio. Algunas cuentas tienen hogares no estándar.

Decisión: Haz inventario de claves no solo en /home sino en directorios home personalizados. También comprueba si las cuentas con “nologin” tienen claves y si deberían tenerlas.

Tarea 5: Encuentra cada authorized_keys en el host (rápido, ruidoso, efectivo)

cr0x@server:~$ sudo find / -xdev -type f -name authorized_keys -o -name authorized_keys2 2>/dev/null | head
/root/.ssh/authorized_keys
/home/alice/.ssh/authorized_keys
/home/bob/.ssh/authorized_keys
/srv/deploy/.ssh/authorized_keys

Significado: Estas son las puertas de clave en este sistema (dado lo visto en la Tarea 1). Si ves ubicaciones inesperadas, ahí es donde lo “temporal” suele volverse permanente.

Decisión: Para la rotación, debes actualizar cada una. Para prevención, aplica una única fuente de verdad gestionada.

Tarea 6: Extrae huellas desde authorized_keys y elimina duplicados entre cuentas

cr0x@server:~$ sudo awk '{print $1" "$2}' /home/alice/.ssh/authorized_keys | ssh-keygen -lf -
256 SHA256:6Oq2j0J3j7cD4Yp8v0Fh7u2i9lXkYxJr3tZx0mQy7xM alice@laptop (ED25519)

Significado: Ahora tienes una huella estable para la clave. Esa es la identidad contra la que rotas/revocas.

Decisión: Crea una lista de huellas a eliminar. Si la misma huella aparece en múltiples cuentas, la revocación debe coordinarse o perderás un camino de acceso.

Tarea 7: Confirma que una clave sospechosa todavía es aceptada (prueba server-side con logs de sshd)

cr0x@server:~$ sudo journalctl -u ssh -n 30 --no-pager
Dec 30 09:10:42 server sshd[22144]: Accepted publickey for alice from 10.30.4.18 port 51222 ssh2: ED25519 SHA256:6Oq2j0J3j7cD4Yp8v0Fh7u2i9lXkYxJr3tZx0mQy7xM
Dec 30 09:11:03 server sshd[22190]: Failed publickey for bob from 10.30.4.18 port 51301 ssh2: ED25519 SHA256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Dec 30 09:11:03 server sshd[22190]: Connection closed by authenticating user bob 10.30.4.18 port 51301 [preauth]

Significado: Los logs del servidor muestran qué huella fue aceptada o rechazada.

Decisión: Si la huella “revocada” sigue siendo aceptada, existe en algún lugar de la ruta de autorización (otra cuenta, otro archivo, claves centralizadas o un wrapper de comando forzado).

Tarea 8: Comprueba si una cuenta está bloqueada (cortocircuito rápido de offboarding)

cr0x@server:~$ sudo passwd -S alice
alice P 2025-12-01 0 99999 7 -1

Significado: El estado P indica que la cuenta tiene un hash de contraseña usable. Esto no indica directamente acceso por clave SSH, pero te dice si el login por contraseña podría funcionar si está habilitado.

Decisión: Para offboarding inmediato: bloquea la cuenta (L) y elimina las claves. También confirma que PasswordAuthentication esté deshabilitado (Tarea 1).

Tarea 9: Bloquea una cuenta y verifica que está bloqueada

cr0x@server:~$ sudo usermod -L alice
cr0x@server:~$ sudo passwd -S alice
alice L 2025-12-01 0 99999 7 -1

Significado: L significa que la contraseña está bloqueada. El acceso por clave aún puede funcionar a menos que también lo controles vía opciones de sshd o eliminación de claves.

Decisión: No te quedes aquí. Elimina el material de clave o deshabilita la cuenta para SSH usando DenyUsers/Match User si necesitas un corte inmediato de SSH.

Tarea 10: Forzar “no SSH para este usuario” usando un bloque Match (quirúrgico, reversible)

cr0x@server:~$ sudo sh -c 'printf "\nMatch User alice\n  PasswordAuthentication no\n  PubkeyAuthentication no\n  KbdInteractiveAuthentication no\n" >> /etc/ssh/sshd_config.d/99-disable-alice.conf'
cr0x@server:~$ sudo sshd -t
cr0x@server:~$ sudo systemctl reload ssh
cr0x@server:~$ sudo sshd -T -C user=alice,host=server,addr=10.30.4.18 | egrep -i 'pubkeyauthentication|passwordauthentication|kbdinteractiveauthentication'
pubkeyauthentication no
passwordauthentication no
kbdinteractiveauthentication no

Significado: La configuración efectiva para ese usuario tiene la autenticación por clave deshabilitada. Esto bloquea SSH aunque las claves sigan existiendo.

Decisión: Usa esto como bloqueo de emergencia mientras rastreas claves distribuidas. Elimina la regla Match una vez completada la limpieza.

Tarea 11: Confirma que permisos de archivos no rompan silenciosamente la autenticación

cr0x@server:~$ sudo namei -l /home/alice/.ssh/authorized_keys
f: /home/alice/.ssh/authorized_keys
drwxr-xr-x root  root  /
drwxr-xr-x root  root  home
drwxr-xr-x alice alice alice
drwx------ alice alice .ssh
-rw------- alice alice authorized_keys

Significado: Esto pasa las expectativas habituales de permisos estrictos. Si ves directorios con permisos de grupo o mundo escribibles, sshd puede ignorar el archivo (dependiendo de la configuración).

Decisión: Arregla permisos antes de culpar a las claves. “La rotación de claves falló” suele ser en realidad “sshd se negó a leer el archivo”.

Tarea 12: Prueba el comportamiento del cliente con SSH verboso (el suero de la verdad)

cr0x@server:~$ ssh -vvv -i ~/.ssh/id_ed25519 alice@server
debug1: Offering public key: /home/cr0x/.ssh/id_ed25519 ED25519 SHA256:6Oq2j0J3j7cD4Yp8v0Fh7u2i9lXkYxJr3tZx0mQy7xM explicit
debug1: Server accepts key: /home/cr0x/.ssh/id_ed25519 ED25519 SHA256:6Oq2j0J3j7cD4Yp8v0Fh7u2i9lXkYxJr3tZx0mQy7xM explicit
debug1: Authentication succeeded (publickey).
Welcome to Debian GNU/Linux

Significado: Puedes ver qué clave se ofreció, si el servidor la aceptó y si la autenticación tuvo éxito.

Decisión: Si se ofrece la clave equivocada, arregla el cliente (agente, config, archivos de identidad). Si el servidor acepta una clave vieja, sigue estando autorizada en algún sitio.

Tarea 13: Elimina una clave específica de forma segura coincidiendo con el cuerpo de la clave, no con el comentario

cr0x@server:~$ sudo grep -n 'AAAAC3NzaC1lZDI1NTE5AAAAIFakeKeyBodyGoesHereButLooksRealToHumans' /home/alice/.ssh/authorized_keys
3:ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFakeKeyBodyGoesHereButLooksRealToHumans old-laptop
cr0x@server:~$ sudo sed -i '3d' /home/alice/.ssh/authorized_keys
cr0x@server:~$ sudo tail -n +1 /home/alice/.ssh/authorized_keys
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINewKeyBodyLooksDifferent new-laptop

Significado: Encontraste la línea exacta por el cuerpo de la clave y la eliminaste por número de línea.

Decisión: Esto es más seguro que buscar por el comentario. Tras la eliminación, prueba el inicio de sesión y revisa logs por retrocesos inesperados.

Tarea 14: Detecta claves duplicadas en el host (detector barato de proliferación)

cr0x@server:~$ sudo find /home /root /srv -xdev -name authorized_keys -type f -print0 2>/dev/null | xargs -0 awk '{print $1" "$2}' | sort | uniq -c | sort -nr | head
      4 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDuplicateKeyBodyExample
      2 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQAnotherDupExample

Significado: La misma clave pública aparece varias veces en cuentas/rutas distintas.

Decisión: Las claves duplicadas suelen ser un problema de gobernanza. Decide si es una clave compartida de bot (malo pero común) o un reuso accidental (peor). De cualquier forma, planifica separar identidades.

Tarea 15: Confirma que sshd rechazará contraseñas y keyboard-interactive

cr0x@server:~$ sudo sshd -T | egrep -i 'passwordauthentication|kbdinteractiveauthentication|usepam'
passwordauthentication no
kbdinteractiveauthentication no
usepam yes

Significado: Contraseña y keyboard-interactive están deshabilitados, pero PAM sigue usándose (a menudo para setup de sesión).

Decisión: Buena base para autenticación por clave. Si habilitas métodos interactivos durante la resolución, deja un recordatorio para volver a deshabilitarlos—las excepciones temporales se fosilizan rápido.

Tarea 16: Detecta y detén el agent forwarding en servidores donde no deba existir

cr0x@server:~$ sudo sshd -T | egrep -i 'allowagentforwarding|allowtcpforwarding|permittty'
allowagentforwarding yes
allowtcpforwarding yes
permittty yes

Significado: El reenvío de agente y el reenvío TCP están permitidos por defecto aquí.

Decisión: En servidores de producción, especialmente los que pueden alcanzar otros sistemas sensibles, considera deshabilitar el reenvío o limitarlo con reglas Match. El reenvío amplía el radio de impacto de una sesión comprometida.

Broma #2: Si no rotas las claves SSH, no “envejecen como el vino.” Envejecen como leche en una sala de servidores.

Tres micro-historias corporativas desde el terreno

Micro-historia 1: El incidente causado por una suposición incorrecta

La empresa tenía una política: “Todas las claves SSH son gestionadas por gestión de configuración.” Todo el mundo lo creía porque había sido cierto para la flota principal de aplicaciones. Los SRE rotaron una clave de desarrollador comprometida y la eliminaron del repo de CM. Declararon victoria, escribieron la nota post-incidente y siguieron adelante.

Una semana después, un auditor preguntó algo simple: “¿Esa clave todavía puede acceder a algo?” Alguien ejecutó un intento de SSH desde una máquina en cuarentena (de forma segura, con aprobaciones) y entró—en una caja de reporting de bases de datos. No en la base de datos principal. Un host “temporal” de reporting creado para un proyecto de fin de trimestre años atrás nunca se integró correctamente en CM.

La suposición equivocada no fue técnica; fue organizacional. Asumieron que la cobertura de sus herramientas coincidía con su modelo mental. No fue así. La clave vivía en /root/.ssh/authorized_keys en un host que nadie parcheaba regularmente porque “no era producción”. Tenía alcance de red suficiente para leer réplicas. Eso ya es producción.

La solución no fue heroica. Construyeron un trabajo que enumeraba huellas de authorized_keys en todos los hosts alcanzables y comparaba huellas con una lista aprobada. La primera ejecución fue embarazosa. La segunda fue accionable. La tercera fue rutinaria.

Moraleja: si no puedes medir la colocación de claves, no tienes gestión de claves. Tienes esperanza.

Micro-historia 2: La optimización que salió mal

Un equipo de plataforma decidió centralizar claves SSH rápidamente. Implementaron un AuthorizedKeysCommand que consultaba un servicio HTTP interno: dame un usuario, devuelve claves. Gran idea, enviado rápido, aplaudido en la reunión.

Entonces llegó el primer outage real. El servicio de claves tuvo una falla parcial: la API estaba arriba, pero la latencia se disparó por un problema en la base de datos aguas abajo. Los logins SSH no “fallaban”. Se quedaban colgados durante la autenticación. Los ingenieros no podían entrar para arreglar el incidente que causaba la latencia. Mientras tanto, la automatización que dependía de SSH empezó a fallar lenta e impredeciblemente.

El equipo había optimizado por “fuente única de verdad” y olvidó diseñar para fallos. No había caché, ni fallback, ni breaker, ni ruta de emergencia local. Habían movido el problema de “claves distribuidas” a “dependencia central para cada login”.

Se recuperaron restaurando temporalmente authorized_keys locales para on-call y cuentas break-glass, luego rediseñaron el servicio de claves: caché en hosts, timeouts estrictos y un diseño que falla cerrado para usuarios normales pero falla abierto para break-glass bajo condiciones controladas (y con alarmas).

Moraleja: la centralización no es comida gratis. Es intercambiar un desorden que puedes buscar con grep por un servicio que debes operar como si importara—porque importa.

Micro-historia 3: La práctica aburrida pero correcta que salvó el día

Una compañía del sector financiero con una flota pequeña tenía un hábito que parecía dolorosamente conservador: cada rotación de claves tenía un runbook y una ventana de “superposición de dos claves”. También mantenían un proceso pequeño de break-glass con pruebas trimestrales. La gente se quejaba de que era lento. La gente siempre se queja de cosas que funcionan.

Una tarde, se robó un portátil con claves SSH del coche de un ingeniero. El ingeniero lo reportó de inmediato. Seguridad declaró compromiso de credenciales y pidió pruebas de que el acceso fue revocado en producción.

El on-call siguió el runbook: identificar huellas de las claves públicas conocidas del ingeniero, bloquear el usuario vía un archivo Match en los bastiones en minutos y luego empujar una actualización de CM eliminando las huellas de todos los hosts de producción. La ventana de superposición no fue necesaria; esta fue una revocación contundente. Validaron la revocación intentado autenticarse desde un entorno controlado y revisando logs de sshd por huellas rechazadas.

La evidencia para auditoría se generó sola: tickets de cambio, diff de CM, logs de despliegue y logs de sistema mostrando intentos rechazados. El incidente permaneció pequeño porque la práctica era aburrida, documentada y ensayada.

Moraleja: los controles de seguridad más efectivos suelen ser los que tu equipo puede ejecutar bajo estrés sin improvisar.

Errores comunes: síntoma → causa raíz → solución

Estos son los modos de fallo recurrentes que hacen que la rotación de claves parezca maldita. La mayoría son previsibles. Eso es buena noticia.

1) Síntoma: “Eliminamos la clave, pero todavía entra”

Causa raíz: La clave existe en otra authorized_keys, en otra ruta definida por AuthorizedKeysFile, o está siendo recuperada por AuthorizedKeysCommand. A veces es la misma clave privada usada en otro sitio por reenvío de agente.

Solución: Identifica por huella en los logs de sshd, luego busca en la flota esa huella/cuerpo de clave. Revisa bastiones y cuentas compartidas. Deshabilita agent forwarding donde no sea requerido.

2) Síntoma: “La clave nueva no funciona en un servidor, funciona en el resto”

Causa raíz: Permisos/propiedad de ~/.ssh o authorized_keys son demasiado permisivos, o el directorio home del usuario no tiene permisos correctos.

Solución: Usa namei -l para verificar cada componente del path. Corrige propiedad y modos. Confirma la configuración efectiva de sshd y el comportamiento de StrictModes.

3) Síntoma: “CI dejó de desplegar justo después de la rotación”

Causa raíz: La automatización usaba una clave compartida de despliegue que no fue rotada, o estaba fijada en un store de secretos y no actualizada en todas partes. A veces el runner de CI usa un agente con múltiples identidades y ahora ofrece primero la clave equivocada.

Solución: Inspecciona logs del job de CI para ver la huella ofrecida. Fija la clave exacta con IdentitiesOnly yes y IdentityFile explícito en el runner. Rota las claves de bots por separado con un cutover escalonado.

4) Síntoma: “Después de centralizar claves, los logins SSH se cuelgan aleatoriamente”

Causa raíz: AuthorizedKeysCommand depende de un servicio lento. La ruta de autenticación ahora está acoplada a la red y es sensible a latencia.

Solución: Añade caché, timeouts y observabilidad. Asegura que el comando falle rápido. Considera snapshots locales cacheados de claves con refresco periódico.

5) Síntoma: “La gente sigue añadiendo claves y nunca las quita”

Causa raíz: Sin propietario, sin proceso y sin visibilidad. Eliminar da miedo porque nadie sabe qué se romperá.

Solución: Haz obligatorio gestionarlo vía CM o store central. Añade reportes (claves por cuenta, antigüedad, últimas veces vistas en logs). Requiere caducidad o revisión periódica.

6) Síntoma: “Aparecieron advertencias de host key durante el cambio”

Causa raíz: Rotaste claves de host (o reconstruiste servidores) sin un plan para distribuir known_hosts, o reasignaste DNS/IP causando discrepancias.

Solución: Separa la gestión de claves de host de la rotación de claves de usuario. Usa mecanismos estables para distribuir claves de host o actualizaciones cuidadosamente gestionadas de known_hosts en la automatización.

Listas de verificación / plan paso a paso

Este es el plan que funciona cuando estás cansado, ocupado y rodeado de sistemas a los que no les importan tus sentimientos.

Checklist A: Rotación controlada (cambio planeado)

  1. Define el alcance: qué usuarios, qué hosts, qué cuentas de automatización.
  2. Extrae huellas de las claves viejas a retirar (almacénalas en el ticket de cambio).
  3. Inventaría rutas de autorización: sshd -T, encuentra archivos authorized_keys, identifica cualquier AuthorizedKeysCommand.
  4. Prepara claves nuevas: añade las claves nuevas en todos los lugares necesarios; no elimines las viejas todavía.
  5. Valida acceso: usa ssh -vvv desde clientes representativos; revisa journalctl -u ssh por huellas aceptadas.
  6. Establece la ventana de gracia: comunica la fecha límite; mantenla corta.
  7. Corte: elimina huellas viejas; recarga sshd si la configuración cambió.
  8. Prueba la revocación: intenta iniciar sesión con la clave vieja desde un entorno controlado; verifica que los logs muestren rechazo.
  9. Limpieza: elimina bloques Match temporales, tickets y archivos de claves obsoletos. Actualiza el inventario.

Checklist B: Revocación de emergencia (sospecha de compromiso)

  1. Identifica huellas de las claves sospechosas (desde inventario de dispositivos del empleado, logs o almacenes de claves).
  2. Bloquea rápido en puntos de estrangulamiento: bastiones primero. Usa Match User o DenyUsers como medida inmediata.
  3. Deshabilita emisión si usas certificados; de lo contrario elimina claves del store central/CM.
  4. Busca duplicados: las claves compartidas de despliegue y las claves copiadas son comunes. No asumas que está en un sólo sitio.
  5. Valida con logs: verifica intentos rechazados para la huella.
  6. Limpieza post-incidente: rota claves de bots, reduce el reenvío, limita opciones de clave y corrige la brecha de inventario que permitió la proliferación.

Checklist C: Línea base anti-proliferación para sshd en Debian 13

  • Autenticación por contraseña desactivada salvo una razón fuerte.
  • Centraliza decisiones de autorización (CM, comando central de claves o certificados).
  • Restringe el ingreso de red a SSH (solo bastión/VPN).
  • Deshabilita agent/tcp forwarding por defecto; habilita por rol vía Match.
  • Registra lo suficiente para auditar (y envía logs fuera del host).
  • El acceso break-glass existe, es mínimo y está probado.

Preguntas frecuentes

1) ¿Necesito rotar claves SSH regularmente, incluso sin una brecha?

Sí, pero no de forma ciega. Rota en cambios de rol, offboarding, pérdida de dispositivo y en intervalos policy-based para accesos de alto riesgo. Si puedes moverte a certificados SSH de corta duración, la rotación pasa a ser mayormente automática.

2) ¿Cuál es la forma más limpia de revocar acceso para un usuario ahora mismo?

Bloquea SSH para ese usuario con una regla Match User en los bastiones (o directamente en el host si es necesario), y luego elimina sus claves de la fuente real de autorización. Valida con logs y un intento controlado de inicio de sesión.

3) Si bloqueo la cuenta Linux, ¿eso impide el login por clave SSH?

No de forma fiable. El bloqueo de cuenta afecta la autenticación por contraseña y a veces el comportamiento de PAM, pero la autenticación por clave pública puede seguir teniendo éxito según la configuración. Trata el bloqueo de cuenta como un cortocircuito, no como la solución completa.

4) ¿Cómo sé qué clave se usó en un inicio de sesión?

Revisa los logs de sshd: las líneas de autenticación pública exitosa incluyen el tipo de clave y la huella. En Debian, journalctl -u ssh suele ser la vía más rápida.

5) Tenemos cientos de servidores. ¿Greppear authorized_keys por todas partes es la única opción?

Es la opción que “funciona hoy”, no la escalable. A escala de flota, usa enforcement de gestión de configuración, un comando autorizado central con caché o—mejor—certificados de usuario SSH.

6) ¿Son complicados de operar los certificados SSH?

Operativamente son diferentes, no inherentemente más difíciles. Cambias la distribución de claves por operaciones de CA: firma segura, políticas, sincronización de tiempo y flujos de emisión. Si ya gestionas PKI o sistemas de identidad en producción, encajan naturalmente.

7) ¿Deberíamos permitir agent forwarding?

Por defecto no. Permítelo solo donde reduzca significativamente la distribución de claves y donde los hosts destino sean de confianza y estén endurecidos. El agent forwarding convierte un servidor comprometido en un escalón de ataque.

8) ¿Cuál es la forma más rápida de detectar proliferación de claves en un solo host?

Ejecuta sshd -T para confirmar el mecanismo de auth, luego enumera archivos authorized_keys y cuenta cuerpos/huellas de clave duplicadas entre ellos. Los duplicados son tu primera señal de alarma.

9) ¿Por qué todavía vemos claves viejas en archivos después de “migrar a claves centralizadas”?

Porque las migraciones suelen ser parciales. Si AuthorizedKeysFile sigue habilitado y los archivos aún existen, sshd puede seguir honrándolos. Decide un camino y luego aplícalo, incluyendo remover u omitir archivos legados.

10) ¿Cómo demostramos a los auditores que una clave fue revocada?

Almacena la huella en el registro del cambio, muestra el diff/remoción de la fuente de autorización y captura logs de sshd que muestren rechazo/ausencia de aceptación después del corte.

Conclusión: próximos pasos que perduran

Si te llevas una lección del caso #13, que sea esta: la rotación de claves es un problema de gobernanza con sombrero criptográfico. Debian 13 te da las mismas herramientas afiladas; tu trabajo es usarlas de forma consistente.

Haz esto a continuación, en orden:

  1. Elige tu modelo de autorización (archivos locales gestionados, claves centralizadas o certificados) y deja de mezclar casualmente.
  2. Construye un inventario basado en huellas para poder responder “quién tiene acceso” sin adivinar.
  3. Practica la revocación de emergencia usando puntos de estrangulamiento en bastiones y reglas Match probadas.
  4. Acorta la vida de las credenciales donde puedas—TTL de certificados, opciones de clave más estrictas, menos claves compartidas.
  5. Mide la proliferación: número de claves por cuenta, duplicados y claves no vistas en logs durante meses.

Haz que la higiene de claves sea aburrida. Tu yo futuro, de guardia a hora intempestiva, te lo agradecerá—y seguirá quejándose, pero con menos outages.

← Anterior
Problemas de MTU en redes Docker: por qué fallan las solicitudes grandes y cómo arreglar MTU/MSS
Siguiente →
Debian 13: SSHFS vs NFS — elige el que no se bloquee al azar (y configúralo bien)

Deja un comentario