Son las 09:12. Tienes un despliegue en curso, un compañero esperando y tu terminal acaba de responder con las dos palabras menos útiles en operaciones: Permiso denegado. Ayer podías SSH al servidor. Hoy no. Nada “cambió”, salvo que todo siempre cambia.
Esta es una guía de campo para Ubuntu 24.04 (Noble) cuando SSH deja de dejarte entrar. No es teoría: es la realidad en producción: claves que antes funcionaban, cuentas que existían, configuraciones que “deberían estar bien” y capas de políticas que te bloquean en silencio mientras insisten en que estás equivocado.
Plan de diagnóstico rápido (primero/segundo/tercero)
Cuando SSH pasa de “funciona” a “permiso denegado”, no empieces editando archivos al azar. Empieza por probar qué está pasando, dónde falla y cuál lado está mintiendo. Aquí está la vía más rápida que conozco y que no crea problemas nuevos mientras persigues el viejo.
Primero: confirma que estás llegando a la máquina y al usuario correctos
- Resuelve el objetivo y captura la ruta. Confirma hostname → IP y si vas vía bastión/ProxyJump.
- Confirma el nombre de usuario. “Permiso denegado” a menudo significa “usuario equivocado” más “no hay autenticación de reserva permitida”.
- Verifica la continuidad de la clave del host. Un host equivocado o reconstruido aparece como cambio en la clave del host, pero la gente a menudo lo “arregla” borrando known_hosts y luego conectándose a la cosa equivocada.
Segundo: ejecuta un intento cliente verboso y léelo como un registro
- Usa
-vvvuna vez. No adivines; el cliente te dice qué identidades ofreció y qué se aceptó o rechazó. - Decide: ¿falla en la selección de clave, en la aceptación de la clave del servidor o en la autorización de la cuenta?
Tercero: inspecciona los registros del servidor (o usa acceso por consola si estás bloqueado)
- Si tienes alguna vía privilegiada (consola cloud, IPMI/iDRAC, consola serie, KVM), úsala. No es “hacer trampa”, es para eso por lo que pagas.
- Lee
journalctlparasshdy mensajes de PAM/cuenta. - Corrige lo más estrecho que explique los registros. Reiniciar sshd rara vez es la solución; normalmente es cómo conviertes un pequeño error en una caída.
Qué significa realmente “Permiso denegado” en SSH
El mensaje es famosamente poco útil porque el cliente resume una negociación de múltiples pasos. En Ubuntu 24.04 con OpenSSH, “Permiso denegado” generalmente significa:
- El servidor aceptó la conexión, pero ningún método de autenticación tuvo éxito.
- El servidor rechazó tu clave, contraseña o intento de autenticación interactiva por teclado.
- O, más sutil: la autenticación tuvo éxito pero la fase de cuenta falló (PAM, reglas de acceso, usuario bloqueado, shell inválido), y la experiencia del usuario aún colapsa en “Permiso denegado”.
Hay tres grandes categorías:
- Problemas de identidad del lado cliente: clave equivocada, agente equivocado, nombre de usuario equivocado, stanza de configuración equivocada.
- Problemas de archivos/configuración del lado servidor: permisos rotos, bloques Match, authorized_keys no legible, ruta de AuthorizedKeysFile incorrecta, etc.
- Control por políticas/cuentas: usuario bloqueado, expirado, restricciones PAM, AllowUsers/DenyUsers, requisitos de MFA no satisfechos.
Una cita para mantenerte honesto cuando te tiente “reiniciar hasta que funcione”:
“La esperanza no es una estrategia.” — Gene Kranz
Hechos interesantes y breve historia (porque importa)
Un poco de contexto te hace más rápido en el diagnóstico porque dejas de esperar que SSH se comporte como una cosa monolítica. Es una pila.
- SSH reemplazó a rsh/telnet principalmente porque esas herramientas antiguas enviaban credenciales en texto claro. El modelo de seguridad de SSH explica por qué es exigente—y por qué tú también deberías ser exigente.
- Las comprobaciones estrictas de permisos de OpenSSH son deliberadas. Si tu
~/.sshes escribible por el grupo, asume que otro podría inyectar claves. Prefiere bloquearte a ser “servicial”. - “Permission denied (publickey)” es un resumen del lado cliente. El servidor puede rechazar claves por muchas razones: tipo de clave incorrecto, principal equivocado, permisos malos, restricciones de forced-command o política de cuenta.
- Las claves de host son la identidad del servidor. Previenen ataques man-in-the-middle. Cuando la gente borra known_hosts a ciegas, elige conveniencia sobre detección.
- PAM puede negarte tras autenticarte. Por eso importan los registros: puedes “autenticarse” y aun así ser expulsado en la fase de cuenta/sesión.
- Los valores por defecto de OpenSSH evolucionan. Algoritmos y tamaños de clave aceptables hace una década pueden estar deshabilitados ahora. Las actualizaciones pueden “romper” claves antiguas, especialmente RSA débiles.
- La integración de OpenSSH en Ubuntu con systemd es estrecha. Los registros suelen estar en journald, y el comportamiento del servicio (como activación por socket en algunas configuraciones) puede cambiar expectativas.
- Las imágenes cloud suelen usar ajustes de SSH con opiniones fuertes. Por ejemplo, contraseñas deshabilitadas, usuarios predeterminados específicos y cloud-init gestionando
authorized_keys.
Causa #1: No estás usando la clave que crees (agente, identidad, archivo)
Esta es la falla número 1 de “funcionaba ayer” en la vida real porque normalmente no es el servidor. Eres tú, tu portátil, tu agente, tu configuración, tu túnel dividido de VPN, tu nuevo terminal o una herramienta corporativa que rotó algo mientras dormías.
Tarea 1: Ejecuta un intento SSH verboso y identifica qué clave se ofrece
cr0x@server:~$ ssh -vvv user@host.example.com
OpenSSH_9.6p1 Ubuntu-3ubuntu13, OpenSSL 3.0.13 30 Jan 2024
debug1: Reading configuration data /home/cr0x/.ssh/config
debug1: Connecting to host.example.com [203.0.113.10] port 22.
debug1: identity file /home/cr0x/.ssh/id_ed25519 type 3
debug1: identity file /home/cr0x/.ssh/id_rsa type 0
debug1: Offering public key: /home/cr0x/.ssh/id_ed25519 ED25519 SHA256:abc... agent
debug1: Authentications that can continue: publickey
debug1: Offering public key: /home/cr0x/.ssh/id_rsa RSA SHA256:def... agent
debug1: Authentications that can continue: publickey
user@host.example.com: Permission denied (publickey).
Qué significa: Ofreciste dos claves. Ambas fueron rechazadas. Eso acota: o el servidor no tiene ninguna de las claves públicas en authorized_keys, o no puede leerlas, o la política del servidor te bloquea.
Decisión: Si esperabas otra clave, arregla el cliente primero (usar explícitamente -i, limpiar ~/.ssh/config o corregir el agente). Si esperabas que una de esas claves funcionara, pasa a las comprobaciones del servidor (Causa #2 y #3).
Tarea 2: Fuerza la clave y evita la confusión del agente
cr0x@server:~$ ssh -i ~/.ssh/prod_ed25519 -o IdentitiesOnly=yes -vv user@host.example.com
debug1: identity file /home/cr0x/.ssh/prod_ed25519 type 3
debug1: Offering public key: /home/cr0x/.ssh/prod_ed25519 ED25519 SHA256:xyz...
debug1: Authentications that can continue: publickey
user@host.example.com: Permission denied (publickey).
Qué significa: Definitivamente ofreciste la clave prevista. Igual falló.
Decisión: Deja de tocar tu portátil. Ve a verificar que el servidor tenga la clave pública coincidente y que pueda leerla.
Tarea 3: Verifica qué claves tiene realmente tu agente
cr0x@server:~$ ssh-add -l
256 SHA256:abc... /home/cr0x/.ssh/id_ed25519 (ED25519)
2048 SHA256:def... /home/cr0x/.ssh/id_rsa (RSA)
Qué significa: Esas son las identidades que tu agente ofrecerá a menos que lo restrinjas.
Decisión: Si falta la clave correcta, agrégala (ssh-add ~/.ssh/prod_ed25519) o indica a SSH que la use con -i y IdentitiesOnly=yes. Si la clave correcta está presente pero es rechazada, continúa con las comprobaciones del servidor.
Tarea 4: Inspecciona la precedencia de tu configuración SSH (la trampa silenciosa)
cr0x@server:~$ ssh -G host.example.com | sed -n '1,80p'
user user
hostname host.example.com
port 22
identityfile ~/.ssh/id_ed25519
identitiesonly no
proxyjump bastion.example.com
pubkeyauthentication yes
passwordauthentication no
Qué significa: ssh -G muestra la configuración final calculada. Si identityfile no es lo que esperas—o user no lo es—el cliente está haciendo exactamente lo que le dijiste, solo que no lo que querías.
Decisión: Arregla ~/.ssh/config para que los hosts de producción usen explícitamente IdentityFile y IdentitiesOnly yes. La ambigüedad es linda en novelas, no en control de acceso.
Chiste seco #1: Los agentes SSH son como compañeros “serviciales”: aparecen sin invitación y ofrecen con total confianza las credenciales equivocadas.
Causa #2: Permisos/propietario en el servidor cambiaron (home, .ssh, authorized_keys)
Si la autenticación por clave SSH solía funcionar y de repente no, el culpable servidor más común es un cambio de permisos. Esto ocurre tras:
- una migración del directorio home,
- un
chmod -Rdemasiado entusiasta, - restaurar desde una copia de seguridad con propietario incorrecto,
- cambiar usuarios/grupos (incluyendo identidad centralizada),
- o un script de “endurecimiento de seguridad” que no entendió las reglas de OpenSSH.
Tarea 5: Lee los registros del servidor para razones de rechazo de clave
Necesitarás acceso por consola u otra ruta privilegiada si SSH está totalmente roto.
cr0x@server:~$ sudo journalctl -u ssh -n 80 --no-pager
Aug 07 10:12:01 host sshd[12345]: Authentication refused: bad ownership or modes for directory /home/user
Aug 07 10:12:01 host sshd[12345]: Connection closed by authenticating user user 198.51.100.22 port 51234 [preauth]
Qué significa: Este es el clásico: los permisos en el home del usuario (o .ssh) son demasiado permisivos, o el propietario es incorrecto.
Decisión: Corrige propietario y modos. No “relajes” las opciones de sshd para tolerar permisos malos; corrige el estado del sistema de archivos subyacente.
Tarea 6: Comprueba propietario y modos de home y archivos SSH
cr0x@server:~$ sudo namei -l /home/user/.ssh/authorized_keys
f: /home/user/.ssh/authorized_keys
drwxr-xr-x root root /
drwxr-xr-x root root home
drwxrwxr-x user user user
drwxr-xr-x user user .ssh
-rw-r--r-- user user authorized_keys
Qué significa: /home/user es escribible por el grupo (drwxrwxr-x). Eso es suficiente para que OpenSSH rechace claves, dependiendo de la configuración. Incluso si .ssh parece bien, el directorio padre puede fallar la comprobación.
Decisión: Quita el permiso de escritura por grupo en el directorio home y asegúrate de que el propietario sea correcto.
Tarea 7: Aplica permisos sensatos (quirúrgico, no caos recursivo)
cr0x@server:~$ sudo chown -R user:user /home/user/.ssh
cr0x@server:~$ sudo chmod 0755 /home/user
cr0x@server:~$ sudo chmod 0700 /home/user/.ssh
cr0x@server:~$ sudo chmod 0600 /home/user/.ssh/authorized_keys
cr0x@server:~$ sudo ls -ld /home/user /home/user/.ssh /home/user/.ssh/authorized_keys
drwxr-xr-x 9 user user 4096 Aug 7 10:00 /home/user
drwx------ 2 user user 4096 Aug 7 10:01 /home/user/.ssh
-rw------- 1 user user 402 Aug 7 10:01 /home/user/.ssh/authorized_keys
Qué significa: Estos son los valores conservadores que mantienen feliz a sshd: home no escribible por grupo/otros; .ssh privado; archivo de claves privado.
Decisión: Vuelve a probar el inicio de sesión. Si aún falla y los registros ya no se quejan de propietario/modos, pasa a causas de configuración/política.
Tarea 8: Confirma que la clave pública en el servidor coincide con la privada que usas
Esto evita el conejo de las “pares de claves equivocadas”.
cr0x@server:~$ ssh-keygen -lf /home/user/.ssh/authorized_keys | head -n 3
256 SHA256:xyz... comment@laptop (ED25519)
2048 SHA256:old... legacy@laptop (RSA)
Qué significa: Esas son las huellas digitales de las claves públicas autorizadas. Compáralas con la huella de tu clave cliente.
Decisión: Si al servidor le falta la clave correcta, añádela (preferiblemente a través de tu vía de aprovisionamiento estándar). Si está presente, sigue a configuración/política de sshd.
Causa #3: Cambió la configuración de sshd (AllowUsers, bloques Match, métodos de auth)
En Ubuntu 24.04, normalmente ejecutas OpenSSH con una configuración dividida entre /etc/ssh/sshd_config e includes en /etc/ssh/sshd_config.d/. Eso es bueno. También es cómo un cambio “pequeño” se vuelve una sorpresa.
Tarea 9: Valida la configuración de sshd (sintaxis y ajustes efectivos)
cr0x@server:~$ sudo sshd -t
cr0x@server:~$ echo $?
0
Qué significa: Código de salida 0 indica sintaxis válida. Un valor distinto de cero significa que probablemente rompiste la configuración de sshd; podrías aún tener un demonio en ejecución, pero recargar/reiniciar fallará.
Decisión: Si la sintaxis falla, corrígela primero. Si la sintaxis está OK, inspecciona los ajustes efectivos.
Tarea 10: Imprime los ajustes efectivos de sshd (la verdad, no lo que recuerdas)
cr0x@server:~$ sudo sshd -T | egrep -i 'passwordauthentication|pubkeyauthentication|authorizedkeysfile|allowusers|denyusers|permitrootlogin|match'
pubkeyauthentication yes
passwordauthentication no
authorizedkeysfile .ssh/authorized_keys .ssh/authorized_keys2
permitrootlogin prohibit-password
Qué significa: Esta es la configuración evaluada (menos el contexto Match a menos que lo pruebes). Muestra si la autenticación por contraseña está deshabilitada y dónde sshd espera encontrar las claves.
Decisión: Si AuthorizedKeysFile apunta a un lugar inesperado, tus claves podrían estar en el sitio equivocado. Si PubkeyAuthentication está desactivado, has encontrado el problema. Si existen reglas Allow/Deny, inspéctalas a continuación.
Tarea 11: Busca bloques Match que se apliquen solo a tu usuario o IP origen
cr0x@server:~$ sudo awk 'BEGIN{p=0} /^Match /{p=1} {if(p)print}' /etc/ssh/sshd_config /etc/ssh/sshd_config.d/*.conf 2>/dev/null
Match User user
AuthenticationMethods publickey
X11Forwarding no
Qué significa: Tienes una política específica por usuario. Quizá alguien añadió AuthenticationMethods publickey (bien) pero quitó la clave correcta, o la combinó con otro requisito, o puso un comando forzado que ahora falla.
Decisión: Asegúrate de que el bloque Match refleje la realidad. Si requieres solo publickey, la clave debe existir y ser legible. Si requieres múltiples métodos, confirma que el cliente pueda satisfacerlos.
Tarea 12: Confirma que sshd está ejecutando la configuración que crees
cr0x@server:~$ systemctl status ssh --no-pager
● ssh.service - OpenBSD Secure Shell server
Loaded: loaded (/usr/lib/systemd/system/ssh.service; enabled; preset: enabled)
Active: active (running) since Wed 2025-08-07 09:59:10 UTC; 15min ago
Docs: man:sshd(8)
man:sshd_config(5)
Main PID: 1122 (sshd)
Tasks: 1 (limit: 2321)
Memory: 2.3M
CPU: 79ms
CGroup: /system.slice/ssh.service
└─1122 "sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups"
Qué significa: sshd está en ejecución. Genial. Eso no significa que te permita entrar. Pero sí significa que puedes recargar de forma segura después de cambiar la configuración (aún prueba con sshd -t primero).
Decisión: Si cambiaste la configuración y no recargaste, haz una recarga segura tras la validación.
cr0x@server:~$ sudo sshd -t && sudo systemctl reload ssh
Qué significa: Recargar aplica la configuración sin cerrar sesiones existentes. Reiniciar cierra sesiones y es la forma de convertir un bloqueo en varios bloqueos.
Decisión: Prefiere recargar. Si la recarga falla, corrige la configuración y vuelve a intentarlo—no reinicies por frustración.
Causa #4: La cuenta o la política te bloquea (usuario bloqueado, clave expirada, PAM, access.conf)
Esta es la zona de “todo parece correcto, las claves coinciden, los permisos están bien, sshd está sano, pero aún así no puedo entrar”. También es donde los entornos corporativos esconden los objetos afilados: políticas PAM, identidad centralizada, expiración de cuentas y listas de denegación “temporales” que se vuelven permanentes.
Tarea 13: Comprueba si la cuenta está bloqueada o expirada
cr0x@server:~$ sudo passwd -S user
user L 2025-07-10 0 99999 7 -1
Qué significa: La L indica que la cuenta está bloqueada. La autenticación por clave SSH aún puede bloquearse en la fase de cuenta dependiendo de PAM y la configuración; en muchos entornos, una contraseña bloqueada bloquea efectivamente los inicios de sesión.
Decisión: Si esta cuenta debe ser usable, desbloquéala (siguiendo tu proceso de control de cambios). Si debe estar bloqueada, no intentes eludir la política y usa la cuenta de emergencia o el flujo de trabajo correcto.
Tarea 14: Comprueba si el shell es válido (sí, esto aún pasa)
cr0x@server:~$ getent passwd user
user:x:1001:1001:User,,,:/home/user:/usr/sbin/nologin
Qué significa: El shell de la cuenta es /usr/sbin/nologin. SSH puede autenticar pero luego rechazar la sesión (a menudo parece permiso denegado o desconexión inmediata).
Decisión: Si el usuario debe tener acceso shell, asigna un shell válido como /bin/bash o el que sea estándar. Si es intencionalmente nologin, no lo fuerces.
Tarea 15: Busca restricciones PAM en los registros
cr0x@server:~$ sudo journalctl -u ssh -n 120 --no-pager | egrep -i 'pam|account|access|denied|failure'
Aug 07 10:14:09 host sshd[12555]: pam_access(sshd:account): access denied for user `user' from `198.51.100.22'
Aug 07 10:14:09 host sshd[12555]: Failed password for user from 198.51.100.22 port 51288 ssh2
Qué significa: PAM está negando acceso a la cuenta basándose en la IP origen o reglas. La línea del método de autenticación después puede ser confusa; la denegación es explícita.
Decisión: Inspecciona /etc/security/access.conf y la pila PAM de sshd. Corrige la regla o la suposición de la IP origen. No desactives módulos PAM a la ligera; recuperarás acceso y perderás auditoría/cumplimiento.
Tarea 16: Comprueba la configuración PAM para sshd (qué módulos se ejecutan)
cr0x@server:~$ sudo grep -nE 'pam_access|pam_sss|pam_faillock|pam_tally2' /etc/pam.d/sshd
15:account required pam_access.so
22:account [default=bad success=ok user_unknown=ignore] pam_sss.so
Qué significa: pam_access.so está activo. Eso explica bloqueos por origen. pam_sss.so sugiere que hay identidad centralizada (SSSD) involucrada.
Decisión: Si las reglas de acceso son demasiado estrictas, ajústalas. Si SSSD está caído o mal configurado, puede que necesites restaurar la salud del servicio de identidad (no solo SSH).
Tarea 17: Detecta fallos de SSSD / identidad que se hacen pasar por fallos de SSH
cr0x@server:~$ systemctl status sssd --no-pager
● sssd.service - System Security Services Daemon
Loaded: loaded (/usr/lib/systemd/system/sssd.service; enabled; preset: enabled)
Active: active (running) since Wed 2025-08-07 08:01:44 UTC; 2h 13min ago
Qué significa: SSSD está en ejecución, pero “en ejecución” no es “funcionando”. Aun así, un SSSD muerto es una victoria fácil.
Decisión: Si SSSD está caído, recupéralo. Si está activo, revisa sus registros por fallos de búsqueda de auth/cuenta (fuera del alcance de este artículo, pero ya sabes hacia dónde va eso).
Causa #5: La máquina no es la máquina (host keys, DNS/IP, bastión, ProxyJump)
“SSH funcionaba ayer” suele ser cierto. Es solo que ayer estabas haciendo SSH a una máquina diferente y no lo sabías. Cambios de DNS, reasignación de IP flotante, balanceadores delante de SSH (sí, la gente hace eso) y bastiones pueden crear esa ilusión.
Tarea 18: Confirma DNS y ruta hacia el objetivo
cr0x@server:~$ getent hosts host.example.com
203.0.113.10 host.example.com
Qué significa: Esta es la IP que tu sistema usará. Si cambió desde ayer, probablemente estés llamando a otra puerta.
Decisión: Si la IP es inesperada, para y valida el objetivo correcto (inventario, consola cloud). Corrige DNS o actualiza tu configuración SSH para usar la dirección estable.
Tarea 19: Comprueba la discrepancia de la clave del host (la advertencia honesta)
cr0x@server:~$ ssh user@host.example.com
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Host key verification failed.
Qué significa: O el host fue reconstruido, o DNS/IP cambió, o alguien está interceptando tráfico. En redes empresariales, las reconstrucciones y la reutilización de IP son comunes; los ataques son menos comunes, pero no puedes asumirlos.
Decisión: Verifica la nueva clave del host por un canal de confianza fuera de banda (consola, CMDB, registros de aprovisionamiento). Solo entonces actualiza known_hosts.
Tarea 20: Elimina una única entrada known_hosts problemática de forma segura
cr0x@server:~$ ssh-keygen -R host.example.com
# Host host.example.com found: line 42
/home/cr0x/.ssh/known_hosts updated.
Original contents retained as /home/cr0x/.ssh/known_hosts.old
Qué significa: Eliminaste una entrada de host, no nukeaste el archivo entero.
Decisión: Reconecta y valida la nueva huella. Si no puedes validarla, no sigas. “Probablemente esté bien” es como se escriben los informes de incidentes.
Tarea 21: Confirma la ruta ProxyJump/bastión (puede estar bloqueado río arriba)
cr0x@server:~$ ssh -G host.example.com | egrep -i 'proxyjump|proxycommand|hostname|user'
user user
hostname host.example.com
proxyjump bastion.example.com
Qué significa: Tu conexión pasa por un bastión. Permiso denegado puede estar ocurriendo en el salto del bastión, no en el host final.
Decisión: Prueba a conectar al bastión directamente con verbosidad y confirma la autenticación allí primero.
cr0x@server:~$ ssh -vvv user@bastion.example.com
debug1: Authentications that can continue: publickey
user@bastion.example.com: Permission denied (publickey).
Qué significa: El bastión te está rechazando. El objetivo podría estar bien.
Decisión: Arregla el acceso al bastión (claves, listas blancas, políticas) antes de tocar el host objetivo.
Chiste seco #2: Nada te hace crecer el carácter como depurar “Permiso denegado” durante una hora—solo para descubrir que estabas haciendo SSH al entorno de staging con confianza de producción.
Tres mini-historias corporativas (realistas, anonimizadas y dolorosamente educativas)
Mini-historia 1: El incidente causado por una suposición errónea
Tenían una flota de servidores Ubuntu detrás de un bastión. El acceso era solo por claves, sin contraseñas, y la empresa acababa de desplegar una nueva herramienta de gestión de portátiles. La herramienta reemplazó silenciosamente la integración del agente SSH—seguía siendo un agente, seguía “funcionando”, pero dejó de cargar las mismas claves por defecto.
A las 08:30, un ingeniero intentó parchear un CVE de alto riesgo. SSH dijo Permission denied (publickey). Él asumió que el servidor estaba roto, porque “yo no cambié nada”. Un segundo ingeniero tuvo el mismo error y asumió que el bastión estaba caído. Abrieron un incidente, llamaron al equipo de plataforma y empezaron a planear acceso por consola de emergencia.
Los registros en el bastión fueron claros: docenas de claves rechazadas, ninguna con la huella esperada. En los clientes, ssh -vvv mostraba que el agente ofrecía una clave RSA antigua y una clave de GitHub—ninguna autorizada para producción. La clave correcta existía en disco, pero nunca se ofreció porque la nueva herramienta había cambiado el comportamiento del agente y ~/.ssh/config no tenía IdentityFile explícito para el bastión.
La solución fue aburrida: actualizar la configuración SSH para fijar la identidad correcta para el bastión y poner IdentitiesOnly yes. El postmortem también fue aburrido, que es señal de que fue útil: la suposición errónea fue “el servidor cambió”. El servidor no cambió. El cliente sí.
También añadieron una comprobación previa al playbook: si SSH falla, recopilar salida de ssh -G y ssh -vvv antes de escalar. Ese paso evitó toda una categoría de falsas alarmas futuras.
Mini-historia 2: La optimización que salió mal
Otra compañía tuvo un “sprint de endurecimiento de seguridad”. Alguien decidió estandarizar permisos de directorio home en todas las máquinas Linux. El script puso /home/* en 0775 para que un grupo compartido pudiera “ayudar con la colaboración”. Parecía inofensivo. Incluso se aprobó en un ticket porque el revisor pensó que “escribir por grupo” solo afecta ediciones locales.
En minutos, la autenticación por claves SSH empezó a fallar para un subconjunto de usuarios. No para todos—solo para aquellos cuyos homes se volvieron escribibles por el grupo y cuya configuración de sshd todavía tenía comprobaciones de modo estrictas (el valor por defecto). El on-call vio Permission denied (publickey) y asumió que las claves habían sido eliminadas o que cloud-init había sobrescrito authorized_keys.
Los registros de sshd contaban la verdad: Authentication refused: bad ownership or modes for directory /home/user. La “optimización” (colaboración vía escritura por grupo) violó las comprobaciones de seguridad de OpenSSH. El sprint de endurecimiento irónicamente redujo la seguridad al animar a la gente a proponer desactivar las comprobaciones estrictas solo para recuperar acceso.
La solución fue revertir permisos de directorio home a 0755 (o más restrictivos), e implementar la colaboración mediante un directorio de proyecto compartido con ACL correctas, no debilitando los límites del home del usuario. También cambiaron sus scripts para aplicar permisos solo a rutas seguras conocidas en lugar de volar todo el árbol.
La lección no fue “no automatices”. Fue “no automatices sin entender las invariantes en las que el sistema confía”. SSH depende de invariantes de permisos de archivos. Si las rompes, asume compromiso—lo cual es un rasgo razonable para un demonio de seguridad.
Mini-historia 3: La práctica aburrida pero correcta que salvó el día
Un equipo ejecutaba Ubuntu 24.04 en una mezcla de hosts físicos e instancias cloud. Tenían una práctica simple: todo cambio que afectara SSH requería (1) validación con sshd -t, (2) un systemctl reload ssh, no un restart, y (3) una segunda sesión abierta antes de cerrar la primera.
No era glamoroso. No llenaba diapositivas. Evitó caídas.
Durante una ventana de mantenimiento, un ingeniero añadió un bloque Match Address para aplicar autenticación más estricta desde internet público mientras mantenía el acceso interno fluido. La configuración era sintácticamente válida, pero lógicamente errónea: coincidía con más direcciones de las previstas, aplicando efectivamente las reglas de “internet público” a hosts de salto internos. Resultado: la gente empezó a ser denegada.
Como usaron reload y mantuvieron una segunda sesión abierta, nunca se bloquearon. Inmediatamente leyeron logs, vieron el comportamiento del match, corrigieron la condición del match, recargaron otra vez y verificaron desde rutas internas y externas. Sin drama, sin recuperación por consola, sin heroísmos a medianoche.
La práctica que los salvó no fue genial. Fue disciplina: validar, recargar, mantener una sesión de respaldo hasta probar que la puerta funciona.
Errores comunes: síntoma → causa raíz → solución
Esta sección es opinada porque refleja lo que la gente realmente hace bajo estrés. Si te reconoces, felicidades: eres humano. Ahora para.
1) Síntoma: “Permission denied (publickey)” tras copiar archivos rutinariamente al servidor
- Causa raíz: permisos de
~/.ssho del directorio home cambiaron (escribible por grupo, propietario equivocado). - Solución: Usa
namei -lpara encontrar el primer directorio malo; establece home en0755,.sshen0700,authorized_keysen0600.
2) Síntoma: Funciona desde un portátil, falla desde otro
- Causa raíz: Diferente clave ofrecida por diferencias en agente/config; o la IP origen está bloqueada por reglas PAM/access.
- Solución: Compara
ssh -Gyssh -vvvdesde ambas máquinas; revisa registros del servidor por denegaciones depam_access.
3) Síntoma: “Permiso denegado” justo después de “limpiar” known_hosts
- Causa raíz: Te conectaste al host equivocado (DNS/IP cambió) y ahora confías ciegamente en una nueva clave, o estás en una instancia reconstruida sin los
authorized_keyscorrectos. - Solución: Verifica la IP objetivo con
getent hosts. Verifica la clave del host por un canal fuera de banda. Luego verifica que las claves autorizadas estén presentes y correctas.
4) Síntoma: Puedes autenticarte pero te desconectan de inmediato
- Causa raíz: Shell inválido (
/usr/sbin/nologin), comando forzado que falla, o error en módulo de sesión PAM. - Solución: Revisa el shell con
getent passwd, inspecciona logs desshdy verifica cualquier comando forzado o entorno restringido.
5) Síntoma: Solo un usuario está bloqueado; otros pueden SSH sin problema
- Causa raíz: Bloque Match para ese usuario, authorized_keys incorrecto, cuenta bloqueada o política por usuario.
- Solución: Busca en
sshd_configysshd_config.dporMatch User. Valida estado de cuenta conpasswd -S. Corrige propietario/modos en el home de ese usuario.
6) Síntoma: Se rompió justo después de una actualización de OpenSSH/Ubuntu
- Causa raíz: Algoritmo o tipo de clave obsoleto deshabilitado; o comportamiento de configuración de sshd cambiado por snippets incluidos.
- Solución: Confirma tipo de clave (
ssh-keygen -lf), prefiere ED25519. Inspecciona configuración efectiva consshd -Ty revisa includes en/etc/ssh/sshd_config.d/.
Listas de verificación / plan paso a paso
Si quieres un plan para ejecutar bajo presión, aquí lo tienes. Está diseñado para minimizar daños colaterales y maximizar la señal.
Lista A: Diagnóstico cliente (2 minutos)
- Confirma el usuario/host objetivo (no te rías; házlo).
- Ejecuta
ssh -vvvuna vez y guarda la salida. - Revisa la configuración computada:
ssh -G host | head. - Lista las claves del agente:
ssh-add -l. - Reintenta con clave fijada:
ssh -i ~/.ssh/prod_ed25519 -o IdentitiesOnly=yes user@host.
Lista B: Lado servidor “¿por qué sshd me rechazó?” (5–10 minutos)
- Lee registros:
journalctl -u ssh -n 120. - Si los registros mencionan propietario/modos, ejecuta
namei -l /home/user/.ssh/authorized_keys. - Corrige permisos (home,
.ssh,authorized_keys), luego vuelve a probar. - Si los logs mencionan PAM access, inspecciona
/etc/pam.d/sshdy/etc/security/access.conf. - Valida configuración de sshd:
sshd -ty ajustes efectivos:sshd -T. - Recarga sshd de forma segura:
systemctl reload ssh.
Lista C: “¿Estamos hablando siquiera con el host correcto?” (3 minutos)
- Resuelve host:
getent hosts host.example.com. - Comprueba si cambió la clave del host (no ignores las advertencias).
- Si usas bastión, prueba la autenticación en el bastión por separado.
- Confirma que la huella de la clave del host coincida con lo esperado vía consola.
Consejos operativos que puedes tomar prestados
- Siempre deja una sesión funcionando mientras cambias configuraciones relacionadas con SSH.
- Usa reload, no restart, a menos que disfrutes bloqueos espontáneos.
- No debilites las comprobaciones de permisos de SSH para “volver a entrar”. Corrige los permisos. El demonio tiene razón al ser estricto.
- Haz la identidad explícita en
~/.ssh/configpara hosts críticos: ruta de la clave, usuario yIdentitiesOnly yes.
Preguntas frecuentes
1) ¿Por qué SSH dice “Permiso denegado” cuando mi clave está definitivamente en el servidor?
Porque “en el servidor” no basta. sshd debe poder leer esa clave, y debe confiar en la ruta hacia ella. Permisos malos en /home/user o ~/.ssh comúnmente provocan rechazo silencioso de la clave con un registro como “bad ownership or modes”.
2) ¿Cómo sé qué clave está intentando SSH?
Usa ssh -vvv. Busca líneas como “Offering public key:” e “identity file … type …”. Si ofrece una clave que no esperabas, fuerza la correcta con -i y IdentitiesOnly=yes.
3) ¿Es seguro borrar ~/.ssh/known_hosts?
Es tan “seguro” como desactivar detectores de humo porque siguen pitando. Elimina solo la entrada específica del host con ssh-keygen -R, y solo después de validar la nueva huella del host.
4) SSH funcionó y luego tras un reinicio dejó de funcionar. ¿Qué cambió?
A menudo: servicios de identidad (SSSD) no arrancaron bien, directorios home montados diferente, permisos cambiados por automatización, o la IP de la instancia cambió y estás golpeando otro host. Empieza con registros (journalctl -u ssh) y verifica que te conectas a la IP esperada.
5) ¿Puede una contraseña bloqueada impedir login por clave SSH?
Sí, dependiendo de PAM y políticas de cuenta. Incluso si public key auth tiene éxito, la fase de cuenta de PAM puede rechazar una cuenta bloqueada/expirada. Comprueba con passwd -S user y logs de PAM.
6) Solo obtengo “Permission denied (publickey)” y ningún otro método. ¿Por qué?
Porque el servidor probablemente tiene PasswordAuthentication no y/o KbdInteractiveAuthentication no. Esto es común y, en general, bueno. También significa que tu ruta de claves debe ser correcta y la clave autorizada y legible.
7) ¿Cuál es el comando más rápido del lado servidor para ver si la configuración de sshd es el problema?
sudo sshd -T para imprimir ajustes efectivos y sudo sshd -t para validar sintaxis. Combínalo con journalctl -u ssh para ver por qué el demonio te rechazó.
8) ¿Por qué SSH falla solo desde la red de la oficina pero funciona desde mi red doméstica (o viceversa)?
Política basada en origen: reglas de pam_access, reglas de firewall, AllowUsers con patrones user@host, o un bloque Match en base a dirección. Los registros suelen mostrar el punto de decisión.
9) Veo “Authentication refused: bad ownership or modes” pero todo parece bien en ~/.ssh. ¿Ahora qué?
Revisa toda la ruta con namei -l. El permiso malo suele estar en /home/user o en un directorio padre, no en .ssh directamente.
10) ¿Debería reiniciar sshd cuando hago troubleshooting?
Prefiere systemctl reload ssh después de sshd -t. Reiniciar puede cerrar tu última sesión funcional y convertir una corrección en un bloqueo. Reload es la opción madura.
Próximos pasos prácticos
Si SSH funcionaba ayer y hoy es “Permiso denegado”, tu trabajo es convertir esa queja vaga en una de cinco causas concretas: clave equivocada, permisos malos, política sshd, política de cuenta o host objetivo equivocado. No improvises—mide.
- Ejecuta
ssh -vvve identifica qué clave se ofrece y rechaza. - Confirma la configuración computada del cliente con
ssh -Gy fija la identidad correcta. - En el servidor, lee
journalctl -u ssh. Normalmente te dice la razón real. - Corrige permisos usando
namei -lpara encontrar el primer directorio inseguro en la ruta. - Valida la configuración de sshd con
sshd -t, confirma ajustes efectivos consshd -Ty recarga de forma segura. - Cuando los logs implican PAM o estado de cuenta, trátalo como un incidente de identidad/política, no solo como un incidente de SSH.
Y una vez que hayas vuelto a entrar: haz SSH determinista. Claves explícitas por host, permisos estrictos y un runbook que empiece por los registros. Tu yo futuro estará cansado y merecerá la amabilidad.