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

Туннель SSH через несколько прыжков

Туннель данных через SSH довольно прост:

ssh -D9999 username@example.com

устанавливает порт 9999 на вашей localhost как туннель на example.com, но у меня есть более специфическая потребность:

  • Я работаю локально над localhost
  • host1 доступен только для localhost
  • host2 принимает соединения только от host1
  • Мне нужно создать туннель от localhost до host2

Эффективно, я хочу создать “мульти-хоп туннель” SSH. Как я могу это сделать? В идеале, я хотел бы сделать это без необходимости быть суперпользователем на any машинах.

Ответы (15)

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

В принципе, у вас есть три возможности:

  1. Тоннель от localhost до host1:

  2. Тоннель от localhost до host1 и от host1 до host2:

  3. Тоннели от localhost до host1 и от localhost до host2:

Обычно я бы выбрал вариант 1. Если соединение с host1 до host2 должно быть защищено, используйте вариант 2. Вариант 3 в основном полезен для доступа к сервису на host2, который доступен только с host2.

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

Есть отличный ответ, объясняющий использование конфигурационной директивы ProxyCommand для SSH :

Добавьте это в вашу ~/.ssh/config (см. man 5 ssh_config для подробностей):

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

Тогда ssh host2 будет автоматически туннелировать через host1 (также работает с переадресацией X11 и т.д.). ).

Это также работает для целого класса хостов, например, идентифицированных по домену:

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

Обновление

OpenSSH 7.3 вводит директиву ProxyJump, упрощая первый пример до

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

OpenSSH v7.3 поддерживает коммутатор -J и опцию ProxyJump, которые позволяют один или несколько хостов с раздельными запятыми, так что вы можете просто сделать это сейчас:

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

У нас есть один шлюз ssh в нашей частной сети. Если я нахожусь снаружи и мне нужна удаленная оболочка на машине внутри частной сети, мне нужно ssh войти в шлюз и оттуда на частную машину.

Для автоматизации этой процедуры я использую следующий скрипт:

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

What is happening:

  1. Установите туннель для протокола ssh (порт 22) на частную машину.

  2. Только в случае успеха, ssh на частную машину через туннель. (оператор && обеспечивает это).

  3. После закрытия закрытого сеанса ssh, я хочу, чтобы туннель ssh тоже закрылся. Это делается с помощью трюка “sleep 10”. Обычно первая команда ssh закрывается через 10 секунд, но за это время вторая команда ssh установит соединение с помощью туннеля. В результате первая команда ssh держит туннель открытым до тех пор, пока не будут выполнены следующие два условия: “Сон 10” завершен и туннель больше не используется.

18
18
18
2012-01-11 11:02:06 +0000

Прочитав вышеизложенное и склеив все вместе, я создал следующий Perl скрипт (сохраните его как mssh в /usr/bin и сделайте исполняемым):

#!/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($_);
}

Использование:

Для доступа к HOSTC через HOSTA и HOSTB (один и тот же пользователь):

mssh HOSTA HOSTB HOSTC

Для доступа к HOSTC через HOSTA и HOSTB и использования SSH-портномеров по умолчанию и различных пользователей:

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

Для доступа к HOSTC через HOSTA и HOSTB и использования X-forwarding:

mssh HOSTA HOSTB HOSTC -X

Для доступа к порту 8080 на HOSTC через HOSTA и HOSTB:

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

Этот ответ похож на кинан, так как предполагает использование ProxyCommand. Но удобнее использовать IMO.

Если у вас на хмелевых машинах установлен netcat, вы можете добавить этот фрагмент в ~/.ssh/config:

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

Тогда

ssh -D9999 host1+host2 -l username

сделает то, что вы просили.

Я пришел сюда в поисках оригинального места, где я прочитал этот трюк. Я размещу ссылку, когда найду его.

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

Я сделал то, что think вы хотели сделать с

ssh -D 9999 -J host1 host2

Я запрашиваю оба пароля, затем я могу использовать localhost:9999 для SOCKS прокси на host2. Это ближайший пример, который я могу придумать к примеру, который вы показали в первую очередь.

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

-L 9999:host2:80

Означает привязку к localhost:9999 и любой пакет, отправленный localhost:9999 пересылать его на host2:80

-R 9999:localhost:9999

Означает, что любой пакет, полученный host1:9999 пересылать его обратно localhost:9999

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

вы должны иметь возможность использовать переадресацию портов для доступа к сервису на host2 с localhost. Хороший путеводитель находится здесь . Выдержка:

Существует два вида переадресации портов: локальная и удаленная. Они также называются исходящими и входящими туннелями соответственно. Локальная переадресация портов переадресовывает трафик, приходящий на локальный порт на указанный удаленный порт.

Например, если выдать команду

ssh2 -L 1234:localhost:23 username@host

весь трафик, приходящий на порт 1234 на клиенте, будет переадресовываться на порт 23 на сервере (хосте). Обратите внимание, что локальный хост будет разрешен sshdserver после установления соединения. В этом случае localhost обращается к самому серверу (хосту).

