From 0386df0042d68f8b2ffb34643b7aed9342e23762 Mon Sep 17 00:00:00 2001 From: sebres Date: Thu, 18 Apr 2019 20:31:39 +0200 Subject: [PATCH] introduced new options: `dbmaxmatches` (fail2ban.conf) and `maxmatches` (jail.conf); setting `maxmatches` and `dbmaxmatches` to 0 saves memory usage and database size (closes gh-2118). --- config/fail2ban.conf | 6 ++++++ config/jail.conf | 3 +++ fail2ban/client/fail2banreader.py | 3 ++- fail2ban/client/jailreader.py | 1 + fail2ban/server/server.py | 6 ++++++ fail2ban/server/transmitter.py | 22 ++++++++++++++++++++++ fail2ban/tests/clientreadertestcase.py | 18 ++++++++++-------- fail2ban/tests/fail2banclienttestcase.py | 1 + fail2ban/tests/servertestcase.py | 6 ++++++ 9 files changed, 57 insertions(+), 9 deletions(-) diff --git a/config/fail2ban.conf b/config/fail2ban.conf index 08e8fcf3..cf1cf5ae 100644 --- a/config/fail2ban.conf +++ b/config/fail2ban.conf @@ -68,6 +68,12 @@ dbfile = /var/lib/fail2ban/fail2ban.sqlite3 # Values: [ SECONDS ] Default: 86400 (24hours) dbpurgeage = 1d +# Options: dbmaxmatches +# Notes.: Number of matches stored in database per ticket (resolvable via +# tags / in actions) +# Values: [ INT ] Default: 10 +dbmaxmatches = 10 + [Thread] # Options: stacksize diff --git a/config/jail.conf b/config/jail.conf index e61731c6..32b6647e 100644 --- a/config/jail.conf +++ b/config/jail.conf @@ -69,6 +69,9 @@ findtime = 10m # "maxretry" is the number of failures before a host get banned. maxretry = 5 +# "maxmatches" is the number of matches stored in ticket (resolvable via tag in actions). +maxmatches = %(maxretry)s + # "backend" specifies the backend used to get files modification. # Available options are "pyinotify", "gamin", "polling", "systemd" and "auto". # This option can be overridden in each jail as well. diff --git a/fail2ban/client/fail2banreader.py b/fail2ban/client/fail2banreader.py index 5760e0ba..3270b767 100644 --- a/fail2ban/client/fail2banreader.py +++ b/fail2ban/client/fail2banreader.py @@ -54,6 +54,7 @@ class Fail2banReader(ConfigReader): ["string", "logtarget", "STDERR"], ["string", "syslogsocket", "auto"], ["string", "dbfile", "/var/lib/fail2ban/fail2ban.sqlite3"], + ["int", "dbmaxmatches", None], ["string", "dbpurgeage", "1d"]] self.__opts = ConfigReader.getOptions(self, "Definition", opts) if updateMainOpt: @@ -73,7 +74,7 @@ class Fail2banReader(ConfigReader): # Also dbfile should be set before all other database options. # So adding order indices into items, to be stripped after sorting, upon return order = {"thread":0, "syslogsocket":11, "loglevel":12, "logtarget":13, - "dbfile":50, "dbpurgeage":51} + "dbfile":50, "dbmaxmatches":51, "dbpurgeage":51} stream = list() for opt in self.__opts: if opt in order: diff --git a/fail2ban/client/jailreader.py b/fail2ban/client/jailreader.py index 4f9d08d3..ba76e706 100644 --- a/fail2ban/client/jailreader.py +++ b/fail2ban/client/jailreader.py @@ -93,6 +93,7 @@ class JailReader(ConfigReader): opts = [["bool", "enabled", False], ["string", "backend", "auto"], ["int", "maxretry", None], + ["int", "maxmatches", None], ["string", "findtime", None], ["string", "bantime", None], ["string", "usedns", None], # be sure usedns is before all regex(s) in stream diff --git a/fail2ban/server/server.py b/fail2ban/server/server.py index 33edf315..35f966e8 100644 --- a/fail2ban/server/server.py +++ b/fail2ban/server/server.py @@ -443,6 +443,12 @@ class Server: def getUseDns(self, name): return self.__jails[name].filter.getUseDns() + def setMaxMatches(self, name, value): + self.__jails[name].filter.failManager.maxMatches = value + + def getMaxMatches(self, name): + return self.__jails[name].filter.failManager.maxMatches + def setMaxRetry(self, name, value): self.__jails[name].filter.setMaxRetry(value) diff --git a/fail2ban/server/transmitter.py b/fail2ban/server/transmitter.py index 9d1ddbc9..0faecb85 100644 --- a/fail2ban/server/transmitter.py +++ b/fail2ban/server/transmitter.py @@ -183,6 +183,15 @@ class Transmitter: else: if self.__quiet: return return db.filename + elif name == "dbmaxmatches": + db = self.__server.getDatabase() + if db is None: + logSys.msg("dbmaxmatches setting was not in effect since no db yet") + return None + else: + db.maxMatches = int(command[1]) + if self.__quiet: return + return db.maxMatches elif name == "dbpurgeage": db = self.__server.getDatabase() if db is None: @@ -310,6 +319,11 @@ class Transmitter: self.__server.setLogTimeZone(name, value) if self.__quiet: return return self.__server.getLogTimeZone(name) + elif command[1] == "maxmatches": + value = command[2] + self.__server.setMaxMatches(name, int(value)) + if self.__quiet: return + return self.__server.getMaxMatches(name) elif command[1] == "maxretry": value = command[2] self.__server.setMaxRetry(name, int(value)) @@ -398,6 +412,12 @@ class Transmitter: return None else: return db.filename + elif name == "dbmaxmatches": + db = self.__server.getDatabase() + if db is None: + return None + else: + return db.maxMatches elif name == "dbpurgeage": db = self.__server.getDatabase() if db is None: @@ -433,6 +453,8 @@ class Transmitter: return self.__server.getDatePattern(name) elif command[1] == "logtimezone": return self.__server.getLogTimeZone(name) + elif command[1] == "maxmatches": + return self.__server.getMaxMatches(name) elif command[1] == "maxretry": return self.__server.getMaxRetry(name) elif command[1] == "maxlines": diff --git a/fail2ban/tests/clientreadertestcase.py b/fail2ban/tests/clientreadertestcase.py index 29629a53..8320370b 100644 --- a/fail2ban/tests/clientreadertestcase.py +++ b/fail2ban/tests/clientreadertestcase.py @@ -881,18 +881,20 @@ class JailsReaderTest(LogCaptureTestCase): self.assertTrue( find_set('syslogsocket') < find_set('loglevel') < find_set('logtarget') ) - # then dbfile should be before dbpurgeage + # then dbfile should be before dbmaxmatches and dbpurgeage self.assertTrue(find_set('dbpurgeage') > find_set('dbfile')) + self.assertTrue(find_set('dbmaxmatches') > find_set('dbfile')) # and there is logging information left to be passed into the # server - self.assertSortedEqual(commands, - [['set', 'dbfile', - '/var/lib/fail2ban/fail2ban.sqlite3'], - ['set', 'dbpurgeage', '1d'], - ['set', 'loglevel', "INFO"], - ['set', 'logtarget', '/var/log/fail2ban.log'], - ['set', 'syslogsocket', 'auto']]) + self.assertSortedEqual(commands,[ + ['set', 'syslogsocket', 'auto'], + ['set', 'loglevel', "INFO"], + ['set', 'logtarget', '/var/log/fail2ban.log'], + ['set', 'dbfile', '/var/lib/fail2ban/fail2ban.sqlite3'], + ['set', 'dbmaxmatches', 10], + ['set', 'dbpurgeage', '1d'], + ]) # and if we force change configurator's fail2ban's baseDir # there should be an error message (test visually ;) -- diff --git a/fail2ban/tests/fail2banclienttestcase.py b/fail2ban/tests/fail2banclienttestcase.py index c532143e..12b092a8 100644 --- a/fail2ban/tests/fail2banclienttestcase.py +++ b/fail2ban/tests/fail2banclienttestcase.py @@ -179,6 +179,7 @@ def _start_params(tmp, use_stock=False, use_stock_cfg=None, "pidfile = " + pjoin(tmp, "f2b.pid"), "backend = polling", "dbfile = " + db, + "dbmaxmatches = 100", "dbpurgeage = 1d", "", ) diff --git a/fail2ban/tests/servertestcase.py b/fail2ban/tests/servertestcase.py index aba5dd7f..b44674f2 100644 --- a/fail2ban/tests/servertestcase.py +++ b/fail2ban/tests/servertestcase.py @@ -374,6 +374,12 @@ class Transmitter(TransmitterBase): self.assertLogged("Ban 192.0.2.2", wait=True) self.assertNotLogged("Ban 192.0.2.1") + def testJailMaxMatches(self): + self.setGetTest("maxmatches", "5", 5, jail=self.jailName) + self.setGetTest("maxmatches", "2", 2, jail=self.jailName) + self.setGetTest("maxmatches", "-2", -2, jail=self.jailName) + self.setGetTestNOK("maxmatches", "Duck", jail=self.jailName) + def testJailMaxRetry(self): self.setGetTest("maxretry", "5", 5, jail=self.jailName) self.setGetTest("maxretry", "2", 2, jail=self.jailName)