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).
pull/2402/head
sebres 2019-04-18 20:31:39 +02:00
parent 1083788e70
commit 0386df0042
9 changed files with 57 additions and 9 deletions

View File

@ -68,6 +68,12 @@ dbfile = /var/lib/fail2ban/fail2ban.sqlite3
# Values: [ SECONDS ] Default: 86400 (24hours) # Values: [ SECONDS ] Default: 86400 (24hours)
dbpurgeage = 1d dbpurgeage = 1d
# Options: dbmaxmatches
# Notes.: Number of matches stored in database per ticket (resolvable via
# tags <ipmatches>/<ipjailmatches> in actions)
# Values: [ INT ] Default: 10
dbmaxmatches = 10
[Thread] [Thread]
# Options: stacksize # Options: stacksize

View File

@ -69,6 +69,9 @@ findtime = 10m
# "maxretry" is the number of failures before a host get banned. # "maxretry" is the number of failures before a host get banned.
maxretry = 5 maxretry = 5
# "maxmatches" is the number of matches stored in ticket (resolvable via tag <matches> in actions).
maxmatches = %(maxretry)s
# "backend" specifies the backend used to get files modification. # "backend" specifies the backend used to get files modification.
# Available options are "pyinotify", "gamin", "polling", "systemd" and "auto". # Available options are "pyinotify", "gamin", "polling", "systemd" and "auto".
# This option can be overridden in each jail as well. # This option can be overridden in each jail as well.

View File

@ -54,6 +54,7 @@ class Fail2banReader(ConfigReader):
["string", "logtarget", "STDERR"], ["string", "logtarget", "STDERR"],
["string", "syslogsocket", "auto"], ["string", "syslogsocket", "auto"],
["string", "dbfile", "/var/lib/fail2ban/fail2ban.sqlite3"], ["string", "dbfile", "/var/lib/fail2ban/fail2ban.sqlite3"],
["int", "dbmaxmatches", None],
["string", "dbpurgeage", "1d"]] ["string", "dbpurgeage", "1d"]]
self.__opts = ConfigReader.getOptions(self, "Definition", opts) self.__opts = ConfigReader.getOptions(self, "Definition", opts)
if updateMainOpt: if updateMainOpt:
@ -73,7 +74,7 @@ class Fail2banReader(ConfigReader):
# Also dbfile should be set before all other database options. # Also dbfile should be set before all other database options.
# So adding order indices into items, to be stripped after sorting, upon return # So adding order indices into items, to be stripped after sorting, upon return
order = {"thread":0, "syslogsocket":11, "loglevel":12, "logtarget":13, order = {"thread":0, "syslogsocket":11, "loglevel":12, "logtarget":13,
"dbfile":50, "dbpurgeage":51} "dbfile":50, "dbmaxmatches":51, "dbpurgeage":51}
stream = list() stream = list()
for opt in self.__opts: for opt in self.__opts:
if opt in order: if opt in order:

View File

