mirror of https://github.com/fail2ban/fail2ban
[tickets] use slots in ticket (saves memory, and have better performance); uses the same ticket between Fail- and BanManagers, so makes possible operate with more actual data between workers (e. g. ban count and time by increment from observer)
parent
b893356c49
commit
157a85451a
|
@ -256,7 +256,7 @@ class BanManager:
|
||||||
# we should always use correct time to calculate correct end time (ban time is variable now,
|
# we should always use correct time to calculate correct end time (ban time is variable now,
|
||||||
# + possible double banning by restore from database and from log file)
|
# + possible double banning by restore from database and from log file)
|
||||||
# so use as lastTime always time from ticket.
|
# so use as lastTime always time from ticket.
|
||||||
return BanTicket(ticket=ticket)
|
return BanTicket.wrap(ticket)
|
||||||
|
|
||||||
##
|
##
|
||||||
# Add a ban ticket.
|
# Add a ban ticket.
|
||||||
|
|
|
@ -27,7 +27,7 @@ __license__ = "GPL"
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from .ticket import FailTicket
|
from .ticket import FailTicket, BanTicket
|
||||||
from ..helpers import getLogger, BgService
|
from ..helpers import getLogger, BgService
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
|
@ -103,13 +103,13 @@ class FailManager:
|
||||||
fData.setMatches(matches[-self.maxEntries:])
|
fData.setMatches(matches[-self.maxEntries:])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# not found - already banned - prevent to add failure if comes from observer:
|
# not found - already banned - prevent to add failure if comes from observer:
|
||||||
if observed:
|
if observed or isinstance(ticket, BanTicket):
|
||||||
return
|
return
|
||||||
# if already FailTicket - add it direct, otherwise create (using copy all ticket data):
|
# if already FailTicket - add it direct, otherwise create (using copy all ticket data):
|
||||||
if isinstance(ticket, FailTicket):
|
if isinstance(ticket, FailTicket):
|
||||||
fData = ticket;
|
fData = ticket;
|
||||||
else:
|
else:
|
||||||
fData = FailTicket(ticket=ticket)
|
fData = FailTicket.wrap(ticket)
|
||||||
if count > ticket.getAttempt():
|
if count > ticket.getAttempt():
|
||||||
fData.setRetry(count)
|
fData.setRetry(count)
|
||||||
self.__failList[fid] = fData
|
self.__failList[fid] = fData
|
||||||
|
|
|
@ -33,6 +33,7 @@ logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Ticket(object):
|
class Ticket(object):
|
||||||
|
__slots__ = ('_ip', '_flags', '_banCount', '_banTime', '_time', '_data', '_retry', '_lastReset')
|
||||||
|
|
||||||
MAX_TIME = 0X7FFFFFFFFFFF ;# 4461763-th year
|
MAX_TIME = 0X7FFFFFFFFFFF ;# 4461763-th year
|
||||||
|
|
||||||
|
@ -59,36 +60,44 @@ class Ticket(object):
|
||||||
self._data[k] = v
|
self._data[k] = v
|
||||||
if ticket:
|
if ticket:
|
||||||
# ticket available - copy whole information from ticket:
|
# ticket available - copy whole information from ticket:
|
||||||
self.__dict__.update(i for i in ticket.__dict__.iteritems() if i[0] in self.__dict__)
|
self.update(ticket)
|
||||||
|
#self.__dict__.update(i for i in ticket.__dict__.iteritems() if i[0] in self.__dict__)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s: ip=%s time=%s bantime=%s bancount=%s #attempts=%d matches=%r" % \
|
return "%s: ip=%s time=%s bantime=%s bancount=%s #attempts=%d matches=%r" % \
|
||||||
(self.__class__.__name__.split('.')[-1], self.__ip, self._time,
|
(self.__class__.__name__.split('.')[-1], self._ip, self._time,
|
||||||
self._banTime, self._banCount,
|
self._banTime, self._banCount,
|
||||||
self._data['failures'], self._data.get('matches', []))
|
self._data['failures'], self._data.get('matches', []))
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return str(self)
|
return str(self)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
try:
|
try:
|
||||||
return self.__ip == other.__ip and \
|
return self._ip == other._ip and \
|
||||||
round(self._time, 2) == round(other._time, 2) and \
|
round(self._time, 2) == round(other._time, 2) and \
|
||||||
self._data == other._data
|
self._data == other._data
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def update(self, ticket):
|
||||||
|
for n in ticket.__slots__:
|
||||||
|
v = getattr(ticket, n, None)
|
||||||
|
if v is not None:
|
||||||
|
setattr(self, n, v)
|
||||||
|
|
||||||
|
|
||||||
def setIP(self, value):
|
def setIP(self, value):
|
||||||
# guarantee using IPAddr instead of unicode, str for the IP
|
# guarantee using IPAddr instead of unicode, str for the IP
|
||||||
if isinstance(value, basestring):
|
if isinstance(value, basestring):
|
||||||
value = IPAddr(value)
|
value = IPAddr(value)
|
||||||
self.__ip = value
|
self._ip = value
|
||||||
|
|
||||||
def getID(self):
|
def getID(self):
|
||||||
return self._data.get('fid', self.__ip)
|
return self._data.get('fid', self._ip)
|
||||||
|
|
||||||
def getIP(self):
|
def getIP(self):
|
||||||
return self.__ip
|
return self._ip
|
||||||
|
|
||||||
def setTime(self, value):
|
def setTime(self, value):
|
||||||
self._time = value
|
self._time = value
|
||||||
|
@ -204,21 +213,21 @@ class FailTicket(Ticket):
|
||||||
|
|
||||||
def __init__(self, ip=None, time=None, matches=None, data={}, ticket=None):
|
def __init__(self, ip=None, time=None, matches=None, data={}, ticket=None):
|
||||||
# this class variables:
|
# this class variables:
|
||||||
self.__retry = 0
|
self._retry = 0
|
||||||
self.__lastReset = None
|
self._lastReset = None
|
||||||
# create/copy using default ticket constructor:
|
# create/copy using default ticket constructor:
|
||||||
Ticket.__init__(self, ip, time, matches, data, ticket)
|
Ticket.__init__(self, ip, time, matches, data, ticket)
|
||||||
# init:
|
# init:
|
||||||
if ticket is None:
|
if ticket is None:
|
||||||
self.__lastReset = time if time is not None else self.getTime()
|
self._lastReset = time if time is not None else self.getTime()
|
||||||
if not self.__retry:
|
if not self._retry:
|
||||||
self.__retry = self._data['failures'];
|
self._retry = self._data['failures'];
|
||||||
|
|
||||||
def setRetry(self, value):
|
def setRetry(self, value):
|
||||||
""" Set artificial retry count, normally equal failures / attempt,
|
""" Set artificial retry count, normally equal failures / attempt,
|
||||||
used in incremental features (BanTimeIncr) to increase retry count for bad IPs
|
used in incremental features (BanTimeIncr) to increase retry count for bad IPs
|
||||||
"""
|
"""
|
||||||
self.__retry = value
|
self._retry = value
|
||||||
if not self._data['failures']:
|
if not self._data['failures']:
|
||||||
self._data['failures'] = 1
|
self._data['failures'] = 1
|
||||||
if not value:
|
if not value:
|
||||||
|
@ -229,10 +238,10 @@ class FailTicket(Ticket):
|
||||||
""" Returns failures / attempt count or
|
""" Returns failures / attempt count or
|
||||||
artificial retry count increased for bad IPs
|
artificial retry count increased for bad IPs
|
||||||
"""
|
"""
|
||||||
return max(self.__retry, self._data['failures'])
|
return max(self._retry, self._data['failures'])
|
||||||
|
|
||||||
def inc(self, matches=None, attempt=1, count=1):
|
def inc(self, matches=None, attempt=1, count=1):
|
||||||
self.__retry += count
|
self._retry += count
|
||||||
self._data['failures'] += attempt
|
self._data['failures'] += attempt
|
||||||
if matches:
|
if matches:
|
||||||
# we should duplicate "matches", because possibly referenced to multiple tickets:
|
# we should duplicate "matches", because possibly referenced to multiple tickets:
|
||||||
|
@ -249,15 +258,24 @@ class FailTicket(Ticket):
|
||||||
return self._time
|
return self._time
|
||||||
|
|
||||||
def getLastReset(self):
|
def getLastReset(self):
|
||||||
return self.__lastReset
|
return self._lastReset
|
||||||
|
|
||||||
def setLastReset(self, value):
|
def setLastReset(self, value):
|
||||||
self.__lastReset = value
|
self._lastReset = value
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def wrap(o):
|
||||||
|
o.__class__ = FailTicket
|
||||||
|
return o
|
||||||
|
|
||||||
##
|
##
|
||||||
# Ban Ticket.
|
# Ban Ticket.
|
||||||
#
|
#
|
||||||
# This class extends the Ticket class. It is mainly used by the BanManager.
|
# This class extends the Ticket class. It is mainly used by the BanManager.
|
||||||
|
|
||||||
class BanTicket(Ticket):
|
class BanTicket(FailTicket):
|
||||||
pass
|
|
||||||
|
@staticmethod
|
||||||
|
def wrap(o):
|
||||||
|
o.__class__ = BanTicket
|
||||||
|
return o
|
||||||
|
|
Loading…
Reference in New Issue