简体   繁体   中英

ssh tunnel for local (not remote) command execution

I want to create a Linux shell (bash-) script which creates an SSH tunnel, runs a local command which uses that tunnel and finally closes the tunnel and the surrounding SSH connection.

To make this less difficult to explain consider there is a local SSH configuration to a host called 'remoteserver' containing a local private key without a password, so

ssh remoteserver -L 4444:targetserver:5555

would directly open a connection to the remote server and create a tunnel from the local port 4444 to a target server. And consider the local command would be localclient --port 4444 , how would a script look like that opens the tunnel, executes the local command and closes the tunnel after the local client application is finished?

As it should be possible to keep other parallel ongoing SSH connections, I don't want something like sudo killall ssh .

You can try something like

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

The tunnel will close itself automatically after $TIMEOUT seconds. Note that using the & is only valid with passwordless connections. Otherwise you need to use the -f flag of SSH.

Alternatively,

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

will kill the tunnel just after localclient executes. Note that this will not work with the -f flag because the process double forks.

An ssh -only solution (does not work for me)

The second suggestion of @damienfrancois is adquate, but for some reason I did not like the idea of having to kill something.

Also, I could not believe ssh has no built-in mechanism for this. And indeed it has. This is how things should work:

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

But for some reason, this did not work as expected for me: I first tried with something simple like w for the LocalCommand but the -N made the command hang, so I used sleep 0 instead, which appeared to work.

But when I inserted my actual network command, the result ran extremely slowly. Instead of finishing in less than one second, it took 40 seconds before even the start message appeared. I terminated the command after 20 minutes.

A pipe solution (works for me)

After some experimentation, this is what I now use:

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

where keepalive.sh on the remote side is this:

while true; do
   sleep 1
   echo "keepalive"
done

The sleep 0.5 is needed to make (sort of) sure the tunnel is set up before localclient starts (and for a far-away or slow remoteserver , you may need a longer wait). The tunnel is terminated as soon as localclient finishes because the shell terminates the pipe as soon as the second command closes its standard input.

Is this better than @damienfrancois's solution? Depends.

The need for keepalive.sh is a clear drawback, but the way in which the tunnel is cleared away no matter how and why localclient terminates is cleaner.

I need this in a Makefile context and like the fact it fits on a single line.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM