Tuesday, December 27, 2011

Using Iptables to JBoss run as non-root.

Configuring an iptables firewall on Ubuntu Hardy Heron server

Introduction

In this article we'll set up a simple firewall on an Ubuntu 8.04 server. The firewall has two purposes:

  1. Block all ports except the few which are used to provide services
  2. Map incoming port 80 to port 8080, so that our Java web servers can run as non-root

And all this must be done on a remote server, so we have to do it in a way that doesn't lock us out.

The services we need

We need to open ports for the following:

  1. http, port 80
  2. smtp, port 25
  3. sshd, port 22
  4. smtp for relaying, we have this on port 10025
  5. imap, TLS encrypted in our case, port 143
  6. ftp: we might decide to enable FTP service
  7. OpenVPN will also be enabled at some future point

Trying ufw: the uncomplicated firewall

Ubuntu 8.04 ships with a tool called ufw, the uncomplicated firewall, which makes it easier to manipulate firewall rules than issuing iptables commands directly. It seems like a convenient thing to use, but I could not get it to be compatible with NAT port rewriting. Therefore I gave up and did it the old fashioned way, using the iptables commands. This is not a problem; the ip-tables commands are easy enough to use directly.

Using iptables directly

The machine starts out with empty iptables rule chains, meaning that iptables is not active. Start by allowing all traffic on loopback. If you don't do this, various processes, such as Postfix, will break:

iptables -I INPUT 1 -i lo -j ACCEPT

Then create a rule which allows established tcp connections to stay up:

iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

Then add the individual ports we want:

iptables -A INPUT -p tcp --dport 80 -j ACCEPT iptables -A INPUT -p tcp --dport 22 -j ACCEPT

Add a drop-all at the end of the chain for anything which doesn't match one of the accepted ports:

iptables -A INPUT -j DROP

Now that is a basic server firewall configuration. But there's one problem here. Our web servers are all Java-based (JBoss AS or Tomcat). Pure Java applications have no way of binding to port 80 without running as root, because they cannot change user. Apache HTTPD is able to drop root after binding, becaue it can access operating system calls, but Java processes cannot.

Therefore we need some way to run a Java server process and let it have access to port 80. The solution is to use port redirection.

Note that this "privileged ports" concept has probably caused more security breaches in the history of Unix / Linux than any other single thing, and is the oldest design flaw / bug in the Unix / Linux systems. It should really be removed. Data from the external ports are the most dangerous and so should be handled with the least access permissions. What does Unix / Linux do? The exact opposite. This is unfortunate. But so many system administrators believe the old nonsense that if you let non-root process bind to privileged ports something bad will happen, this bug is here to stay. None of these administrators who believe this are able to explain exactly what bad thing would happen, but they continue to believe it. It is actually based in a strange psychological belief that root is for stuff that's "important", because root is the most powerful user, and the most powerful user should be doing the most important stuff, right? Unfortunately this is completely wrong; tasks should all happen with the least possible permissions needed to accomplish the task. The design of Postfix is a good example of privilege minimization. Postfix is a set of small, isolated, single-purpose tasks, with minimal access permissions, not running as root, mostly in chroot environments.

Fortunately iptables gets us out of this problem.

Redirecting port 8080 to port 80

Use one command to create a redirection rule:

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

But, this rule doesn't work. What happens is that the prerouting rules are all applied before the input chain happens. What this means is that port 80 has already been remapped to port 8080 before the packet is checked on the input chain. This is a problem because there's no rule to allow port 8080. We need to add one, before the drop command.

We can use the insert style of the iptables command to do this. First use iptables -L to list the chain, and insert the rule for 8080 in some place before the final DROP:

iptables -I INPUT 3 -p tcp --dport 8080 -j ACCEPT

This inserts it at position 3. Note that we are not really using the allow rule for port 80, because port 80 is getting mapped to 8080 before the allow rule could trigger. We could go ahead and delete that allow rule. However, it doesn't hurt to leave it there.

Test it. You should be able to get to the Tomcat server, running as non-root on port 8080, from outside, going to port 80. Note that trying to go to Tomcat on port 80 on loopback won't work at this point because there's no rule for it. Add:

iptables -t nat -A OUTPUT -o lo -p tcp --dport 80 -j REDIRECT --to-port 8080

Saving the rules

It would be nice if there were some standard rules file to save these into. There is not, not at this time within Ubuntu Server 8.04.

So we save it to our own file:

iptables-save > /etc/iptables.rules

and modify the /etc/network/interfaces file:

    pre-up iptables-restore < /etc/iptables.rules

Put this after the iface eth0 line / stanza. The complete stanza looks like:

iface eth0 inet static          address 333.333.333.333          netmask 255.255.255.0          gateway 333.333.333.1          pre-up iptables-restore < /etc/iptables.rules

Test

Reboot the machine and make sure everything comes up ok. You don't want the machine to be in a non-bootable state once it's in production.

Notes on JBoss Application Server bind addresses

Tomcat, by itself, will bind to the host's external interface address. JBoss AS does not. By default, JBoss AS will only listen for connections on localhost.

What we want is for it to listen on the external interface, on port 8080.

No comments:

Post a Comment