Persistent SSH Local Port Forwarding: Preventing Connection Drops for Web Service Access


5 views

When accessing web services through SSH port forwarding, connections tend to drop unpredictably due to HTTP's stateless nature compared to persistent protocols like SMB. The browser-initiated TCP connections terminate after each request, causing the SSH tunnel to timeout.

First, modify your /etc/ssh/sshd_config on the remote server:

ClientAliveInterval 60
ClientAliveCountMax 3
TCPKeepAlive yes

Use these SSH options for more resilient forwarding:

ssh -o ServerAliveInterval=30 \\
    -o ServerAliveCountMax=3 \\
    -o TCPKeepAlive=yes \\
    -N -L 8080:localhost:80 user@host

Here's an improved version of your watchdog script using SSH ControlMaster:

#!/bin/bash

while true; do
  if ! ssh -O check myapp.cloudapp.net &>/dev/null; then
    ssh -fN -M -S ~/.ssh/control-%r@%h:%p \\
        -o ExitOnForwardFailure=yes \\
        -o ServerAliveInterval=30 \\
        -o TCPKeepAlive=yes \\
        -L 8080:localhost:80 \\
        myapp.cloudapp.net
  fi
  sleep 10
done

The autossh package provides automatic reconnection:

autossh -M 0 -f -N -L 8080:localhost:80 user@host \\
  -o "ServerAliveInterval 30" \\
  -o "ServerAliveCountMax 3"

Create a systemd unit for reliable operation:

[Unit]
Description=Persistent SSH tunnel
After=network.target

[Service]
Restart=always
ExecStart=/usr/bin/autossh -M 0 -N -L 8080:localhost:80 user@host

[Install]
WantedBy=multi-user.target

For HTTP services, consider these approaches:

  1. Configure your web server to use persistent connections
  2. Add a background websocket ping from client-side JavaScript
  3. Use curl --keepalive-time 30 for API access

When working with cloud-based services like Azure VMs, SSH local port forwarding becomes essential for accessing internal services without exposing public ports. The standard approach:

ssh -L 8080:localhost:80 user@remote_host

works perfectly for persistent protocols like SMB or database connections. However, HTTP/HTTPS traffic presents unique challenges due to its stateless nature and connection pooling behaviors.

Several factors contribute to connection instability:

  • HTTP/1.1 keep-alive timeouts
  • Browser connection pooling behaviors
  • SSH server TCP keepalive defaults
  • Intermediate network devices dropping idle connections

1. Server-Side Configuration

Modify your SSH server config (/etc/ssh/sshd_config):

ClientAliveInterval 60
ClientAliveCountMax 3
TCPKeepAlive yes

2. Client-Side Optimization

Create a more resilient client script:

#!/bin/bash
while :
do
  ssh -o ServerAliveInterval=60 \
      -o ExitOnForwardFailure=yes \
      -o TCPKeepAlive=yes \
      -N -L 8080:localhost:80 user@remote_host
  echo "Reconnecting at $(date)"
  sleep 5
done

3. Advanced: Autossh for Automatic Reconnection

Install and configure autossh for bulletproof forwarding:

sudo apt install autossh
autossh -M 0 -f -N -o "ServerAliveInterval 60" \
        -o "ServerAliveCountMax 3" \
        -L 8080:localhost:80 user@remote_host

Add these verification techniques:

# Check established connections
ss -tnlp | grep ssh

# Continuous connectivity test
watch -n 1 curl -I http://localhost:8080

For critical production systems, consider:

  • SSH multiplexing with ControlMaster
  • VPN solutions like WireGuard
  • Cloud-native solutions (Azure Private Link, AWS PrivateLink)

Remember that the optimal solution depends on your specific network environment and security requirements.