mirror of https://github.com/fail2ban/fail2ban
option "multipliers" added, how proposed from @yarikoptic;
the calculate formula is rewritten to lambda / compiled solution (up to 10 million times per seconds); code review;pull/716/head
parent
ccacfc1047
commit
c48e404e63
|
@ -47,21 +47,38 @@ before = paths-debian.conf
|
||||||
# "bantimeextra.enabled" allows to use database for searching of previously banned ip's to increase a
|
# "bantimeextra.enabled" allows to use database for searching of previously banned ip's to increase a
|
||||||
# default ban time using special formula, default it is banTime * 1, 2, 4, 8, 16, 32...
|
# default ban time using special formula, default it is banTime * 1, 2, 4, 8, 16, 32...
|
||||||
#bantimeextra.enabled = true
|
#bantimeextra.enabled = true
|
||||||
|
|
||||||
# "bantimeextra.findtime" is the max number of seconds that we search in the database,
|
# "bantimeextra.findtime" is the max number of seconds that we search in the database,
|
||||||
# if it is not specified - whole database will be used for ban searching
|
# if it is not specified - whole database will be used for ban searching
|
||||||
# (please observe current "dbpurgeage" value of fail2ban.conf).
|
# (please observe current "dbpurgeage" value of fail2ban.conf).
|
||||||
#bantimeextra.findtime = 24*60*60
|
#bantimeextra.findtime = 24*60*60
|
||||||
|
|
||||||
# "bantimeextra.rndtime" is the max number of seconds using for mixing with random time
|
# "bantimeextra.rndtime" is the max number of seconds using for mixing with random time
|
||||||
# to prevent "clever" botnets calculate exact time IP can be unbanned again:
|
# to prevent "clever" botnets calculate exact time IP can be unbanned again:
|
||||||
#bantimeextra.rndtime = 5*60
|
#bantimeextra.rndtime = 5*60
|
||||||
|
|
||||||
# "bantimeextra.maxtime" is the max number of seconds using the ban time can reach (don't grows further)
|
# "bantimeextra.maxtime" is the max number of seconds using the ban time can reach (don't grows further)
|
||||||
#bantimeextra.maxtime = 24*60*60
|
#bantimeextra.maxtime = 24*60*60
|
||||||
|
|
||||||
# "bantimeextra.factor" is a coefficient to calculate exponent growing of the formula,
|
# "bantimeextra.factor" is a coefficient to calculate exponent growing of the formula,
|
||||||
# by default value of factor "2.0 / 2.885385" and default value of formula, the ban time
|
# for formula solution by default value of factor is "2.0 / 2.885385" and with default value of formula, the ban time
|
||||||
# grows by 1, 2, 4, 8, 16 ...
|
# grows by 1, 2, 4, 8, 16 ...
|
||||||
|
# for multipliers solution by default value of factor is "1" and the ban time grows by specified multipliers
|
||||||
|
# corresponding ban count only
|
||||||
#bantimeextra.factor = 2.0 / 2.885385
|
#bantimeextra.factor = 2.0 / 2.885385
|
||||||
|
|
||||||
# "bantimeextra.formula" used to calculate next value of ban time;
|
# "bantimeextra.formula" used to calculate next value of ban time;
|
||||||
#bantimeextra.formula = banTime * math.exp(float(banCount)*banFactor)/math.exp(1*banFactor)
|
#bantimeextra.formula = banTime * math.exp(float(banCount)*banFactor)/math.exp(1*banFactor)
|
||||||
|
|
||||||
|
# "bantimeextra.multipliers" used to calculate next value of ban time instead of formula, coresponding
|
||||||
|
# previously ban count and given "bantimeextra.factor" (for multipliers default is 1);
|
||||||
|
# following example grows ban time by 1, 2, 4, 8, 16 ... and if last ban count greater as multipliers count,
|
||||||
|
# always used last multiplier (64 in example), for factor '1' and original ban time 600 - 10.6 hours
|
||||||
|
#bantimeextra.multipliers = 1 2 4 8 16 32 64
|
||||||
|
# following example can be used for small initial ban time (bantime=60) - it grows more aggressive at begin,
|
||||||
|
# for bantime=60 the multipliers are minutes and equal: 1 min, 5 min, 30 min, 1 hour, 5 hour, 12 hour, 1 day, 2 day
|
||||||
|
#bantimeextra.multipliers = 1 5 30 60 300 720 1440 2880
|
||||||
|
|
||||||
# "bantimeextra.overalljails" (if true) specifies the search of IP in the database will be executed
|
# "bantimeextra.overalljails" (if true) specifies the search of IP in the database will be executed
|
||||||
# cross over all jails, if false (dafault), only current jail of the ban IP will be searched
|
# cross over all jails, if false (dafault), only current jail of the ban IP will be searched
|
||||||
#bantimeextra.overalljails = false
|
#bantimeextra.overalljails = false
|
||||||
|
|
|
@ -97,6 +97,7 @@ class JailReader(ConfigReader):
|
||||||
["string", "bantimeextra.findtime", None],
|
["string", "bantimeextra.findtime", None],
|
||||||
["string", "bantimeextra.factor", None],
|
["string", "bantimeextra.factor", None],
|
||||||
["string", "bantimeextra.formula", None],
|
["string", "bantimeextra.formula", None],
|
||||||
|
["string", "bantimeextra.multipliers", None],
|
||||||
["string", "bantimeextra.maxtime", None],
|
["string", "bantimeextra.maxtime", None],
|
||||||
["string", "bantimeextra.rndtime", None],
|
["string", "bantimeextra.rndtime", None],
|
||||||
["bool", "bantimeextra.overalljails", None],
|
["bool", "bantimeextra.overalljails", None],
|
||||||
|
|
|
@ -242,10 +242,18 @@ class Actions(JailThread, Mapping):
|
||||||
logSys.debug(self._jail.name + ": action terminated")
|
logSys.debug(self._jail.name + ": action terminated")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
class BanTimeIncr:
|
||||||
|
def __init__(self, banTime, banCount):
|
||||||
|
self.Time = banTime
|
||||||
|
self.Count = banCount
|
||||||
|
|
||||||
def setBanTimeExtra(self, opt, value):
|
def setBanTimeExtra(self, opt, value):
|
||||||
# merge previous extra with new option:
|
# merge previous extra with new option:
|
||||||
be = self._banExtra;
|
be = self._banExtra;
|
||||||
|
if value is not None:
|
||||||
be[opt] = value;
|
be[opt] = value;
|
||||||
|
else:
|
||||||
|
del be[opt]
|
||||||
logSys.info('Set banTimeExtra.%s = %s', opt, value)
|
logSys.info('Set banTimeExtra.%s = %s', opt, value)
|
||||||
if opt == 'enabled':
|
if opt == 'enabled':
|
||||||
if isinstance(value, str):
|
if isinstance(value, str):
|
||||||
|
@ -255,18 +263,31 @@ class Actions(JailThread, Mapping):
|
||||||
if opt in ['findtime', 'maxtime', 'rndtime']:
|
if opt in ['findtime', 'maxtime', 'rndtime']:
|
||||||
if not value is None:
|
if not value is None:
|
||||||
be[opt] = eval(value)
|
be[opt] = eval(value)
|
||||||
if opt == 'factor' or be.get('factor', None) is None:
|
# prepare formula lambda:
|
||||||
be['factor'] = eval(be.get('factor', "2.0 / 2.885385"));
|
if opt in ['formula', 'factor', 'maxtime', 'rndtime', 'multipliers'] or be.get('evformula', None) is None:
|
||||||
# prepare formula :
|
# split multifiers to an array begins with 0 (or empty if not set):
|
||||||
if opt in ['formula', 'maxtime', 'rndtime'] or be.get('evformula', None) is None:
|
if opt == 'multipliers':
|
||||||
be['formula'] = be.get('formula', 'banTime * math.exp(float(banCount)*banFactor)/math.exp(1*banFactor)')
|
be['evmultipliers'] = [int(i) for i in (value.split(' ') if value is not None and value != '' else [])]
|
||||||
evformula = be['formula'];
|
# if we have multifiers - use it in lambda, otherwise compile and use formula within lambda
|
||||||
evformula = ('max(banTime, %s)' % evformula)
|
multipliers = be.get('evmultipliers', [])
|
||||||
|
if len(multipliers):
|
||||||
|
banFactor = eval(be.get('factor', "1"))
|
||||||
|
evformula = lambda ban, banFactor=banFactor: (
|
||||||
|
ban.Time * banFactor * multipliers[ban.Count if ban.Count < len(multipliers) else -1]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
banFactor = eval(be.get('factor', "2.0 / 2.885385"))
|
||||||
|
formula = be.get('formula', 'ban.Time * math.exp(float(ban.Count+1)*banFactor)/math.exp(1*banFactor)')
|
||||||
|
formula = compile(formula, '~inline-conf-expr~', 'eval')
|
||||||
|
evformula = lambda ban, banFactor=banFactor, formula=formula: max(ban.Time, eval(formula))
|
||||||
|
# extend lambda with max time :
|
||||||
if not be.get('maxtime', None) is None:
|
if not be.get('maxtime', None) is None:
|
||||||
evformula = ('min(%s, %s)' % (evformula, be['maxtime']))
|
maxtime = be['maxtime']
|
||||||
# mix with random time (to prevent botnet calc exact time IP can be unbanned):
|
evformula = lambda ban, evformula=evformula: min(evformula(ban), maxtime)
|
||||||
|
# mix lambda with random time (to prevent bot-nets to calculate exact time IP can be unbanned):
|
||||||
if not be.get('rndtime', None) is None:
|
if not be.get('rndtime', None) is None:
|
||||||
evformula = ('(%s + random.random() * %s)' % (evformula, be['rndtime']))
|
rndtime = be['rndtime']
|
||||||
|
evformula = lambda ban, evformula=evformula: (evformula(ban) + random.random() * rndtime)
|
||||||
# set to extra dict:
|
# set to extra dict:
|
||||||
be['evformula'] = evformula
|
be['evformula'] = evformula
|
||||||
#logSys.info('banTimeExtra : %s' % json.dumps(be))
|
#logSys.info('banTimeExtra : %s' % json.dumps(be))
|
||||||
|
@ -274,6 +295,9 @@ class Actions(JailThread, Mapping):
|
||||||
def getBanTimeExtra(self, opt):
|
def getBanTimeExtra(self, opt):
|
||||||
return self._banExtra.get(opt, None)
|
return self._banExtra.get(opt, None)
|
||||||
|
|
||||||
|
def calcBanTime(self, banTime, banCount):
|
||||||
|
return self._banExtra['evformula'](self.BanTimeIncr(banTime, banCount))
|
||||||
|
|
||||||
def incrBanTime(self, bTicket, ip):
|
def incrBanTime(self, bTicket, ip):
|
||||||
"""Check for IP address to increment ban time (if was already banned).
|
"""Check for IP address to increment ban time (if was already banned).
|
||||||
|
|
||||||
|
@ -288,7 +312,6 @@ class Actions(JailThread, Mapping):
|
||||||
try:
|
try:
|
||||||
be = self._banExtra;
|
be = self._banExtra;
|
||||||
if banTime > 0 and be.get('enabled', False):
|
if banTime > 0 and be.get('enabled', False):
|
||||||
banFactor = be['factor'];
|
|
||||||
# search IP in database and increase time if found:
|
# search IP in database and increase time if found:
|
||||||
for banCount, timeOfBan, lastBanTime in \
|
for banCount, timeOfBan, lastBanTime in \
|
||||||
self._jail.database.getBan(ip, self._jail, be.get('findtime', None), be.get('overalljails', False) \
|
self._jail.database.getBan(ip, self._jail, be.get('findtime', None), be.get('overalljails', False) \
|
||||||
|
@ -296,7 +319,8 @@ class Actions(JailThread, Mapping):
|
||||||
#logSys.debug('IP %s was already banned: %s #, %s' % (ip, banCount, timeOfBan));
|
#logSys.debug('IP %s was already banned: %s #, %s' % (ip, banCount, timeOfBan));
|
||||||
bTicket.setBanCount(banCount);
|
bTicket.setBanCount(banCount);
|
||||||
# calculate new ban time
|
# calculate new ban time
|
||||||
banTime = eval(be['evformula'])
|
if banCount > 0:
|
||||||
|
banTime = be['evformula'](self.BanTimeIncr(banTime, banCount))
|
||||||
bTicket.setBanTime(banTime);
|
bTicket.setBanTime(banTime);
|
||||||
logSys.info('[%s] %s was already banned: %s # at last %s - increase time %s to %s' % (self._jail.name, ip, banCount,
|
logSys.info('[%s] %s was already banned: %s # at last %s - increase time %s to %s' % (self._jail.name, ip, banCount,
|
||||||
datetime.datetime.fromtimestamp(timeOfBan).strftime("%Y-%m-%d %H:%M:%S"),
|
datetime.datetime.fromtimestamp(timeOfBan).strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
|
|
Loading…
Reference in New Issue