Удаленная переадресация портов делает все наоборот: она перенаправляет трафик, приходящий на удаленный порт, на указанный локальный порт.

Например, если вы выдадите команду

ssh2 -R 1234:localhost:23 username@host

весь трафик, приходящий на порт 1234 на сервере (хосте) будет перенаправлен на порт 23 на клиенте (localhost).

В вашем cast, замените localhost в примере на host2 и host на host1.

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

Мой ответ действительно такой же, как и все остальные ответы здесь, но я хотел уточнить полезность ~/.ssh/config и ProxyJump.

Допустим, мне нужно добраться до места назначения в 3-х прыжках, и для каждого прыжка мне нужно было определенное имя пользователя, хост, порт и идентификатор. Из-за критериев идентификации, это можно сделать только с помощью ~/.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

С вашего компьютера, вы можете тестировать каждый прыжок по отдельности, т.е.

$ 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

Еще одна интересная вещь в ~/.ssh/config файле заключается в том, что это также позволит sftp передачи файлов, например,

$ 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

В этом ответе я приведу конкретный пример. Нужно просто заменить имена хостов, логины и пароли компьютеров на свои.

Описание проблемы

Предположим, что у нас следующая топология сети:

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

Ради конкретики, предположим, что у нас следующие имена хостов, имена пользователей и пароли компьютеров:

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

Цель: мы хотим установить SOCKS прокси, который прослушивает порт 9991 из LocalPC так, чтобы каждый раз, когда соединение на LocalPC инициируется с порта 9991, оно проходило через mit.edu, а затем hec.edu.

Пример использования: hec.edu имеет HTTP сервер, который доступен только на http://127.0.0.1:8001 , из соображений безопасности. Мы хотели бы иметь возможность посетить http://127.0.0.1:8001 , открыв веб-браузер на LocalPC.

  • *

Configuration

In LocalPC, добавьте ~/.ssh/config:

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

Затем в терминале LocalPC, запустите:

ssh -D9991 HEC

Он спросит у вас пароль bob на mit.edu (то есть, dylan123), затем спросит пароль john на hec.edu (то есть, doe456)

В этот момент SOCKS прокси запущен на порту 9991 из LocalPC.

Например, если вы хотите посетить веб-страницу на LocalPC, используя SOCKS прокси, вы можете сделать в Firefox:

Некоторые замечания:

  • в ~/.ssh/config, HEC это имя подключения: вы можете изменить его на что угодно.
  • -D9991 говорит ssh, чтобы настроить SOCKS4 прокси на порту 9991.
0
0
0
2020-02-22 07:13:01 +0000

Только это помогло мне более чем на двух хостах:

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

Это подскажет вам три пароля. Вдохновленный этим ответом

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

В моем случае я сделал

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

, где host2:8890 работает на ноутбуке Jupyter.

Тогда я настроил Firefox на использование localhost:9999 в качестве хоста SOCKS.

Так что теперь у меня есть ноутбук, работающий на host2, доступный Firefox на localhost:8890 на моей машине.

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

Вариант 2 из лучшего ответа может быть использован с другими пользователями ssh, чем текущий aka : user@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

Три варианта, упомянутые в принятом ответе, вообще не сработали. Так как у меня не очень много разрешений на оба хоста, и похоже, что у нашей команды DevOps довольно строгие правила, когда дело доходит до аутентификации, и она занимается MFA. Каким-то образом команды выше не могут хорошо играть с нашей аутентификацией.

Контекст действительно похож на ответы выше, хотя: я не могу ssh в производственный сервер напрямую, и должен сделать 1 прыжок с помощью прыжкового сервера.

Еще одно решение - наивный

Я закончил это очень наивным способом: вместо того, чтобы пытаться запустить все команды на моем ноутбуке, я запускаю команды ** на каждой машине** , как показано ниже:

  1. SSH в вашем прыжковом сервере, а затем запустить ssh -v -L 6969:localhost:2222 -N your-protected.dest.server. Если вам будет предложено ввести любой пароль, введите его.
  2. Теперь на вашем ноутбуке, запустите ssh -v -L 6969:localhost:6969 -N your-jump-server.host.name. Это перенаправит любой ваш запрос на порт 6969 на вашем ноутбуке на “jump server”. Затем, в свою очередь, так как мы настроили на предыдущем шаге, jump server снова переадресует запросы порта 6969 на порт 2222 на защищенном сервере назначения.

Вы должны увидеть команду “зависает” там после того, как вы распечатаете какое-то сообщение - это означает, что они работают! Одно исключение - вы не должны видеть сообщение об ошибке типа Could not request local forwarding., если вы это видите, то оно все равно не работает :(. Теперь вы можете попробовать запустить запрос на порт 6969 с вашего ноутбука, и посмотреть, работает ли он.

Надеюсь, если вы тот, кто провалил все вышеперечисленные методы, возможно, вы можете попробовать это.

Похожие вопросы

19
12
13
7
4