ProxyJump SSH: Configura ~/.ssh/config para Acceder a Múltiples VPS vía Bastion Host

Configura ProxyJump en ~/.ssh/config para conectarte a múltiples VPS internas vía bastion host sin exponer SSH a internet. Guía técnica paso a paso.

Exponer SSH directamente en cada VPS es cómodo al principio — abres el puerto 22, lo liberas en el firewall y te conectas. En producción, esto se convierte en un problema: la superficie de ataque crece linealmente con cada servidor añadido, los logs de intentos de fuerza bruta se dispersan por múltiples máquinas y gestionar claves autorizadas en N VPS rápidamente se vuelve insostenible.

La solución estándar de la industria es el bastion host (también llamado jump host): una única VPS pequeña, endurecida y expuesta públicamente que sirve como punto único de entrada. Todas las demás VPS quedan en red privada, sin puerto 22 público. Te conectas al bastion y desde él saltas a las VPS internas.

Este tutorial muestra cómo configurar esto correctamente usando la directiva nativa ProxyJump de OpenSSH en el archivo ~/.ssh/config. Tiempo de ejecución: 15-20 minutos. Persona objetivo: sysadmin con 2+ VPS en producción que quiere reducir la superficie de ataque y centralizar el acceso.

Prerrequisitos

Antes de empezar

Necesitas OpenSSH 7.3 o superior en el cliente local (verifica con ssh -V). Las distribuciones Linux modernas y macOS 10.15+ ya vienen con una versión compatible. Windows 10 build 1803+ y Windows 11 también soportan ProxyJump vía el OpenSSH nativo de PowerShell.

Confirma el escenario antes de continuar:

Bastion host bastion.ejemplo.com (IP pública)
VPS interna 1 10.0.0.10 (red privada)
VPS interna 2 10.0.0.11 (red privada)
Puerto SSH bastion 22 (o personalizado)

También necesitas: clave SSH generada (ed25519 recomendado) con la pública ya copiada al bastion vía ssh-copy-id, y las claves correspondientes copiadas a cada VPS interna. Acceso sudo en los servidores por si necesitas ajustar /etc/ssh/sshd_config.

Estructura del archivo ~/.ssh/config

El ~/.ssh/config es donde declaras alias y parámetros para hosts SSH. El cliente OpenSSH lo lee automáticamente y permite simplificar conexiones — en vez de escribir ssh -p 2222 -i ~/.ssh/id_ed25519 [email protected], solo escribes ssh bastion.

Cada bloque empieza con Host <alias> seguido de parámetros indentados. La lectura es top-down: la primera regla que coincide con el nombre del host gana. Usa Host * al final para defaults globales.

01

Verifica que el archivo exista y tenga los permisos correctos:

mkdir -p ~/.ssh
touch ~/.ssh/config
chmod 700 ~/.ssh
chmod 600 ~/.ssh/config

Permisos incorrectos (cualquier cosa distinta de 600 para el config y 700 para el directorio) hacen que el cliente OpenSSH ignore el archivo silenciosamente en algunas distribuciones — es lo primero que hay que revisar cuando “mi configuración no funciona”.

02

Abre el archivo en el editor de tu preferencia:

nano ~/.ssh/config

Vas a añadir 3 bloques: uno para el bastion host, uno para cada VPS interna y (opcionalmente) un bloque Host * con defaults razonables.

Configuración básica con ProxyJump

La directiva ProxyJump (atajo -J en la línea de comandos) le dice a OpenSSH que abra una conexión TCP al host destino a través de otro host intermedio, todo en una única invocación. El salto es transparente: ejecutas ssh vps-app y el cliente se conecta al bastion, abre un túnel hasta la VPS interna y se autentica en ella end-to-end.

03

Añade el bloque del bastion host:

Host bastion
    HostName bastion.ejemplo.com
    User deploy
    Port 22
    IdentityFile ~/.ssh/id_ed25519_bastion
    IdentitiesOnly yes

IdentitiesOnly yes es importante: fuerza al cliente a usar solo la clave declarada en IdentityFile, en vez de probar todas las claves cargadas en el ssh-agent. Sin esto, el bastion puede recibir 5-6 intentos con claves incorrectas antes de la correcta, y configuraciones con MaxAuthTries bajo rechazan la conexión.

04

Añade los bloques de las VPS internas con ProxyJump:

