, 2 min read

Blocking IP addresses with ipset

Original post is here eklausmeier.goip.de/blog/2020/07-14-blocking-ip-addresses-with-ipset.

In Chinese Hackers I mentioned that I block ssh attackers with the help of fail2ban. Unfortunately, fail2ban uses iptables to create firewall rules in "Chain f2b-SSH" for each individual IP address. For a modern processor this is no problem, even if you have thousands of these rules. While for a low powered ARM processer this can have a noticeable influence on network performance. In Using Odroid as IP Router I wrote:

Added 10-Jan-2019: I previously added ca. 3000 iptables rules for blocking IP address ranges which attacked me on port 22 (ssh). That many rules will deteriorate your network performance significantly. My download speed went down from 100 MBit/s to 20 MBit/s.

An alternate or additional approach to this slowdown due to iptables is to use ipset, Arch package ipset. This ipset maintains hash tables of IP addresses or networks. At the time when I still used the Odroid, then the main sshd-server copied the blocked IP addresses to the Odroid, which in turn populated the hash table with the IP addresses to be blocked.

Setting up ipset and populating the hash tables from fail2ban can be done as below.

sqlite3 -csv /var/lib/fail2ban/fail2ban.sqlite3 "select distinct ip from bips order by ip" |    \
        perl -e 'BEGIN {print "create reisTmp hash:ip family inet hashsize 65536 maxelem 65536\n"; }
                print "add reisTmp $_" while (<>);'     \
        > ~/tmp/reisTmp

The name reisTmp is arbitrary. This way we have a file which looks like this:

create reisTmp hash:ip family inet hashsize 65536 maxelem 65536
add reisTmp
add reisTmp
add reisTmp
add reisTmp
add reisTmp
add reisTmp
. . .

This file is transfered from the sshd/fail2ban-server to our router, if required, and is the input to be run through ipset:

ipset restore -f ~klm/tmp/reisTmp
ipset swap reisTmp reisbauer
ipset destroy reisTmp

This assumes you have created a hash table in ipset called reisbauer. Again, this name is arbitrary. Above scenario assumes that populating this hash table takes some time, then just swap hash tables, which is almost instant.

To re-initiate the hash table after reboot, you use

systemctl enable ipset
systemctl start ipset

This basically does

ipset -f /etc/ipset.conf restore

To activate your set for blocking you must tell iptables to check elements in your ipset:

iptables -t raw -A PREROUTING -i eth0 -p tcp -m tcp --dport 22 -m set --match-set reisbauer src -j DROP