2010-01-16 05:58:48 +0000 2010-01-16 05:58:48 +0000
354
354

Un túnel de SSH a través de múltiples saltos

Los datos del túnel sobre SSH son bastante sencillos:

ssh -D9999 username@example.com

establece el puerto 9999 en tu localhost como un túnel hacia example.com, pero tengo una necesidad más específica:

  • Estoy trabajando localmente en localhost
  • host1 es accesible al localhost
  • host2 sólo acepta conexiones desde host1
  • Necesito crear un túnel desde localhost hasta host2

Efectivamente, quiero crear un túnel SSH “multi-hop”. ¿Cómo puedo hacerlo? Idealmente, me gustaría hacer esto sin necesidad de ser superusuario en cualquier de las máquinas.

Respuestas (15)

341
341
341
2010-01-17 21:31:56 +0000

Básicamente tienes tres posibilidades:

  1. Túnel del localhost al host1:

  2. Túnel del localhost al host1 y del host1 al host2:

  3. Túnel del localhost al host1 y del localhost al host2:

Normalmente, yo iría con la opción 1. Si la conexión de host1 a host2 necesita ser asegurada, ve con la opción 2. La opción 3 es útil principalmente para acceder a un servicio en host2 al que sólo se puede acceder desde el propio host2.

158
158
158
2010-08-01 17:10:27 +0000

Hay una excelente respuesta que explica el uso de la directiva de configuración ProxyCommand para SSH :

Añade esto a tu ~/.ssh/config (ver man 5 ssh_config para más detalles):

Host host2
  ProxyCommand ssh host1 -W %h:%p

Entonces ssh host2 automáticamente hará un túnel a través de host1 (también funciona con el reenvío de X11, etc.) Esto también funciona para una clase entera de hosts, por ejemplo, identificados por el dominio:

Host *.mycompany.com
  ProxyCommand ssh gateway.mycompany.com -W %h:%p

Update

OpenSSH 7.3 introduce una directiva ProxyJump, simplificando el primer ejemplo a

Host host2
  ProxyJump host1
35
35
35
2016-08-10 09:11:34 +0000

OpenSSH v7.3 en adelante soporta un interruptor -J y una opción ProxyJump, que permite uno o más hosts de salto separados por comas, así que, puedes simplemente hacer esto ahora:

ssh -J jumpuser1@jumphost1,jumpuser2@jumphost2,...,jumpuserN@jumphostN user@host
20
20
20
2010-01-24 18:47:37 +0000

Tenemos una puerta de enlace ssh en nuestra red privada. Si estoy fuera y quiero un shell remoto en una máquina dentro de la red privada, tendría que ssh en la puerta de enlace y desde allí a la máquina privada.

Para automatizar este procedimiento, utilizo el siguiente script:

#!/bin/bash
ssh -f -L some_port:private_machine:22 user@gateway "sleep 10" && ssh -p some_port private_user@localhost

¿Qué está pasando?

  1. Establecer un túnel para el protocolo ssh (puerto 22) a la máquina privada.
  2. Sólo si esto tiene éxito, ssh a la máquina privada usando el túnel. (el operador de && asegura esto).
  3. Después de cerrar la sesión privada de ssh, quiero que el túnel de ssh se cierre también. Esto se hace a través del truco “dormir 10”. Normalmente, el primer comando ssh se cerraría después de 10 segundos, pero durante este tiempo, el segundo comando ssh habrá establecido una conexión usando el túnel. Como resultado, el primer comando ssh mantiene el túnel abierto hasta que se cumplen las dos condiciones siguientes: Dormir 10 ha terminado y el túnel ya no se utiliza.
18
18
18
2012-01-11 11:02:06 +0000

Después de leer lo anterior y de pegar todo junto, he creado el siguiente script Perl (guárdalo como mssh en /usr/bin y hazlo ejecutable):

#!/usr/bin/perl

$iport = 13021;
$first = 1;

foreach (@ARGV) {
  if (/^-/) {
    $args .= " $_";
  }
  elsif (/^((.+)@)?([^:]+):?(\d+)?$/) {
    $user = $1;
    $host = $3;
    $port = $4 || 22;
    if ($first) {
      $cmd = "ssh ${user}${host} -p $port -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no";
      $args = '';
      $first = 0;
    }
    else {
      $cmd .= " -L $iport:$host:$port";
      push @cmds, "$cmd -f sleep 10 $args";
      $cmd = "ssh ${user}localhost -p $iport -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no";
      $args = '';
      $iport ++;
    }
  }
}
push @cmds, "$cmd $args";

foreach (@cmds) {
  print "$_\n";
  system($_);
}

Uso:

Para acceder al HOSTC a través de HOSTA y HOSTB (mismo usuario):

mssh HOSTA HOSTB HOSTC

Para acceder al HOSTC vía HOSTA y HOSTB y usar los números SSH no predeterminados y diferentes usuarios:

mssh user1@HOSTA:1234 user2@HOSTB:1222 user3@HOSTC:78231

