How do you setup egress rules with iptables (on Ubuntu)?
I was trying to find information on egress rules and iptables out there, but either the information was incomplete or inaccurate. After digging around for a bit and disconnecting my ssh session a few times, I figured it out and thought I'd share it on ServerFault.
Egress rules are key to any security policy, and required for meeting many security standards (such as PCI DSS).
NOTE: The commands in here 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. If someone wants to wikify this and add RH or other dist differences, go for it.
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).
Sample Rule Set
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 (but see "Other Egress Rules" on opening up DNS queries). Keep in mind that iptables will evaluate the IP for those addresses AT THE TIME THE RULE IS APPLIED. So if ubuntu changes those addresses, you need to recreate those rules or restart the computer (the rules are applied on startup).
Let’s take a look at each of these rules:
Local Interface Rule
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.
Secret Sauce Rule (Established/Related Sessions)
sudo iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
This is the rule 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.
Allowing Ubuntu apt-get update
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 this rule 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 AND restart or recreate these rules, as iptables evaluates the addresses when the rule is applied.
The "Killer" Rule
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.
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
.
Other Egress Rules
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). Note that adding these means you don't have to use /etc/hosts
in the above example, but is arguably less secure.
sudo iptables -A OUTPUT -p tcp --dport 53 -j ACCEPT
sudo iptables -A OUTPUT -p udp --dport 53 -j ACCEPT
To allow NTP time syncing client:
sudo iptables -A OUTPUT -p udp -m owner --uid-owner systemd-timesync -j ACCEPT
(feel free to add)
Related videos on Youtube
Comments
-
user1124702 over 1 year
How do you set up egress rules using iptables on Ubuntu?
-
Cian almost 15 yearsMaybe it's just me, but I feel questions you put up to answer yourself should be community wiki. Down voted to reflect my belief in that fact.
-
Doug Luxem almost 15 yearsAnswering your own question is perfectly fine. Community wiki not required either - meta.stackexchange.com/questions/12513/…
-
Cian almost 15 yearsThe way I see it, it's fine to ask a question, and update it at a later point when you find an answer. IMO, asking a question specifically to post your completed answer is something that should be community wiki, or not done.
-
user1124702 almost 15 yearsI initially posted this as a community wiki, but was told explicitly that a community wiki entry is for questions that are more opinion related. See: serverfault.com/questions/6599/…. There is even a badge for this.
-
user1124702 almost 15 yearsAlso the people that downvoted because they misunderstand community wiki should remove their downvotes in the interest of the clear intent that SO should be a knowledge repository --- read the metas and the FAQ's, please.
-
Lance Roberts almost 15 yearsIt's perfectly fine and accepted on SO-family sites to post a question and an immediate answer. This is not a CW situation. Community-Wiki is there for those questions that the OP feels should be editable by a larger base of people.
-
-
Lance Roberts almost 15 yearsThanks for posting this info, the hardest thing to do with any aspect of computing is stringing all the details together.
-
TommyPeanuts about 5 yearsA couple of thoughts: It may be better to set up the loopback, RELATED,ESTABLISHED and package repo rules first, then open various ports (including DNS port 53) after that with
iptables -A OUTPUT -p tcp --dport $port -j ACCEPT
Also, I'm not sure, but it may be more secure to useiptables -A OUTPUT -m conntrack --ctstate NEW -p tcp --dport $port -j ACCEPT
for those port rules. And do they need corresponding rules for--sport
too? Finally, for ipv6, I think you need to explicitly allow icmpv6 too.