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.