SSH: El Túnel Seguro Explicado
Cada vez que escribes ssh usuario@servidor, inicias uno de los protocolos más elegantes de la computación moderna. En milisegundos, dos máquinas que nunca se han visto establecen un canal cifrado imposible de espiar, verifican sus identidades, y te dan acceso a un shell remoto.
SSH (Secure Shell) reemplazó a Telnet, rsh y otros protocolos inseguros que enviaban contraseñas en texto plano. Hoy es la columna vertebral de la administración de sistemas, Git, transferencia de archivos y mucho más.
La vida antes de SSH
En los años 90, conectarse a un servidor remoto significaba usar Telnet:
Tu PC ----[password: secreto123]----> Internet ----> Servidor
(texto plano!)
Cualquier router en el camino podía leer tu contraseña.
Tatu Ylönen, un investigador finlandés, creó SSH en 1995 después de un ataque de sniffing en su universidad. La idea era simple pero revolucionaria: cifrar todo.
Arquitectura de SSH
SSH opera sobre TCP (generalmente puerto 22) y consta de tres capas:
┌─────────────────────────────────────────┐
│ Capa de Conexión │ ← Canales multiplexados
│ (sesiones, port forwarding, X11) │
├─────────────────────────────────────────┤
│ Capa de Autenticación │ ← Verifica identidad del usuario
│ (password, publickey, keyboard-int) │
├─────────────────────────────────────────┤
│ Capa de Transporte │ ← Cifrado, integridad, compresión
│ (key exchange, encryption, MAC) │
├─────────────────────────────────────────┤
│ TCP/IP │
└─────────────────────────────────────────┘
El handshake SSH paso a paso
Veamos qué sucede cuando te conectas a un servidor:
Paso 1: Intercambio de versiones
Cliente Servidor
| |
|---- SSH-2.0-OpenSSH_9.0 --------->|
|<--- SSH-2.0-OpenSSH_9.2 ----------|
| |
Ambos anuncian su versión del protocolo. SSH-2.0 es el estándar actual (SSH-1.0 está obsoleto y es inseguro).
Paso 2: Negociación de algoritmos
Cliente y servidor intercambian listas de algoritmos soportados:
Cliente envía: Servidor envía:
┌────────────────────────┐ ┌────────────────────────┐
│ kex_algorithms: │ │ kex_algorithms: │
│ curve25519-sha256 │ │ curve25519-sha256 │
│ ecdh-sha2-nistp256 │ │ diffie-hellman-group16│
│ │ │ │
│ host_key_algorithms: │ │ host_key_algorithms: │
│ ssh-ed25519 │ │ ssh-ed25519 │
│ rsa-sha2-512 │ │ rsa-sha2-256 │
│ │ │ │
│ encryption: │ │ encryption: │
│ chacha20-poly1305 │ │ chacha20-poly1305 │
│ aes256-gcm │ │ aes256-ctr │
│ │ │ │
│ mac: │ │ mac: │
│ hmac-sha2-256 │ │ hmac-sha2-256 │
└────────────────────────┘ └────────────────────────┘
Seleccionan el primer algoritmo en común de cada categoría. Por eso el orden importa en tu ssh_config.
Paso 3: Intercambio de claves (Key Exchange)
Aquí ocurre la magia criptográfica. Usando Diffie-Hellman (o variantes como ECDH), ambas partes generan una clave secreta compartida sin transmitirla:
Cliente Servidor
| |
│ Genera: a (secreto privado) │ Genera: b (secreto privado)
│ Calcula: A = g^a mod p │ Calcula: B = g^b mod p
| |
|------------ A (público) ---------->|
|<----------- B (público) -----------|
| |
│ Calcula: K = B^a mod p │ Calcula: K = A^b mod p
| |
| K = K (¡mismo valor!) |
Un atacante que capture A y B no puede calcular K sin conocer
aob. Esto es el problema del logaritmo discreto.
Paso 4: Verificación del servidor
El servidor firma el hash de la sesión con su clave privada del host:
H = hash(V_C || V_S || I_C || I_S || K_S || e || f || K)
Servidor envía: K_S (clave pública) + firma(H)
El cliente verifica:
- ¿La firma es válida? (demuestra que el servidor tiene la clave privada)
- ¿Conozco esta clave pública? (verifica contra
~/.ssh/known_hosts)
Cliente
│
│ ¿Clave conocida?
│ ├─ Sí, coincide → Continuar
│ ├─ Sí, diferente → ¡ALERTA!
│ └─ No → Preguntar al usuario
Ese mensaje que ves la primera vez:
The authenticity of host 'servidor.com' can't be established.
ED25519 key fingerprint is SHA256:abc123...
Are you sure you want to continue connecting (yes/no)?
Es SSH protegiéndote de ataques Man-in-the-Middle.
Paso 5: Cifrado activado
A partir de aquí, todo viaja cifrado con la clave K derivada:
┌──────────────────────────────────────────────┐
│ Paquete SSH cifrado │
├──────────┬─────────────────────┬─────────────┤
│ Longitud │ Payload cifrado │ MAC │
│ (4 bytes)│ (datos + padding) │ (integridad)│
└──────────┴─────────────────────┴─────────────┘
Autenticación de usuario
Con el canal seguro establecido, el usuario debe autenticarse:
Método 1: Password
Cliente Servidor
| |
|--- userauth: password + "secret"-->|
|<-- userauth: success/failure ------|
Simple pero arriesgado: si el servidor está comprometido, captura tu contraseña.
Método 2: Public Key (recomendado)
~/.ssh/id_ed25519 → Clave privada (nunca sale de tu PC)
~/.ssh/id_ed25519.pub → Clave pública (en el servidor)
El flujo:
Cliente Servidor
| |
|--- "Quiero autenticar con clave"-->|
|<-- "OK, envía prueba" -------------|
| |
│ Firma el session_id con |
│ la clave privada |
| |
|--- firma(session_id) ------------->|
| |
| Servidor verifica: |
| ¿La firma corresponde a |
| una clave en authorized_keys?
| |
|<-- userauth: success --------------|
La clave privada nunca viaja. Solo una firma única para esta sesión.
Método 3: Agente SSH
Para no escribir la passphrase cada vez:
┌────────────┐ ┌────────────┐ ┌────────────┐
│ Tu PC │ │ ssh-agent │ │ Servidor │
│ │ │ (memoria) │ │ │
│ ssh user@s │─────>│ clave priv │─────>│ authoriz. │
│ │ │ descifrada │ │ keys │
└────────────┘ └────────────┘ └────────────┘
# Iniciar agente
eval $(ssh-agent)
# Agregar clave (pide passphrase una vez)
ssh-add ~/.ssh/id_ed25519
# Verificar claves cargadas
ssh-add -l
SSH como túnel: Port Forwarding
SSH no solo sirve para shells. Puede crear túneles cifrados para cualquier tráfico TCP.
Local Port Forwarding (-L)
Acceder a un servicio remoto como si fuera local:
Tu PC Servidor SSH BD interna
│ │ │
│ localhost:3306 │ 10.0.0.5:3306 │
│◄─────────────────────────►│◄──────────────────────►│
│ túnel cifrado │ red interna │
ssh -L 3306:10.0.0.5:3306 usuario@servidor
# Ahora puedes conectar:
mysql -h localhost -P 3306
Remote Port Forwarding (-R)
Exponer un servicio local al mundo a través del servidor:
Tu PC (detrás de NAT) Servidor SSH (IP pública) Internet
│ │ │
│ localhost:3000 │ servidor:8080 │
│◄──────────────────────►│◄───────────────────────│
│ túnel cifrado │ │
ssh -R 8080:localhost:3000 usuario@servidor
# Cualquiera puede acceder a servidor:8080
# y llega a tu localhost:3000
Dynamic Port Forwarding (-D)
Crear un proxy SOCKS:
ssh -D 1080 usuario@servidor
# Configura tu navegador para usar SOCKS5 localhost:1080
# Todo el tráfico pasa por el servidor SSH
Anatomía de las claves SSH
Generar un par de claves
ssh-keygen -t ed25519 -C "tu@email.com"
Esto crea:
~/.ssh/id_ed25519 # Clave privada (¡proteger!)
~/.ssh/id_ed25519.pub # Clave pública (compartir)
Contenido de la clave pública
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJqG... tu@email.com
│ │ │
│ │ └─ Comentario
│ └─ Clave codificada en base64
└─ Tipo de algoritmo
Algoritmos recomendados (2026)
| Algoritmo | Tamaño | Velocidad | Seguridad | Recomendación |
|---|---|---|---|---|
| Ed25519 | 256 bits | Muy rápido | Excelente | Preferido |
| RSA | 4096 bits | Lento | Buena | Aceptable |
| ECDSA | 256-521 bits | Rápido | Buena | OK (evitar NIST P-curves si desconfías) |
| DSA | 1024 bits | - | Débil | No usar |
Archivos de configuración
Cliente: ~/.ssh/config
# Configuración global
Host *
AddKeysToAgent yes
IdentitiesOnly yes
# Servidor de producción
Host prod
HostName 203.0.113.50
User deploy
Port 2222
IdentityFile ~/.ssh/id_prod
# Saltar a través de bastion
Host interno
HostName 10.0.0.100
User admin
ProxyJump bastion
Host bastion
HostName bastion.empresa.com
User admin
Ahora ssh prod equivale a escribir todas las opciones.
Servidor: /etc/ssh/sshd_config
# Seguridad básica
Port 2222 # Cambiar puerto por defecto
PermitRootLogin no # Nunca login directo como root
PasswordAuthentication no # Solo claves públicas
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
# Algoritmos seguros
KexAlgorithms curve25519-sha256,ecdh-sha2-nistp521
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
# Límites
MaxAuthTries 3
MaxSessions 10
ClientAliveInterval 300
ClientAliveCountMax 2
Seguridad: buenas prácticas
1. Usar claves, no contraseñas
# Copiar clave pública al servidor
ssh-copy-id usuario@servidor
# O manualmente:
cat ~/.ssh/id_ed25519.pub | ssh usuario@servidor \
"mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
2. Proteger la clave privada
# Permisos correctos
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub
3. Usar passphrase
# Cambiar passphrase de clave existente
ssh-keygen -p -f ~/.ssh/id_ed25519
4. Rotar claves periódicamente
# Generar nueva clave
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_2026
# Agregar al servidor, verificar, y luego remover la antigua
5. Monitorear accesos
# Ver intentos de login
sudo grep "sshd" /var/log/auth.log | tail -50
# Conexiones activas
who
w
SSH en acción: casos de uso
Ejecutar comando remoto
ssh servidor "df -h && free -m"
Transferir archivos (SCP/SFTP)
# Copiar archivo
scp archivo.txt servidor:/ruta/destino/
# Copiar directorio
scp -r carpeta/ servidor:/ruta/
# SFTP interactivo
sftp servidor
Montar sistema de archivos remoto (SSHFS)
sshfs servidor:/var/www ~/remoto
# Ahora ~/remoto muestra los archivos del servidor
Ejecutar aplicaciones gráficas (X11 Forwarding)
ssh -X servidor
firefox # Se abre en tu pantalla local
Troubleshooting común
Ver qué está pasando
ssh -vvv usuario@servidor
# -v = verbose
# -vv = más verbose
# -vv = máximo detalle
Errores frecuentes
| Error | Causa probable | Solución |
|---|---|---|
Permission denied (publickey) | Clave no en authorized_keys | Verificar clave y permisos |
Host key verification failed | Clave del servidor cambió | Verificar (¿MITM?) y actualizar known_hosts |
Connection refused | sshd no corriendo o puerto incorrecto | Verificar servicio y firewall |
Connection timed out | Firewall bloqueando | Verificar reglas de red |
Regenerar host keys del servidor
sudo rm /etc/ssh/ssh_host_*
sudo ssh-keygen -A
sudo systemctl restart sshd
Conclusión
SSH es mucho más que un “Telnet cifrado”. Es un protocolo criptográficamente sólido que:
- Negocia algoritmos automáticamente
- Intercambia claves sin transmitir secretos
- Verifica la identidad del servidor (previene MITM)
- Autentica usuarios de forma segura
- Cifra todo el tráfico
- Túnela cualquier protocolo TCP
Cada ssh usuario@servidor desencadena esta danza criptográfica en milisegundos. Y aunque lo damos por sentado, SSH es una de las herramientas más importantes para la seguridad de la infraestructura moderna.
La próxima vez que veas ese prompt de $ en un servidor remoto, recuerda: estás hablando a través de un túnel matemáticamente imposible de espiar.
Este artículo es parte de la serie sobre Protocolos de Red.