mirror of https://github.com/fail2ban/fail2ban
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
parent
1083788e70
commit
0386df0042
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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":
|
||||||
|
|
|
@ -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 ;) --
|
||||||
|
|
|
@ -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",
|
||||||
"",
|
"",
|
||||||
)
|
)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue