Configuring Egress (Outbound) Rules with iptables (ubuntu style)

Wednesday, August 26, 2009 3:49:08 PM (Pacific Daylight Time, UTC-07:00)
by Adam Brand
There is a lot of information on iptables (the Linux firewall) out there, but most of them focus on ingress rules.

Unfortunately, in many cases, simply creating ingress rules are insufficient. On systems you really want to secure, you should also set up egress (outbound) rules to restrict outbound traffic.


NOTE: The commands in this post assume a Debian-based distribution (e.g., Ubuntu server). The iptables commands should be the same across distributions, but check your own distro’s reference guide for how to save and load iptables as those steps vary.

ALSO NOTE: Because of the column width in this blog, some of the rules wrap to a second line. Keep in mind that all the lines start with "sudo iptables".

In the below example, we’ll set up fairly common rules for a server that only really needs to get package updates. Remember that these are case sensitive commands, and also that the order you type them is the order that they are evaluated (i.e., if you are connecting over SSH, don’t do the -A OUTPUT -j REJECT first).
sudo iptables -A OUTPUT -o lo -p all -j ACCEPT
sudo iptables -A OUTPUT -m state --state RELATED, ESTABLISHED -j ACCEPT
sudo iptables -A OUTPUT -p tcp --dport 80 -d security.ubuntu.com -j ACCEPT
sudo iptables -A OUTPUT -p tcp --dport 80 -d us.archive.ubuntu.com -j ACCEPT
sudo iptables -A OUTPUT -j REJECT

IMPORTANT: Do not add the security.ubuntu.com or us.archive.ubuntu.com rules unless you make a /etc/hosts entry for those...otherwise you will also need to let DNS out and it will be SLOW.

Let’s take a look at each of these rules:


sudo iptables -A OUTPUT -o lo -p all -j ACCEPT

This is adding an entry saying that we should accept any traffic that wants to go outbound on the local (127.0.0.1) interface. Some applications use this interface to exchange information, and we don’t want to break those.


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

This is the “secret sauce” rule, and one that is often forgotten, leading to disconnected sessions and confusion. What this says is allow outbound traffic that associated with a session that is already established or related to an established session. For example, if you have SSH on your server, you can open up port 22 inbound, but how will the server send data back to clients (that may even suggest an alternate higher port for subsequent communications)? That’s what this rule allows.


sudo iptables -A OUTPUT -p tcp --dport 80 -d security.ubuntu.com -j ACCEPT
sudo iptables -A OUTPUT -p tcp --dport 80 -d us.archive.ubuntu.com -j ACCEPT

These rules simply tell iptables to allow traffic going to port 80 for ubuntu’s update servers. These may be different depending on your region, and there may be more, so you will have to do an apt-get update after you do this to make sure that it works for you. IMPORTANT: As I mentioned before, DO NOT enable these rules without first putting an entry mapping the appropriate IP’s in /etc/hosts. Also if Ubuntu changes these IP’s, you will need to update your hosts entry. Otherwise you would have to enable DNS to resolve those names and there could potentially be an external lookup for every outbound packet (ouch!).


sudo iptables -A OUTPUT -j REJECT

This is the rule that kills all the other traffic. This should definitely be the last rule in your chain, or the other rules won’t work.

That’s it!


Don’t forget to do:
sudo sh -c "iptables-save > /etc/iptables.rules"

if you want to keep the rules, and then add:
pre-up iptables-restore < /etc/iptables.rules
under the interface (eth0 or whatever) in /etc/network/interfaces.


Here are some other egress rules you may be interested in:


To allow “ping” to work (from the server):
sudo iptables -A OUTPUT -p icmp --icmp echo-request -j ACCEPT
sudo iptables -A OUTPUT -p icmp --icmp echo-reply -j ACCEPT


To allow DNS to work (from the server):
sudo iptables -A OUTPUT -p tcp --dport 53 -j ACCEPT
sudo iptables -A OUTPUT -p udp --dport 53 -j ACCEPT


So now you have no excuse to leave your outbound traffic unguarded…get to it! :-)