311

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

ssh -D9999 username@example.com

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

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

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

13 ответов13

297

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

  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 .

148

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

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

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

У нас есть один шлюз 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 завершен и туннель больше не используется.
18

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

Этот ответ похож на 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

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

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

4
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

2

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

ssh -D 9999 -J host1 host2

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

2

Вы должны иметь возможность использовать переадресацию портов для доступа к сервису на 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 .

1

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

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

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

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 .
0

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

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

0

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

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

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

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

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

0

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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .