What's Actually Happening

SSH port forwarding (tunneling) lets you forward traffic from a local port through an SSH connection to a remote destination. When it fails, you typically see errors about connection refused or channel open failures.

The Error You'll See

bash
$ ssh -L 8080:localhost:80 user@server
channel 0: open failed: connect failed: Connection refused

Or when trying to use the tunnel:

bash
$ curl http://localhost:8080
curl: (7) Failed to connect to localhost port 8080: Connection refused

Why This Happens

  1. 1.Target service not running - The service you're trying to reach through the tunnel isn't listening
  2. 2.Wrong target host/port - You specified the wrong destination in the -L argument
  3. 3.GatewayPorts disabled - Server doesn't allow remote port forwarding
  4. 4.AllowTcpForwarding disabled - Server explicitly disabled port forwarding
  5. 5.Firewall blocking - Local or remote firewall blocking the connection

Step 1: Understand the Port Forwarding Syntax

bash
ssh -L [local_port]:[target_host]:[target_port] user@ssh_server

For example: - -L 8080:localhost:80 - Forward local 8080 to SSH server's port 80 - -L 8080:internal.server:80 - Forward local 8080 to internal.server's port 80 (via SSH server)

Step 2: Verify the Target Service

On the SSH server, check if the target is accessible:

```bash # If forwarding to localhost:80 on the SSH server ssh user@server "curl -I http://localhost:80"

# If forwarding to another internal server ssh user@server "curl -I http://internal.server:80" ```

If this fails, the target service isn't running or accessible.

Step 3: Check Server Port Forwarding Settings

bash
grep -E "AllowTcpForwarding|GatewayPorts" /etc/ssh/sshd_config

If AllowTcpForwarding no, the server has disabled port forwarding. Change it:

bash
sudo sed -i 's/#AllowTcpForwarding yes/AllowTcpForwarding yes/' /etc/ssh/sshd_config
sudo systemctl restart sshd

Step 4: Test the Tunnel

Start SSH with verbose output:

bash
ssh -v -L 8080:localhost:80 user@server

Look for: - debug1: Local connections to LOCALHOST:8080 forwarded to remote address localhost:80 - debug1: channel 0: new [port listener]

Step 5: Check for Port Conflicts

Make sure the local port isn't already in use:

bash
# On your local machine
netstat -tlnp | grep 8080
# Or
lsof -i :8080

If something is already listening on port 8080, either stop it or use a different local port.

Step 6: Dynamic Port Forwarding Alternative

For more flexibility, use dynamic port forwarding (SOCKS proxy):

bash
ssh -D 1080 user@server

Then configure your application to use SOCKS proxy at localhost:1080.

Verify the Fix

```bash # Start the tunnel ssh -L 8080:localhost:80 user@server -N -f

# Test the tunnel curl http://localhost:8080

# Should see the target service response ```