Permission denied (publickey) es el error de SSH más visto que existe, y el mensaje no te está haciendo ningún favor. No significa «tu clave está mal». Significa: el servidor rechazó todos los métodos de autenticación que ofreciste, y la clave pública era el único método que estaba dispuesto a discutir. El paréntesis es la lista de métodos del servidor que siguen abiertos —(publickey) significa que la auth por contraseña está desactivada, así que una clave tiene que funcionar, y ninguna de las tuyas lo hizo.
La lista puede ser más larga, y vale la pena leerla. Permission denied (publickey,password) significa que el servidor también aceptaría una contraseña —así que si pretendías teclear una y nunca te la pidieron, tu cliente se rindió con las claves y debería haber recurrido a ella; fuerza el aviso con -o PubkeyAuthentication=no para probar. Permission denied (publickey,keyboard-interactive) es la misma idea con un método interactivo (a menudo un aviso de PAM o 2FA) aún sobre la mesa. Lo que haya dentro del paréntesis es el conjunto completo de puertas que el servidor dejó abiertas; si la que esperabas no está en la lista, el servidor nunca iba a aceptarla.
Es una red amplia: el nombre de usuario equivocado produce este error. También una clave que el cliente nunca ofreció, una clave que el servidor nunca recibió, un problema de permisos que sshd se niega a pasar por alto en silencio, y una etiqueta de SELinux de la que nunca has oído hablar. El arreglo es sistemático, no a tientas —aquí está el bucle, luego los culpables en orden de cuán a menudo son de verdad la respuesta. (¿Pusheando a GitHub o GitLab? Esa es otra lista —la clave SSH de GitHub/GitLab no funciona.)
El bucle de diagnóstico: lee lo que intentó el cliente
Ejecuta la conexión que falla con la máxima verbosidad (y si el muro de salida parece ruido, cómo leer ssh -vvv (en inglés) descifra las líneas que importan):
ssh -vvv user@host
Tres cosas que buscar en la salida:
Authentications that can continue: publickey—la lista de métodos del servidor. Sipasswordno está, nunca se pedirá una contraseña; una clave debe tener éxito.Offering public key: /home/you/.ssh/id_ed25519—cada clave que el cliente intentó. Si la clave que pretendías no está en esta lista, el problema es del lado del cliente (culpable 2). Si lo está —- qué volvió.
Server accepts keyseguido de un intercambio de firma significa que la clave coincidió y algo posterior falló (raro). La imagen habitual es la oferta seguida de inmediato por la siguiente clave oPermission denied—el servidor miró la clave y se encogió de hombros, lo que apunta al lado del servidor (culpables 3–8).
Si tienes cualquier otra vía de entrada a la máquina (consola web del proveedor, otro usuario, otro admin), el log del servidor nombra la razón real sin rodeos:
sudo journalctl -u sshd -f # distros con systemd
sudo tail -f /var/log/auth.log # Debian/Ubuntu
sudo tail -f /var/log/secure # RHEL/Alma/Rocky
Líneas como Authentication refused: bad ownership or modes for directory /home/deploy cierran la investigación en el acto.
Culpable 1: usuario equivocado
La causa más común con diferencia, sobre todo en imágenes de nube (es el primer muro de un primer inicio de sesión en EC2 o un VPS (en inglés)), porque el usuario de login no es el que supondrías: ubuntu en imágenes de Ubuntu, ec2-user en Amazon Linux, admin en AMIs de Debian, root en la mayoría de VPS pelados, azureuser en los valores por defecto de Azure. La clave correcta con el nombre de usuario equivocado produce exactamente este error —el servidor comprueba ~wronguser/.ssh/authorized_keys, no encuentra nada, deniega.
Antes de tocar nada más, confirma el usuario que documenta tu imagen, y fíjalo en ~/.ssh/config para no reteclearlo nunca (la guía de ssh_config (en inglés) cubre el patrón):
Host web1
HostName 203.0.113.10
User ubuntu
Culpable 2: tu clave nunca se ofreció
Si ssh -vvv no muestra tu clave en una línea Offering public key, el cliente no sabe de ella. Apúntalo a ella explícitamente:
ssh -i ~/.ssh/id_ed25519 user@host
Si eso funciona, hazlo permanente con IdentityFile en el bloque de configuración de arriba. Dos trampas relacionadas: un agente atiborrado de muchas claves puede toparse con el límite de intentos del servidor antes de llegar a la correcta (Too many authentication failures —arréglalo con IdentitiesOnly yes), y una clave con una frase de contraseña que cancelaste se omite en silencio. Comprueba qué tiene de verdad el agente:
ssh-add -l
En Windows esta lista puede parecer vacía aunque acabes de cargar una clave — Git Bash, el servicio OpenSSH y WSL ejecutan cada uno un agente distinto, y ssh-add -l solo responde por el ssh que estás usando de verdad; ssh-agent en Windows desenreda los tres.
Culpable 3: la clave pública no está en el servidor
La mitad pública debe ser una línea en ~user/.ssh/authorized_keys —para ese usuario, en el home de ese usuario. La forma fiable de ponerla ahí (usa auth por contraseña o una clave existente para la copia):
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@host
Hacerlo a mano —digamos, a través de una consola del proveedor— añade la única línea y nunca sobrescribas el archivo:
mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys
# pega la única línea de id_ed25519.pub, Enter, Ctrl-D
Fallos clásicos: la clave aterrizó en el archivo de root pero entras como deploy; un editor partió la línea y ahora son dos líneas; pegaste la clave privada (el archivo empieza por ssh-ed25519 AAAA… —si ves -----BEGIN, esa es la mitad equivocada).
Culpable 4: permisos y propiedad
El StrictModes de sshd (activado por defecto) se niega a honrar authorized_keys si la ruta hasta él es escribible por alguien que no sea el propietario. Este es el clásico «copié la clave y aun así no funciona». El arreglo completo, ejecutado como el usuario objetivo:
chmod go-w ~
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chown -R $USER:$USER ~/.ssh
La línea del directorio home importa y se olvida: un /home/deploy escribible por el grupo falla la comprobación tan rotundamente como un archivo de clave legible por todos. La línea bad ownership or modes del log del servidor te dice exactamente a qué directorio objeta.
Culpable 5: formato de clave o frase de contraseña equivocados
OpenSSH no puede leer los archivos .ppk de PuTTY —apuntar -i a uno falla con invalid format en la salida detallada. Exporta una clave en formato OpenSSH desde PuTTYgen (Conversions → Export OpenSSH key), o genera una clave moderna nueva y dala de alta (Ed25519, en casi todos los casos (en inglés)). Si estás en Windows y aún vives en PuTTYgen, un cliente SSH para Windows que importa sesiones de PuTTY y usa claves OpenSSH directamente te quita de encima este paso de conversión. Para verificar que un archivo de clave privada es legible y que la frase de contraseña es la que crees, pídele que imprima su mitad pública:
ssh-keygen -y -f ~/.ssh/id_ed25519
Si eso imprime una clave, el archivo y la frase de contraseña están bien —y puedes comparar la salida con la línea en authorized_keys para confirmar que son de verdad la misma clave.
Culpable 6: la configuración del servidor no lo permite
Menos común, pero barato de comprobar cuando tienes acceso al servidor. Pregúntale a sshd con qué está corriendo de verdad:
sudo sshd -T | grep -Ei 'pubkeyauthentication|authorizedkeysfile'
PubkeyAuthentication debe ser yes (por defecto, pero los scripts de endurecimiento a veces lo cambian), y AuthorizedKeysFile te dice dónde mira sshd —si alguien lo apuntó a /etc/ssh/authorized_keys/%u, tu ~/.ssh/authorized_keys cuidadosamente colocado está siendo ignorado. Tras cualquier cambio en /etc/ssh/sshd_config:
sudo systemctl reload sshd
Culpable 7: contextos de SELinux (familia RHEL)
En RHEL, Alma, Rocky, Fedora y compañía, un ~/.ssh creado por medios inusuales —restaurado de un backup, copiado como root, hecho en un volumen montado— puede llevar la etiqueta de SELinux equivocada, y a sshd se le deniega el acceso de lectura a un archivo con permisos perfectos. Todo lo de arriba cuadra, el error persiste, y ls -Z ~/.ssh muestra algo distinto de ssh_home_t. Un comando repara las etiquetas:
restorecon -R -v ~/.ssh
Si te has quedado sin ideas en una máquina de la familia RHEL, ejecútalo igualmente —es inofensivo cuando las etiquetas ya estaban bien.
Culpable 8: el login de root está desactivado
Los valores por defecto modernos ponen PermitRootLogin prohibit-password (solo clave) o no (nunca). Si estás intentando root@host y la clave está genuinamente en /root/.ssh/authorized_keys, comprueba:
sudo sshd -T | grep -i permitrootlogin
Con no, ninguna credencial funcionará nunca para root —entra como el usuario sin privilegios y usa sudo, que es el arreglo hacia el que el valor por defecto te empuja de todas formas.
Respuestas rápidas
¿Permission denied (publickey) significa que mi clave está corrupta?
Casi nunca. Significa que el servidor no aceptó ninguno de los métodos que ofreciste —normalmente el usuario equivocado o la clave equivocada, no una clave rota. Confirma que la clave es legible y que la frase de contraseña es correcta con ssh-keygen -y -f ~/.ssh/id_ed25519.
¿Cuál es la diferencia entre (publickey) y (publickey,password)?
El paréntesis lista los métodos que el servidor aún permite. (publickey) es solo claves; (publickey,password) significa que una contraseña también funcionaría, así que deberías haber recibido el aviso —si no lo recibiste, tu cliente se rindió tras agotar las claves.
Funcionaba ayer y ahora falla —¿qué cambió?
Un servidor reconstruido, una clave rotada, o un agente que soltó la clave tras un tiempo de espera. Ejecuta ssh-add -l primero para ver qué está cargado, luego comprueba si reimaginaron la máquina (lo que además lanza host key verification failed (en inglés)).
Haz que deje de pasar
Casi todos los culpables de arriba son un hecho por host que no deberías tener que volver a deducir: qué usuario, qué clave, qué puerto. Fíjalos en ~/.ssh/config y la clase de error desaparece en gran parte —y para los errores que quedan, el anillo descifrador cubre los otros nueve que te encontrarás.
Los mismos hechos pueden vivir en un cliente en lugar de en un dot-file. Termalin guarda el usuario, el puerto y la clave con el host guardado, desbloquea tus claves una vez a través de su agente integrado (sin que una clave se omita en silencio por cancelar la frase de contraseña), verifica la identidad del servidor en la primera conexión, y muestra el estado en vivo por host —así que «¿está siquiera arriba?» y «¿estoy usando la clave correcta?» dejan de ser pasos de diagnóstico. También importa sesiones de PuTTY (en inglés) directamente, lo que retira el culpable 5 para siempre.
Termalin es un cliente SSH gratis y multiplataforma que mantiene los usuarios, puertos y claves por host junto al host y verifica los servidores en la primera conexión — descárgalo, o lee cómo maneja las claves de forma segura.