How do you setup egress rules with iptables (on Ubuntu)?

7,922

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)

Share:
7,922

Related videos on Youtube

user1124702
Author by

user1124702

Entrepreneur, sysadmin, techie.

Updated on September 17, 2022

Comments

  • user1124702
    user1124702 over 1 year

    How do you set up egress rules using iptables on Ubuntu?

    • Cian
      Cian almost 15 years
      Maybe 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
      Doug Luxem almost 15 years
      Answering your own question is perfectly fine. Community wiki not required either - meta.stackexchange.com/questions/12513/…
    • Cian
      Cian almost 15 years
      The 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
      user1124702 almost 15 years
      I 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
      user1124702 almost 15 years
      Also 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
      Lance Roberts almost 15 years
      It'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
    Lance Roberts almost 15 years
    Thanks for posting this info, the hardest thing to do with any aspect of computing is stringing all the details together.
  • TommyPeanuts
    TommyPeanuts about 5 years
    A 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 use iptables -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.