23

I need to configure my machine as to allow HTTP traffic to/from serverfault.com only. All other websites, services ports are not accessible. I came up with these iptables rules:

#drop everything
iptables -P INPUT DROP
iptables -P OUTPUT DROP

#Now, allow connection to website serverfault.com on port 80
iptables -A OUTPUT -p tcp -d serverfault.com --dport 80 -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

#allow loopback
iptables -I INPUT 1 -i lo -j ACCEPT

It doesn't work quite well:

  • After I drop everything, and move on to rule 3:

    iptables -A OUTPUT -p tcp -d serverfault.com --dport 80 -j ACCEPT

I get this error:

iptables v1.4.4: host/network `serverfault.com' not found
Try `iptables -h' or 'iptables --help' for more information.

Do you think it is related to DNS? Should I allow it as well? Or should I just put IP addresses in the rules? Do you think what I'm trying to do could be achieved with simpler rules? How?

I would appreciate any help or hints on this. Thanks a lot!

Zenet
  • 928
  • 5
  • 10
  • 15
  • 2
    Don't forget sstatic.net and others. serverfault.com doesn't entirely come from serverfault.com – Zoredache Jan 05 '11 at 02:18
  • Can you run a proxy on another system? This is the best/easiest solution: http://serverfault.com/questions/215134 – mattdm Jan 05 '11 at 02:29

5 Answers5

33

With IPTables rules, order matters. The rules are added, and applied, in order. Moreover, when adding rules manually they get applied immediately. Thus, in your example, any packets going through the INPUT and OUTPUT chains start getting dropped as soon as the default policy is set. This is also, incidentally, why you received the error message you did. What is happening is this:

  1. The default DROP policy get applied
  2. IPTables receives a hostname as a destination
  3. IPTables attempts a DNS lookup on 'serverfault.com'
  4. The DNS lookup is blocked by the DROP action

While the source/destination options will accept hostnames, it is strongly discouraged. To quote the man page,

Hostnames will be resolved once only, before the rule is submitted to the kernel. Please note that specifying any name to be resolved with a remote query such as DNS is a really bad idea.

Slillibri hit the nail on the head which his answer, you missed the DNS ACCEPT rule. In your case it won't matter, but generally I would set the default policy later on the process. The last thing you want is to be working remotely and allow SSH after turning on a default deny.

Also, depending on your distribution, you should be able to save your firewall rules such that they will be automatically applied at start time.

Knowing all that, and rearranging your script, here is what I would recommend.

# Allow loopback
iptables -I INPUT 1 -i lo -j ACCEPT

# Allow DNS
iptables -A OUTPUT -p udp --dport 53 -j ACCEPT

# Now, allow connection to website serverfault.com on port 80
iptables -A OUTPUT -p tcp -d serverfault.com --dport 80 -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# Drop everything
iptables -P INPUT DROP
iptables -P OUTPUT DROP
Piezo Pea
  • 3
  • 4
Scott Pack
  • 14,907
  • 10
  • 53
  • 83
  • 3
    Beautiful, well crafted answer. – JakeRobinson Jan 05 '11 at 02:51
  • 1
    I appreciate it, though you must also have missed my mistake. Oops. – Scott Pack Jan 05 '11 at 03:03
  • 3
    Actually, you can put your `iptables -P` statements anywhere in your script, since chain policies are only applied when packets "fall off" the end of a chain. I usually put the policy statements (typically `DROP` policies) at the top of my iptables scripts. – Steven Monday Jan 05 '11 at 05:57
  • 1
    @Steven: You're absolutely right. The OP appears to be typing them in interactively. Let's pretend you are connected to your host over SSH and your first rule is for the policy DROP. Your SSH connection will be dropped before you can type in any more rules. This is the same timing issue, if a different manifestation, than she ran into. – Scott Pack Jan 05 '11 at 12:39
  • Thanks. Was trying this. But didn't work without adding `sudo iptables -I OUTPUT 1 -o lo -j ACCEPT`. Shouldn't that be added ? – kiranpradeep Jun 11 '16 at 10:47
7

Add

iptables -A OUPUT -p udp --dport 53 -j ACCEPT

to allow DNS lookups.

slillibri
  • 1,643
  • 1
  • 9
  • 8
5

This kind of requirement may be better handled with a web proxy and/or filter. Dansgaurdian can be configured to do this. You will need to use NAT rules to force your traffic through the filter.

Using iptables to filter will allow any sites available from the relevant IP addresses. This is normally a small subset of the entire web.

BillThor
  • 27,737
  • 3
  • 37
  • 69
  • 1
    Totally agree with this. `iptables` is probably the wrong tool to use here, as it doesn't deal particularly well with DNS names. A web proxy with appropriate filter settings is a much better fit. – Steven Monday Jan 05 '11 at 06:06
2

I'm afraid iptables doesn't work at this level, it only cares about the ip address, not the hostname. If you want to block access to other name virtual hosts on the same ip, you'll need to look at putting in .htaccess files.

Niall Donegan
  • 3,869
  • 20
  • 17
  • The thing is, when I try rule 3 without "dropping everything" it works fine with iptables! – Zenet Jan 04 '11 at 20:33
  • 2
    hmm, I misread the question. Right, what's happening in the background is that iptables will resolve serverfault.com to 64.34.119.12 (see answer from slillibri to understand why the resolving wasn't working). However, because iptables doesn't understand hostnames, and is just allowing the ip, you will be able to connect to any website on that ip, if it does have multiple sites. – Niall Donegan Jan 04 '11 at 20:59
  • 2
    @Emily, it may work fine, in that the rule is added, but if the serverfault.com IP changes, traffic will not be allowed. Permitting a site like google.com which has hundreds of addresses which are changed frequently would not work at all. – Zoredache Jan 05 '11 at 02:17
1

You need to configure this at your webserver. iptables is a packet filter. HTTP transactions send the site name (i.e. stackoverflow) as part of the TCP payload (i.e. not as part of the TCP header which is what iptables reads easily).

Given that, and the fact that HTTP transactions are almost certainly going to be spread over multiple packets (i.e. you can't just match a string in the HTTP header), this is much better handled by your webserver configuration or by a proxy in front of it.

It would be useful to know the reasoning behind this, there are a couple of other alternatives:

  1. Redirect to the correct URL if they enter the wrong URL (e.g. redirect to stackoverflow.com if they enter www.stackoverflow.com)
  2. Tell your webserver not to serve hosts other than stackoverflow.com
  3. Put the site on a separate IP that nothing else resolves to and only get your webserver to listen on that.
Philip Reynolds
  • 9,799
  • 1
  • 34
  • 33
  • Hi Phil, I'm not sure I understand what webserver? I don't have a webserver. I'm doing this configuration on my computer. – Zenet Jan 04 '11 at 21:50