SincroDev Logo SincroDev

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 a o b. 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:

  1. ¿La firma es válida? (demuestra que el servidor tiene la clave privada)
  2. ¿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)

AlgoritmoTamañoVelocidadSeguridadRecomendación
Ed25519256 bitsMuy rápidoExcelentePreferido
RSA4096 bitsLentoBuenaAceptable
ECDSA256-521 bitsRápidoBuenaOK (evitar NIST P-curves si desconfías)
DSA1024 bits-DébilNo 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

ErrorCausa probableSolución
Permission denied (publickey)Clave no en authorized_keysVerificar clave y permisos
Host key verification failedClave del servidor cambióVerificar (¿MITM?) y actualizar known_hosts
Connection refusedsshd no corriendo o puerto incorrectoVerificar servicio y firewall
Connection timed outFirewall bloqueandoVerificar 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.