Proper Ways to Gracefully Terminate an SSH Tunnel Connection


10 views

When working with SSH tunnels, proper termination is crucial for maintaining clean system states and preventing orphaned processes. The common Ctrl+C or Ctrl+D methods work but aren't always ideal for scripted environments.

Here are three professional approaches to closing your SSH tunnel:

# Method 1: Using the -f flag for background operation
ssh -f -N -L 5001:localhost:1019 eonil@test.local
# Then terminate with:
pkill -f "ssh -2 -N -L 5001:localhost:1019"

# Method 2: Using a control socket
ssh -f -N -M -S /tmp/ssh_tunnel -L 5001:localhost:1019 eonil@test.local
# Termination command:
ssh -S /tmp/ssh_tunnel -O exit eonil@test.local

# Method 3: PID capture and kill
ssh_pid=$!
# Later termination:
kill $ssh_pid

For complex environments with multiple tunnels, consider these patterns:

# Create named tunnel
ssh -f -N -L 5001:localhost:1019 -E /var/log/ssh_tunnel.log -o TCPKeepAlive=yes eonil@test.local

# Create management script (tunnel_manager.sh):
#!/bin/bash
case "$1" in
  start)
    ssh -f -N -L $2:localhost:$3 $4
    ;;
  stop)
    pkill -f "ssh -f -N -L $2:localhost:$3"
    ;;
esac
  • Orphaned processes: Always verify termination with ps aux | grep ssh
  • Port conflicts: Check port availability with netstat -tuln | grep 5001
  • Authentication timeouts: Add -o ServerAliveInterval=60 to connection string

For mission-critical tunnels:

# Use autossh for automatic reconnection
autossh -M 0 -f -N -L 5001:localhost:1019 eonil@test.local

# Set up monitoring with:
while true; do
  if ! nc -z localhost 5001; then
    pkill -f "autossh.*5001:localhost:1019"
    autossh -M 0 -f -N -L 5001:localhost:1019 eonil@test.local
  fi
  sleep 60
done

ssh -2 -N -L 5001:localhost:1019 eonil@test.local

When you terminate an SSH tunnel with Ctrl+C or Ctrl+D, you're essentially sending a SIGINT (interrupt signal) to the SSH process. While this works, it's not the cleanest method because:

  • It immediately terminates the TCP connections
  • May leave orphaned processes on the remote server
  • Could cause issues with connection tracking

Method 1: Using SSH Escape Sequence

The proper way involves SSH's built-in escape character:

~.

(Enter the tilde followed by period while connected)

This sends a clean disconnect signal through the SSH protocol itself.

Method 2: Killing by Process ID

For more control:


# Find the process
ps aux | grep "ssh -2 -N -L"

# Then kill it properly
kill -TERM [PID]

Method 3: Using ControlMaster (Advanced)

If you setup ControlMaster in your ~/.ssh/config:


Host test.local
  ControlMaster auto
  ControlPath ~/.ssh/sockets/%r@%h-%p
  ControlPersist 5m

You can then use:

ssh -O exit test.local

For scripts, consider this wrapper:


#!/bin/bash
ssh -f -N -L 5001:localhost:1019 eonil@test.local
trap "kill -TERM $!" EXIT
# Your commands here

This ensures the tunnel closes when your script exits.

If connections hang despite proper termination:


# Check for orphaned connections
netstat -tulnp | grep 5001

# Force cleanup if needed
fuser -k 5001/tcp