Host vps-app
    HostName 10.0.0.10
    User deploy
    IdentityFile ~/.ssh/id_ed25519_internal
    IdentitiesOnly yes
    ProxyJump bastion

Host vps-db
    HostName 10.0.0.11
    User deploy
    IdentityFile ~/.ssh/id_ed25519_internal
    IdentitiesOnly yes
    ProxyJump bastion

El valor bastion en ProxyJump referencia el bloque Host bastion declarado antes — no es un hostname literal, es el alias SSH. Puedes usar la misma clave para las VPS internas (si están en una red de confianza) o separar por host según la política de seguridad.

05

Añade defaults globales al final del archivo:

Host *
    AddKeysToAgent yes
    ServerAliveInterval 60
    ServerAliveCountMax 3
    HashKnownHosts yes

ServerAliveInterval 60 hace que el cliente envíe un keepalive cada 60 segundos — evita que NAT/firewall corten conexiones idle. HashKnownHosts yes enmascara los hostnames en ~/.ssh/known_hosts, útil para no filtrar topología interna en caso de que el archivo se exponga.

Salto múltiple encadenado

En entornos segmentados con más de una capa de bastion (ej: bastion público → bastion DMZ → VPS app), ProxyJump acepta una lista separada por coma.

06

Configura el salto doble:

Host vps-prod
    HostName 10.10.0.50
    User deploy
    ProxyJump bastion,dmz-jump

El cliente se conecta secuencialmente: local → bastion → dmz-jump → vps-prod. Cada salto añade latencia (handshake TCP + autenticación SSH). En la práctica, 2 saltos es el límite donde la experiencia de uso todavía es fluida — por encima de eso, considera una VPN.

Latencia acumulada

Cada salto añade el RTT entre los hosts involucrados. Si el bastion está en Brasil y la DMZ en Europa, el RTT total para abrir el prompt SSH puede pasar de 500ms. Para operación interactiva intensiva, considera consolidar los bastions en la misma región.

Endurecer el bastion host

El bastion se convierte en el objetivo concentrado de ataques — vale la pena endurecerlo más allá del estándar.

07

En el bastion, edita /etc/ssh/sshd_config:

sudo nano /etc/ssh/sshd_config

Aplica esta configuración:

PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AllowUsers deploy
MaxAuthTries 3
LoginGraceTime 30
ClientAliveInterval 300
ClientAliveCountMax 2
AllowTcpForwarding yes

AllowTcpForwarding yes es obligatorio — sin esto ProxyJump se rompe con el error channel 0: open failed: administratively prohibited. AllowUsers deploy restringe quién puede autenticarse (lista blanca). MaxAuthTries 3 corta la fuerza bruta pronto.

08

Recarga el servicio SSH y prueba antes de cerrar la sesión actual:

sudo sshd -t && sudo systemctl reload ssh

sshd -t valida la sintaxis del archivo antes del reload. Saltarse esta validación y tener un error de sintaxis deja el servicio sin levantarse — quedas bloqueado fuera.

No cierres la sesión actual antes de validar

Siempre abre una segunda sesión SSH en una terminal aparte y prueba el login con la nueva configuración antes de terminar la sesión original. Si cometiste algún error (clave incorrecta, firewall bloqueando, sshd no se levantó), todavía tienes la sesión antigua activa para revertir.

Verificación

Con todo configurado, probar es simple — el cliente abstrae el salto.

09

Conéctate a la VPS interna usando el alias:

ssh vps-app

Si todo está correcto, caes directo en el prompt de vps-app sin ninguna interacción intermedia. Puedes añadir -v (ssh -v vps-app) para ver la negociación verbosa y confirmar que el salto vía bastion ocurrió — busca líneas como debug1: Setting up multiplex master socket y debug1: Connecting to 10.0.0.10 [10.0.0.10] port 22.

10

Confirma que el puerto SSH de las VPS internas está realmente cerrado externamente:

nmap -p 22 10.0.0.10

Ejecutando esto desde cualquier máquina fuera de la red privada, el resultado debe ser filtered o closed. Si muestra open, tu VPS interna todavía tiene el puerto 22 accesible públicamente — corrige el firewall (ufw deny 22 o regla de security group en el proveedor).

Resolución de problemas

Error: “channel 0: open failed: administratively prohibited”

