Managing SSH tunnels running in the background

A common problem for me when setting up SSH tunnels is that even though the ssh client process is running, data is not going through.

Steps to reproduce:

  • Create a tunnel (ssh -D  or ssh -L  or ssh -R ) on some port
  • Data is not going through, so we need to restart it
  • The connection cannot be established because bind: Address already in use
  • We have to kill the old client, ps aux | grep ssh , to find the right  process and then kill -9  it
  • Re-create the tunnel

Instead, we could leverage sockets when creating the connection, which allow communication with the SSH process (as opposed to searching for the pid ). This is enabled by the -S option:

ssh -NMfn -S /tmp/server1.local.socket -o ExitOnForwardFailure=yes -D 8080 server1.local 

# Additional flags: 
# -N Don't open a terminal
# -M Master mode, necessary for socket commands
# -f Go to background
# -S Control socket

Once the socket is set up, we can send commands to it. For example, exit  to close the connection (regardless of the state of the connection):

ssh -S /tmp/server1.local.socket -O exit server1.local

Finally, here is a script that will create SOCKS tunnels (proxies) to a list of hostnames. When the script is run again, it will attempt to close any existing tunnels and establish brand new connections:

#!/bin/bash


# These are the servers that we will set up SOCKS tunnels to
HOSTS="server1.local server2.remote"
BASE_PORT=11000

# Some fancy output
bold=$(tput bold)
normal=$(tput sgr0)

# Send exit to all existing sockets
for HOST in $HOSTS; do
  if [ -e /tmp/$HOST.socket ]; then
    ssh -S /tmp/$HOST.socket -O exit $HOST
  fi
done

# Set up variables and nice output
PORT=$BASE_PORT
STATUS_LINE="%-35s%-15s${bold}%s${normal}"
printf "${bold}$STATUS_LINE\n${normal}" "HOST" "SOCKS PORT" "STATUS"
for HOST in $HOSTS; do
  printf "$STATUS_LINE" "$HOST" "$PORT" "CONNECTING"
  # Attempt to create tunnel
  ssh -NMfn -S /tmp/$HOST.socket -o ConnectTimeout=5 -o ExitOnForwardFailure=yes -D $PORT $HOST &> /dev/null
  # Report outcome
  if (( $? == 0 )); then
    printf "\r$STATUS_LINE\n" "$HOST" "$PORT" "DONE      "
  else
    printf "\r$STATUS_LINE\n" "$HOST" "$PORT" "FAILED    "
  fi

  # Increment port number
  PORT=$(expr $PORT + 1)                                                                                                                                                                                                                     
done