mirror of https://github.com/fail2ban/fail2ban
ban time incr: 2st test case added (code optimized for test cases), to test both stand-alone:
python ./bin/fail2ban-testcases -l debug 'BanTimeIncr'pull/716/head
parent
237706e39f
commit
14167ed778
|
@ -300,7 +300,7 @@ class Actions(JailThread, Mapping):
|
||||||
def calcBanTime(self, banTime, banCount):
|
def calcBanTime(self, banTime, banCount):
|
||||||
return self._banExtra['evformula'](self.BanTimeIncr(banTime, banCount))
|
return self._banExtra['evformula'](self.BanTimeIncr(banTime, banCount))
|
||||||
|
|
||||||
def incrBanTime(self, bTicket, ip):
|
def incrBanTime(self, bTicket):
|
||||||
"""Check for IP address to increment ban time (if was already banned).
|
"""Check for IP address to increment ban time (if was already banned).
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
|
@ -308,6 +308,7 @@ class Actions(JailThread, Mapping):
|
||||||
float
|
float
|
||||||
new ban time.
|
new ban time.
|
||||||
"""
|
"""
|
||||||
|
ip = bTicket.getIP()
|
||||||
orgBanTime = self.__banManager.getBanTime()
|
orgBanTime = self.__banManager.getBanTime()
|
||||||
banTime = orgBanTime
|
banTime = orgBanTime
|
||||||
# check ip was already banned (increment time of ban):
|
# check ip was already banned (increment time of ban):
|
||||||
|
@ -374,7 +375,7 @@ class Actions(JailThread, Mapping):
|
||||||
try:
|
try:
|
||||||
# if ban time was not set:
|
# if ban time was not set:
|
||||||
if not ticket.getRestored() and bTicket.getBanTime() is None:
|
if not ticket.getRestored() and bTicket.getBanTime() is None:
|
||||||
btime = self.incrBanTime(bTicket, ip)
|
btime = self.incrBanTime(bTicket)
|
||||||
bTicket.setBanTime(btime);
|
bTicket.setBanTime(btime);
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logSys.error('%s', e, exc_info=logSys.getEffectiveLevel()<=logging.DEBUG)
|
logSys.error('%s', e, exc_info=logSys.getEffectiveLevel()<=logging.DEBUG)
|
||||||
|
|
|
@ -505,7 +505,9 @@ class Fail2BanDb(object):
|
||||||
cur = self._db.cursor()
|
cur = self._db.cursor()
|
||||||
return cur.execute(query, queryArgs)
|
return cur.execute(query, queryArgs)
|
||||||
|
|
||||||
def _getCurrentBans(self, jail = None, ip = None, forbantime=None):
|
def _getCurrentBans(self, jail = None, ip = None, forbantime=None, fromtime=None):
|
||||||
|
if fromtime is None:
|
||||||
|
fromtime = MyTime.time()
|
||||||
#query = "SELECT count(ip), max(timeofban) FROM bans WHERE ip = ?"
|
#query = "SELECT count(ip), max(timeofban) FROM bans WHERE ip = ?"
|
||||||
query = "SELECT ip, max(timeofban), bantime, bancount, data FROM bans WHERE 1"
|
query = "SELECT ip, max(timeofban), bantime, bancount, data FROM bans WHERE 1"
|
||||||
queryArgs = []
|
queryArgs = []
|
||||||
|
@ -516,17 +518,17 @@ class Fail2BanDb(object):
|
||||||
query += " AND ip=?"
|
query += " AND ip=?"
|
||||||
queryArgs.append(ip)
|
queryArgs.append(ip)
|
||||||
query += " AND timeofban + bantime > ?"
|
query += " AND timeofban + bantime > ?"
|
||||||
queryArgs.append(MyTime.time())
|
queryArgs.append(fromtime)
|
||||||
if forbantime is not None:
|
if forbantime is not None:
|
||||||
query += " AND timeofban > ?"
|
query += " AND timeofban > ?"
|
||||||
queryArgs.append(MyTime.time() - forbantime)
|
queryArgs.append(fromtime - forbantime)
|
||||||
query += " GROUP BY ip ORDER BY ip, timeofban DESC"
|
query += " GROUP BY ip ORDER BY ip, timeofban DESC"
|
||||||
cur = self._db.cursor()
|
cur = self._db.cursor()
|
||||||
#logSys.debug((query, queryArgs));
|
#logSys.debug((query, queryArgs));
|
||||||
return cur.execute(query, queryArgs)
|
return cur.execute(query, queryArgs)
|
||||||
|
|
||||||
def getCurrentBans(self, jail = None, ip = None, forbantime=None):
|
def getCurrentBans(self, jail = None, ip = None, forbantime=None, fromtime=None):
|
||||||
if forbantime is None:
|
if forbantime is None and jail is not None:
|
||||||
cacheKey = (ip, jail)
|
cacheKey = (ip, jail)
|
||||||
if cacheKey in self._bansMergedCache:
|
if cacheKey in self._bansMergedCache:
|
||||||
return self._bansMergedCache[cacheKey]
|
return self._bansMergedCache[cacheKey]
|
||||||
|
@ -534,7 +536,7 @@ class Fail2BanDb(object):
|
||||||
tickets = []
|
tickets = []
|
||||||
ticket = None
|
ticket = None
|
||||||
|
|
||||||
results = list(self._getCurrentBans(jail=jail, ip=ip, forbantime=forbantime))
|
results = list(self._getCurrentBans(jail=jail, ip=ip, forbantime=forbantime, fromtime=fromtime))
|
||||||
|
|
||||||
if results:
|
if results:
|
||||||
matches = []
|
matches = []
|
||||||
|
@ -552,7 +554,7 @@ class Fail2BanDb(object):
|
||||||
ticket.setAttempt(failures)
|
ticket.setAttempt(failures)
|
||||||
tickets.append(ticket)
|
tickets.append(ticket)
|
||||||
|
|
||||||
if forbantime is None:
|
if forbantime is None and jail is not None:
|
||||||
self._bansMergedCache[cacheKey] = tickets if ip is None else ticket
|
self._bansMergedCache[cacheKey] = tickets if ip is None else ticket
|
||||||
return tickets if ip is None else ticket
|
return tickets if ip is None else ticket
|
||||||
|
|
||||||
|
|
|
@ -278,3 +278,131 @@ class DatabaseTest(unittest.TestCase):
|
||||||
self.db.purge() # Should leave jail as ban present
|
self.db.purge() # Should leave jail as ban present
|
||||||
self.assertEqual(len(self.db.getJailNames()), 1)
|
self.assertEqual(len(self.db.getJailNames()), 1)
|
||||||
self.assertEqual(len(self.db.getBans(jail=self.jail)), 1)
|
self.assertEqual(len(self.db.getBans(jail=self.jail)), 1)
|
||||||
|
|
||||||
|
|
||||||
|
# Author: Serg G. Brester (sebres)
|
||||||
|
#
|
||||||
|
|
||||||
|
__author__ = "Serg Brester"
|
||||||
|
__copyright__ = "Copyright (c) 2014 Serg G. Brester"
|
||||||
|
|
||||||
|
class BanTimeIncr(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""Call before every test case."""
|
||||||
|
if Fail2BanDb is None and sys.version_info >= (2,7): # pragma: no cover
|
||||||
|
raise unittest.SkipTest(
|
||||||
|
"Unable to import fail2ban database module as sqlite is not "
|
||||||
|
"available.")
|
||||||
|
elif Fail2BanDb is None:
|
||||||
|
return
|
||||||
|
_, self.dbFilename = tempfile.mkstemp(".db", "fail2ban_")
|
||||||
|
self.db = Fail2BanDb(self.dbFilename)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
"""Call after every test case."""
|
||||||
|
if Fail2BanDb is None: # pragma: no cover
|
||||||
|
return
|
||||||
|
# Cleanup
|
||||||
|
os.remove(self.dbFilename)
|
||||||
|
|
||||||
|
def testBanTimeIncr(self):
|
||||||
|
if Fail2BanDb is None: # pragma: no cover
|
||||||
|
return
|
||||||
|
jail = DummyJail()
|
||||||
|
jail.database = self.db
|
||||||
|
self.db.addJail(jail)
|
||||||
|
a = jail.actions
|
||||||
|
# we tests with initial ban time = 10 seconds:
|
||||||
|
a.setBanTime(10)
|
||||||
|
a.setBanTimeExtra('enabled', 'true')
|
||||||
|
a.setBanTimeExtra('multipliers', '1 2 4 8 16 32 64 128 256 512 1024 2048')
|
||||||
|
ip = "127.0.0.2"
|
||||||
|
# used as start and fromtime (like now but time independence, cause test case can run slow):
|
||||||
|
stime = int(MyTime.time())
|
||||||
|
ticket = FailTicket(ip, stime, [])
|
||||||
|
# test ticket not yet found
|
||||||
|
self.assertEqual(
|
||||||
|
[a.incrBanTime(ticket) for i in xrange(3)],
|
||||||
|
[10, 10, 10]
|
||||||
|
)
|
||||||
|
# add a ticket banned
|
||||||
|
self.db.addBan(jail, ticket)
|
||||||
|
# get a ticket already banned in this jail:
|
||||||
|
self.assertEqual(
|
||||||
|
[(banCount, timeOfBan, lastBanTime) for banCount, timeOfBan, lastBanTime in self.db.getBan(ip, jail, None, False)],
|
||||||
|
[(1, stime, 10)]
|
||||||
|
)
|
||||||
|
# incr time and ban a ticket again :
|
||||||
|
ticket.setTime(stime + 15)
|
||||||
|
self.assertEqual(a.incrBanTime(ticket), 20)
|
||||||
|
self.db.addBan(jail, ticket)
|
||||||
|
# get a ticket already banned in this jail:
|
||||||
|
self.assertEqual(
|
||||||
|
[(banCount, timeOfBan, lastBanTime) for banCount, timeOfBan, lastBanTime in self.db.getBan(ip, jail, None, False)],
|
||||||
|
[(2, stime + 15, 20)]
|
||||||
|
)
|
||||||
|
# get a ticket already banned in all jails:
|
||||||
|
self.assertEqual(
|
||||||
|
[(banCount, timeOfBan, lastBanTime) for banCount, timeOfBan, lastBanTime in self.db.getBan(ip, '', None, True)],
|
||||||
|
[(2, stime + 15, 20)]
|
||||||
|
)
|
||||||
|
# search currently banned and 1 day later (nothing should be found):
|
||||||
|
self.assertEqual(
|
||||||
|
self.db.getCurrentBans(forbantime=-24*60*60, fromtime=stime),
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
# search currently banned anywhere:
|
||||||
|
restored_tickets = self.db.getCurrentBans(fromtime=stime)
|
||||||
|
self.assertEqual(
|
||||||
|
str(restored_tickets),
|
||||||
|
('[FailTicket: ip=%s time=%s bantime=20 bancount=2 #attempts=0 matches=[]]' % (ip, stime + 15))
|
||||||
|
)
|
||||||
|
# search currently banned:
|
||||||
|
restored_tickets = self.db.getCurrentBans(jail=jail, fromtime=stime)
|
||||||
|
self.assertEqual(
|
||||||
|
str(restored_tickets),
|
||||||
|
('[FailTicket: ip=%s time=%s bantime=20 bancount=2 #attempts=0 matches=[]]' % (ip, stime + 15))
|
||||||
|
)
|
||||||
|
restored_tickets[0].setRestored(True)
|
||||||
|
self.assertTrue(restored_tickets[0].getRestored())
|
||||||
|
# increase ban multiple times:
|
||||||
|
for i in xrange(10):
|
||||||
|
ticket.setTime(stime + lastBanTime + 5)
|
||||||
|
banTime = a.incrBanTime(ticket)
|
||||||
|
self.assertEqual(banTime, lastBanTime * 2)
|
||||||
|
self.db.addBan(jail, ticket)
|
||||||
|
lastBanTime = banTime
|
||||||
|
# increase again, but the last multiplier reached (time not increased):
|
||||||
|
ticket.setTime(stime + lastBanTime + 5)
|
||||||
|
banTime = a.incrBanTime(ticket)
|
||||||
|
self.assertNotEqual(banTime, lastBanTime * 2)
|
||||||
|
self.assertEqual(banTime, lastBanTime)
|
||||||
|
self.db.addBan(jail, ticket)
|
||||||
|
lastBanTime = banTime
|
||||||
|
# add two tickets from yesterday: one unbanned (bantime already out-dated):
|
||||||
|
ticket2 = FailTicket(ip+'2', stime-24*60*60, [])
|
||||||
|
ticket2.setBanTime(12*60*60)
|
||||||
|
self.db.addBan(jail, ticket2)
|
||||||
|
# and one from yesterday also, but still currently banned :
|
||||||
|
ticket2 = FailTicket(ip+'1', stime-24*60*60, [])
|
||||||
|
ticket2.setBanTime(36*60*60)
|
||||||
|
self.db.addBan(jail, ticket2)
|
||||||
|
# search currently banned:
|
||||||
|
restored_tickets = self.db.getCurrentBans(fromtime=stime)
|
||||||
|
self.assertEqual(len(restored_tickets), 2)
|
||||||
|
self.assertEqual(
|
||||||
|
str(restored_tickets[0]),
|
||||||
|
'FailTicket: ip=%s time=%s bantime=%s bancount=13 #attempts=0 matches=[]' % (ip, stime + lastBanTime + 5, lastBanTime)
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
str(restored_tickets[1]),
|
||||||
|
'FailTicket: ip=%s time=%s bantime=%s bancount=1 #attempts=0 matches=[]' % (ip+'1', stime-24*60*60, 36*60*60)
|
||||||
|
)
|
||||||
|
# search out-dated (give another fromtime now is -18 hours):
|
||||||
|
restored_tickets = self.db.getCurrentBans(fromtime=stime-18*60*60)
|
||||||
|
self.assertEqual(len(restored_tickets), 3)
|
||||||
|
self.assertEqual(
|
||||||
|
str(restored_tickets[2]),
|
||||||
|
'FailTicket: ip=%s time=%s bantime=%s bancount=1 #attempts=0 matches=[]' % (ip+'2', stime-24*60*60, 12*60*60)
|
||||||
|
)
|
||||||
|
|
|
@ -184,6 +184,7 @@ def gatherTests(regexps=None, no_network=False):
|
||||||
tests.addTest(unittest.makeSuite(misctestcase.CustomDateFormatsTest))
|
tests.addTest(unittest.makeSuite(misctestcase.CustomDateFormatsTest))
|
||||||
# Database
|
# Database
|
||||||
tests.addTest(unittest.makeSuite(databasetestcase.DatabaseTest))
|
tests.addTest(unittest.makeSuite(databasetestcase.DatabaseTest))
|
||||||
|
tests.addTest(unittest.makeSuite(databasetestcase.BanTimeIncr))
|
||||||
|
|
||||||
# Filter
|
# Filter
|
||||||
tests.addTest(unittest.makeSuite(filtertestcase.IgnoreIP))
|
tests.addTest(unittest.makeSuite(filtertestcase.IgnoreIP))
|
||||||
|
|
Loading…
Reference in New Issue