From 01d02ca5e69d3f16e8740f08ada09d736d1a13d1 Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Sat, 19 Jul 2014 15:17:32 +0100 Subject: [PATCH] BF: Remove manually unbanned IPs from persistent database Stops them being restored when Fail2Ban is restarted. Particularly this is an issue with bantime < 0 Fixes gh-768 --- ChangeLog | 2 ++ fail2ban/server/actions.py | 2 ++ fail2ban/server/database.py | 17 ++++++++++++++++- fail2ban/tests/databasetestcase.py | 6 ++++++ 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 60ec9cbc..9ae7b8c3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -39,6 +39,8 @@ ver. 0.9.1 (2014/xx/xx) - better, faster, stronger * Per-distribution paths to the exim's main log * Ignored IPs are no longer banned when being restored from persistent database + * Manually unbanned IPs are now removed from persistent database, such they + wont be banned again when Fail2Ban is restarted - New features: - Added monit filter thanks Jason H Martin. diff --git a/fail2ban/server/actions.py b/fail2ban/server/actions.py index fa0e94df..c8e9c5d9 100644 --- a/fail2ban/server/actions.py +++ b/fail2ban/server/actions.py @@ -197,6 +197,8 @@ class Actions(JailThread, Mapping): if ticket is not None: # Unban the IP. self.__unBan(ticket) + if self._jail.database is not None: + self._jail.database.delBan(self._jail, ticket) else: raise ValueError("IP %s is not banned" % ip) diff --git a/fail2ban/server/database.py b/fail2ban/server/database.py index 47f1a485..351d1829 100644 --- a/fail2ban/server/database.py +++ b/fail2ban/server/database.py @@ -368,10 +368,25 @@ class Fail2BanDb(object): #TODO: Implement data parts once arbitrary match keys completed cur.execute( "INSERT INTO bans(jail, ip, timeofban, data) VALUES(?, ?, ?, ?)", - (jail.name, ticket.getIP(), round(ticket.getTime()), + (jail.name, ticket.getIP(), int(round(ticket.getTime())), {"matches": ticket.getMatches(), "failures": ticket.getAttempt()})) + @commitandrollback + def delBan(self, cur, jail, ticket): + """Delete a ban from the database. + + Parameters + ---------- + jail : Jail + Jail in which the ban has occurred. + ticket : BanTicket + Ticket of the ban to be removed. + """ + cur.execute( + "DELETE FROM bans WHERE jail = ? AND ip = ? AND timeofban = ?", + (jail.name, ticket.getIP(), int(round(ticket.getTime())))) + @commitandrollback def _getBans(self, cur, jail=None, bantime=None, ip=None): query = "SELECT ip, timeofban, data FROM bans WHERE 1" diff --git a/fail2ban/tests/databasetestcase.py b/fail2ban/tests/databasetestcase.py index 2cf8577e..f0757e5b 100644 --- a/fail2ban/tests/databasetestcase.py +++ b/fail2ban/tests/databasetestcase.py @@ -173,6 +173,12 @@ class DatabaseTest(unittest.TestCase): self.assertTrue( isinstance(self.db.getBans(jail=self.jail)[0], FailTicket)) + def testDelBan(self): + self.testAddBan() + ticket = self.db.getBans(jail=self.jail)[0] + self.db.delBan(self.jail, ticket) + self.assertEqual(len(self.db.getBans(jail=self.jail)), 0) + def testGetBansWithTime(self): if Fail2BanDb is None: # pragma: no cover return