I admit it, at the beginning of Rusty’s presentation on iptables years ago, I think it was at the Linux Expo or Raleigh back in the ’90s, I thought: “Just what we need, another network filtering system.” This was shortly after we made the change from ipfwadm to ipchains with little benefit (in fact, some lack of functionality, IIRC) and a re-learning. Rusty won me over pretty quickly though…
One of the big features that made it easy to swallow iptables was that it included backwards compatibility with both ipfwadm and ipchains. It also included, as the name suggests, tables, where you can branch off and handle packets by groups. For example, I have a table for detecting bogons (addresses like 192.168.0.0/24 and 10.0.0.0/8), and just have one rule in my main tables that jumps to the bogon check. In other systems, like FreeBSD’s ipfw, you can use gotos, which work about as well as they do in programming.
Another nice thing about iptables is that it has a plugin architecture for adding functionality. This has been used to great effect for things like firewalling weird application-specific protocols like FTP, marking packets or handing them off for shaping, etc… For example, one I mentioned yesterday was “hashlimit” to help protect from SSH attacks.
Let’s look at a few other interesting modules for iptables. Some of them are built-in to the stock kernel, others have to be patched in (requiring a rebuild of iptables in some cases as well as the kernel).
With condition, you can easily enable or disable a rule from a script by writing to a file under “/proc/net/ipt_condition”. For example: “iptables -I INPUT -m condition –condition killnet -j DROP”. Then if you “echo 1 >/proc/net/ipt_condition/killnet” the input chain will start dropping packets. Then “echo 0” will re-enable input packets.
This includes a “–connrate min:max” and matches when a connection (managed by the conntrack module) is within the range. An optional “!” can appear before the rate to invert the sense of the match. You could, for example, start dropping packets if a particular connection is above a certain rate, for example.
The iprange module allows you to specify source and/or destination addresses as ranges. While the built-in “-s” and “-d” will take a netmask, this can work with an arbitrary range of addresses, for example: “iptables -I INPUT -m iprange -s 192.168.1.5-192.168.1.100 -j ACCEPT”, that isn’t an address range that can be specified by “192.168.1.0/25” sort of syntax.
Match a rule based on the packet length as a range.
Limit a match based on a rate and burst rate. For example, logging can be handy but also offers the opportunity for a denial of service by hitting your logs hard. “iptables -A INPUT -m limit –limit 10/min –limit-burst 30 -j LOG –log-prefix « Dropping:” would add a log target to the end of the input chain. This will log up to 10 messages a minute, with initial bursts of up to 30 messages. You’d normally use this at the end of an INPUT chain which has a default REJECT or DROP policy.
A rule which matches based on the MAC address the packet came from. For example, you could allow packets which have your name server’s IP on them, but come from another MAC address (one way of spoofing packets).
This pair of match and target rules allow packets to be marked with a value, and then later checked. For example:
ipchains -I FORWARD -s 10.0.0.0/8 -j MARK --set-mark 1 ipchains -I FORWARD -d 10.0.0.0/8 -j MARK --set-mark 1 ipchains -I FORWARD -s 192.168.0.0/24 -j MARK --set-mark 1 ipchains -I FORWARD -d 192.168.0.0/24 -j MARK --set-mark 1 ipchains -I FORWARD -m mark --mark 1 -j DROP
These rules mark forwarded packets with 1 if they are in the 10 or 192.168 networks, and then drop them. Of course, you could just jump to a different table. Note that marks can also be used by the “iproute2” tool for routing decisions.
Like “-m tcp –dport 22”, but can match multiple ports: “iptables -I INPUT -m tcp -m multiport –source-ports 25,110,143 -j ACCEPT” would match, in one rule, e-mail related ports.
Allows you to match every nth packet: “iptables -m nth –every 10” would match one packet out of 10. Want to simulate packet loss?
Attempts to match the user or group ID of a packet, process ID, or session ID. Of course, this only works for locally generated packets as they are leaving, and can’t match packets generated by the kernel as they have no owner. For example, ICMP echo-response packets…
Randomly match packets with the given percentage. More fine-tunable than the “nth” match above. Would be great for doing statistical accounting, or simulating network packet loss.
IP sets are a high performance way of matching based on IP addresses. You could, for example, define an ipset of the bogon addresses, and then match them with a single iptables rule:
ipset --create bogons nethash ipset --add bogons 10.0.0.0/8 ipset --add bogons 192.168.0.0/16 ipset --add bogons 0.0.0.0/8 ipset --add bogons 169.254.0.0/16 ipset --add bogons 172.16.0.0/12 ipset --add bogons 192.0.2.0/24 ipset --add bogons 18.104.22.168/24 iptables -A FORWARD -m set --set bogons src -j DROP iptables -A FORWARD -m set --set bogons dst -j DROP iptables -A INPUT -m set --set bogons src -j DROP iptables -A INPUT -m set --set bogons dst -j DROP
Note that the ipset utility is required to use these. There are also many different types of ipsets, including iphash, nethash (for network addresses, as used above), port hashes, and an “iptree” type which can also optionally store a timeout. The latter has huge implications for setting up something to blackhole IPs which touch your SSH port using a command like “ipset –create blackhole iptree –timeout 3600” and adding the IP with “ipset –add blackhole 192.168.1.1”.
Note that the above rules end up being a pretty simple way to block packets coming through your firewall from or to a bogon address, using only 4 rules. This can be done with tables as well, but does require twice as many rules (to match source and destination) and is definitely lower performance.
If you don’t know “state”, you should. It’s a great way of making an extremely effective yet simple firewall for a system. “iptables -I INPUT -i ! lo -m state –state ESTABLISHED,RELATED -j ACCEPT” combined with a default deny policy allows any incoming traffic related to your outgoing connections, and denies everything else. This can also be used to limit new connection attempts while allowing packets for already open connections, as shown in my hashlimit example yesterday.
Match based on a time of day and/or day of week. At one point Evelyn wanted me to set up a block against news and blog sites for her during business hours, to prevent the temptation. “iptables -I OUTPUT -m time –timestart 09:00 –timestop 17:00 –days Mon,Tue,Wed,Thu,Fri -d news.google.com -j REJECT”.
Could also be used to shut down a customer on a particular date: “iptables -I FORWARD -s 10.1.2.3 -m time –datestart 2005:08:29:00:00:00 -j REJECT”, shuts down a customer at midnight on August 29, 2005.
When used with DNAT, causes packets to be forwarded to multiple remote systems. Poor man’s load-balancing. LVS is probably better for this, but this could be useful at times I’m sure.
Much easier than using the “tc” program to set up addresses to shape, use iptables to classify traffic and then “tc” to limit the bandwidth:
iptables -A FORWARD -d 10.1.2.3 -j CLASSIFY --set-class 1:2 iptables -A FORWARD -s 10.1.2.3 -j CLASSIFY --set-class 2:2 tc qdisc del dev eth3 root tc qdisc del dev eth3 root tc qdisc add dev eth3 root handle 1: htb default 1 tc qdisc add dev eth3 root handle 2: htb default 1 tc class add dev eth3 parent 1: classid 1:1 htb rate 100mbit tc class add dev eth3 parent 2: classid 2:1 htb rate 100mbit tc class add dev eth3 parent 1: classid 1:2 htb rate 2mbit tc class add dev eth3 parent 2: classid 2:2 htb rate 3mbit
These rules set up shaping so that traffic coming in to 10.1.2.3 gets shaped to 3mbps and outgoing is shaped to 2mbps, while other traffic on those interfaces gets 100mbps (in the default class).
Connection tracking is great for easily allowing only incoming traffic which is related to your outgoing connections. However, it does cause overhead and, more importantly, on heavily loaded firewall can cause the system to run out of resources. On a router, you want to conntrack traffic destined for the router itself, but the traffic flowing through you do not want to track, you just want it to flow through.
With this target, you can specify that matching packets are directed to a port on the local machine, for doing things like transparent web caching. With transparent caching, users don’t have to change their settings, but web connections are directed to a local cache instead of the real destination.
This target can be used to change the routing of a given packet including setting what interface it will be sent to, what gateway it will use, and can even make a copy of the packets and send them to a different gateway. You can also do some of this using the “iproute2” tool, but the syntax for it is quite difficult to get right. If only it were in the stock kernels.
I’d use it for the VPN, so that only the VPN traffic gets routed over the default gateway, and the rest of the traffic goes over the VPN. I currently do this by setting up a route to the VPN server using the default gateway, and then routing everything else over the VPN. This works but means that other traffic headed for the VPN server is not encrypted with the VPN. Not a huge deal since I use SSH for everything, but would be nice to simply do. I’ve set it up with iproute2, but it turned out to be kind of fragile.
Any packets which match this destination will have information logged about them when they match any action in the iptables rules. Great for debugging why traffic isn’t working as expected, unfortunately it’s not in the stock kernels.
This target allows you to keep per IP statistics on packets and bytes sent and received, optionally including statistics individually for TCP, UDP, and ICMP packets. Extremely low overhead, and when combined with the “random” match allows for statistical accounting. Need to do per user accounting of a class C address space at gigabit speeds? No problemo.
Instead of dropping those SSH attacks, send them to a tarpit. The tarpit ignores attempts to close the connection, but also accepts no data from the remote end, asking the remote to re-send packets. Few local resources are used, but the remote system has to wait up to 25 minutes to time out the connection so it can attack someone else.
This target monitors connections and periodically writes an estimation of the connection’s lag to syslog.
Matches after a session has passed a specified amount of traffic. How about shaping a session after they’ve transferred 1MiB of traffic only? Or using random to simulate packet loss after a bunch of traffic has been sent.
This patch introduces a “dropped” table which packets will traverse when a packet is going to be dropped. In this way you can just “-j DROP” a packet, but still have them be logged by adding some rules to this table.
Match packets based on their source or destination country. I’m sure I’m not the only one who’s been tempted to block incoming e-mail from Asia…
Matches (some) P2P traffic. “iptables -A FORWARD -m ipp2p –edk –kazaa –bit -j DROP”.
This match can be used to look for a string in the payload of a packet. Remember that worm that was hitting a bunch of Cisco routers and causing the firmware to lock up with a big HTTP CGI request that triggers an exploit on some systems? This could block those.