#4101·fail2ban

[BR]: Explicit Ban-Identifier breaks Restore after Server-Restart

Author: m-hauCreated Oct 31, 2025Updated Aug 9, 2026
Labelsbug

Environment:

  • Fail2Ban version : 1.0.2-2 / 1.1.0-8 / master branch
  • OS, including release name/version : Debian 12.12 "bookworm" / Debian 13.1 "trixie" / Debian 13.1 "trixie"
  • Fail2Ban installed via OS/distribution mechanisms
    • Only the first two versions/tests. The third test against master branch was directly from source (git-clone).
  • You have not applied any additional foreign patches to the codebase
  • Some customizations were done to the configuration (provide details below is so)

The issue:

For a jail that explicitly specifies a Ban-Identifier regex capturing group using <F-ID>, restoring the bans after a restart of the fail2ban-server fails with an error.

Steps to reproduce

  1. Start fail2ban-server
  2. Trigger a ban (by writing the logline to the logfile)
  3. Stop the fail2ban-server
  4. Start the fail2ban-server again

Expected behavior

Upon starting the fail2ban-server again, the ban is properly restored from the database.

Observed behavior

An error occurs, preventing the ban-action from executing:

2025-10-31 12:43:25,921 fail2ban.actions        [27]: ERROR   Failed to execute ban jail 'my_jail' action 'dummy' info 'ActionInfo({'ip': '10.20.30.40', 'fid': <function Actions.ActionInfo.<lambda> at 0x7ff529cc0400>, 'raw-ticket': <function Actions.ActionInfo.<lambda> at 0x7ff529cc0b80>})': 'str' object has no attribute 'familyStr'
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/fail2ban/server/actions.py", line 513, in __checkBan
    action.ban(aInfo)
    ~~~~~~~~~~^^^^^^^
  File "/usr/lib/python3/dist-packages/fail2ban/server/action.py", line 562, in ban
    family = aInfo.get('family', '')
  File "<frozen _collections_abc>", line 811, in get
  File "/usr/lib/python3/dist-packages/fail2ban/server/action.py", line 146, in __getitem__
    value = value(self) if hasattr(value, '__code__') and value.__code__.co_argcount else value()
            ~~~~~^^^^^^
  File "/usr/lib/python3/dist-packages/fail2ban/server/actions.py", line 376, in <lambda>
    "family":   lambda self: self['ip'].familyStr,
                             ^^^^^^^^^^^^^^^^^^^^
AttributeError: 'str' object has no attribute 'familyStr'

Any additional information

I did a bit of debugging myself. The cause appears to be the type of the ip data in the ticket is different between the initial ban-creation and the later restored-from-database. This can be seen when inspecting the ticket data in Actions.__checkBan(...) (fail2ban/server/actions.py line 495):

# Create ban
ticket.getID()  type='fail2ban.server.ipdns.IPAddr'  value='10.20.30.40:50'
ticket.getIP()  type='fail2ban.server.ipdns.IPAddr'  value='10.20.30.40'
# Restore ban
ticket.getID()  type='fail2ban.server.ipdns.IPAddr'  value='10.20.30.40:50'
ticket.getIP()  type='str'                           value='10.20.30.40'

Since Ticket.getIP() is apparently expected to always return a IPAddr, the following change fixes the problem. But I can't assess how "correct" this is in the grand scheme of things, which is why I created a bug report instead of a PR.

diff
--- a/fail2ban/server/ticket.py
+++ b/fail2ban/server/ticket.py
@@ -25,7 +25,7 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
 __license__ = "GPL"
 
 from ..helpers import getLogger
-from .ipdns import IPAddr
+from .ipdns import IPAddr, asip
 from .mytime import MyTime
 
 # Gets the instance of the logger.
@@ -96,7 +96,7 @@ class Ticket(object):
                return self._id
        
        def getIP(self):
-               return self._data.get('ip', self._id)
+               return asip(self._data.get('ip', self._id))
        
        def setTime(self, value):
                self._time = value

Configuration, dump and another helpful excerpts

Any customizations done to /etc/fail2ban/ configuration

fail2ban.local

[Definition]
loglevel = 4
logtarget = stdout
allowipv6 = no

jail.d/my_jail.conf

[my_jail]
enabled = yes

filter = my_filter
logpath = /var/log/my_logfile
failregex = <F-ID><HOST>:<F-PORT/></F-ID> you did it wrong
maxretry = 1
bantime = 1d
banaction = dummy

filter.d/my_filter.conf

[Definition]
# empty

Relevant parts of /var/log/fail2ban.log file:

2025-10-31 12:43:25,713 fail2ban.jail           [27]: DEBUG   Starting jail 'my_jail'
2025-10-31 12:43:25,713 fail2ban.filterpyinotify[27]: DEBUG   [my_jail] filter started (pyinotifier)
2025-10-31 12:43:25,713 fail2ban.filterpyinotify[27]: MSG     Log presence detected for file /var/log/my_logfile
2025-10-31 12:43:25,713 fail2ban.action         [27]: #09-Lev. if [ ! -z '/var/run/fail2ban/fail2ban.dummy' ]; then touch /var/run/fail2ban/fail2ban.dummy; fi;
printf %b "123\n" >> /var/run/fail2ban/fail2ban.dummy
echo "[my_jail] dummy /var/run/fail2ban/fail2ban.dummy -- started"
2025-10-31 12:43:25,714 fail2ban.filter         [27]: DEBUG   Seek to find time 1761914005.7141564 (2025-10-31 12:33:25), file size 58
2025-10-31 12:43:25,714 fail2ban.filter         [27]: DEBUG   Position -1 from 58, found time None () within 0 seeks
2025-10-31 12:43:25,719 fail2ban.utils          [27]: #09-Lev. 7ff52a6cbee0 -- exec: if [ ! -z '/var/run/fail2ban/fail2ban.dummy' ]; then touch /var/run/fail2ban/fail2ban.dummy; fi;
printf %b "123\n" >> /var/run/fail2ban/fail2ban.dummy
echo "[my_jail] dummy /var/run/fail2ban/fail2ban.dummy -- started"
2025-10-31 12:43:25,719 fail2ban.utils          [27]: DEBUG   7ff52a6cbee0 -- stdout: '[my_jail] dummy /var/run/fail2ban/fail2ban.dummy -- started'
2025-10-31 12:43:25,720 fail2ban.utils          [27]: DEBUG   7ff52a6cbee0 -- returned successfully 0
2025-10-31 12:43:25,721 fail2ban.actions        [27]: HEAVY   Actions: wait for pending tickets 2 (default 2)
2025-10-31 12:43:25,721 fail2ban.jail           [27]: INFO    Jail 'my_jail' started
2025-10-31 12:43:25,721 fail2ban                [27]: HEAVY   OK : None
2025-10-31 12:43:25,721 fail2ban.transmitter    [27]: HEAVY   Command: ['server-status']
2025-10-31 12:43:25,721 fail2ban.transmitter    [27]: DEBUG   Status: ready
2025-10-31 12:43:25,721 fail2ban                [27]: HEAVY   OK : 'Server ready'
2025-10-31 12:43:25,721 fail2ban.beautifier     [27]: HEAVY   Beautify 'Server ready' with ['server-status']
Server ready
2025-10-31 12:43:25,921 fail2ban.actions        [27]: NOTICE  [my_jail] Restore Ban 10.20.30.40:50
2025-10-31 12:43:25,921 fail2ban.actions        [27]: ERROR   Failed to execute ban jail 'my_jail' action 'dummy' info 'ActionInfo({'ip': '10.20.30.40', 'fid': <function Actions.ActionInfo.<lambda> at 0x7ff529cc0400>, 'raw-ticket': <function Actions.ActionInfo.<lambda> at 0x7ff529cc0b80>})': 'str' object has no attribute 'familyStr'
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/fail2ban/server/actions.py", line 513, in __checkBan
    action.ban(aInfo)
    ~~~~~~~~~~^^^^^^^
  File "/usr/lib/python3/dist-packages/fail2ban/server/action.py", line 562, in ban
    family = aInfo.get('family', '')
  File "<frozen _collections_abc>", line 811, in get
  File "/usr/lib/python3/dist-packages/fail2ban/server/action.py", line 146, in __getitem__
    value = value(self) if hasattr(value, '__code__') and value.__code__.co_argcount else value()
            ~~~~~^^^^^^
  File "/usr/lib/python3/dist-packages/fail2ban/server/actions.py", line 376, in <lambda>
    "family":   lambda self: self['ip'].familyStr,
                             ^^^^^^^^^^^^^^^^^^^^
AttributeError: 'str' object has no attribute 'familyStr'
2025-10-31 12:43:25,933 fail2ban.actions        [27]: DEBUG   Banned 1 / 1, 1 ticket(s) in 'my_jail'
2025-10-31 12:43:25,933 fail2ban.actions        [27]: HEAVY   Actions: wait for pending tickets 2 (default 2)

Relevant lines from monitored log files:

2025-10-31T12:43:23+00:00 10.20.30.40:50 you did it wrong