From b5714f774761925c10e8a2ca54e27a3f7d52c41e Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Wed, 16 Sep 2026 11:46:57 +0200 Subject: [PATCH] fix SMTP notifications overwriting the ticket's ban duration Use the ban duration supplied by ActionInfo instead of replacing it with the jail default. ActionInfo already falls back to the jail duration when the ticket has no explicit ban time. Add regression coverage for increased and permanent ticket durations and the default-duration fallback. --- ChangeLog | 1 + config/action.d/smtp.py | 1 - fail2ban/tests/action_d/test_smtp.py | 14 ++++++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 32f2dec0..f2982a29 100644 --- a/ChangeLog +++ b/ChangeLog @@ -11,6 +11,7 @@ ver. 1.1.2-dev-1 (20??/??/??) - development nightly edition ----------- ### Fixes +* `action.d/smtp.py` - preserve the ticket's ban time in notification emails instead of replacing it with the jail's default * `filter.d/nginx-botsearch.conf` - error-log regex extended with support of journal format (gh-3732) * `filter.d/apache-noscript.conf` - match a request for the `cgi-bin` directory itself (no script name after it), the `cgi-bin` alternative required a following path segment so `/cgi-bin/` and `/cgi-bin` were not matched (gh-4217) diff --git a/config/action.d/smtp.py b/config/action.d/smtp.py index 69e337b6..93561cbc 100644 --- a/config/action.d/smtp.py +++ b/config/action.d/smtp.py @@ -126,7 +126,6 @@ class SMTPAction(ActionBase): self.message_values = CallingMap( jailname = self._jail.name, hostname = socket.gethostname, - bantime = lambda: self._jail.actions.getBanTime(), ) # bypass ban/unban for restored tickets diff --git a/fail2ban/tests/action_d/test_smtp.py b/fail2ban/tests/action_d/test_smtp.py index 6e4b926d..18f77cbb 100644 --- a/fail2ban/tests/action_d/test_smtp.py +++ b/fail2ban/tests/action_d/test_smtp.py @@ -27,6 +27,8 @@ import importlib from ..dummyjail import DummyJail from ..utils import CONFIG_DIR, asyncserver, Utils, uni_decode +from ...server.actions import Actions +from ...server.ticket import BanTicket class _SMTPActionTestCase(): @@ -63,6 +65,7 @@ class _SMTPActionTestCase(): aInfo = { 'ip': "127.0.0.2", 'failures': 3, + 'bantime': self.jail.actions.getBanTime(), 'matches': "Test fail 1\n", 'ipjailmatches': "Test fail 1\nTest Fail2\n", 'ipmatches': "Test fail 1\nTest Fail2\nTest Fail3\n", @@ -97,6 +100,17 @@ class _SMTPActionTestCase(): def testBan(self): self._testBan() + def testBanTime(self): + self.jail.actions.setBanTime(600) + for bantime, expected in ((None, 600), (3600, 3600), (-1, -1)): + with self.subTest(bantime=bantime): + ticket = BanTicket('127.0.0.2') + ticket.setBanTime(bantime) + aInfo = Actions.ActionInfo(ticket, self.jail) + self._exec_and_wait(lambda: self.action.ban(aInfo)) + self.assertIn('banned for %i seconds' % expected, self.smtpd.data) + self.assertEqual(aInfo['bantime'], expected) + def testNOPByRestored(self): self._testBan(restored=True)