Туннель SSH через несколько переходов

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

ssh -D9999 username@example.com

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

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

По сути, я хочу создать SSH-туннель с несколькими прыжками. Как я могу это сделать? В идеале я хотел бы сделать это без необходимости быть суперпользователем на любой из машин.

18 ответов

Решение

У вас есть три возможности:

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

    ssh -L 9999:host2:1234 -N host1
    

    Как отмечено выше, соединение от host1 в host2 не будет обеспечено.

  2. Туннель от localhost в host1 и из host1 в host2:

    ssh -L 9999:localhost:9999 host1 ssh -L 9999:localhost:1234 -N host2
    

    Это откроет туннель от localhost в host1 и еще один туннель из host1 в host2, Однако порт 9999 в host2:1234 может быть использован кем-либо на host1, Это может или не может быть проблемой.

  3. Туннель от localhost в host1 и из localhost в host2:

    ssh -L 9998:host2:22 -N host1
    ssh -L 9999:localhost:1234 -N -p 9998 localhost
    

    Это откроет туннель от localhost в host1 через который сервис SSH на host2 может быть использован. Затем открывается второй туннель localhost в host2 через первый туннель.

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

Существует отличный ответ, объясняющий использование 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

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

ssh -J jumpuser1@jumphost1,jumpuser2@jumphost2,...,jumpuserN@jumphostN user@host

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

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

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

Что происходит:

  1. Установите туннель для протокола ssh (порт 22) на частную машину.
  2. Только если это успешно, ssh на частную машину, используя туннель. (Оператор && обеспечивает это).
  3. После закрытия частного ssh-сеанса я хочу, чтобы ssh-туннель тоже закрывался. Это делается с помощью трюка "спать 10". Обычно первая команда ssh закрывается через 10 секунд, но в течение этого времени вторая команда ssh установит соединение с использованием туннеля. В результате первая команда ssh сохраняет туннель открытым, пока не будут выполнены следующие два условия: сон 10 завершен и туннель больше не используется.

Прочитав вышесказанное и склеив все вместе, я создал следующий 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

Я сделал то, что, как я думаю, ты хотел сделать с

ssh -D 9999 -J host1 host2

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

Мой ответ действительно такой же, как и все другие ответы здесь, но я хотел уточнить полезность ~/.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

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

[yourpc] $ ssh hop1 # will go from your PC to host1 in a single step
[host1] $ exit
[yourpc] $ ssh hop2 # will go from your PC to host2 via host1 (i.e. two steps)
[host2] $ exit
[yourpc] $ ssh hop3 # will go from your PC to host3 via host1 and host2 (i.e. three steps)
[host3] $ exit
[yourpc] $ 

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

[yourpc] $ sftp hop1 # for file transfers between your PC and host1
Connected to hop1.
sftp> quit
[yourpc] $ sftp hop2 # for file transfers between your PC and host2
Connected to hop2.
sftp> quit
[yourpc] $ sftp hop3 # for file transfers between your PC and host3
Connected to hop3.
sftp> quit

Этот ответ похож на kynan, так как включает использование ProxyCommand. Но использовать ИМО удобнее.

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

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

затем

ssh -D9999 host1+host2 -l username

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

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

ssh -L 9999:host2:80 -R 9999:localhost:9999 host1

-L 9999: хост2:80

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

-R 9999: локальный хост: 9999

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

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

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

Он предложит вам три пароля.

Вдохновленный этим ответом

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

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

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

ssh2 -L 1234:localhost:23 username@host

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

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

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

ssh2 -R 1234:localhost:23 username@host

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

В вашем актерском составе заменить localhost в примере с host2 а также host с host1,

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

Постановка задачи

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

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,


конфигурация

В 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,

Лучшее решение — Jump Proxy:

      ssh -N -A -J user1@host1 -D 8123 user2@host2

тогда вы можете использоватьSOCKSв местном через127.0.0.1:8123

Добавьте следующее в свой~/.ssh/config.

      Host jump_host
  HostName example.com
  User <jump_host_user>
  IdentityFile <local/path/to/jump_id>

Host destination_host
  HostName <ip or url>
  User <destination_host_user>
  IdentityFile <local/path/to/id>
  ProxyJump jump_host

Это заставит пункт назначения действовать так, как если бы у вас был прямой доступ, и вы могли позвонить

      ssh -D9999 destination_host

Их использование, конечно, требует, чтобы вы добавили свойid_rsa.pubвauthorized_keysфайл на соответствующих подачах.

Вариант 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

Если вы можете использовать SSH на обеих машинах, взгляните на директиву ssh ProxyCommand. Это позволит вам перейти прямо с локального хоста в host2 (одной простой командой, если вы используете открытые ключи!!). Затем вы можете делать все, что вы хотите с host2.

http://www.statusq.org/archives/2008/07/03/1916/

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

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

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

Затем я настроил Firefox для использования localhost:9999 как носитель НОСКИ.

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

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

Контекст действительно похож на приведенные выше ответы: я не могу напрямую подключиться к производственному серверу, и мне нужно сделать 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 на вашем ноутбуке на сервер перехода. Затем, по очереди, поскольку мы настроили на нашем предыдущем шаге, сервер перехода снова будет пересылать запросы порта 6969 на порт 2222 на защищенном сервере назначения.

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

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

Другие вопросы по тегам