El bastion tiene AllowTcpForwarding no en sshd_config. Edita el archivo en el bastion, cámbialo a yes y ejecuta sudo systemctl reload ssh.

Error: “Permission denied (publickey)” en el segundo salto

La clave declarada en IdentityFile para la VPS interna no está autorizada en ella. Copia la clave pública a la VPS interna vía el bastion: ssh-copy-id -i ~/.ssh/id_ed25519_internal.pub -o ProxyJump=bastion [email protected].

Conexión lenta en el primer salto

Generalmente es DNS inverso lento en el bastion. Añade UseDNS no en /etc/ssh/sshd_config del bastion y recarga el servicio. Reduce el tiempo de autenticación de 5-15 segundos a subsegundo.

La conexión se cae después de unos minutos idle

Falta ServerAliveInterval. Confirma que el bloque Host * en ~/.ssh/config tiene ServerAliveInterval 60 y ServerAliveCountMax 3. Si el NAT del proveedor es agresivo, redúcelo a ServerAliveInterval 30.

Próximos pasos

Con ProxyJump configurado, puedes evolucionar el setup:

  • Añadir 2FA en el bastion vía PAM + Google Authenticator para exigir TOTP además de la clave SSH.
  • Centralizar logs del bastion en un syslog remoto para tener trazabilidad de auditoría de todos los accesos.
  • Implementar grabación de sesión con tlog o auditd en el bastion — útil para compliance.
  • Failover de bastion con 2 instancias y DNS round-robin para evitar single point of failure.
  • Configurar SSH certificate authority para emitir certificados de corta duración en vez de gestionar claves de larga vida.

Si estás montando esta topología en producción, vale la pena recordar que toda VPS Hostini ya viene con IPv6 nativo y red privada entre instancias en la misma región — puedes usar la interfaz privada como ruta desde el bastion hacia las VPS internas, evitando tráfico innecesario por la internet pública. Consulta la página de VPS para detalles de red.

Preguntas frecuentes

¿Cuál es la diferencia entre ProxyJump y ProxyCommand?

ProxyJump (introducido en OpenSSH 7.3, 2016) es la forma moderna y más simple de hacer salto vía host intermedio. ProxyCommand es más antiguo, requiere construir manualmente el comando con `ssh -W %h:%p` y tiene más puntos de fallo. Usa ProxyJump en cualquier entorno con OpenSSH ≥ 7.3.

¿Puedo encadenar múltiples saltos con ProxyJump?

Sí. La directiva `ProxyJump bastion1,bastion2,bastion3` hace salto secuencial por cada host. Útil para entornos segmentados en múltiples zonas (DMZ → app → DB). Cada salto añade latencia — en la práctica, 2 saltos es el punto donde la UX comienza a degradarse.

¿ProxyJump funciona con claves SSH distintas en cada host?

Sí. Cada bloque `Host` en `~/.ssh/config` declara su propio `IdentityFile`. El cliente usa la clave del bastion para autenticarse en el salto y luego la clave de la VPS interna para autenticarse en el destino final. Recomendado: usar agent forwarding deshabilitado y claves separadas por host.

¿El bastion host ve la contraseña o el tráfico de mi sesión en la VPS interna?

No. ProxyJump usa el bastion solo como túnel TCP (modo `-W`). La negociación TLS y la sesión SSH con la VPS interna son end-to-end entre tu cliente y la VPS final. El bastion reenvía bytes cifrados sin capacidad de descifrar.

¿Cómo evito escribir la passphrase de la clave en cada conexión?

Usa `ssh-agent` junto con `AddKeysToAgent yes` en `~/.ssh/config`. La passphrase se pide en la primera conexión y queda cacheada en la memoria del agent hasta el logout. En macOS, añade `UseKeychain yes` para persistir entre sesiones vía el Keychain del sistema.

¿ProxyJump se rompe si el bastion está offline?

Sí — el bastion es punto único de fallo. En entornos críticos, configura 2 bastions con IPs distintas y usa `ProxyJump bastion-a,backup-bastion-b` como fallback manual, o implementa un failover DNS apuntando `bastion.ejemplo.com` a IPs redundantes.

Temas:
Próximos pasos Cloud Ryzen con NVMe y protección DDoS siempre activa.Pon en producción en un VPS Hostini →
¿Te resultó útil este tutorial?
Hablar por WhatsApp