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:
- Verify sshd_config changes took effect (reload vs restart)
- Check local firewall settings on both machines
- Test basic connectivity with telnet/nc first
- 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