iptables port forwarding using puppet

2013-09-02 · Computing

In this guide, I show how to write Puppet resource configuration for adding an iptables rule for forwarding traffic between two ports (such as from Port 80 to Port 8080). Although this technique could serve a number of purposes, I use it principally for running an application (such as a Ruby or Clojure web application, perhaps using Ruby on Rails or Compojure) on a non-privileged port. I assume a Debian-variant OS (such as Debian itself or Ubuntu Server).

In order to not be completely weird (although I admit I did consider it), I want to expose my web applications on the usual Port 80, so it can be easily reached via a browser. If you’re running Nginx or even Apache as a service, this is all well and good; the service is started with root privileges, and Port 80 requests can be handled. But what if you’re running a standalone server like Thin, packaged with your application? In that case, you might have a Procfile looking something like this for Ruby (this example’s Ruby on Rails):

web: bundle exec rails server -p $PORT

or like this for Clojure (if you’re packaging your application into an uberjar):

web: java -jar target/*-*-standalone.jar $PORT

Setting the environment variable PORT=80 will likely yield tears, however, because of course you’re not running your application with root privileges (if you are, please don’t tell me because I shall probably worry on your behalf). Ports under 1024 are privileged, meaning only services which at least start with root privileges can bind to them (often, service webservers subsequently hand off to something with less privileges). What you can do, however, is run your application on another, non-privileged port such as Port 8080, and then forward requests from Port 80 to that port. You can then set the environment variable PORT=8080 instead.

As a temporary iptables command, we could add a REDIRECT rule to the NAT table PREROUTING chain for TCP from Port 80 to Port 8080 using something like this:

iptables -t nat -A PREROUTING -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 8080

This is only to demonstrate what we’re doing, however; such a rule issued at the command-line would not be permanent. I’m using the Puppet firewall module, however, which supports a firewall resource. Henceforth, I assume you’ve set up your firewall according to the module instructions; this is very important. (Please remember to allow established connections, SSH, and to leave a window with an active SSH connection open somewhere whilst you activate the firewall, checking you can establish a new connection before closing it.) Once you’re sure your firewall’s working properly, use Puppet to accomplish the same as the iptables command above:

firewall {'102 forward port 80 to 8080':
  table       => 'nat',
  chain       => 'PREROUTING',
  proto       => 'tcp',
  dport       => '80',
  jump        => 'REDIRECT',
  toports     => '8080'
}

After a Puppet run, list the NAT table rules and verify that the rule has been inserted correctly:

iptables -L -t nat

With any luck, you’ve successfully forwarded Port 80 to Port 8080 (or similar) without locking yourself out of SSH. ;)