Configure Fail2Ban for permanent and persistent bans

NB: This article is not about how Fail2Ban works or how to install it.

If you’re running an Internet facing server, you probably know its exposed services are constantly being probed and attacks are being attempted against it. Fortunately, an extremely useful, nice and nifty tool is here to help: Fail2Ban.

Fail2Ban scans service’s log files for patterns defined as regular expressions and, if an offending pattern is found a certain number of times within a given timeframe, the corresponding source IP is banned (ie: blocked) for a configurable time, using local firewall rules such as iptables.

I’m very touchy when it comes to my server security so I’m using Fail2Ban to perform permanent bans of involved source IPs and I’m going to show you how. The problem however is that those bans do not persist across a Fail2ban server restart or a server reboot.

In this article I will show you how to add two simple lines in Fail2Ban configuration file in order to add persistency across restart.

In a typical installation, Fail2ban configuration files are stored in the /etc/fail2ban/ directory. There’s only two files that needs slight modifications:

  • jail.conf : it’s the main file defining default options and behavior for so called jails, that is for each service monitored, the definition of log file, detection patterns (filters), actions, timers (findtime, max retry, bantime).
  • action.d/iptables-multiport.conf : iptables-multiport is the default action performed by Fail2Ban when an IP is to be banned (or jailed), as defined in the jail.conf configuration file. If you’ve changed the default action, then you’ll have to modify the corresponding action file accordingly.

Configure permanent bans


This is the easiest part. Ban time can be set either globally (ie: for all jails), or per jail. It is controlled through the ‘bantime‘ parameter which defines the number of seconds an IP is banned.

To set a permanent ban, simply set the bantime parameter to a value of -1. Edit the jail.conf file, comment out the existing ‘bantime’ line, and set a new bantime to -1 :

# "bantime" is the number of seconds that a host is banned.
# bantime  = 600

# Permanent ban
bantime = -1 

Configure persistent bans


In order for bans to persist across a service restart, they obviously have to be saved somewhere. No fancy database required, a simple text file will do the trick.

The principle is simple: every time Fail2Ban sets a new ban on an IP, we’ll save the information « jail name and IP address » in a file along the way. Next, upon each Fail2Ban service start, we’ll load this file a re-create the corresponding bans. All it takes is two lines in the right configuration file.

Each ban action is defined in a corresponding configuration file. Within this file, there’s two parameters we’re interested in:

  1. actionstart : here we can define a list of commands that will be executed only once at the start of Fail2Ban. So we’ll add a custom command loading the file /etc/fail2ban/persistent.bans and re-create the corresponding iptables entries.
  2. actionban : here we can defined a list of commands that will be executed when banning an IP. So we’ll add a custom command to save the useful information to the file /etc/fail2ban/persistent.bans.

The default action in Fail2Ban is iptables-multiport (as defined in the file jail.conf), so we have to edit the action.d/iptables-multiport.conf file and add the following highlighted lines:

