List based permanent bans with fail2ban

Today I post something about the nice little tool fail2ban. As you probably know, fail2ban can be used to block those annoying brute force attacks against your servers. Other than the also popular and useful tool DenyHosts it allows the protection of other services than SSH as well (e.g. HTML login pages served by Apache). The working mechanism also differs from that of DenyHosts, as fail2ban uses iptables instead of the BSD style hosts.deny file to block annoying brute forcers. Installation is quite simple, on Debian for example, just install it through apt and you’re good to go even with the default config.

One thing that I was missing, was the option to ban IPs forever. You can basically do this by setting bantime to a negative value, but as soon as you reload your iptables rules (e.g. by restarting the fail2ban service or the whole system) the entries for the permanently banned IPs are gone.
To overcome this issue, I did some minor changes to the actions fail2ban executes on start-up and on banning.

IMPORTANT: I strongly advise you, to be careful while playing around with automated banning tools, especially if you can’t reach your server physically. Make sure, that you have something useful set in the ignoreip option under the [DEFAULT] jail (your current IP address) to not accidentally lock you out of the system (really nasty with permanent banning active…)

  1. First, check the banaction currently used (you need that, to modify the correct actionfile afterwards)
    /etc/fail2ban/jail.local

    
    #
    # ACTIONS
    #
    ...
    banaction = iptables-multiport
    ...
    
  2. Open up the corresponding actionfile and modify according to the sample below (changes are under the # Persistent banning of IPs comment)
    /etc/fail2ban/action.d/iptables-multiport.conf

    
    ...
    actionstart = iptables -N fail2ban-<name>
                  iptables -A fail2ban-<name> -j RETURN
                  iptables -I INPUT -p <protocol> -m multiport --dports <port> -j fail2ban-<name>
                  # Persistent banning of IPs
                  cat /etc/fail2ban/ip.blacklist | while read IP; do iptables -I fail2ban-<name> 1 -s $IP -j DROP; done
    ...
    actionban = iptables -I fail2ban-<name> 1 -s <ip> -j DROP
                # Persistent banning of IPs
                echo '<ip>' >> /etc/fail2ban/ip.blacklist
    ...
    
  3. Your blacklist should look something like this (one IP per line, of course you can add IPs manually)
    /etc/fail2ban/ip.blacklist

    
    ...
    10.0.0.242
    192.168.1.39
    ...
    
  4. Restart fail2ban to make the changes active

Now, what happens is that each time fail2ban starts, it loops through your ip.blacklist and blocks the IPs in there. If fail2ban blocks a new IP, it will automatically append it to the blacklist.

Links
http://www.fail2ban.org
http://www.fail2ban.org/wiki/index.php/Whitelist
http://denyhosts.sourceforge.net

Update
The following config adds some nice features that were missing in the example above:

  • No duplicate iptables rules (@Lin: might be interesting for you)
  • Jail specific blocking rules (similar to Dr. Tyrell’s and samuelE’s suggestions in the comments)
  • Reporting offender IPs to badips.com

/etc/fail2ban/action.d/iptables-multiport.conf:


# Fail2Ban configuration file
#
# Author: Cyril Jaquier
# Modified by Yaroslav Halchenko for multiport banning and Lukas Camenzind for persistent banning 
#
#
[Definition]
# Option:  actionstart
# Notes.:  command executed once at the start of Fail2Ban.
# Values:  CMD
#
actionstart = iptables -N fail2ban-<name>
              iptables -A fail2ban-<name> -j RETURN
              iptables -I INPUT -p <protocol> -m multiport --dports <port> -j fail2ban-<name>
              # Load local list of offenders
              if [ -f /etc/fail2ban/ip.blacklist ]; then cat /etc/fail2ban/ip.blacklist | grep -e <name>$ | cut -d "," -s -f 1 | while read IP; do iptables -I fail2ban-<name> 1 -s $IP -j DROP; done; fi
# Option:  actionstop
# Notes.:  command executed once at the end of Fail2Ban
# Values:  CMD
#
actionstop = iptables -D INPUT -p <protocol> -m multiport --dports <port> -j fail2ban-<name>
             iptables -F fail2ban-<name>
             iptables -X fail2ban-<name>
# Option:  actioncheck
# Notes.:  command executed once before each actionban command
# Values:  CMD
#
actioncheck = iptables -n -L INPUT | grep -q fail2ban-<name>
# Option:  actionban
# Notes.:  command executed when banning an IP. Take care that the
#          command is executed with Fail2Ban user rights.
# Tags:    <ip>  IP address
#          <failures>  number of failures
#          <time>  unix timestamp of the ban time
# Values:  CMD
#
actionban = if ! iptables -C fail2ban-<name> -s <ip> -j DROP; then iptables -I fail2ban-<name> 1 -s <ip> -j DROP; fi
            # Add offenders to local blacklist, if not already there
            if ! grep -Fxq '<ip>,<name>' /etc/fail2ban/ip.blacklist; then echo '<ip>,<name>' >> /etc/fail2ban/ip.blacklist; fi
            # Report offenders to badips.com
            wget -q -O /dev/null www.badips.com/add/<name>/<ip>
# Option:  actionunban
# Notes.:  command executed when unbanning an IP. Take care that the
#          command is executed with Fail2Ban user rights.
# Tags:    <ip>  IP address
#          <failures>  number of failures
#          <time>  unix timestamp of the ban time
# Values:  CMD
#
actionunban = iptables -D fail2ban-<name> -s <ip> -j DROP
              # Disabled clearing out entry from ip.blacklist (somehow happens after each stop of fail2ban)
              # sed --in-place '/<ip>,<name>/d' /etc/fail2ban/ip.blacklist
[Init]
# Defaut name of the chain
#
name = default
# Option:  port
# Notes.:  specifies port to monitor
# Values:  [ NUM | STRING ]  Default:
#
port = ssh
# Option:  protocol
# Notes.:  internally used by config reader for interpolations.
# Values:  [ tcp | udp | icmp | all ] Default: tcp
# 
This entry was posted in Sysadmin and tagged , , , , . Bookmark the permalink.

27 Responses to List based permanent bans with fail2ban

  1. Luiso says:

    I made a change:
    cat /etc/fail2ban/ip.blacklist- | while read IP; do iptables -I fail2ban- 1 -s $IP -j DROP; done

    echo ” >> /etc/fail2ban/ip.blacklist-

    the basic idea is to save exactly the same list that has been blocked.

    • Warwick says:

      Some of the comment has been removed, it should read as follows:


      cat /etc/fail2ban/ip.blacklist- | while read IP; do iptables -I fail2ban- 1 -s $IP -j DROP; done

      echo '' >> /etc/fail2ban/ip.blacklist-

      This creates seperate ip.blacklist files for each row you’re blocking (ssh, apache, etc.).

      • Warwick says:

        And it’s been removed again! Even in code tags! Okay, one more time.


        cat /etc/fail2ban/ip.blacklist-<name> | while read IP; do iptables -I fail2ban-<name> 1 -s $IP -j DROP; done

        echo '<ip>' >> /etc/fail2ban/ip.blacklist-<name>

        If it doesn’t work this time I give up.

  2. milano94 says:

    great info… this would be fail2ban generic setup for all linux varians.
    Thank u very much

  3. david says:

    If only now if we could do the same with those pesky spam referrers !

    Thanks for this post – just what I am looking for. Now I can reboot my server without fiddling !

    Ciao

  4. Robin says:

    Thanks for this, the restart problem has been troubling me for a while.
    What would make it even better would be a mechanism to honour the ban-times originally set. I set a pretty high ban-time anyway, but I do think the principle of ban-time is important so your blacklist does not keep growing indefinitely, as this will eventually make iptables become a resource-hog.
    Any thoughts on how to do that?

  5. Looke says:

    Hi Robin

    Thanks for your comment. To achieve something that honors the ban-time, you need to put a bit more logic inside the “action” part (e.g. grepping and removing the banned ip in “actionunban”). Also, you need to record the time the ban took place (see field time in iptables-multiport.conf) in order to avoid “dead” banned IPs in your blacklist in case you restart fail2ban… But that would be nice to have indeed. Probably, I will try to figure out something.

    Cheers
    Looke

  6. Dr. Tyrell says:

    Alteration of original, to keep one banlist file, and be able to know which filter:

    actionstart = …
    cat /etc/fail2ban/ip.blacklist | while read line; do IP=`echo $line | cut -d’ ‘ -f1`; iptables -I fail2ban- 1 -s $IP -j DROP; done

    actionban = …
    echo ‘ ‘ >> /etc/fail2ban/ip.blacklist

  7. Dr. Tyrell says:

    damn, second try to avoid html interpret…

    actionstart = …
      cat /etc/fail2ban/ip.blacklist | while read line; do IP=`echo $line | cut -d’ ‘ -f1`; iptables -I fail2ban-<name> 1 -s $IP -j DROP; done

    actionban = …
      echo ‘<ip> <name>’ >> /etc/fail2ban/ip.blacklist

  8. BTR Naidu says:

    This was a nice post.

    My requirement is slightly different. I want the IP to be released after specified interval from the ban list. Once released, if an attack comes from the same IP, it bans and releases after the interval again. This loop continues for say 3 / 5 times and after 5 times, the IP is banned permanently.

    This can be done with a custom script but does fail2ban has any inbuilt functionality / configuration to achieve this without external scripting?

    Thanks all in advance,
    Naidu

  9. Phil Hagen says:

    Thanks for this article – it was a huge help in getting a similar solution deployed to my servers. I had a similar requirement to what BTR Naidu describes – normal block-then-unblock for a number of times, then add to permanent blocklist. I’ve detailed the steps here: Permanently Ban Repeat Offenders With fail2ban.

  10. Lin says:

    Hello,

    I implemented the Persistent banning of IPs
    and it works fine but it has an issue.

    It duplicates the iptables rules when an offender ip is saved
    again in /etc/fail2ban/ip.blacklist by fail2ban

    Is there a way to fix it?

    Thanks and Best Regards,

    Lin

  11. Martin Zwigl says:

    Thanks for the great post.
    Do you have any idea how long this list can get without slowing the system too much down?
    Cheers

  12. Steph says:

    actionban = iptables -I fail2ban- 1 -s -j DROP
    # Persistent banning of IPs
    echo ‘ ‘ > > /etc/fail2ban/ip.blacklist

    So any banned address is banned permanently ??

    Thanks !

    • Looke says:

      Hi Steph,

      Yes, if you also set the parameter “bantime” in the jail options to a negative value (http://www.fail2ban.org/wiki/index.php/MANUAL_0_8#Jail_Options), the banned IP addresses will be blocked idefinately (even after a restart of the fail2ban service).

      Cheers,
      Looke

      • samuelE says:

        Hola, realice un cambio a las propuestas anteriores, probé y si agregaba permanentemente pero lo metia en todas las cadenas… con esta modificación lo ingresa en la que originalmente en la cadena que callo por primera vez.

        cat /etc/fail2ban/ip.blacklist | while read line; do IP=`echo $line | cut -d ” ” -f1` NAME=`echo $line | cut -d ” ” -f2`; iptables -I fail2ban-$NAME 1 -s $IP -j DROP; done

        Saludos espero podamos seguir merojando esto entre todos.

        • samuelE says:

          actionstart =…..
          cat /etc/fail2ban/ip.blacklist | while read line; do IP=`echo $line | cut -d ” ” -f1` NAME=`echo $line | cut -d ” ” -f2`; iptables -I fail2ban-$NAME 1 -s $IP -j DROP; done

          actionban = …………..
          echo ‘ ‘ >> /etc/fail2ban/ip.blacklist

          logico que se tiene que guardar el nombre y la cadena en el mismo archivo.

          • samuelE says:

            jajaja mismo problema que Dr. Tyrell ….

            actionban = …………..
            echo ‘ ‘ >> /etc/fail2ban/ip.blacklist

            ejemplo ip.blacklist

            10.1.2.3 Zimbra-audit
            10.2.2.3 Zimbra-account
            10.3.2.3 -Zimbra-recipient

            Saludos

  13. Ergec says:

    For future googlers, while echoing ip top the ip.blacklist, single quotes wrapped around ip didn’t work for me. I had to remove single quotes and just leave

  14. GrumpyAdmin says:

    Hello All,

    Great article.
    Does anyone have a clean current copy of this config?
    I copy & pasted and tried to clean up the above but can’t seem to get it to load the list and for that matter maintain the list.

    Thanks!

  15. Cyrille says:

    Thanks you very much for this really good article and the example conf file. Do you such configuration file for iptables.conf?

    Thanks in advance.

  16. Benny Helms says:

    I doubt this will help anyone, but if there are any anal retentive sorts out there like me, this little snippet of code will get all your banned IPs lined up in numerical order so they are sorted both in the ip.blacklist and in the output of `iptables -L -n’

    Note: most of this is from the author of this post. Mine is just the “sort” line and the three lines following:

    actionban = if ! iptables -C fail2ban- -s -j DROP
    then
    iptables -I fail2ban- 1 -s -j DROP
    fi
    # Add offenders to local blacklist, if not already there
    if ! grep -Fxq ‘,’ /etc/fail2ban/ip.blacklist
    then
    echo ‘,’ >> /etc/fail2ban/ip.blacklist
    sort -n -r -t . -k 1,1 -k 2,2 -k 3,3 -k 4,4 /etc/fail2ban/ip.blacklist > /etc/fail2ban/temp.txt \
    && mv /etc/fail2ban/temp.txt /etc/fail2ban/ip.blacklist \
    && chmod 640 /etc/fail2ban/ip.blacklist \
    && chown root.root /etc/fail2ban/ip.blacklist

    fi
    # Report offenders to badips.com
    wget -q -O /dev/null http://www.badips.com/add//

  17. Benny Helms says:

    Follow up note. The reason they are sorted in reverse in the ip.blacklist file is because of the way they are inserted into the iptables line-up. By putting them in reverse order in blacklist.ip, they will be in normal order when running ‘iptables -L -n’

  18. billy noah says:

    can you explain this please?
    # Disabled clearing out entry from ip.blacklist (somehow happens after each stop of fail2ban)
    # sed --in-place '/,/d' /etc/fail2ban/ip.blacklist

    I see it’s there but commented out. is it an ongoing issue?

  19. billy noah says:

    one more question – is the ail name which follows the comma mandatory for this to work? (e.g., “123.45.67.890,ssh”) I tried a couple ips on a line by themself as you mentioned at the top of the article but had no effect until I added “,ssh” after them. Is there a way to make this optional?

    Also, are comments allowed? Would be nice but I’m not sure if it would mess something up with the parsing of the blocklist file

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>