@ -93,6 +93,7 @@ class JailReader(ConfigReader):
opts = [["bool", "enabled", False], opts = [["bool", "enabled", False],
["string", "backend", "auto"], ["string", "backend", "auto"],
["int", "maxretry", None], ["int", "maxretry", None],
["int", "maxmatches", None],
["string", "findtime", None], ["string", "findtime", None],
["string", "bantime", None], ["string", "bantime", None],
["string", "usedns", None], # be sure usedns is before all regex(s) in stream ["string", "usedns", None], # be sure usedns is before all regex(s) in stream

View File

@ -443,6 +443,12 @@ class Server:
def getUseDns(self, name): def getUseDns(self, name):
return self.__jails[name].filter.getUseDns() 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): def setMaxRetry(self, name, value):
self.__jails[name].filter.setMaxRetry(value) self.__jails[name].filter.setMaxRetry(value)

View File

@ -183,6 +183,15 @@ class Transmitter:
else: else:
if self.__quiet: return if self.__quiet: return
return db.filename 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": elif name == "dbpurgeage":
db = self.__server.getDatabase() db = self.__server.getDatabase()
if db is None: if db is None:
@ -310,6 +319,11 @@ class Transmitter:
self.__server.setLogTimeZone(name, value) self.__server.setLogTimeZone(name, value)
if self.__quiet: return if self.__quiet: return
return self.__server.getLogTimeZone(name) 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": elif command[1] == "maxretry":
value = command[2] value = command[2]
self.__server.setMaxRetry(name, int(value)) self.__server.setMaxRetry(name, int(value))
@ -398,6 +412,12 @@ class Transmitter:
return None return None
else: else:
return db.filename return db.filename
elif name == "dbmaxmatches":
db = self.__server.getDatabase()
if db is None:
return None
else:
return db.maxMatches
elif name == "dbpurgeage": elif name == "dbpurgeage":
db = self.__server.getDatabase() db = self.__server.getDatabase()
if db is None: if db is None:
@ -433,6 +453,8 @@ class Transmitter:
return self.__server.getDatePattern(name) return self.__server.getDatePattern(name)
elif command[1] == "logtimezone": elif command[1] == "logtimezone":
return self.__server.getLogTimeZone(name) return self.__server.getLogTimeZone(name)
elif command[1] == "maxmatches":
return self.__server.getMaxMatches(name)
elif command[1] == "maxretry": elif command[1] == "maxretry":
return self.__server.getMaxRetry(name) return self.__server.getMaxRetry(name)
elif command[1] == "maxlines": elif command[1] == "maxlines":

View File

@ -881,18 +881,20 @@ class JailsReaderTest(LogCaptureTestCase):
self.assertTrue( self.assertTrue(
find_set('syslogsocket') < find_set('loglevel') < find_set('logtarget') 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('dbpurgeage') > find_set('dbfile'))
self.assertTrue(find_set('dbmaxmatches') > find_set('dbfile'))
# and there is logging information left to be passed into the # and there is logging information left to be passed into the
# server # server
self.assertSortedEqual(commands, self.assertSortedEqual(commands,[
[['set', 'dbfile', ['set', 'syslogsocket', 'auto'],
'/var/lib/fail2ban/fail2ban.sqlite3'],
['set', 'dbpurgeage', '1d'],
['set', 'loglevel', "INFO"], ['set', 'loglevel', "INFO"],
['set', 'logtarget', '/var/log/fail2ban.log'], ['set', 'logtarget', '/var/log/fail2ban.log'],
['set', 'syslogsocket', 'auto']]) ['set', 'dbfile', '/var/lib/fail2ban/fail2ban.sqlite3'],
['set', 'dbmaxmatches', 10],
['set', 'dbpurgeage', '1d'],
])
# and if we force change configurator's fail2ban's baseDir # and if we force change configurator's fail2ban's baseDir
# there should be an error message (test visually ;) -- # there should be an error message (test visually ;) --

View File

@ -179,6 +179,7 @@ def _start_params(tmp, use_stock=False, use_stock_cfg=None,
"pidfile = " + pjoin(tmp, "f2b.pid"), "pidfile = " + pjoin(tmp, "f2b.pid"),
"backend = polling", "backend = polling",
"dbfile = " + db, "dbfile = " + db,
"dbmaxmatches = 100",
"dbpurgeage = 1d", "dbpurgeage = 1d",
"", "",
) )

View File

@ -374,6 +374,12 @@ class Transmitter(TransmitterBase):
self.assertLogged("Ban 192.0.2.2", wait=True) self.assertLogged("Ban 192.0.2.2", wait=True)
self.assertNotLogged("Ban 192.0.2.1") 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): def testJailMaxRetry(self):
self.setGetTest("maxretry", "5", 5, jail=self.jailName) self.setGetTest("maxretry", "5", 5, jail=self.jailName)
self.setGetTest("maxretry", "2", 2, jail=self.jailName) self.setGetTest("maxretry", "2", 2, jail=self.jailName)