Ever notice how many braindead people try to break into your email server? It's a daily battle to stay ahead of them. Especially when, like in my case, fail2ban just... well... fails... :-)

For some time I have just manually added IP addresses to my iptables array. The list gets kind of long after a while though. See the following for what I did. And then imagine a lot of these lines.

iptables -A INPUT -s <ip>/<cidr> -p tcp -m tcp --dport 25 -j DROP

For a while now the attacks have been shown in my log as:

warning: hostname Tor-Private.ru does not resolve to address No address associated with hostname

It seems that the dumb idiots are now using the tor network to hide in. The tor network is kind of a big black hole, and it's not straight forward to block them from there. Or is it?

During my investigation I came across the tor abuse faq[1], which lists a way to find the tor gateways that are able to direct traffic to a given website.
So I went ahead and found the IP addresses I needed to block. But now I had a long list of IP addresses that I didn't really want to convert into iptables rule lines. Not manually anyway.

Why did I not just use a user friendly frontend for iptables you might ask. Sometimes I do things the hard way just to find out if there is a more manual/down to earth solution I can use. It's as much for educational purposes as it is a wish for feeling more in control of my system. I will let you decide whether this approach is smart or stupid. ;-)

Searching and reading a bit on the Internet I came across the ipset command. It makes a list that iptables can reference. In this way the iptables ruleset is kept smaller and easier to read.
What I ended up with was a folder in /etc/iptables called ipset.rules
Inside this folder there are text files with the ending ".lst" containing the IP addresses I want to block in cidr notation. I then made a small script to change the lst files into ipset tables that iptables then references. Sounds confusing? It's not so bad...

The *.lst files should have unique filenames. Calling one blacklist.lst and the other blacklist_one.lst will only create one rule in iptables!

In addition to creating the ipset tables (or sets, as they are called), it also creates the iptables rule and stores both ipset and iptables data in their respective config files.

I think the best I can do is display the iptables rules I have and then the script.

# Generated by iptables-save v1.4.21 on Fri Dec 25 13:09:16 2015

:OUTPUT ACCEPT [13825:3323903]
:IN_BLACK - [0:0]
:IN_SMTP - [0:0]
:IN_WHITE - [0:0]
-A INPUT -p icmp -j ACCEPT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p tcp -m multiport --dports 25,465 -m conntrack --ctstate NEW -j IN_SMTP
-A IN_BLACK -p tcp -m multiport --dports 25,465 -m set --match-set torblacklist src -j DROP
-A IN_BLACK -p tcp -m multiport --dports 25,465 -m set --match-set blacklist src -j DROP
-A IN_SMTP -m recent --update --seconds 10 --hitcount 3 --rttl --name smtpbf --mask --rsource -j DROP
-A IN_SMTP -m recent --update --seconds 120 --hitcount 4 --rttl --name smtpbf --mask --rsource -j DROP
-A IN_SMTP -m recent --set --name smtpbf --mask --rsource -j ACCEPT
# Completed on Fri Dec 25 13:09:16 2015

Please note the IP addresses in the above list have been changed to random numbers.

The text files containing the IP addresses are just files containg one IP address per line. When listing a single host e.g. IP address/32, the cidr notation should be left out. See example(s) below:

And here is the script:


## Loops over each file in below path and sets firewall blocking rules accordingly

for file in /etc/iptables/ipsetrules.d/*.lst; do
  echo "populating '$listname' with IP addresses from file: $ipfile"

  if ! ipset list|grep -qw "\<$listname\>"; then 
    ipset create $listname hash:net
    echo "List already exists... skipping creation"
  while read IP; do
    if ! ipset list |grep -qw "\<$IP\>"; then
      # following if statement ignores comments in IP list files

      if ! [[ $IP == "#"* ]]; then
        echo "IP $IP added"
        ipset add $listname $IP
  done < $ipfile

  # create iptables rule if it does not exist

  if ! iptables -nL|grep -q $listname; then
    iptables -I IN_BLACK -p tcp -m multiport --dports 25,465 -m set --match-set $listname src -j DROP
    echo "Firewall rule already exists."

# save results to respective config files

ipset save > /etc/ipset.conf
iptables-save > /etc/iptables/iptables.rules

The script should be mostly self explanatory. At least I hope it is.
Please feel free to leave a comment. At least until I have to close them due to abuse. :-D


[1] https://www.torproject.org/docs/faq-abuse.html.en#Bans