Reverse Tunnel Proxy for Web Servers Behind NAT

Your mission, should you choose to accept it: you have a public webserver and an application server on a private net. There’s no chance of getting a firewall exception for inbound access to your application server (you might not want that either) and it’s behind a NAT device. You want to display the application server’s data on your public webserver. Live.

Impossible? Nah, not with an open source arsenal at your disposal.

Gear: Fedora/RHEL with OpenSSH, Apache, init

Operational Plan: We’re going to create a reverse SSH tunnel from the application server out to your public server, ride that back from the public server via a proxy, and use init and openssh to keep the connection alive.

Some details (not a compete HOWTO):

edit your /etc/inittab on the application server to have a line like:

ssh1:345:respawn:/usr/bin/ssh -nNTx -R 1234:localhost:1234 minimaluser@publicwebserver.example.com >/dev/null 2>&1

This will bring up the connection and restart it if it should go away. telinit q to get it started. Modify ssh options to suit your network requirements.

That ssh process will run as root under init – so make sure you have your root user’s ssh credentials (/root/.ssh/id_rsa.pub by default) added to the ~/minimaluser/.ssh/authorized_keys2 file on the public webserver. I assume here you know how to setup OpenSSH public key authentication – there are tutorials on how to do that.

So, now you have port 1234 on the application server replicated as port 1234 on the public webserver. Firewall it as appropriate on both ends. On the public webserver telnet to that port and speak HTTP at it to make sure it’s working.

Now, since you’re behind NAT your session is probably going to stop working at some point when your NAT session entry gets expired. You could HUP the ssh process, but a better way is to make ssh do keepalives. Add to your /etc/ssh/ssh_config file the lines:

ServerAliveInterval 60

ServerAliveCountMax 60

and kill your ssh process to get it to respawn. Now ssh will send a packet once a minute to the server to keep the NAT table intact and won’t give up for an hour (after that init takes over and tries to respawn).

OK, so your connection is stable now. On to the public webserver configuration.

Configure your apache virtualhost thusly:

RewriteEngine On

RewriteRule /(.*) http://localhost:1234/$1 [P,L]

ProxyPassReverse / http://localhost:1234/

restart apache, and test. You should see your private webserver appear on the public virtualhost you’ve configured. Hurray!

Now, then: this isn’t ideal for any high levels of traffic if you have a long distance to run over an internet. So, go into your /etc/httpd/httpd.conf file and turn on the disk-based cache that Apache has (uncomment 4 lines or so) for proxied content. Add these lines to your virtualhost:

ProxyEngine On

#Fix for Apache bug 37770

SetEnv force-proxy-request-1.0 1

SetEnv proxy-nokeepalive 1

# don't proxy the world

<LocationMatch "^[^/]">

Deny from all

</LocationMatch>

Restart apache again, and static content won’t be fetched over your link more than once an hour.