Iptables

Introduction

While I was looking at ways to run JBoss on port 80 without being root, David Jones pointed me towards using iptables. I played around with it for a bit and thought I’d write up a quick guide. Not a lot of this stuff has to do with forwarding ports, it’s mostly about blocking them.

Notes

The iptables how to was my guide for this post.

Important thing to know: iptables does not remember what its state is between machine recycles. So if I screw my machine up, a restart should fix it.

Phase 1: Initial experimentation

Running the list command before doing anything:

user@machinename:~$ sudo iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Allow ssh and standard port 80:

user@machinename:~$ sudo iptables -A INPUT -p tcp --dport ssh -j ACCEPT
user@machinename:~$ sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
user@machinename:~$ sudo iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:ssh
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:www
[...]

Ok, now let’s turn off all other access:

user@machinename:~$ sudo iptables -A INPUT -j DROP
user@machinename:~$ sudo iptables -L
sudo: unable to resolve host pod2132
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:ssh
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:www
DROP       all  --  anywhere             anywhere

The “unable to resolve” error is interesting. Also, when I did this I lost the ability to save to i-proving. That is unfortunate. So I dropped the latest rule I added to the input chain:

user@machinename:~$ sudo iptables -D INPUT 3
sudo: unable to resolve host pod2132
user@machinename:~$ sudo iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:ssh
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:www
[...]

I also started saving my document more often. Looking back, it appears that I missed this step:

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

So I was leaving out the most important rule: if I’ve got an established connection, allow the other party to send stuff. I need that to be the first rule, so instead of using -A (add) I use -I [num] (insert at position [num]).

user@machinename:~$ sudo iptables -I INPUT 1 -m state --state ESTABLISHED,RELATED -j ACCEPT
user@machinename:~$ sudo iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     all  --  anywhere             anywhere            state RELATED,ESTABLISHED
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:ssh
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:www
[...]

Now I think I’m ready to do this again:

user@machinename:~$ sudo iptables -A INPUT -j DROP
user@machinename:~$ sudo iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     all  --  anywhere             anywhere            state RELATED,ESTABLISHED
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:ssh
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:www
DROP       all  --  anywhere             anywhere

Saving to i-proving and other web stuff all works now.

At this point, I tried to start my application (running on jetty). My intent was to get the server up and demonstrate that I couldn’t access it via port 8080, but instead I got this:

[ERROR] 18:01:15 JDBCExceptionReporter - Connection refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.

Right, we’ve shut down all ports. So I added the PostgreSQL port.

user@machinename:~$ sudo iptables -I INPUT 4 -p tcp --dport 5432 -j ACCEPT
user@machinename:~$ sudo iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     all  --  anywhere             anywhere            state RELATED,ESTABLISHED
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:ssh
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:www
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:postgresql
DROP       all  --  anywhere             anywhere
[...]

After restarting jetty, the server started successfully but I couldn’t access it (on port 8080). That is as expected.

user@machinename:~$ sudo iptables -I INPUT 4 -p tcp --dport 8080 -j ACCEPT
user@machinename:~$ sudo iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     all  --  anywhere             anywhere            state RELATED,ESTABLISHED
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:ssh
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:www
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:webcache
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:postgresql
DROP       all  --  anywhere             anywhere
[...]

Ok, so now I’ve blocked all other ports on my machine. I’m about to leave for the night so I’m going to save my config changes and flush the changes I’ve made.

user@machinename:~$ sudo iptables-save > ~/iptables.rules
user@machinename:~$ sudo iptables -F
user@machinename:~$ sudo iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
[...]

When I come in tomorrow, I’ll be able to run

user@machinename:~$ sudo iptables-restore < ~/iptables.rules

to get back to where I am right now. For reference, the contents of the save file look like this:

# Generated by iptables-save v1.3.8 on Wed Jan 14 18:27:28 2009
*filter
:INPUT ACCEPT [2244701:1298384644]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [1256791:287713120]
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 8080 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 5432 -j ACCEPT
-A INPUT -j DROP
COMMIT
# Completed on Wed Jan 14 18:27:28 2009

Phase 2: The next day

After some reflection, I realized that I didn’t need to add the PostgreSQL port. What I need to do was addressed in the tutorial that I was following – namely, I needed to add the loopback address. That will allow any port requests that originate from my own machine to go through unimpeded.

So, to start the day off I flushed the iptables rules and reloaded my set from yesterday.

user@machinename:~$ sudo iptables -F
user@machinename:~$ sudo iptables-restore < ~/iptables.rules
user@machinename:~$ sudo iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     all  --  anywhere             anywhere            state RELATED,ESTABLISHED
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:ssh
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:www
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:webcache
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:postgresql
DROP       all  --  anywhere             anywhere
[...]

Then I dropped the postgresql rule and added the loopback rule.

user@machinename:~$ sudo iptables -D INPUT 5
user@machinename:~$ sudo iptables -I INPUT 1 -i lo -j ACCEPT
user@machinename:~$ sudo iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     all  --  anywhere             anywhere
ACCEPT     all  --  anywhere             anywhere            state RELATED,ESTABLISHED
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:ssh
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:www
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:webcache
DROP       all  --  anywhere             anywhere
[...]

Now jetty runs properly and I can access other stuff (like my VMware server installation, which runs on port 8222).

Phase 3: Running JBoss on port 80

On Ubuntu, port 80 is restricted and can only be accessed by applications being run by root. Since we don’t want to start JBoss as root, we’d like to redirect between port 8080 and port 80.

It looks like I have to use iptables to modify a different table using the -t parameter. Note that if -t is not specified, it defaults to the filter table (as above). By specifying the nat table, I’m modifying the network address translation.

user@machinename:~$ sudo iptables -t nat -A PREROUTING -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 8080
user@machinename:~$ sudo iptables -t nat -L
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
REDIRECT   tcp  --  anywhere             anywhere            tcp dpt:www redir ports 8080
[...]

Now when I start JBoss, it accepts connections on port 80 from outside machines. Note that from my local machine, I still have to use port 8080 (since the local machine is sending packets to 8080). Theoretically, this command:

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

should resolve that. Well, it does, but it also redirects all traffic to port 8080. So if I try to go to google, for example, I get redirected back to localhost. Since I can just happily use port 8080 on my machine, I’m going to leave this last redirect off.

Phase 4: Don’t lose everything on a reboot

Assuming that we’re happy with the way that we’ve set up our iptables config, we’d now like to confirm that it lasts through a reboot.

First, let’s save our rules.

user@machinename:~$ sudo iptables-save > /etc/iptables.rules
bash: /etc/iptables.rules: Permission denied

Of course /etc is a restricted directory.

user@machinename:~$ sudo sh -c "iptables-save > /etc/iptables.rules"

Then I edited /etc/network/interfaces and added this line to the eth1 interface:

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

After a restart the iptables configuration had been reset as expected.

References

It's only fair to share...
Share on FacebookGoogle+Tweet about this on TwitterShare on LinkedIn

Leave a Reply