Туннель SSH через несколько переходов
Туннелирование данных по SSH довольно просто:
ssh -D9999 username@example.com
устанавливает порт 9999 на вашем localhost
как туннель example.com
, но у меня есть более конкретная потребность:
- Я работаю на местном уровне
localhost
host1
доступно дляlocalhost
host2
принимает только соединения отhost1
- Мне нужно создать туннель из
localhost
вhost2
По сути, я хочу создать SSH-туннель с несколькими прыжками. Как я могу это сделать? В идеале я хотел бы сделать это без необходимости быть суперпользователем на любой из машин.
18 ответов
У вас есть три возможности:
Туннель от
localhost
вhost1
:ssh -L 9999:host2:1234 -N host1
Как отмечено выше, соединение от
host1
вhost2
не будет обеспечено.Туннель от
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
, Это может или не может быть проблемой.Туннель от
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
Что происходит:
- Установите туннель для протокола ssh (порт 22) на частную машину.
- Только если это успешно, ssh на частную машину, используя туннель. (Оператор && обеспечивает это).
- После закрытия частного 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.
В моем случае я сделал
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 прыжок, используя сервер перехода.
Еще одно решение - наивное
В итоге я сделал это очень наивно: вместо того, чтобы пытаться выполнить все команды на своем ноутбуке, я запускаю команды на каждой машине, как показано ниже:
- SSH в ваш сервер перехода, затем запустите
ssh -v -L 6969:localhost:2222 -N your-protected.dest.server
, Если вам будет предложено ввести пароль, введите его. - Теперь на вашем ноутбуке запустите
ssh -v -L 6969:localhost:6969 -N your-jump-server.host.name
, Это перенаправит любой ваш запрос через порт 6969 на вашем ноутбуке на сервер перехода. Затем, по очереди, поскольку мы настроили на нашем предыдущем шаге, сервер перехода снова будет пересылать запросы порта 6969 на порт 2222 на защищенном сервере назначения.
Вы должны увидеть команду "зависает" там после печати некоторых сообщений - это означает, что они работают! Единственное исключение - вы не должны видеть сообщение об ошибке вроде Could not request local forwarding.
Если вы видите это, то это все еще не работает:(. Теперь вы можете попытаться запустить запрос на порт 6969 с вашего ноутбука, и посмотреть, работает ли он.
Надеюсь, если вы не справились со всеми вышеперечисленными методами, возможно, вы можете попробовать это.