[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 <chain> -p <protocol> -m multiport --dports <port> -j fail2ban-<name>
	      cat /etc/fail2ban/persistent.bans | awk '/^fail2ban-<name>/ {print $2}' \
	      | while read IP; do iptables -I fail2ban-<name> 1 -s $IP -j <blocktype>; done

# Option:  actionstop
# Notes.:  command executed once at the end of Fail2Ban
# Values:  CMD
#
actionstop = iptables -D <chain> -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 <chain> | grep -q 'fail2ban-<name>[ \t]'

# Option:  actionban
# Notes.:  command executed when banning an IP. Take care that the
#          command is executed with Fail2Ban user rights.
# Tags:    See jail.conf(5) man page
# Values:  CMD
#
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j <blocktype>
	    echo "fail2ban-<name> <ip>" >> /etc/fail2ban/persistent.bans

Once done, it is required to restart Fail2Ban in order for those change to be applied.

arno@myserver:/etc/fail2ban/action.d $ sudo service fail2ban restart

And that’s it !

Like this article ? Tip me with some bitcoins !

18 réflexions sur “Configure Fail2Ban for permanent and persistent bans

  1. Catwhisperer 20 octobre 2021 / 1 h 08 min

    Unbanning must also be addressed, because if not you get duplicate entries every time it is restarted.

    # Option: actionunban
    # Notes.: command executed when unbanning an IP. Take care that the
    # command is executed with Fail2Ban user rights.
    # Tags: See jail.conf(5) man page
    # Values: CMD
    #
    actionunban = -D f2b- -s -j
    sed -i ‘/fail2ban- /d’ /etc/fail2ban/persistent.bans

    J’aime

  2. JM Lapointe 30 octobre 2018 / 19 h 55 min

    You could also do :

    actionstart =
    cat /etc/fail2ban/bans.d/*.ban | awk ‘/^fail2ban-/ {print $2}’ \
    | while read IP; do iptables -I fail2ban- 1 -s $IP -j ; done

    actionban =
    echo « fail2ban-  » > /etc/fail2ban/bans.d/fail2ban–.ban

    actionunban =
    rm /etc/fail2ban/bans.d/fail2ban–.ban

    It gives more flexibility.

    Cheers for the good work!

    J’aime

  3. JustMe 26 juin 2017 / 23 h 20 min

    Perfect! Thank you for the detailed explanation. I’m using f2b in a VPN server and hate to restart without keeping the bans.

    J’aime

  4. Pau 17 Mai 2017 / 16 h 29 min

    Thank you!!! good job!!

    J’aime

  5. John Bras 6 avril 2017 / 21 h 59 min

    Pardon Monsieur. it is saving duplicate IP on list each time you restart. On line 34 I believe you need to check if IP is in persistent.bans files before you include it with a echo.

    Aimé par 2 personnes

    • Atlas Amfistomos 12 avril 2017 / 11 h 53 min

      Yup, simply replace that line with:

      if ! grep -q « fail2ban-  » /etc/fail2ban/persistent.bans; then echo « fail2ban-  » >> /etc/fail2ban/persistent.bans; fi

      Aimé par 1 personne

      • Atlas Amfistomos 12 avril 2017 / 11 h 53 min

        Argh…. and double quotes are now parsed as ‘ »‘ in the response

        J’aime

      • JJNew 11 Mai 2017 / 10 h 00 min

        Hi Atlas, great tips 😉
        I did not understand where and how to replace the condition to avoid duplicates on both persistence.bans and iptables rules. Could you indicate in a clearer way? Maybe adding the two options to the post, with the code format (to avoid the double quotes problem you’ve got in your comments).
        thank you very much.

        Aimé par 1 personne

      • Kythera 19 septembre 2018 / 20 h 24 min

        Anyone figure it out? I replace line 34 with the example Atlas provides and it fails to work at all.

        J’aime

      • andydegroo 20 septembre 2018 / 5 h 22 min

        I’ll try to use markdown formatting for the correct line:

        « `
        if ! grep -q « fail2man-  » /etc/fail2ban/persistent.bans; then « echo « fail2ban-  » >> /etc/fail2ban/persistent.bans; fi
        « `

        …and hope it works.
        This comment form on WordPress.com has become so « clear » that there are no indications of what is allowed.
        Also the UI here is in French and I understand it just partially.

        Aimé par 1 personne

  6. Chad Reitsma 5 janvier 2017 / 3 h 27 min

    As of version 0.9.0 (2014/03/14) fail2ban includes a persistent database built-in. Although documentation is non-existent at the time I am writing this. It appears to be working on my server. The default database location is /var/lib/fail2ban/fail2ban.sqlite3

    Aimé par 2 personnes

  7. Suyash Jain 23 juin 2016 / 8 h 26 min

    in my case -1 and any value above 436999 throws the following error: Starting fail2ban: ERROR NOK: (‘database disk image is malformed’,)

    J’aime

Répondre à Catwhisperer Annuler la réponse.