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


25 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.