iptables: Running service as non-root, iptables to forward from privileged port

There are multiple approaches to allowing a process to run as a non-root user but still provide access to privileged ports (<1024).

There are applications like Apache that handle this by starting the master process as root, and then worker processes as a less privileged user.  Another way is setting the privilege on a binary using ‘setcap cap_net_bind_service‘ to allow binding to privileged ports.  Yet another is to use the authbind utility.

In this article I will show how to run Tomcat as a non-privileged user on port 8080, and then use an iptables local redirect rule to forward traffic from the privileged port on 80 to Tomcat.

Install Tomcat

First install Tomcat7, which is available in the main repository on both Ubuntu trusty and xenial.

$ sudo apt-get install tomcat7

This creates a user named “tomcat7” and the start-stop-daemon (Ubuntu 14.04) or systemd (Ubuntu 16.04) creates the process running as this user.  This can be verified with “ps -ef | grep tomcat”.

The Tomcat process is running insecure HTTP by default on port 8080 (/etc/tomcat7/server.xml), which is a non-privileged port.    This can be verified with “sudo lsof -i :8080” or “sudo netstat -tulnp | grep 8080”.

Now check an HTTP request to Tomcat on port 8080 using curl, and you should receive back a Tomcat welcome page.

$ curl http://<IP>:8080

Create iptables local forwarding rule

There is only a single iptables rule you need to create in order to do local port forwarding.

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

Now all packets attempting to reach privileged port 80 will be forwarded to the Tomcat service running on port 8080.  From a different host console, now do a curl against port 80, and the Tomcat welcome page will be returned.

$ curl http://<IP>:80

Note that curl directly from the host itself will not work (http://127.0.0.1:80) because local packets do not go through PREROUTING.  But you can add to the iptables OUTPUT chain in order to enable this port forwarding locally.

$ sudo iptables -t nat -A OUTPUT -p tcp --dport 80 --dst 127.0.0.1 --src 0/0 -j REDIRECT --to-ports 8080

Now a local curl to http://127.0.0.1 should return the Tomcat welcome page also.

iptables maintenance

The rules can be listed using:

$ sudo iptables -L -t nat -v --line-numbers

Rules can be deleted using this syntax:

$ sudo iptables -t nat -D <chain=PREROUTING|OUTPUT|etc> <number>

Persisting the rules

By default, the rules you just added will not be persisted through a reboot.  In order to save them:

# Ubuntu trusty 14.04
$ sudo apt-get install iptables-persistent
$ sudo iptables-save > /etc/iptables/rules.v4

# Ubuntu xenial 16.04
$ sudo apt-get install iptables-persistent
$ sudo netfilter-persistent save

 

REFERENCES

https://superuser.com/questions/710253/allow-non-root-process-to-bind-to-port-80-and-443/892391#892391 (CAP_NET_BIND_SERVICE)

https://unix.stackexchange.com/questions/10735/allowing-a-user-to-let-listen-to-a-port-below-1024 (iptables redirect, inetd alternatives)

http://randomthoughtsonjavaprogramming.blogspot.com/2015/10/running-glassfish-on-port-80.html

https://superuser.com/questions/710253/allow-non-root-process-to-bind-to-port-80-and-443 (cap_net_bind_service and authbind)

https://httpd.apache.org/docs/2.4/mod/worker.html (worker processes, non-root)

https://askubuntu.com/questions/694036/apache-as-non-root

https://wiki.apache.org/httpd/NonRootPortBinding (set_cap and alternative iptables method)

https://wiki.debian.org/Firewalls-local-port-redirection (single iptables redirect rule)