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
sebres 2014-05-06 16:07:16 +02:00
parent ccacfc1047
commit c48e404e63
3 changed files with 56 additions and 14 deletions

View File

@ -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

View File

@ -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],

View File

@ -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"),