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


332

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

ssh -D9999 username@example.com

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

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

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


2
Для чего ты это использовал? Я хочу использовать его для прокси носки. Это будет работать?
зубцы

2
Да, вы должны иметь возможность использовать туннельное соединение в качестве прокси-сервера SOCKS, если только вам не будет host2отказано в переадресации
Мала

Я думал о создании оболочки по SSH, которая бы настраивала это, используя многократное использование ProxyCommand.
Павел Шимерда

@prongs Удалось ли вам использовать это для SOCKS прокси (все эти годы назад)?
Друкс

Ответы:


324

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

  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самого себя.


17
вариант 3 был то, что я искал, спасибо!
Мала

1
Я хочу сделать просмотр таким образом. Какой из них лучший? Я попробовал первый, но это не сработало. Я установил прокси в моем браузере localhost: 1234, но не повезло. :( пожалуйста, помогите ..
зубцы

1
@prongs попробуй вариант 3
Мала

6
@Noli Если вы используете ssh-agent (что вам и нужно), вы можете переслать его через соединения, используя -Aопцию ssh.
Мика Фишер

2
@musically_ut - это правильно, 2-я команда ssh основана на host1, поэтому, если вы хотите подключиться к ней локально, вам нужно только перезапустить первую часть снова. Если вы добавите -f, он сразу же будет локально задним фоном, таким образом ssh -f -L 9999:localhost:9999 host1, переподключится, если вы нажали ctrl-c в первом случае. Или добавьте -fк оригинальной команде двойной SSH при первом запуске, чтобы сразу все фоновые.
Марк Фишер

153

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

3
Есть ли способ сделать это условно? Я только хочу сделать это иногда. Кроме того, это специально для команд, но я ищу что-то для всего порта 22 (ssh, sftp и т. Д.).
Стефан,

1
@ Стефан, что вы имеете в виду специально для команд ? Ваша конфигурация SSH используется кем угодно ssh, включая gitи sftpт. Д. Afaik.
Кинан

1
@ Stephane Я не знаю, как включить это условно (например, только когда вы находитесь за пределами сети целевого хоста). Я установил эту опцию для всех рассматриваемых хостов в блоке конфигурации и затем (не) комментировал строку по мере необходимости. Не идеально, но это работает.
Кинан

2
@Stephane уверен: ssh -F /path/to/altconfig. Остерегайтесь, это будет игнорировать всю систему /etc/ssh/ssh_config.
Кинан

19
Простой способ сделать настройки "условными" - это определить два разных хоста в .ssh / config, которые имеют одинаковое имя хоста. Подключитесь к host2-tunnel, если вы хотите туннель, и host2, если у вас нет.
Стив Беннетт

25

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

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

ssh -J user1 @ host1 -YC4c arcfour, blowfish-cbc user2 @ host2 firefox -no-remote Это ускорит получение firefox с host2 на localhost.
Джаур

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

1
Очень умно!!! ЛЮБИТЬ ЭТО!
Хенди Ираван

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

1
это круто
Мала

1
Я серьезно не могу отблагодарить вас достаточно, этот сценарий облегчает мою жизнь каждый день. Единственное, что я изменил, - это добавил int (rand (1000)) в iport, чтобы позволить нескольким экземплярам работать одновременно. Я определенно должен тебе пива.
Мала

Это работает очень хорошо. Дальнейшее улучшение будет заключаться в разрешении HOSTB, HOSTC и т. Д. С использованием локальных хостов / etc / hosts и ~ / .ssh / config
Стив Беннетт,

Также я второй комментарий Мала. Без рандомизированного порта, если вы попытаетесь это сделать, mssh HOSTA HOSTDвы на самом деле окажетесь в HOSTB (и, возможно, не поймете ...)
Стив Беннетт

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

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

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


2
Я считаю , что это зарождение трюк: wiki.gentoo.org/wiki/SSH_jump_host
ОДС

5

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

ssh -D 9999 -J host1 host2

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


Это сработало отлично!
Артур Сильва

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 напрямую с localhost: 9999
dvtoever

После этого ответа я получаю channel 3: open failed: administratively prohibited: open failed сообщение об ошибке.
Франк Дернонкур

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.


согласно этой статье, соединение будет защищено только до среднего компьютера (host1). Есть ли способ убедиться, что все это в безопасности?
Мала

Я никогда не пробовал этого, но если host1 и host2 оба являются серверами ssh, вы можете настроить туннель от host1 до host2, а затем настроить туннель от localhost до host1 для одной и той же службы (получение локального и удаленного порты справа). Я не знаю, возможно ли это в одной команде от localhost.
Фидели

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:

введите описание изображения здесь

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

  • in ~/.ssh/config, HECэто имя соединения: вы можете изменить его на что угодно.
  • -D9991Говорит sshо создании SOCKS4 прокси на порту 9991.

0

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

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


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

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

Три варианта, упомянутые в принятом ответе, вообще не сработали. Поскольку у меня нет особых прав на оба хоста, и похоже, что наша команда 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 с вашего ноутбука и посмотреть, работает ли оно.

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.