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
.
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.
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.
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.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With