How to Set Up a Reverse SSH Tunnel for Remote API Testing: A Complete Guide with Nginx and iptables Solutions


2 views

When developing API integrations (PayPal, Tropo, etc.), you often need to expose local development servers to the internet. Reverse SSH tunneling provides a secure way to forward traffic from your remote server to your local machine. Here's how it works at the protocol level:

Local Machine (client) --SSH Tunnel--> Remote Server (public)
5000/tcp                 7777/tcp                 8888/tcp

The foundational command establishes the tunnel:

ssh -nNT -R :7777:localhost:5000 user@server.com

Key flags explanation:
-n - Redirects stdin from /dev/null
-N - No remote command execution
-T - Disable pseudo-terminal allocation
-R - Remote port forwarding

On your remote server, check the listening ports:

netstat -ant | grep 7777
# Output should show:
# tcp        0      0 127.0.0.1:7777          0.0.0.0:*               LISTEN
# tcp6       0      0 ::1:7777                :::*                    LISTEN

To expose the tunneled port publicly, the working Nginx configuration should be:

upstream tunnel {
    server 127.0.0.1:7777;  # Critical change from 0.0.0.0
}

server {
    listen 8888;
    server_name server.com;
    location / {
        proxy_pass http://tunnel;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

For a cleaner solution without Nginx, modify your SSH daemon configuration:

# /etc/ssh/sshd_config
GatewayPorts yes

Then reload SSH and establish the tunnel with:

ssh -nNT -R '*:8888:localhost:5000' user@server.com

Verify with:

sudo lsof -i tcp:8888
# Should show sshd listening on *:8888
  • Use SSH key authentication instead of passwords
  • Consider port knocking for additional security
  • Limit exposure with firewall rules (iptables/ufw)
  • Monitor connections with tools like fail2ban

For systems without Nginx, use these iptables rules:

iptables -t nat -A PREROUTING -p tcp --dport 8888 -j REDIRECT --to-port 7777
iptables -A INPUT -p tcp --dport 7777 -j ACCEPT

Save rules persistently:

iptables-save > /etc/iptables.rules

If connections fail:

  1. Verify sshd_config changes took effect (reload vs restart)
  2. Check local firewall settings on both machines
  3. Test basic connectivity with telnet/nc first
  4. Inspect SSH verbose output with -vvv flag

When developing API integrations with services like PayPal or Tropo, we often need to expose our local development environment to the internet. While services like tunnlr.com provide this functionality, setting up your own reverse SSH tunnel offers more control and flexibility.

The fundamental command establishes a tunnel from your remote server back to your local machine:

ssh -nNT -R :7777:localhost:5000 user@server

Verify it's working with:

netstat -ant | grep 7777
curl localhost:7777

The main obstacle preventing public access is SSH's default configuration. You need to modify /etc/ssh/sshd_config:

GatewayPorts yes

Then reload the SSH daemon (note: reload often works better than restart on Ubuntu):

sudo /etc/init.d/ssh reload

With GatewayPorts enabled, use this enhanced command:

ssh -nNT -R '*:8888:localhost:5000' user@server

Verify the public binding:

sudo lsof -i tcp:8888

While the direct SSH method is most elegant, here are other viable solutions:

Nginx Proxy Method

If you prefer using Nginx as a proxy:

upstream tunnel {
    server 127.0.0.1:7777;
}
server {
    listen 8888;
    server_name server.com;
    location / {
        proxy_pass http://tunnel;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

IPTables Port Forwarding

For a firewall-level solution:

sudo iptables -t nat -A PREROUTING -p tcp --dport 8888 -j REDIRECT --to-port 7777
sudo iptables-save | sudo tee /etc/iptables.rules

When exposing your development environment:

  • Use SSH key authentication exclusively
  • Consider fail2ban for brute force protection
  • Set up automatic tunnel reconnection with autossh
autossh -M 0 -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -nNT -R '*:8888:localhost:5000' user@server

Common issues and fixes:

# Check SSH server logs
tail -f /var/log/auth.log

# Verify port availability
nc -zv localhost 7777

# Check for process conflicts
sudo netstat -tulnp | grep 8888