туннель ssh для выполнения локальной (не удаленной) команды

Я хочу создать сценарий оболочки Linux (bash-), который создает туннель SSH, запускает локальную команду, которая использует этот туннель и, наконец, закрывает туннель и окружающее соединение SSH.

Чтобы сделать это менее сложно объяснить, существует локальная конфигурация SSH для хоста с именем «remoteserver», содержащего локальный закрытый ключ без пароля, поэтому

ssh remoteserver -L 4444:targetserver:5555

будет напрямую открывать соединение с удаленным сервером и создавать туннель из локального порта 4444 на целевой сервер. И рассмотрим локальную команду localclient --port 4444 , как выглядит скрипт, который открывает туннель, выполняет локальную команду и закрывает туннель после завершения локального клиентского приложения?

Поскольку должно быть возможно сохранить другие параллельные текущие SSH-соединения, я не хочу что-то вроде sudo killall ssh .

Вы можете попробовать что-то вроде

 TIMEOUT=60 # seconds ssh remoteserver -L 4444:targetserver:5555 sleep $TIMEOUT & localclient --port 4444 

Тоннель автоматически закроется после $ TIMEOUT секунд. Обратите внимание, что использование & действительно только с подключением без пароля. В противном случае вам нужно использовать флаг -f SSH.

С другой стороны,

 ssh -N remoteserver -L 4444:targetserver:5555 & sshpid=$! localclient --port 4444 kill $sshpid 

будет убивать туннель сразу после выполнения localclient . Обратите внимание, что это не будет работать с флагом -f потому что процесс двойной вилки.

Решение ssh -only (не работает для меня)

Второе предложение @damienfrancois – adquate, но по какой-то причине мне не понравилась идея что-то kill .

Кроме того, я не мог поверить, что у ssh нет встроенного механизма для этого. И это действительно так. Вот как все должно работать:

 ssh remoteserver -o "PermitLocalCommand yes" -o "LocalCommand localclient --port 4444" \ -L 4444:targetserver:5555 -N 

Но по какой-то причине это не сработало так, как ожидалось для меня: сначала я попытался с чем-то простым, как w для LocalCommand но -N заставил команду зависать, поэтому вместо этого я использовал sleep 0 , который, похоже, работал.

Но когда я ввел свою фактическую сетевую команду, результат работал очень медленно. Вместо завершения менее чем за одну секунду потребовалось 40 секунд, прежде чем появилось сообщение о запуске . Я завершил команду через 20 минут.

Решение трубы (работает для меня)

После некоторых экспериментов это то, что я сейчас использую:

 ssh remoteserver -L 4444:targetserver:5555 keepalive.sh \ | (sleep 0.5; localclient --port 4444) 

где keepalive.sh на удаленной стороне:

 while true; do sleep 1 echo "keepalive" done 

sleep 0.5 необходим, чтобы (вроде) убедиться, что туннель настроен до localclient (и для отдаленного или медленного remoteserver вам может потребоваться более длинное ожидание). Туннель завершается, как только localclient заканчивается, потому что shell завершает работу трубы, как только вторая команда закрывает свой стандартный вход.

Это лучше, чем решение @ damienfrancois? Зависит.

Необходимость keepalive.sh является явным недостатком, но способ, которым туннель очищается независимо от того, как и почему localclient заканчивается, является более чистым.

Мне нужно это в контексте Makefile и, как факт, он подходит для одной строки.