Today I will show you the iptables rules I set on my main personal computer, with detailed comments about why I came to use these rules after several years of Linux desktop usage. The rules I use now have been simplified as much as I could and are based on common rules and advice that can be found on the network and also on input I got from experienced network administrators. I’ve been using them unmodified for a few years. They are designed for desktop users either directly connected to the Internet or behind a router. They are a bit restrictive in some aspects but we’ll see you can easily create a few holes for specific purposes. So here they are:
# iptables -v -L Chain INPUT (policy DROP 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 663K 905M ACCEPT all -- any any anywhere anywhere state RELATED,ESTABLISHED 105 6300 ACCEPT all -- lo any anywhere anywhere 0 0 ACCEPT icmp -- any any anywhere anywhere icmp destination-unreachable 0 0 ACCEPT icmp -- any any anywhere anywhere icmp time-exceeded 0 0 ACCEPT icmp -- any any anywhere anywhere icmp source-quench 0 0 ACCEPT icmp -- any any anywhere anywhere icmp parameter-problem 0 0 DROP tcp -- any any anywhere anywhere tcp flags:!FIN,SYN,RST,ACK/SYN state NEW Chain FORWARD (policy DROP 0 packets, 0 bytes) pkts bytes target prot opt in out source destination Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination
We’ll start by the most obvious rules. The FORWARD chain has a policy of “DROP” and no specific rules. A desktop computer isn’t usually employed as a router or to share an Internet connection, so there’s no reason in allowing forwarding.
The OUTPUT chain has a policy of “ACCEPT” and no rules. Basically, we are allowing everything going out of our computer. While this isn’t the most secure policy at all, it’s usually enough for a desktop computer. Many paranoid people would not let everything out. For example, to prevent their computers from being used to send spam due to a mistake somewhere else, sometimes people forbid from sending traffic from the source port 25, or in general from source ports below 1024, where most common services are. We could do that, but I think it’s not really needed for a desktop computer. We’ll put more effort blocking incoming traffic, and we can keep a relaxed policy on outgoing traffic.
Finally, the guts of the rules. The INPUT chain has a policy of DROP. That is, everything not explicitly allowed will be forbidden. If anything passes through all the rules, the traffic will be discarded silently without making noise.
The rules in the INPUT chain are sorted according to the typical frequency of hits. “Popular” and frequent traffic will be quickly accepted instead of having to check many rules before. That’s why the first rule is to allow RELATED and ESTABLISHED traffic, for any protocol. The any part is important. This is the rule that, basically, allows us to receive replies and normal traffic for connections we start ourselves. For example, when we open a web page with our web browser, we’ll send traffic one way and when we receive the reply, the connection will be ESTABLISHED and we’ll see the reply. This first rule is the most important one because, just due to it, we can use the computer “normally”.
The stateful packet firewall in Linux is quite clever and understands established connections even when the underlying protocol has no notion of connections. For example, that first rule allows us to receive DNS replies from queries we made ourselves, using the UDP protocol, or allows receiving ICMP echo replies from our own requests. In other words, we can ping other computers thanks to that rule.
On to the second rule, it looks like it would accept any traffic from anywhere, but the keyword here is lo:
105 6300 ACCEPT all -- lo any anywhere anywhere
Then, you can see I allow some specific types of ICMP packets that usually signal network problems. None of those require a reply to be sent, so we accept them and try to interpret what they would mean if they ever come in. I don’t think it’s possible to get anything more than a DoS attack with those rules, but comments are welcome. And, of course, you can be DoS’ed just by someone saturating you with incoming traffic. Again, this is a matter of getting your priorities sorted. If you feel paranoid, well, drop those rules.
Finally, at the end of the chain we have the famous specific rule to block incoming traffic with state “NEW” and the SYN flag not set in TCP. This rule is quite specific and an explanation for it can be found in many iptables manuals, FAQs and tutorials. I put the rule in the end because the first rule is not affected by it, because the second rule isn’t either (we are allowing ALL traffic coming from “lo”, after all), and the ICMP rules are not affected either.
However, we still keep it there even if the traffic was going to be dropped anyway due to the chain policy, because when we want to create a hole in these rules, we do it by adding more rules at the end of the INPUT chain. For example, sometimes I want to allow incoming traffic to a specific port where I have configured a server that is supposed to be reached from other machines, to serve a specific content in a specific point in time. For that, I have created a couple of scripts called “service-open” and “service-close”, that can be used followed by a list of service names or port numbers. For example, when I start a web server to allow someone in my home network to get a file from my computer, I usually run the command “service-open 8080″ (the server would be listening on that port). Once the file is served, I run “service-close 8080″ and shut the server down. Those commands add and remove rules at the end of the INPUT chain, so that’s why I put the last rule there, so it’s present before any holes I punch through my firewall in those special cases. If you frequently run a P2P application on your computer, you may want to open a hole permanently to some port and save it as part of your usual rules. I don’t, so I keep everything closed.
The content of my scripts are:
# cat /usr/local/sbin/service-open #!/bin/sh if test $# -eq 0; then echo usage: $( basename $0 ) service ... 1>&2 exit 1 fi while test $# -ne 0; do /usr/sbin/iptables -A INPUT -p tcp --dport "$1" -j ACCEPT /usr/sbin/iptables -A INPUT -p udp --dport "$1" -j ACCEPT shift done
# cat /usr/local/sbin/service-close #!/bin/sh if test $# -eq 0; then echo usage: $( basename $0 ) service ... 1>&2 exit 1 fi while test $# -ne 0; do /usr/sbin/iptables -D INPUT -p tcp --dport "$1" -j ACCEPT /usr/sbin/iptables -D INPUT -p udp --dport "$1" -j ACCEPT shift done
Those scripts play nicely with my set of rules because they are designed with my rules in mind. Also, you can see they are dead simple.
With the set of rules I have described, you can use your computer normally, you can easily let more traffic through in specific cases and, more importantly, you’ll be “invisible” on the network. Nobody will know if your computer is really there or not unless you send them traffic or if they found out by other means. And, also, it’s a very small set of rules and it’s very easy to remember and understand, and to create scripts that modify it easily.
Edit: The commands needed to create those rules:
iptables -P FORWARD DROP iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT iptables -A INPUT -i lo -j ACCEPT iptables -A INPUT -p icmp -m icmp --icmp-type 3 -j ACCEPT iptables -A INPUT -p icmp -m icmp --icmp-type 11 -j ACCEPT iptables -A INPUT -p icmp -m icmp --icmp-type 4 -j ACCEPT iptables -A INPUT -p icmp -m icmp --icmp-type 12 -j ACCEPT iptables -A INPUT -p tcp -m tcp ! --tcp-flags FIN,SYN,RST,ACK SYN -m state --state NEW -j DROP iptables -P INPUT DROP