another voice in the babble on the net

“loose” iptables firewall for servers

This page describes and documents the “loose” policy stance applied in the iptables script iptables.loose.sh discussed in my post of 9 September 2012. Its sister, tight, policy is described and documented here.

Note that we policy drop on all three chains (INPUT, OUTPUT and FORWARD) in the filter table. This means that we must specify allowed outbound connections as well as allowed inbound. This is safer than allowing a default outbound to anywhere, but inevitably is more complex since we must specify exact rules for each outbound connection (and its return). Note also that I add a (redundant) set of “DROP” directives at the end of this script. You do not need to do this, but I find it helps me when reading the output of “iptables -nvL” because it then expicitly lists the drops at the end of the rules. (I can’t shake old Cisco IOS habits…..)

First some defines. This allows us to vary the policy applied by the script without editing the body of the script itself.

First flush all rules from all chains in the filter table and zero the packet and byte counters.

$IPTABLES -F
$IPTABLES -Z

Set the default policy on all chains. We use DROP rather than REJECT. This can be a religious decision. Strict adherence to standards implies we should “REJECT” with a helpful ICMP error message so that unwanted connections do not simply hang or timeout for odd reasons. However, doing so can mean that incoming packets with a spoofed source address can get replies sent to that source address when they are not expecting them. DDOS bots exploit this behaviour. I’d rather break standards than help a DDOS bot.

Some people advocate logging dropped packets. I don’t. Unless you have a very large (separate) partition for /var you can very rapidly run out of logspace – particularly if your server is deliberately targetted. This can result in a self-imposed denial of service as your root partition fills up.

Now the rules.

Anti spoof rules to drop all RFC1918 addresses, martian networks, multicasts, all ones and all zeros etc. This should not really be necessary because the ISP should apply these filters at the border routers. But just in case …… (and I routinely see lots of broadcast traffic).

Note however, that if you are running a VPN endpoint (such as openvpn) on your server, you will need to modify these rules otherwise your VPN access will be blocked.

Drop invalid packets. There is some dispute as to whether this is still necessary. Most “odd” tcp flag combinations should be handled correctly in modern kernel implementation. However, I leave this in because in my experience, my servers get hit by such traffic. Try it yourself. Leave the rules in place for a few days and then take a look at the packet counts against those rules.

Allow inbound SSH access for remote management. I like to limit my SSH access to one known secure source. You may have more than that. I recommend you avoid the lazy approach of allowing all inbound SSH, even if you are using fail2ban. (And as an aside, I don’t like fail2ban anyway, because I have a deep seated dislike of setuid scripts. They are are dangerous.)

Allow DNS queries to our trusted servers. Both TCP and UDP are required. And, yes, I know UDP is connectionless. I know that the concept of an ESTABLISHED connection is therefore questionable, but the syntax allows this and I find it useful. If the formulation offends your sense of decency, then by all means change it.

Allow outbound connections initiated by us, but only to specified services (let’s not be too lax eh?). Unlike the “tight” policy (q.v.), this ruleset runs the risk of us allowing connections out to servers we should not trust. If our $SERVICESOUT define permits access to port 80 (or 443) a hostile program internally could call out to a command and control server or a staging server and could then download further malware such as a rootkit. However, this rule is easier to manage. You pays your money….

Now allow selected ICMP messages – outbound source must always be our ip to prevent outbound spoof and we only really want to allow types 0, 3 and 8 (ping reply, destination unreachable and ping). Blocking all icmp is NOT a good idea. In particular, fragmentation error reporting is is vital to the PMTU discovery process – see RFC 2923 for example and https://en.wikipedia.org/wiki/Path_MTU_Discovery. Marc Slemko wrote an excellent article about this back in 1998.

So – ping from outside inwards – but rate limit to 1/sec. This helps prevent us being used in a spoofed address ping flood. But note that rate limiting here may not be necessary on most modern distros. Check the contents of the files: /proc/sys/net/ipv4/icmp_ratelimit and /proc/sys/net/ipv4/icmp_ratemask. Note, however, that by default icmp_ratelimit only applies to ICMP error messages and source quench, not all ICMP replies (or icmp echo). See frozentux for some useful information. For a listing of all ICMP types and codes, see the IANA reference here.

Lastly the belt to the braces above. These lines are not necessary, but they give a reassuring positive affirmation in the output of “iptables -nvL” that we /do/ have a default drop in place at the end of our ruleset.

psp

random

“Cheery was aware that Commander Vimes didn't like the phrase 'The innocent have nothing to fear', believing the innocent had everything to fear, mostly from the guilty but in the longer term even more from those who say things like 'The innocent have nothing to fear'.”