Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 768
muffel Avatar asked Oct 19 '13 18:10

muffel


People also ask

What are the 3 types of SSH tunneling?

Transporting arbitrary data streams over SSH sessions is also known as SSH tunneling. OpenSSH, a popular open-source SSH server, supports three types of tunneling features- local port forwarding, remote port forwarding, and dynamic port forwarding.

Is SSH tunneling the same as port forwarding?

SSH tunneling, or SSH port forwarding, is a method of transporting arbitrary data over an encrypted SSH connection. SSH tunnels allow connections made to a local port (that is, to a port on your own desktop) to be forwarded to a remote machine via a secure channel.

What is difference between port forwarding and tunneling?

Tunneling, also known as "port forwarding," is the transmission of data intended for use only within a private, usually corporate network through a public network in such a way that the routing nodes in the public network are unaware that the transmission is part of a private network.

How do I tunnel traffic through SSH?

To create an SSH tunnel, you need: Target server offering network services (http, vnc, etc.) to the client. SSH server listening for connections from the client. SSH client configured to forward traffic from a local listening port, through the SSH server, to the target server.


2 Answers

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.

like image 55
damienfrancois Avatar answered Sep 18 '22 13:09

damienfrancois


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.

like image 37
Lutz Prechelt Avatar answered Sep 16 '22 13:09

Lutz Prechelt