Script Bash para configurar um túnel SSH temporário


132

No Cygwin, quero um script Bash para:

  1. Crie um túnel SSH para um servidor remoto.
  2. Faça algum trabalho local que use o túnel.
  3. Então desligue o túnel.

A parte do desligamento me deixou perplexo.

Atualmente, tenho uma solução ruim. Em um shell, eu executo o seguinte para criar um túnel:

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

Então, em outra janela do shell, eu faço o meu trabalho:

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

Finalmente, quando termino, fecho a primeira janela do shell para matar o túnel.

Eu gostaria de fazer isso tudo em um script como:

# Create tunnel
# Do work
# Kill tunnel

Como acompanho o processo do túnel, para saber qual deles matar?


Eu escrevi um script que iria ajudar a fazer tunelamento ssh, você pode verificá-la em: github.com/gdbtek/ssh-tunneling.git
Nam Nguyen

Respostas:


324

Você pode fazer isso corretamente com um ssh 'control socket'. Para conversar com um processo SSH já em execução e obtê-lo, execute-o etc. Use o 'socket de controle' (-M para master e -S para socket) da seguinte maneira:

$ 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. 

Observe que my-ctrl-socket será um arquivo real criado.

Eu recebi essas informações de uma resposta muito RTFM na lista de discussão do OpenSSH .


6
Esta é a melhor resposta que eu já vi sobre o assunto até agora. Muito obrigado, deve ser o aceito. Eu uso isso para conectar-me à minha VM Vagrant e executar um script de atualização do FlywayDB.
Christian

2
Aparentemente, os soquetes de controle não funcionam em todos os lugares. Por exemplo, eu Operation not permittedentro no ambiente de integração contínua drone.io:muxserver_listen: link mux listener ssh-ctrl-socket.wsASkszgSBlK7kqD => ssh-ctrl-socket: Operation not permitted
Mikko Ohtamaa 2/15/15

Então, o que acontece com o my-ctrl-socketarquivo após a execução? Quando o faço ls -lana pasta atual, não consigo mais ver o arquivo.
Sachinruk 23/08/19

2
Se você o usar em um script, será necessário aguardar o soquete de controle por alguns segundos para ficar disponível. Minha solução:while [ ! -e $ctrl_socket ]; do sleep 0.1; done
Adam Wallner

quando eu fizer isso eu estou começando open failed: administratively prohibited: open failede eu não acho que a abertura do túnel
Andy Ray

21

Você pode dizer ao SSH para fazer o segundo plano com a opção -f, mas não receberá o PID com $ !. Além disso, em vez de fazer com que seu script durma uma quantidade arbitrária de tempo antes de usar o encapsulamento, você pode usar -o ExitOnForwardFailure = yes com -f e o SSH aguardará que todos os encaminhamentos de portas remotas sejam estabelecidos com sucesso antes de se colocar em segundo plano. Você pode receber a saída do ps para obter o PID. Por exemplo, você pode usar

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

e tenha certeza de que está obtendo o PID desejado


Você pode não perceber, mas isso é meio que genial. Eu estava procurando uma maneira de rastrear os PIDs do túnel SSH e quase acabei usando os scripts de serviço systemd. Não é mais: posso aceitar o processo SSH necessário usando o nome do túnel. De alguma forma, essa ideia me ignorou completamente. Muito obrigado!
aexl

19
  • Você pode dizer sshpara entrar em segundo plano &e não criar um shell do outro lado (basta abrir o túnel) com uma bandeira da linha de comando (vejo que você já fez isso com -N).
  • Salve o PID com PID=$!
  • Faça suas coisas
  • kill $PID

EDIT: Fixo $? para $! e adicionou o &


3
Se meu script morrer em algum lugar antes de chegar ao KILL, tenho que ter cuidado para lidar com isso.
jm.

2
@jm: trap 'kill $PID' 1 2 15cobrirá muitos casos de falha de script.
Norman Ramsey

3
Para que isso funcione de forma confiável, tive que "dormir" um pouco depois de criar o túnel, mas antes de usá-lo.
jm.

@NormanRamsey Eu acho que você quer dizer trap "kill $PID", porque o bash irá únicas variáveis Interpolar dentro de strings entre aspas duplas
JuanCaicedo

1
@JuanCaicedo A distinção seria importante apenas se a PIDvariável fosse redefinida mais tarde. A variável é expandida quando o trapbuilt-in é chamado (a abordagem do OP) ou quando um sinal é capturado (sua abordagem); ambas as abordagens produzem o mesmo resultado aqui.
Witiko

4

Prefiro iniciar um novo shell para tarefas separadas e geralmente uso a seguinte combinação de comandos:

  $ sudo bash; exit

ou às vezes:

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

Esses comandos criam um shell aninhado onde eu posso fazer todo o meu trabalho; Quando termino, clico em CTRL-D e o shell pai limpa e sai também. Você pode facilmente bash;inserir seu script de encapsulamento ssh imediatamente antes da killpeça, para que, ao sair do shell aninhado, seu encapsulamento seja fechado:

#!/bin/bash
ssh -nNT ... &
PID=$!
bash
kill $PID

Muito interessante. Isso pode lidar melhor com o problema da "armadilha". Terá que tentar.
jm.

2

Você pode iniciar o sshcom um &a final, colocá-lo em segundo plano e pegar seu ID ao fazê-lo. Então você só precisa fazer uma killidentificação desse tipo quando terminar.


Esteja ciente se estiver usando o e comercial ("&"). É uma abordagem feia, pois você precisará determinar a conexão real estabelecida por si mesmo. Isso pode causar a execução de mais código, o que não espera a conexão real ser totalmente estabelecida. Além disso, a conexão não será interrompida automaticamente se o script for interrompido.
Jonathan

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.