Jesse Lawson

buy me a coffee ☕ / home / blog / tutorials / portfolio / hire me! / contact

Jan 27, 2015 - sysadmin

Automatically Block Banned IPs with fail2ban, iptables, and ipset

In this tutorial, we’ll develop a script that will get all the IP addresses blocked by fail2ban on the ssh chain and then add them to an ipset that will be automatically blocked by iptables. Talk about power traffic management!

One of the most frustrating parts about running a web hosting company is the exposure to spam and bad bot traffic. At DashingWP, I often have to scrub through IP logs to determine what traffic is legitimate and what traffic should be blocked outright. In fact, more web hosting companies could do this but they choose not to because more traffic = more money.

If you’ve read any of my other tutorials, you know I have a folder full of tools that I develop to help manage my servers. One of them is an automatic bad bot and spam traffic filter script, which I call scrub_fail2ban.sh. This script will perform three primary functions:

  1. Build a list of IPs to scrub based off of the IPs blocked by fail2ban — specifically, this script will create a list of IPs added by fail2ban to the fail2ban-ssh chain. These are IPs that failed to login via SSH; since I don’t allow SSH access to my clients, I know exactly what IPs should be accessing my boxes.

  2. Add the offending IPs to an ipset, which is a very fast, very efficient way of blocking tens of thousands of IP addresses at a time.

  3. Restart the fail2ban service so that the network is not bogged down by too many entries in the iptables’ raw DROP chain.

Here’s the code:

#!/bin/bash

# This script will spit out all of the IPs that have been blocked by fail2ban-ssh, then for each one, add it to our `ipset blacklist`. It will then restart fail2ban to flush the fail2ban-ssh drop chain. 

# Build the ipset if it's not already built
ipset create blacklist hash:ip

# Build a list of IPs to scrub
iptables -L fail2ban-ssh -v -n | grep -E '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | awk '{print $8}' > blockthese.txt

# Read that list into an array
declare -a scrublist
readarray -t scrublist < blockthese.txt

# loop through each IP address in the array
for i in "${scrublist[@]}"
do
        # Add that IP to the blacklist
        echo -e "Adding $i to blacklist...\n"
        ipset add blacklist $i
done

echo -e "All finished."

# Delete the temporary file we just made
rm blockthese.txt

# Just in case, we'll reissue the iptables rule to drop all from the blacklist set
iptables -I INPUT -m set --match-set blacklist src -p TCP --destination-port 80 -j DROP

# Now we'll restart fail2ban
sudo service fail2ban restart

# And that's it

You’ll notice that this code will attempt to build the ipset regardless of whether or not it exists. It does the same thing with the iptable DROP entry for the blacklist ipset. Both of these are failsafes and will not affect anything if they throw an error.

To make this run nightly, go to crontab (crontab -e [hit enter]) and add an entry like this:

30 2 * * * /var/your_tools/scrub_fail2ban.sh

And that’s it!