Я просматривал ответы здесь, на superuser, однако не нашел ни одного ответа, который соответствовал бы моему варианту использования.
Я хочу объединить две команды, которые я использую, чтобы проложить туннель через него:
ssh -L 22:hostB:22 user@hostA
и
ssh -L 10002:localhost:10001 user@localhost
Сейчас я ввожу первую команду, открываю другой терминал и ввожу вторую. Если я делаю что-то вроде
ssh … && ssh …
вторая команда выполняется только после того, как я «выйду» из первого соединения.
Более продвинутые параметры SSH, такие как jumphost, не работают из-за конфигурации сервера на hostA, поэтому я почти уверен, что это единственный вариант сделать порт 10001 доступным таким образом.
Есть ли способ объединить эти две команды в один псевдоним или использовать какой-нибудь простой bash-скрипт с таймаутами и т. д.?
решение1
Я предполагаю, что вы не можете сделать это просто ssh -L 10002:hostB:10001 user@hostA
потому, что
- либо все, что прослушивает B,
10001
привязывается только к локальному интерфейсу, - или брандмауэр на B запрещает подключение к
hostB:10001
A, - или что-то в этом роде.
ssh … && ssh …
вторая команда выполняется только после того, как я «выйду» из первого соединения.
Да, это нормально. Часть перед &&
должна завершиться и вернуть статус выхода, прежде чем будет запущена следующая команда (или нет, в зависимости от статуса).
Это почти сразу же запустит второй код, ssh
но он некорректен:
( сш … & ) && сш …
Он несовершенен, потому что первый ssh
не влияет на второй. Второй, вероятно, запустится задолго до того, как первый установит туннель. Запрос паролей также может стать проблематичным.
Для этого варианта использования ssh
есть -f
вариант. Отman 1 ssh
:
-f
Запрашиваетssh
переход в фоновый режим непосредственно перед выполнением команды. […]
Итак, основная команда может быть такой:
ssh -fNL 22:hostB:22 user@hostA && ssh -NL 10002:localhost:10001 user@localhost
Вам решать, хотите ли вы использовать -N
второй ssh
или нет. Теперь трюк в том, что первый ssh
переходит в фоновый режим после запроса пароля (если применимо) и после установления первого туннеля, так что временно есть два "первых" ssh
процесса. Тот, что в фоновом режиме, начинает обрабатывать туннель; тот, что на переднем плане, завершает работу, это позволяет &&
работать.
Вы, вероятно, хотите -o ExitOnForwardFailure=yes
. Если первый туннель не может быть установлен, первый ssh
завершится неудачно, а второй не будет запущен. Улучшенная команда:
ssh -fNL 22:hostB:22 \
-o ExitOnForwardFailure=yes \
user@hostA &&
ssh -NL 10002:localhost:10001 user@localhost
Обратите внимание, что когда второй процесс ssh
завершается, первый (в фоновом режиме) остается. Преимущество в том, что вы можете запустить единственный второй процесс ssh
позже. Недостаток в том, что вам придется вручную завершить первый процесс, если вы не хотите, чтобы он оставался. Имея это в виду, рассмотрите следующий подход:
ssh -fNL 22:hostB:22 \
-o ExitOnForwardFailure=yes \
user@hostA
ssh -NL 10002:localhost:10001 user@localhost
Обратите внимание, что нет &&
. Теперь, если старый первый ssh
все еще работает, то новый потерпит неудачу, потому что он не сможет привязаться к порту. Второй ssh
будет работать независимо, он будет использовать первый туннель, и неважно, насколько старый туннель.
Может случиться, что старого нет ssh
, и первый ssh
по каким-то причинам выходит из строя. Если так, то второй будет работать и, вероятно, выйдет из строя (соединение отклонено), потому что порт никто не слушает 22
.
Тем не менее, вы можете не захотеть оставлять "простой" ssh
процесс, вы можете захотеть завершить его автоматически после второго ssh
выхода. Очевидно, killall ssh
что это может вызвать сопутствующий ущерб. Давайте найдем что-то более тонкое:
-M
Переводитssh
клиент в режим «главного» для совместного использования подключения. […][…]
-O ctl_cmd
Управление активным главным процессом мультиплексирования соединений. […][…]
-S ctl_path
Указывает местоположение управляющего сокета для совместного использования соединения […]
#!/bin/sh
socket=/tmp/hostA.socket
ssh -fNL 22:hostB:22 \
-o ExitOnForwardFailure=yes \
-MS "$socket"
user@hostA &&
{
ssh -NL 10002:localhost:10001 user@localhost
ssh -S "$socket" -O exit dummy
}
После завершения второй команды ssh
третья прикажет первой выйти. В третьей ssh
команде важен сокет, а не имя хоста; все равно нужно указать какое-то имя хоста, поэтому dummy
. Первая команда ssh
удалит сокет перед выходом, мусора не должно остаться.
Остается еще несколько проблем:
- Если скрипт прервется, то
ssh
останется первый. Либо сбросьте&&
, либо отправьте-O exit
в самом начале на всякий случай (он будет нулевымssh
). Если первый
ssh
внезапно умрет, сокет может остаться. Это заставит любой новый первыйssh
выйти из строя. Хорошей идеей может быть расширение нулевогоssh
следующим образом:ssh -S "$socket" -O exit dummy || rm "$socket"
/tmp/hostA.socket
может быть не лучшим местом для сокета. Вssh_config
эквиваленте-S
опции командной строки естьControlPath
. Смотрите, чтоman 5 ssh_config
говорит об этом:Рекомендуется, чтобы любой файл,
ControlPath
используемый для совместного использования соединений, включал как минимум%h
,%p
, и%r
(или альтернативно%C
) и был помещен в каталог, недоступный для записи другими пользователями.«Каталог, недоступный для записи другим пользователям» может быть
/run/user/$UID
, если ваша ОС поддерживает это (я полагаю, этоsystemd
особенность ).