Mass blocking of evil IP addresses with iptables and IP sets
When running a Linux server or firewall it may be useful to use iptables to block a list of known “evil” IP addresses. There are many organizations maintaining “block lists” of such IPs, such as Spamhaus, DShield, and OpenBL. Blocking a lot of IPs can be done by creating a lot of corresponding iptables rules but a cleaner solution is to use iptables in conjunction with an IP set.
One advantage to using iptables with IP sets is that only one simple iptables rule is needed. I like this since the output iptables-save or iptables -L will remain clean. The rule references an IP set that contains the actual list of evil IPs and is controlled by the ipset command. This command is not always installed by default, but it can easily be installed with apt-get install ipset (Debian, Ubuntu), yum install ipset (RHEL, CentOS, Fedora), or something similar depending on the Linux distribution. The command is straightforward to use. An IP set named “evil_ips” that will contain a list of IPs can be created with:
ipset create evil_ips iphash
And an evil IP can be added to it with:
ipset add evil_ips 10.1.1.10
Other ipset commands include del (to remove a single entry from a list), flush (to remove all entries), and destroy (to remove the entire list). Check man ipset or ipset help for more. Once the list is created it can be used in iptables rules. For example, the following example rule would block every IP in the “evil_ips” IP set on a Linux server:
iptables -A INPUT -m set --match-set evil_ips src -j DROP
Or for a Linux firewall:
iptables -A FORWARD -m set --match-set evil_ips src -j DROP
Another advantage is that the list of IPs can be updated as necessary without reloading or recreating the iptables rules. Managing that IP set is a task best performed using a script. Any scripting language should do but I prefer bash. Naturally I searched Google for such a script, but I couldn’t seem to find one that did exactly what I wanted so I decided to write my own. The following will download a text file containing a list of evil IPs, create or update an IP set with those IPs, and if necessary create the iptables rule.
#!/bin/bash -x SETNAME="evil_ips" if [ -x `which curl` -a -x `which ipset` ]; then evil_ips=$(curl 'http://host/list.txt.gz' 2>/dev/null | gunzip) if [ -n "$evil_ips" ]; then # only proceed if new IPs are obtained logger -t "evil_ip_block" "Adding IPs to be blocked." ipset list $SETNAME &>/dev/null # check if the IP set exists if [ $? -ne 0 ]; then ipset create $SETNAME iphash # create new IP set iptables -I INPUT 2 -m set --match-set $SETNAME src -j DROP else ipset flush $SETNAME # clear existing IP set fi # populate the new or existing empty IP set for i in $evil_ips ; do ipset add $SETNAME $i ; done else logger -t "evil_ip_block" "No IPs to add." fi else logger -t "evil_ip_block" "Needed command not found." fi
Compared to some of the scripts I found while searching Google, this example script includes at least some error handling, does some basic logging, doesn’t use any temporary files, and will make the IP set match the block list rather than only appending and never removing IPs… after all, even evil IPs can sometimes be reformed. 🙂
The script can be configured as a cron job and it can easily be updated to use different or multiple iptables rules. To learn bash I suggest the free Advanced Bash-Scripting Guide from TLDP and Shell Scripting from Wrox. One disadvantage to my example is that it uses iptables directly instead of distribution-specific iptables management tools like ufw (Ubuntu) and firewall-cmd (Fedora). Another thing I might add is support for multiple block lists. But for now it is doing what I need.