Skrypt Bash, aby skonfigurować tymczasowy tunel SSH

134

Na Cygwin chcę, aby skrypt Bash:

  1. Utwórz tunel SSH do zdalnego serwera.
  2. Wykonaj prace lokalnie, korzystając z tunelu.
  3. Następnie zamknij tunel.

Część z zamknięciem wprawia mnie w zakłopotanie.

Obecnie mam kiepskie rozwiązanie. W jednej powłoce uruchamiam następujące, aby utworzyć tunel:

# Create the tunnel - this works! It runs forever, until the shell is quit.
ssh -nNT -L 50000:localhost:3306 [email protected]

Następnie w innym oknie powłoki wykonuję swoją pracę:

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

Wreszcie, kiedy skończę, zamykam pierwsze okno powłoki, aby zabić tunel.

Chciałbym to wszystko zrobić w jednym skrypcie, takim jak:

# Create tunnel
# Do work
# Kill tunnel

Jak mogę śledzić proces tunelowania, żeby wiedzieć, który zabić?

jm.
źródło
Napisałem skrypt, który pomógłby w tunelowaniu ssh, możesz go sprawdzić pod adresem: github.com/gdbtek/ssh-tunneling.git
Nam Nguyen

Odpowiedzi:

334

Możesz to zrobić czysto za pomocą gniazda sterującego ssh. Aby porozmawiać z już działającym procesem SSH i uzyskać jego pid, zabij go itp. Użyj 'gniazda sterującego' (-M dla mastera i -S dla gniazda) w następujący sposób:

$ ssh -M -S my-ctrl-socket -fnNT -L 50000:localhost:3306 [email protected]
$ ssh -S my-ctrl-socket -O check [email protected]
Master running (pid=3517) 
$ ssh -S my-ctrl-socket -O exit [email protected]
Exit request sent. 

Zauważ, że my-ctrl-socket będzie rzeczywistym plikiem, który jest tworzony.

Mam tę informację od bardzo RTFM odpowiedzi na liście mailingowej OpenSSH .

Chris McCormick
źródło
7
To najlepsza odpowiedź, jaką dotychczas widziałem na ten temat. Dziękuję bardzo, powinien być zaakceptowany. Używam tego do łączenia się z moją Vagrant VM i uruchamiania skryptu aktualizacyjnego FlywayDB.
Christian,
2
Najwyraźniej gniazda sterujące nie wszędzie działają. Na przykład dostaję się Operation not permittedna środowisko ciągłej integracji drone.io:muxserver_listen: link mux listener ssh-ctrl-socket.wsASkszgSBlK7kqD => ssh-ctrl-socket: Operation not permitted
Mikko Ohtamaa
Więc co się dzieje z my-ctrl-socketplikiem po uruchomieniu? Kiedy znajduję się ls -law bieżącym folderze, nie widzę już pliku.
sachinruk
2
Jeśli używasz go w skrypcie, musisz poczekać, aż gniazdo sterujące stanie się dostępne przez kilka sekund. Moje rozwiązanie:while [ ! -e $ctrl_socket ]; do sleep 0.1; done
Adam Wallner,
kiedy to robię, dostaję open failed: administratively prohibited: open failedi nie sądzę, żeby otwierało tunel
Andy Ray
21

Możesz nakazać SSH, aby działało w tle za pomocą opcji -f, ale nie otrzymasz PID z $ !. Ponadto zamiast spać przez dowolną ilość czasu przed użyciem tunelu przez skrypt, możesz użyć -o ExitOnForwardFailure = yes z -f, a SSH będzie czekało na pomyślne ustanowienie wszystkich zdalnych portów przed umieszczeniem się w tle. Możesz grepować wyjście ps, aby uzyskać PID. Na przykład możesz użyć

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

i upewnij się, że otrzymujesz pożądany PID

Maine Guy
źródło
Możesz nie zdawać sobie z tego sprawy, ale to jest genialne. Szukałem sposobu na śledzenie identyfikatorów PID tunelu SSH i prawie skończyło się na użyciu skryptów usług systemd. Już nie: mogę grepować proces SSH, którego potrzebuję, używając nazwy tunelu. Ten pomysł w jakiś sposób całkowicie mnie ominął. Wielkie dzięki!
aexl
19
  • Możesz powiedzieć, sshaby przejść do tła &i nie tworzyć powłoki po drugiej stronie (po prostu otwórz tunel) z flagą wiersza poleceń (widzę, że już to zrobiłeś-N ).
  • Zapisz PID za pomocą PID=$!
  • Rób swoje
  • kill $PID

EDYCJA: Naprawiono $? do $! i dodał &

ZeissS
źródło
3
Jeśli mój skrypt gdzieś umrze, zanim dojdzie do KILL, muszę być ostrożny, aby sobie z tym poradzić.
jm.
2
@jm: trap 'kill $PID' 1 2 15obejmie wiele przypadków awarii skryptu.
Norman Ramsey,
3
Aby to działało niezawodnie, musiałem trochę "przespać" PO utworzeniu tunelu, ale przed jego użyciem.
jm.
@NormanRamsey Myślę, że masz na myśli trap "kill $PID", ponieważ bash interpoluje zmienne tylko w podwójnych cudzysłowach
JuanCaicedo
1
@JuanCaicedo To rozróżnienie byłoby ważne tylko wtedy, gdyby PIDzmienna została później przedefiniowana. Zmienna jest rozwijana, gdy trapwywoływana jest funkcja wbudowana (podejście OP) lub gdy sygnał został przechwycony (twoje podejście); oba podejścia dają ten sam rezultat.
Witiko
4

Wolę uruchamiać nową powłokę do oddzielnych zadań i często używam następującej kombinacji poleceń:

  $ sudo bash; exit

lub czasami:

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

Te polecenia tworzą zagnieżdżoną powłokę, w której mogę wykonywać całą swoją pracę; kiedy skończę, wciskam CTRL-D, a powłoka rodzica czyści się i również wychodzi. Możesz łatwo wrzucić bash;do skryptu tunelu ssh tuż przed killczęścią, aby po wylogowaniu się z zagnieżdżonej powłoki Twój tunel został zamknięty:

#!/bin/bash
ssh -nNT ... &
PID=$!
bash
kill $PID
za dużo php
źródło
Bardzo interesujące. Może to lepiej rozwiązać problem „pułapki”. Będę musiał tego spróbować.
jm.
2

Możesz uruchomić sshz &zakończeniem a, aby umieścić go w tle i pobrać jego identyfikator podczas wykonywania. Następnie musisz po prostu zrobić killz tego identyfikatora, kiedy skończysz.

Valentin Rocher
źródło
Pamiętaj, jeśli używasz znaku handlowego „i” („&”). Jest to brzydkie podejście, ponieważ będziesz musiał określić rzeczywiste połączenie ustanowione dla siebie. Może to spowodować wykonanie kolejnego kodu, który nie oczekuje na pełne nawiązanie połączenia. Ponadto połączenie nie zostanie automatycznie przerwane, jeśli skrypt się zepsuje.
Jonathan
2

Kolejna potencjalna opcja - jeśli możesz zainstalować pakiet oczekujący, powinieneś być w stanie napisać skrypt. Oto kilka dobrych przykładów: http://en.wikipedia.org/wiki/Expect

Phil Pelanne
źródło