Rewriting my iptables rules using ipsets

August 7, 2019

On Mastodon, I was tempted:

My home and office workstation have complicated networking, but their firewall rules are actually relatively simple. Maybe it's time to switch them over from annoying iptables to the new shiny nftables stuff, which might at least be more readable (and involve less repetition).

Feedback convinced me to not go that far. Instead, today I rewrote my iptables rules in terms of ipsets (with multiple set matches), which eliminated a great deal of their prior annoyance (although not all of it).

My workstation firewall rules did not previously use ipsets because I first wrote them before ipsets were a thing; in fact, they date from the days of ipchains and Linux 2.2. In the pre-ipset world, this meant a separate iptables rule for each combination of source IP, destination port, and protocol that I wanted to block (or allow). On my office workstation, this wound up with over 180 INPUT table rules (most of them generated automatically).

Contrary to what I asserted a few years ago, most of the actual firewall rules being expressed by all of these iptables rules are pretty straightforward. Once I simplified things a bit, there are some ports that only my local machine can access, some ports that only 'friendly' machines can access, and some machines I don't like that should be blocked from a large collection of ports, even ones that are normally generally accessible. This has an obvious translation to ipset based rules, especially if I don't try to be too clever, and the result is a lot fewer rules that are a lot easier to look over. There's still some annoying repetition because I want to match both the TCP and UDP versions of most ports, but I can live with that.

(Enough of the ports that I want to block access to come in both TCP and UDP versions that it's not worth making a finer distinction. That would lead to more ipsets, which is more annoying in practice.)

When I did the rewrite, I did simplify some of the fine distinctions I had previously made between various ports and various machines. I also dropped some things that were obsolete, both in terms of ports that I was blocking and things like preventing unencrypted GRE traffic, since I no longer use IPsec. I could have done this sort of reform without a rewrite, but I had nothing to push me to do it until now and it wouldn't have been as much of a win. The actual rewrite was a pretty quick process and the resulting shell script is what I consider to be straightforward.

(The new rules also have some improvements; for example, I now have some IPv6 blocks on my home machine. Since I already had an ipset of ports, I could say 'block incoming IPv6 traffic from my external interface to these ports' in a single ip6tables rule.)

As far as I'm concerned, so far the three big wins of the rewrite are that 'iptables -nL INPUT' no longer scrolls excessively, I'm no longer dependent on my ancient automation to generate iptables rules, and I've wound up writing a shell script to totally clear out all of my iptables rules and tables (because I kept wanting to re-run my setup script as I changed it). That my ancient automation silently broke for a while (again) and left my office workstation without most of its blocks since late March is one thing that pushed me into making this change now.

(Late March is when I updated to my first Fedora 5.x kernel, and guess what my ancient automation threw up its hands at. If you're curious why, it was (still) looking at the kernel version to decide whether to use ipchains or iptables.)

Written on 07 August 2019.
« dup(2) and shared file descriptors
What has to happen with Unix virtual memory when you have no swap space »

Page tools: View Source, Add Comment.
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Wed Aug 7 01:16:36 2019
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.