Bash script to set up a temporary SSH tunnel


On Cygwin, I want a Bash script to:

  1. Create an SSH tunnel to a remote server.
  2. Do some work locally that uses the tunnel.
  3. Then shut down the tunnel.

The shutdown part has me perplexed.

Currently, I have a lame solution. In one shell I run the following to create a tunnel:

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

Then, in another shell window, I do my work:

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

Finally, when I am done, I close the first shell window to kill the tunnel.

I'd like to do this all in one script like:

# Create tunnel
# Do work
# Kill tunnel

How do I keep track of the tunnel process, so I know which one to kill?

7/21/2019 10:27:41 PM

Accepted Answer

You can do this cleanly with an ssh 'control socket'. To talk to an already-running SSH process and get it's pid, kill it etc. Use the 'control socket' (-M for master and -S for socket) as follows:

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

Note that my-ctrl-socket will be an actual file that is created.

I got this info from a very RTFM reply on the OpenSSH mailing list.

4/29/2014 3:05:18 PM

You can tell SSH to background itself with the -f option but you won't get the PID with $!. Also instead of having your script sleep an arbitrary amount of time before you use the tunnel, you can use -o ExitOnForwardFailure=yes with -f and SSH will wait for all remote port forwards to be successfully established before placing itself in the background. You can grep the output of ps to get the PID. For example you can use

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

and be pretty sure you're getting the desired PID

Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow