mirror of https://github.com/fail2ban/fail2ban
introduced new flag "banned" as property, used to recognize the ticket was really banned;
get/set restored flag functions rewritten to property "restored" similar to "banned"; several code optimizations and tests extensions;pull/1557/head
parent
2108216d33
commit
f6197200a9
|
@ -359,9 +359,10 @@ class Actions(JailThread, Mapping):
|
|||
aInfo["ipjailmatches"] = lambda: "\n".join(mi4ip().getMatches())
|
||||
aInfo["ipfailures"] = lambda: mi4ip(True).getAttempt()
|
||||
aInfo["ipjailfailures"] = lambda: mi4ip().getAttempt()
|
||||
if self.__banManager.addBanTicket(bTicket):
|
||||
reason = {}
|
||||
if self.__banManager.addBanTicket(bTicket, reason=reason):
|
||||
cnt += 1
|
||||
logSys.notice("[%s] %sBan %s", self._jail.name, ('' if not bTicket.getRestored() else 'Restore '), ip)
|
||||
logSys.notice("[%s] %sBan %s", self._jail.name, ('' if not bTicket.restored else 'Restore '), ip)
|
||||
for name, action in self._actions.iteritems():
|
||||
try:
|
||||
action.ban(aInfo.copy())
|
||||
|
@ -371,8 +372,22 @@ class Actions(JailThread, Mapping):
|
|||
"info '%r': %s",
|
||||
self._jail.name, name, aInfo, e,
|
||||
exc_info=logSys.getEffectiveLevel()<=logging.DEBUG)
|
||||
# after all actions are processed set banned flag:
|
||||
bTicket.banned = True
|
||||
else:
|
||||
logSys.notice("[%s] %s already banned", self._jail.name, ip)
|
||||
bTicket = reason['ticket']
|
||||
# if already banned (otherwise still process some action)
|
||||
if bTicket.banned:
|
||||
# compare time of failure occurrence with time ticket was really banned:
|
||||
diftm = ticket.getTime() - bTicket.getTime()
|
||||
# log already banned with following level:
|
||||
# DEBUG - before 3 seconds - certain interval for it, because of possible latency by recognizing in backends, etc.
|
||||
# NOTICE - before 60 seconds - may still occurre if action are slow, or very high load in backend,
|
||||
# WARNING - after 60 seconds - very long time, something may be wrong
|
||||
ll = logging.DEBUG if diftm < 3 \
|
||||
else logging.NOTICE if diftm < 60 \
|
||||
else logging.WARNING
|
||||
logSys.log(ll, "[%s] %s already banned", self._jail.name, ip)
|
||||
if cnt:
|
||||
logSys.debug("Banned %s / %s, %s ticket(s) in %r", cnt,
|
||||
self.__banManager.getBanTotal(), self.__banManager.size(), self._jail.name)
|
||||
|
|
|
@ -256,29 +256,30 @@ class BanManager:
|
|||
# @param ticket the ticket
|
||||
# @return True if the IP address is not in the ban list
|
||||
|
||||
def addBanTicket(self, ticket):
|
||||
def addBanTicket(self, ticket, reason={}):
|
||||
eob = ticket.getEndOfBanTime(self.__banTime)
|
||||
with self.__lock:
|
||||
# check already banned
|
||||
fid = ticket.getID()
|
||||
oldticket = self.__banList.get(fid)
|
||||
if oldticket:
|
||||
# if already permanent
|
||||
btold, told = oldticket.getBanTime(self.__banTime), oldticket.getTime()
|
||||
if btold == -1:
|
||||
return False
|
||||
# if given time is less than already banned time
|
||||
btnew, tnew = ticket.getBanTime(self.__banTime), ticket.getTime()
|
||||
if btnew != -1 and tnew + btnew <= told + btold:
|
||||
return False
|
||||
# we have longest ban - set new (increment) ban time
|
||||
oldticket.setTime(tnew)
|
||||
oldticket.setBanTime(btnew)
|
||||
reason['ticket'] = oldticket
|
||||
# if new time for end of ban is larger than already banned end-time:
|
||||
if eob > oldticket.getEndOfBanTime(self.__banTime):
|
||||
# we have longest ban - set new (increment) ban time
|
||||
reason['prolong'] = 1
|
||||
btm = ticket.getBanTime(self.__banTime)
|
||||
# if not permanent:
|
||||
if btm != -1:
|
||||
diftm = ticket.getTime() - oldticket.getTime()
|
||||
if diftm > 0:
|
||||
btm += diftm
|
||||
oldticket.setBanTime(btm)
|
||||
return False
|
||||
# not yet banned - add new
|
||||
# not yet banned - add new one:
|
||||
self.__banList[fid] = ticket
|
||||
self.__banTotal += 1
|
||||
# correct next unban time:
|
||||
eob = ticket.getEndOfBanTime(self.__banTime)
|
||||
if self.__nextUnbanTime > eob:
|
||||
self.__nextUnbanTime = eob
|
||||
return True
|
||||
|
|
|
@ -28,7 +28,7 @@ import Queue
|
|||
|
||||
from .actions import Actions
|
||||
from ..client.jailreader import JailReader
|
||||
from ..helpers import getLogger
|
||||
from ..helpers import getLogger, MyTime
|
||||
|
||||
# Gets the instance of the logger.
|
||||
logSys = getLogger(__name__)
|
||||
|
@ -194,7 +194,7 @@ class Jail(object):
|
|||
Used by filter to add a failure for banning.
|
||||
"""
|
||||
self.__queue.put(ticket)
|
||||
if not ticket.getRestored() and self.database is not None:
|
||||
if not ticket.restored and self.database is not None:
|
||||
self.database.addBan(self, ticket)
|
||||
|
||||
def getFailTicket(self):
|
||||
|
@ -203,7 +203,8 @@ class Jail(object):
|
|||
Used by actions to get a failure for banning.
|
||||
"""
|
||||
try:
|
||||
return self.__queue.get(False)
|
||||
ticket = self.__queue.get(False)
|
||||
return ticket
|
||||
except Queue.Empty:
|
||||
return False
|
||||
|
||||
|
@ -217,7 +218,17 @@ class Jail(object):
|
|||
#logSys.debug('restored ticket: %s', ticket)
|
||||
if not self.filter.inIgnoreIPList(ticket.getIP(), log_ignore=True):
|
||||
# mark ticked was restored from database - does not put it again into db:
|
||||
ticket.setRestored(True)
|
||||
ticket.restored = True
|
||||
# correct start time / ban time (by the same end of ban):
|
||||
btm = ticket.getBanTime(forbantime)
|
||||
diftm = MyTime.time() - ticket.getTime()
|
||||
if btm != -1 and diftm > 0:
|
||||
btm -= diftm
|
||||
# ignore obsolete tickets:
|
||||
if btm != -1 and btm <= 0:
|
||||
continue
|
||||
ticket.setTime(MyTime.time())
|
||||
ticket.setBanTime(btm)
|
||||
self.putFailTicket(ticket)
|
||||
except Exception as e: # pragma: no cover
|
||||
logSys.error('%s', e, exc_info=logSys.getEffectiveLevel()<=logging.DEBUG)
|
||||
|
|
|
@ -34,9 +34,10 @@ from .mytime import MyTime
|
|||
logSys = getLogger(__name__)
|
||||
|
||||
|
||||
class Ticket:
|
||||
class Ticket(object):
|
||||
|
||||
RESTORED = 0x01
|
||||
BANNED = 0x08
|
||||
|
||||
def __init__(self, ip=None, time=None, matches=None, data={}, ticket=None):
|
||||
"""Ticket constructor
|
||||
|
@ -135,14 +136,25 @@ class Ticket:
|
|||
def getMatches(self):
|
||||
return self._data.get('matches', [])
|
||||
|
||||
def setRestored(self, value):
|
||||
@property
|
||||
def restored(self):
|
||||
return self._flags & Ticket.RESTORED
|
||||
@restored.setter
|
||||
def restored(self, value):
|
||||
if value:
|
||||
self._flags = Ticket.RESTORED
|
||||
self._flags |= Ticket.RESTORED
|
||||
else:
|
||||
self._flags &= ~(Ticket.RESTORED)
|
||||
|
||||
def getRestored(self):
|
||||
return self._flags & Ticket.RESTORED
|
||||
@property
|
||||
def banned(self):
|
||||
return self._flags & Ticket.BANNED
|
||||
@banned.setter
|
||||
def banned(self, value):
|
||||
if value:
|
||||
self._flags |= Ticket.BANNED
|
||||
else:
|
||||
self._flags &= ~(Ticket.BANNED)
|
||||
|
||||
def setData(self, *args, **argv):
|
||||
# if overwrite - set data and filter None values:
|
||||
|
|
|
@ -53,11 +53,15 @@ class AddFailure(unittest.TestCase):
|
|||
self.assertEqual(self.__banManager.size(), 1)
|
||||
|
||||
def testAddDuplicateWithTime(self):
|
||||
defBanTime = self.__banManager.getBanTime()
|
||||
prevEndOfBanTime = 0
|
||||
# add again a duplicate :
|
||||
# 1) with newer start time and the same ban time
|
||||
# 0) with same start time and the same (default) ban time
|
||||
# 1) with newer start time and the same (default) ban time
|
||||
# 2) with same start time and longer ban time
|
||||
# 3) with permanent ban time (-1)
|
||||
for tnew, btnew in (
|
||||
(1167605999.0, None),
|
||||
(1167605999.0 + 100, None),
|
||||
(1167605999.0, 24*60*60),
|
||||
(1167605999.0, -1),
|
||||
|
@ -71,9 +75,14 @@ class AddFailure(unittest.TestCase):
|
|||
self.assertEqual(self.__banManager.size(), 1)
|
||||
# pop ticket and check it was prolonged :
|
||||
banticket = self.__banManager.getTicketByID(ticket2.getID())
|
||||
self.assertEqual(banticket.getTime(), ticket2.getTime())
|
||||
self.assertEqual(banticket.getTime(), ticket2.getTime())
|
||||
self.assertEqual(banticket.getBanTime(), ticket2.getBanTime(self.__banManager.getBanTime()))
|
||||
self.assertEqual(banticket.getEndOfBanTime(defBanTime), ticket2.getEndOfBanTime(defBanTime))
|
||||
self.assertTrue(banticket.getEndOfBanTime(defBanTime) > prevEndOfBanTime)
|
||||
prevEndOfBanTime = ticket1.getEndOfBanTime(defBanTime)
|
||||
# but the start time should not be changed (+ 100 is ignored):
|
||||
self.assertEqual(banticket.getTime(), 1167605999.0)
|
||||
# if prolong to permanent, it should also have permanent ban time:
|
||||
if btnew == -1:
|
||||
self.assertEqual(banticket.getBanTime(defBanTime), -1)
|
||||
|
||||
def testInListOK(self):
|
||||
self.assertTrue(self.__banManager.addBanTicket(self.__ticket))
|
||||
|
|
|
@ -108,6 +108,24 @@ class TicketTests(unittest.TestCase):
|
|||
self.assertEqual(ft2.getLastTime(), ft.getLastTime())
|
||||
self.assertEqual(ft2.getBanTime(), ft.getBanTime())
|
||||
|
||||
def testTicketFlags(self):
|
||||
flags = ('restored', 'banned')
|
||||
ticket = Ticket('test', 0)
|
||||
trueflags = []
|
||||
for v in (True, False, True):
|
||||
for f in flags:
|
||||
setattr(ticket, f, v)
|
||||
if v:
|
||||
trueflags.append(f)
|
||||
else:
|
||||
trueflags.remove(f)
|
||||
for f2 in flags:
|
||||
self.assertEqual(bool(getattr(ticket, f2)), f2 in trueflags)
|
||||
## inherite props from another tockets:
|
||||
ticket = FailTicket(ticket=ticket)
|
||||
for f2 in flags:
|
||||
self.assertTrue(bool(getattr(ticket, f2)))
|
||||
|
||||
def testTicketData(self):
|
||||
t = BanTicket('193.168.0.128', None, ['first', 'second'])
|
||||
# expand data (no overwrites, matches are available) :
|
||||
|
|
Loading…
Reference in New Issue