Skip to content

Mass blocking of evil IP addresses with iptables and IP sets

2013 November 22
by Kirk Kosinski

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.

7 Responses Post a comment
  1. September 9, 2015

    Thanks that’s exactly what I needed to read. A small python script checking the ssh invalid logs + ipset will kick out some people from my server 🙂

  2. John Damm Sørensen permalink
    April 11, 2016

    Great article.

    Another way of generating the set could be this script:

    ipset create evil-ips hash:ip
    cat base.txt | xargs -L1 -I xx echo add evil_ips xx | ipset –

    To save the set:
    ipset save evil_ips -file evil_ips.ipset

    Then you will be able to restore the set after a reboot with:
    ipset restore -file evil_ips.ipset

    The file evil_ips.ipset will contain something like this:

    create evil-ips hash:ip family inet hashsize 32768 maxelem 65536
    add evil-ips 217.118.95.65
    add evil-ips 93.61.30.38
    add evil-ips 176.102.85.33
    add evil-ips 94.23.164.236
    add evil-ips 93.54.28.240
    add evil-ips 158.69.205.212

  3. Adam permalink
    February 4, 2017

    Nice one.

    Thanks alot man.

  4. Chris permalink
    August 15, 2017

    Thanks! In order to use CIDR blocks rather than individual IP addresses, change the “iphash” parameter to “nethash”

Trackbacks & Pingbacks

  1. 使用 ipset 与 iptables 拦截黑名单中的大量 IP 地址 - Kkyy.ME
  2. Implementing an IP blacklist with firewalld | Kirk Kosinski

Leave a Reply

Note: You may use basic HTML in your comments. Your email address will not be published.

Subscribe to this comment feed via RSS

*