Bash-скрипт для настройки временного SSH-туннеля

133

В Cygwin я хочу, чтобы сценарий Bash:

  1. Создайте SSH-туннель к удаленному серверу.
  2. Выполните некоторую работу локально, используя туннель.
  3. Затем закройте туннель.

Часть выключения озадачила меня.

В настоящее время у меня есть хромое решение. В одной оболочке я запускаю следующее, чтобы создать туннель:

# Create the tunnel - this works! It runs forever, until the shell is quit.
ssh -nNT -L 50000:localhost:3306 jm@sampledomain.com

Затем в другом окне оболочки я делаю свою работу:

# Do some MySQL stuff over local port 50000 (which goes to remote port 3306)

Наконец, когда я закончу, я закрываю первое окно оболочки, чтобы убить туннель.

Я бы хотел сделать все это одним скриптом, например:

# Create tunnel
# Do work
# Kill tunnel

Как мне отслеживать процесс туннеля, чтобы знать, какой из них убить?

JM.
источник
Я написал сценарий, который поможет выполнить туннелирование ssh, вы можете проверить его по адресу: github.com/gdbtek/ssh-tunneling.git
Нам Нгуен

Ответы:

326

Вы можете сделать это чисто с помощью ssh 'control socket'. Чтобы поговорить с уже запущенным процессом SSH и получить его pid, уничтожьте его и т. Д. Используйте «управляющий сокет» (-M для главного и -S для сокета) следующим образом:

$ ssh -M -S my-ctrl-socket -fnNT -L 50000:localhost:3306 jm@sampledomain.com
$ ssh -S my-ctrl-socket -O check jm@sampledomain.com
Master running (pid=3517) 
$ ssh -S my-ctrl-socket -O exit jm@sampledomain.com
Exit request sent. 

Обратите внимание, что my-ctrl-socket будет фактическим созданным файлом.

Я получил эту информацию из очень RTFM-ответа в списке рассылки OpenSSH .

Крис МакКормик
источник
7
Это лучший ответ, который я когда-либо видел по этой теме. Спасибо большое, он должен быть принят. Я использую это для подключения к моей Vagrant VM и запускаю скрипт обновления FlywayDB.
Christian
2
Видимо управляющие розетки работают не везде. Например, я попадаю Operation not permittedв среду непрерывной интеграции drone.io:muxserver_listen: link mux listener ssh-ctrl-socket.wsASkszgSBlK7kqD => ssh-ctrl-socket: Operation not permitted
Микко Охтамаа
Итак, что происходит с my-ctrl-socketфайлом после его запуска? Когда я это делаю ls -laв текущей папке, я больше не вижу файл.
sachinruk
2
Если вы используете его в сценарии, вам нужно подождать, пока сокет управления станет доступным в течение нескольких секунд. Мое решение:while [ ! -e $ctrl_socket ]; do sleep 0.1; done
Адам Валлнер
когда я это делаю, я получаю open failed: administratively prohibited: open failedи не думаю, что это открывает туннель
Энди Рэй
21

Вы можете указать SSH, чтобы он работал в фоновом режиме с помощью параметра -f, но вы не получите PID с помощью $ !. Кроме того, вместо того, чтобы ваш скрипт спал произвольное количество времени перед использованием туннеля, вы можете использовать -o ExitOnForwardFailure = yes с -f, и SSH будет ждать, пока все удаленные переадресации портов будут успешно установлены, прежде чем перейти в фоновый режим. Вы можете grep выводить ps, чтобы получить PID. Например, вы можете использовать

...
ssh -Cfo ExitOnForwardFailure=yes -NL 9999:localhost:5900 $REMOTE_HOST
PID=$(pgrep -f 'NL 9999:')
[ "$PID" ] || exit 1
...

и будьте уверены, что получаете желаемый PID

Мэн Гай
источник
Вы можете этого не осознавать, но это своего рода гений. Я искал способ отслеживать PID туннеля SSH и почти закончил использовать сценарии службы systemd. Больше нет: я могу найти нужный мне SSH-процесс, используя имя туннеля. Эта идея как-то полностью меня обошла. Большое спасибо!
aexl
19
  • Вы можете указать sshпереходить в фоновый &режим и не создавать оболочку на другой стороне (просто откройте туннель) с помощью флага командной строки (я вижу, вы уже сделали это с -N).
  • Сохраните PID с помощью PID=$!
  • Делай свое дело
  • kill $PID

РЕДАКТИРОВАТЬ: Исправлено $? в $! и добавил &

ZeissS
источник
3
Если мой скрипт где-то умирает, прежде чем попадет в УБИЙСТВО, я должен быть осторожен с этим.
jm.
2
@jm: trap 'kill $PID' 1 2 15расскажет о многих случаях сбоя скрипта.
Норман Рэмси,
3
Чтобы это работало надежно, мне пришлось немного "поспать" ПОСЛЕ создания туннеля, но перед его использованием.
jm.
@NormanRamsey, я думаю, вы имеете в виду trap "kill $PID", потому что bash будет интерполировать переменные только внутри строк с
двойными кавычками
1
@JuanCaicedo Это различие будет важно, только если PIDпеременная будет переопределена позже. Переменная либо расширяется при trapвызове встроенной функции (подход OP), либо при обнаружении сигнала (ваш подход); здесь оба подхода дают одинаковый результат.
Witiko
4

Я предпочитаю запускать новую оболочку для отдельных задач и часто использую следующую комбинацию команд:

  $ sudo bash; exit

или иногда:

  $ : > sensitive-temporary-data.txt; bash; rm -f sensitive-temporary-data.txt; exit

Эти команды создают вложенную оболочку, в которой я могу выполнять всю свою работу; когда я закончу, я нажимаю CTRL-D, и родительская оболочка также очищается и закрывается. Вы можете легко добавить bash;в свой сценарий туннеля ssh непосредственно перед killчастью, чтобы при выходе из вложенной оболочки туннель был закрыт:

#!/bin/bash
ssh -nNT ... &
PID=$!
bash
kill $PID
слишком много php
источник
Очень интересно. Это может лучше справиться с проблемой "ловушки". Придется попробовать.
jm.
2

Вы можете запустить его sshс помощью &конца, чтобы поместить его в фоновый режим и получить его идентификатор при выполнении. Затем вам просто нужно выполнить killэтот идентификатор, когда вы закончите.

Валентин Роше
источник
Помните, если используете амперсанд ("&"). Это уродливый подход, поскольку вам придется самостоятельно определять фактическое соединение. Это может привести к тому, что будет выполняться следующий код, который не ждет полного установления фактического соединения. Кроме того, соединение не будет прервано автоматически, если скрипт сломается.
Джонатан
2

Другой потенциальный вариант - если вы можете установить пакет expect, вы сможете написать все это в сценарии. Вот несколько хороших примеров: http://en.wikipedia.org/wiki/Expect

Фил Пеланн
источник