Para acceder al HOSTC vía HOSTA y HOSTB y usar X-forwarding:

mssh HOSTA HOSTB HOSTC -X

Para acceder al puerto 8080 en el HOSTC vía HOSTA y HOSTB:

mssh HOSTA HOSTB -L8080:HOSTC:8080
8
8
8
2013-03-13 09:57:28 +0000

Esta respuesta es similar a la de Kynan, ya que implica el uso del ProxyCommand. Pero es más conveniente usar IMO.

Si tienes instalado netcat en tus máquinas de salto puedes añadir este recorte a tu ~/.ssh/config:

Host *+*
    ProxyCommand ssh $(echo %h | sed 's/+[^+]*$//;s/\([^+%%]*\)%%\([^+]*\)$/ -l /;s/:/ -p /') nc $(echo %h | sed 's/^.*+//;/:/!s/$/ %p/;s/:/ /')

Entonces

ssh -D9999 host1+host2 -l username

haré lo que pediste.

Vine aquí buscando el lugar original donde leí este truco. Pondré un enlace cuando lo encuentre.

6
6
6
2017-11-21 11:06:21 +0000

Hice lo que yo creo que querías hacer con

ssh -D 9999 -J host1 host2

Se me piden ambas contraseñas, entonces puedo usar localhost:9999 para un proxy SOCKS para el host2. Es lo más cercano que se me ocurre al ejemplo que mostraste en primer lugar.

4
4
4
2010-01-19 02:03:35 +0000
ssh -L 9999:host2:80 -R 9999:localhost:9999 host1

-L 9999:host2:80

Significa que cualquier paquete enviado a localhost:9999 lo reenvía al host2:80

-R 9999:localhost:9999

Significa que cualquier paquete recibido por el host1:9999 lo reenvía al localhost:9999

2
2
2
2010-01-16 06:34:17 +0000

deberías poder usar el reenvío de puertos para acceder a un servicio en host2 desde localhost. Una buena guía se encuentra aquí . Extracto:

Hay dos tipos de reenvío de puertos: el reenvío local y el remoto. También se llaman túneles de salida y de entrada, respectivamente. El reenvío de puerto local reenvía el tráfico que llega a un puerto local a un puerto remoto especificado.

Por ejemplo, si se emite el comando

ssh2 -L 1234:localhost:23 username@host

todo el tráfico que llega al puerto 1234 en el cliente será reenviado al puerto 23 en el servidor (host). Tenga en cuenta que el localhost será resuelto por el sshdserver después de que se establezca la conexión. Por lo tanto, en este caso localhost se refiere al propio servidor (host).

El reenvío de puerto remoto hace lo contrario: reenvía el tráfico que llega a un puerto remoto a un puerto local especificado.

Por ejemplo, si se emite el comando ssh2 -R 1234:localhost:23 username@host

todo el tráfico que llega al puerto 1234 del servidor (host) se reenviará al puerto 23 del cliente (localhost).

En su reparto, sustituya localhost en el ejemplo por host2 y host por host1.

1
1
1
2019-12-18 04:08:37 +0000

Mi respuesta es realmente la misma que todas las demás respuestas aquí, pero, quería aclarar la utilidad de ~/.ssh/config y ProxyJump.

Digamos que necesito llegar a un destino en 3 saltos, y, para cada salto, necesito un nombre de usuario, host, puerto e identidad específicos. Debido a los criterios de identidad, esto sólo se puede hacer con el archivo de configuración ~/.ssh/config:

Host hop1
    User user1
    HostName host1
    Port 22
    IdentityFile ~/.ssh/pem/identity1.pem

Host hop2
    User user2
    HostName host2
    Port 22
    IdentityFile ~/.ssh/pem/identity2.pem
    ProxyJump hop1

Host hop3
    User user3
    HostName host3
    Port 22
    IdentityFile ~/.ssh/pem/identity3.pem
    ProxyJump hop2

Desde tu ordenador, puedes probar cada salto individualmente, es decir,

$ ssh hop1 # will go from your PC to the host1 in a single step
$ ssh hop2 # will go from your PC to host1, then from host1 to host2, i.e. in two steps
$ ssh hop3 # will go from your PC to host1, then to host2, then to host3, i.e. in three steps

Otra cosa genial del archivo ~/.ssh/config es que esto también permitirá las transferencias de archivos sftp, por ejemplo,

$ sftp hop1 # will connect your PC to host1 (i.e. file transfer between your PC and host1)
$ sftp hop2 # will connect your PC to host1 to host2 (i.e. file transfer between your PC and host2)
$ sftp hop3 # will connect your PC to host1 to host2 to host3 (i.e. file transfer between your PC and host3)
1
1
1
2017-03-18 20:13:02 +0000

En esta respuesta voy a dar un ejemplo concreto. Sólo tienes que sustituir los nombres de host, nombres de usuario y contraseñas de los ordenadores por los tuyos. Asumamos que tenemos la siguiente topología de red:

