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
|
||||
# default ban time using special formula, default it is banTime * 1, 2, 4, 8, 16, 32...
|
||||
#bantimeextra.enabled = true
|
||||
|
||||
# "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
|
||||
# (please observe current "dbpurgeage" value of fail2ban.conf).
|
||||
#bantimeextra.findtime = 24*60*60
|
||||
|
||||
# "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:
|
||||
#bantimeextra.rndtime = 5*60
|
||||
|
||||
# "bantimeextra.maxtime" is the max number of seconds using the ban time can reach (don't grows further)
|
||||
#bantimeextra.maxtime = 24*60*60
|
||||
|
||||
# "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 ...
|
||||
# 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.formula" used to calculate next value of ban time;
|
||||
#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
|
||||
# cross over all jails, if false (dafault), only current jail of the ban IP will be searched
|
||||
#bantimeextra.overalljails = false
|
||||
|
|
|
@ -97,6 +97,7 @@ class JailReader(ConfigReader):
|
|||
["string", "bantimeextra.findtime", None],
|
||||
["string", "bantimeextra.factor", None],
|
||||
["string", "bantimeextra.formula", None],
|
||||
["string", "bantimeextra.multipliers", None],
|
||||
["string", "bantimeextra.maxtime", None],
|
||||
["string", "bantimeextra.rndtime", None],
|
||||
["bool", "bantimeextra.overalljails", None],
|
||||
|
|
|
@ -242,10 +242,18 @@ class Actions(JailThread, Mapping):
|
|||
logSys.debug(self._jail.name + ": action terminated")
|
||||
return True
|
||||
|
||||
class BanTimeIncr:
|
||||
def __init__(self, banTime, banCount):
|
||||
self.Time = banTime
|
||||
self.Count = banCount
|
||||
|
||||
def setBanTimeExtra(self, opt, value):
|
||||
# merge previous extra with new option:
|
||||
be = self._banExtra;
|
||||
be[opt] = value;
|
||||
if value is not None:
|
||||
be[opt] = value;
|
||||
else:
|
||||
del be[opt]
|
||||
logSys.info('Set banTimeExtra.%s = %s', opt, value)
|
||||
if opt == 'enabled':
|
||||
if isinstance(value, str):
|
||||
|
@ -255,18 +263,31 @@ class Actions(JailThread, Mapping):
|
|||
if opt in ['findtime', 'maxtime', 'rndtime']:
|
||||
if not value is None:
|
||||
be[opt] = eval(value)
|
||||
if opt == 'factor' or be.get('factor', None) is None:
|
||||
be['factor'] = eval(be.get('factor', "2.0 / 2.885385"));
|
||||
# prepare formula :
|
||||
if opt in ['formula', 'maxtime', 'rndtime'] or be.get('evformula', None) is None:
|
||||
be['formula'] = be.get('formula', 'banTime * math.exp(float(banCount)*banFactor)/math.exp(1*banFactor)')
|
||||
evformula = be['formula'];
|
||||
evformula = ('max(banTime, %s)' % evformula)
|
||||
# prepare formula lambda:
|
||||
if opt in ['formula', 'factor', 'maxtime', 'rndtime', 'multipliers'] or be.get('evformula', None) is None:
|
||||
# split multifiers to an array begins with 0 (or empty if not set):
|
||||
if opt == 'multipliers':
|
||||
be['evmultipliers'] = [int(i) for i in (value.split(' ') if value is not None and value != '' else [])]
|
||||
# if we have multifiers - use it in lambda, otherwise compile and use formula within lambda
|
||||
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:
|
||||
evformula = ('min(%s, %s)' % (evformula, be['maxtime']))
|
||||
# mix with random time (to prevent botnet calc exact time IP can be unbanned):
|
||||
maxtime = be['maxtime']
|
||||
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:
|
||||
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:
|
||||
be['evformula'] = evformula
|
||||
#logSys.info('banTimeExtra : %s' % json.dumps(be))
|
||||
|
@ -274,6 +295,9 @@ class Actions(JailThread, Mapping):
|
|||
def getBanTimeExtra(self, opt):
|
||||
return self._banExtra.get(opt, None)
|
||||
|
||||
def calcBanTime(self, banTime, banCount):
|
||||
return self._banExtra['evformula'](self.BanTimeIncr(banTime, banCount))
|
||||
|
||||
def incrBanTime(self, bTicket, ip):
|
||||
"""Check for IP address to increment ban time (if was already banned).
|
||||
|
||||
|
@ -288,7 +312,6 @@ class Actions(JailThread, Mapping):
|
|||
try:
|
||||
be = self._banExtra;
|
||||
if banTime > 0 and be.get('enabled', False):
|
||||
banFactor = be['factor'];
|
||||
# search IP in database and increase time if found:
|
||||
for banCount, timeOfBan, lastBanTime in \
|
||||
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));
|
||||
bTicket.setBanCount(banCount);
|
||||
# calculate new ban time
|
||||
banTime = eval(be['evformula'])
|
||||
if banCount > 0:
|
||||
banTime = be['evformula'](self.BanTimeIncr(banTime, banCount))
|
||||
bTicket.setBanTime(banTime);
|
||||
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"),
|
||||
|
|
Loading…
Reference in New Issue