La autenticación por contraseña en SSH tiene un problema fundamental: la contraseña viaja por la red (aunque cifrada) y existe en el servidor como un hash atacable. El modelo de llave pública/privada invierte esa lógica: el servidor nunca ve tu secreto. Solo verifica que eres quien dices ser usando matemáticas de clave asimétrica.
El mecanismo es este: generás un par de llaves matemáticamente relacionadas. La llave pública es como un candado abierto que podés dejar en cualquier servidor. La llave privada es la única llave que abre ese candado, y nunca sale de tu máquina. Cuando te conectás, el servidor te lanza un reto cifrado con tu llave pública; tu cliente lo resuelve con la privada y demuestra identidad sin transmitir ningún secreto.
Usá esto en lugar de contraseñas siempre que puedas: para acceso a servidores de producción es prácticamente obligatorio, y para automatizaciones (scripts, CI/CD, rsync programado) es la única opción sensata. Lo que rompe el esquema son los permisos: SSH es deliberadamente paranoico con los permisos de ~/.ssh/. Si el directorio o los archivos son accesibles por otros usuarios, el daemon descarta las llaves en silencio y el error que ves parece un problema de credenciales cuando en realidad es de permisos.
La passphrase de la llave privada añade una segunda capa: si alguien roba el archivo id_ed25519, todavía necesita la passphrase para usarlo. El problema es que teclearla en cada conexión es insostenible. Ahí entra ssh-agent: un proceso que guarda la llave descifrada en memoria durante tu sesión y la proporciona al cliente SSH cuando la necesita, sin volver a pedirte nada.
Para el algoritmo, ed25519 es la elección correcta hoy. Es más rápido que RSA, produce llaves más cortas (y por tanto authorized_keys más limpios), y su seguridad no depende del tamaño de clave sino de la curva elíptica subyacente (Curve25519). RSA con 4096 bits todavía es aceptable para compatibilidad con sistemas viejos, pero en hardware moderno y servidores actualizados no hay razón para preferirlo.
# 1. Generar el par de llaves ed25519 # -C añade un comentario legible al final de la clave pública # (útil para identificar la llave en authorized_keys de múltiples servidores) ssh-keygen -t ed25519 -C "alvaro@workstation-2024" # El comando preguntará dónde guardar la llave y pedirá passphrase. # Acepta la ruta por defecto (~/.ssh/id_ed25519) a menos que necesites # múltiples identidades. Pon una passphrase real, no la dejes vacía. # 2. Verificar que los permisos son correctos (ssh-keygen los pone bien, # pero si copiaste las llaves desde otro sitio pueden estar mal) ls -la ~/.ssh/ # ~/.ssh/ debe ser 700 (drwx------) # id_ed25519 debe ser 600 (-rw-------) # id_ed25519.pub debe ser 644 o 600, no importa mucho pero 600 está bien # Corrección si algo está mal: chmod 700 ~/.ssh chmod 600 ~/.ssh/id_ed25519 chmod 600 ~/.ssh/id_ed25519.pub # 3. Copiar la llave pública al servidor # ssh-copy-id la agrega a ~/.ssh/authorized_keys en el servidor, # creando el archivo y el directorio con permisos correctos si no existen ssh-copy-id alvaro@192.168.1.50 # Si el servidor usa un puerto no estándar: ssh-copy-id -p 2222 alvaro@192.168.1.50 # 4. Probar que funciona (debería pedir passphrase, no contraseña del sistema) ssh alvaro@192.168.1.50 # 5. Arrancar ssh-agent e iniciar sesión con la llave # eval $(...) es necesario porque ssh-agent imprime variables de entorno # que necesitan ejecutarse en el shell actual, no en un subshell eval $(ssh-agent) # Añadir la llave al agente (pide passphrase una sola vez) ssh-add ~/.ssh/id_ed25519 # Verificar qué llaves tiene cargadas el agente ssh-add -l # A partir de aquí, ssh alvaro@192.168.1.50 no pide nada ssh alvaro@192.168.1.50
El flujo de ssh-copy-id merece atención: se conecta al servidor usando el método de autenticación disponible en ese momento (probablemente contraseña, por eso es la última vez que la usarás), lee ~/.ssh/id_ed25519.pub de tu máquina local, y hace un cat >> ~/.ssh/authorized_keys en el servidor con los permisos correctos. Puedes hacer lo mismo a mano si ssh-copy-id no está disponible: ssh-copy-id es simplemente un script de shell conveniente.
El eval $(ssh-agent) en el paso 5 tiene una trampa que vale la pena entender. ssh-agent arranca un proceso separado y escribe en stdout algo como SSH_AUTH_SOCK=/tmp/ssh-XXXXX/agent.1234; export SSH_AUTH_SOCK. Sin el eval, esas líneas no se ejecutan en tu shell y las herramientas SSH no saben dónde encontrar al agente. El eval toma esa salida y la ejecuta en el contexto del shell actual.
ssh-add -l después de cargar la llave debería mostrarte la huella digital de id_ed25519 con su comentario (alvaro@workstation-2024). Si ves The agent has no identities, el ssh-add no se ejecutó o falló silenciosamente porque la passphrase era incorrecta.
Un detalle sobre la persistencia: ssh-agent vive mientras dure la sesión de terminal donde lo arrancaste. Si abres otra terminal, SSH_AUTH_SOCK no existe ahí y tendrás que volver a hacer ssh-add. La solución habitual en entornos de escritorio es que el gestor de sesión (GNOME Keyring, KDE Wallet) arranque un agente persistente al inicio de sesión. En servidores o sesiones tmux/screen, hay que gestionar esto con más cuidado, por ejemplo añadiendo el eval $(ssh-agent) al .bash_profile (no al .bashrc) y controlando que no se arranquen múltiples instancias del agente.
La llave pública en authorized_keys puede llevar opciones antes de la propia clave: command="backup.sh" para restringir qué comando puede ejecutar esa llave, no-port-forwarding para deshabilitar túneles, o from="10.0.0.0/8" para limitar desde qué IPs es válida. Esas restricciones viven en el servidor y son transparentes al cliente, lo que las hace útiles para llaves de automatización donde no quieres que una llave comprometida abra acceso shell completo.
N° 82