our local computer <---> server 1 <---> server 2

Para concretar, asumamos que tenemos los siguientes nombres de host, nombres de usuario y contraseñas de los ordenadores:

LocalPC <---> hostname: mit.edu <---> hec.edu
                          username: bob username: john 
                          password: dylan123 password: doe456

Objetivo: queremos configurar un proxy SOCKS que escuche en el puerto 9991 de LocalPC para que cada vez que se inicie una conexión en LocalPC desde el puerto 9991 pase por mit.edu y luego por hec.edu.

Ejemplo de caso de uso: hec.edu tiene un servidor HTTP que sólo es accesible en http://127.0.0.1:8001 , por razones de seguridad. Nos gustaría poder visitar http://127.0.0.1:8001 abriendo un navegador web en LocalPC.


Configuración

En LocalPC, añada en ~/.ssh/config:

Host HEC
    HostName hec.edu
    User john
    ProxyCommand ssh bob@mit.edu -W %h:%p

Luego en el terminal de LocalPC, ejecute:

ssh -D9991 HEC

Le pedirá la contraseña de bob en mit.edu (es decir.., dylan123), luego le pedirá la contraseña de john en hec.edu (es decir, doe456).

En ese punto, el proxy SOCKS está ahora funcionando en el puerto 9991 de LocalPC.

Por ejemplo, si quieres visitar una página web en LocalPC usando el proxy SOCKS, puedes hacer en Firefox:

Algunas observaciones:

  • en ~/.ssh/config, HEC es el nombre de la conexión: puedes cambiarlo por lo que quieras.
  • El -D9991 le dice al ssh que configure un proxy SOCKS4 en el puerto 9991.
0
0
0
2020-02-22 07:13:01 +0000

Sólo esto me ayudó en más de dos hosts:

ssh -L 6010:localhost:6010 user1@host1 \
-t ssh -L 6010:localhost:6010 user2@host2 \
-t ssh -L 6010:localhost:6010 user3@host3

Le indicará tres contraseñas. Inspirado por esta respuesta

0
0
0
2018-07-26 05:24:38 +0000

En mi caso hice

localhost$ ssh -D 9999 host1
host1$ ssh -L 8890:localhost:8890 host2

donde host2:8890 se ejecuta en un Notebook Jupyter.

Luego configuré Firefox para usar localhost:9999 como un host SOCKS.

Así que ahora tengo el notebook ejecutándose en host2 accesible por Firefox en localhost:8890 en mi máquina.

0
0
0
2017-10-05 08:43:30 +0000

La opción 2 de la mejor respuesta podría ser usada con diferentes usuarios ssh que el actual alias : usuario@host

export local_host_port=30000
    export host1_user=xyz
    export host1=mac-host
    export host1_port=30000
    export host2=192.168.56.115
    export host2_user=ysg
    export host2_port=13306

    # Tunnel from localhost to host1 and from host1 to host2
    # you could chain those as well to host3 ... hostn
    ssh -tt -L $local_host_port:localhost:$host1_port $host1_user@$host1 \
    ssh -tt -L $host1_port:localhost:$host2_port $host2_user@$host2
0
0
0
2019-08-20 19:09:55 +0000

Las tres opciones mencionadas en la respuesta aceptada no funcionaron para mí en absoluto. Ya que no tengo mucho permiso sobre ambos anfitriones, y parece que nuestro equipo de DevOps tiene unas reglas bastante estrictas en cuanto a la autentificación y están haciendo MFA. De alguna manera los comandos de arriba no pueden jugar bien con nuestra autenticación.

El contexto es realmente similar a las respuestas de arriba, sin embargo: No puedo entrar en el servidor de producción directamente, y tengo que hacer 1 salto usando un servidor de salto.

Otra solución más - una ingenua

Terminé haciéndolo de una manera muy ingenua: en lugar de tratar de ejecutar todos los comandos en mi portátil, ejecuto los comandos en cada una de las máquinas , como se muestra a continuación:

  1. SSH en tu servidor de salto, y luego ejecuto ssh -v -L 6969:localhost:2222 -N your-protected.dest.server. Si se te pide cualquier contraseña, escríbela.
  2. Ahora en tu laptop, ejecuta ssh -v -L 6969:localhost:6969 -N your-jump-server.host.name. Esto reenviará cualquiera de sus peticiones en el puerto 6969 de su portátil, al servidor de salto. Luego por turno, ya que configuramos en nuestro paso anterior, el servidor de salto reenviará nuevamente las solicitudes del puerto 6969 al puerto 2222 en el servidor de destino protegido.

Deberías ver el comando “cuelga” allí después de imprimir algún mensaje - ¡significa que están funcionando! Una excepción - no deberías ver un mensaje de error como Could not request local forwarding., si lo ves, entonces todavía no funciona :(. Ahora puede intentar disparar la petición en el puerto 6969 desde su portátil, y ver si funciona.

Con suerte, si es alguien que ha fallado todos los métodos anteriores, quizás pueda intentar esto.