mirror of https://github.com/fail2ban/fail2ban
Merge branch 'differentiate-ip-fid--gh-3217'
commit
bc075ea682
|
@ -18,6 +18,9 @@ ver. 1.0.1-dev-1 (20??/??/??) - development nightly edition
|
|||
- due to change of `actioncheck` behavior (gh-488), some actions can be incompatible as regards
|
||||
the invariant check, if `actionban` or `actionunban` would not throw an error (exit code
|
||||
different from 0) in case of unsane environment.
|
||||
- actions that have used tag `<ip>` (instead of `<fid>` or `<F-ID>`) to get failure-ID may become
|
||||
incompatible, if filter uses IP-related tags (like `<ADDR>` or `<HOST>`) additionally to `<F-ID>`
|
||||
and the values are different (gh-3217)
|
||||
|
||||
### Fixes
|
||||
* readline fixed to consider interim new-line character as part of code point in multi-byte logs
|
||||
|
@ -34,6 +37,8 @@ ver. 1.0.1-dev-1 (20??/??/??) - development nightly edition
|
|||
* better recognition of log rotation, better performance by reopen: avoid unnecessary seek to begin of file
|
||||
(and hash calculation)
|
||||
* file filter reads only complete lines (ended with new-line) now, so waits for end of line (for its completion)
|
||||
* actions differentiate tags `<ip>` and `<fid>` (`<F-ID>`), if IP-address deviates from ID then the value
|
||||
of `<ip>` is not equal `<fid>` anymore (gh-3217)
|
||||
* `action.d/ufw.conf` (gh-3018):
|
||||
- new option `add` (default `prepend`), can be supplied as `insert 1` for ufw versions before v.0.36 (gh-2331, gh-3018)
|
||||
- new options `kill-mode` and `kill` to drop established connections of intruder (see action for details, gh-3018)
|
||||
|
|
|
@ -331,7 +331,7 @@ class Fail2banRegex(object):
|
|||
fltFile = None
|
||||
fltOpt = {}
|
||||
if regextype == 'fail':
|
||||
if re.search(r'^/{0,3}[\w/_\-.]+(?:\[.*\])?$', value):
|
||||
if re.search(r'^(?ms)/{0,3}[\w/_\-.]+(?:\[.*\])?$', value):
|
||||
try:
|
||||
fltName, fltOpt = extractOptions(value)
|
||||
if "." in fltName[~5:]:
|
||||
|
@ -520,10 +520,14 @@ class Fail2banRegex(object):
|
|||
def _prepaireOutput(self):
|
||||
"""Prepares output- and fetch-function corresponding given '--out' option (format)"""
|
||||
ofmt = self._opts.out
|
||||
if ofmt in ('id', 'ip'):
|
||||
if ofmt in ('id', 'fid'):
|
||||
def _out(ret):
|
||||
for r in ret:
|
||||
output(r[1])
|
||||
elif ofmt == 'ip':
|
||||
def _out(ret):
|
||||
for r in ret:
|
||||
output(r[3].get('ip', r[1]))
|
||||
elif ofmt == 'msg':
|
||||
def _out(ret):
|
||||
for r in ret:
|
||||
|
|
|
@ -72,8 +72,9 @@ class FilterReader(DefinitionInitConfigReader):
|
|||
def _fillStream(stream, opts, jailName):
|
||||
prio0idx = 0
|
||||
for opt, value in opts.iteritems():
|
||||
# Do not send a command if the value is not set (empty).
|
||||
if value is None: continue
|
||||
if opt in ("failregex", "ignoreregex"):
|
||||
if value is None: continue
|
||||
multi = []
|
||||
for regex in value.split('\n'):
|
||||
# Do not send a command if the rule is empty.
|
||||
|
@ -91,8 +92,6 @@ class FilterReader(DefinitionInitConfigReader):
|
|||
elif opt in ('datepattern'):
|
||||
stream.append(["set", jailName, opt, value])
|
||||
elif opt == 'journalmatch':
|
||||
# Do not send a command if the match is empty.
|
||||
if value is None: continue
|
||||
for match in value.split("\n"):
|
||||
if match == '': continue
|
||||
stream.append(
|
||||
|
|
|
@ -497,7 +497,7 @@ class Actions(JailThread, Mapping):
|
|||
|
||||
bTicket = BanTicket.wrap(ticket)
|
||||
btime = ticket.getBanTime(self.banManager.getBanTime())
|
||||
ip = bTicket.getIP()
|
||||
ip = bTicket.getID()
|
||||
aInfo = self._getActionInfo(bTicket)
|
||||
reason = {}
|
||||
if self.banManager.addBanTicket(bTicket, reason=reason):
|
||||
|
@ -575,10 +575,10 @@ class Actions(JailThread, Mapping):
|
|||
Ticket to reban
|
||||
"""
|
||||
actions = actions or self._actions
|
||||
ip = ticket.getIP()
|
||||
ip = ticket.getID()
|
||||
aInfo = self._getActionInfo(ticket)
|
||||
if log:
|
||||
logSys.notice("[%s] Reban %s%s", self._jail.name, aInfo["ip"], (', action %r' % actions.keys()[0] if len(actions) == 1 else ''))
|
||||
logSys.notice("[%s] Reban %s%s", self._jail.name, ip, (', action %r' % actions.keys()[0] if len(actions) == 1 else ''))
|
||||
for name, action in actions.iteritems():
|
||||
try:
|
||||
logSys.debug("[%s] action %r: reban %s", self._jail.name, name, ip)
|
||||
|
@ -703,10 +703,10 @@ class Actions(JailThread, Mapping):
|
|||
unbactions = self._actions
|
||||
else:
|
||||
unbactions = actions
|
||||
ip = ticket.getIP()
|
||||
ip = ticket.getID()
|
||||
aInfo = self._getActionInfo(ticket)
|
||||
if log:
|
||||
logSys.notice("[%s] Unban %s", self._jail.name, aInfo["ip"])
|
||||
logSys.notice("[%s] Unban %s", self._jail.name, ip)
|
||||
for name, action in unbactions.iteritems():
|
||||
try:
|
||||
logSys.debug("[%s] action %r: unban %s", self._jail.name, name, ip)
|
||||
|
|
|
@ -599,7 +599,7 @@ class Fail2BanDb(object):
|
|||
ticket : BanTicket
|
||||
Ticket of the ban to be added.
|
||||
"""
|
||||
ip = str(ticket.getIP())
|
||||
ip = str(ticket.getID())
|
||||
try:
|
||||
del self._bansMergedCache[(ip, jail)]
|
||||
except KeyError:
|
||||
|
|
|
@ -553,7 +553,7 @@ class Filter(JailThread):
|
|||
ticket = None
|
||||
if isinstance(ip, FailTicket):
|
||||
ticket = ip
|
||||
ip = ticket.getIP()
|
||||
ip = ticket.getID()
|
||||
elif not isinstance(ip, IPAddr):
|
||||
ip = IPAddr(ip)
|
||||
return self._inIgnoreIPList(ip, ticket, log_ignore)
|
||||
|
@ -702,10 +702,7 @@ class Filter(JailThread):
|
|||
"""Processes the line for failures and populates failManager
|
||||
"""
|
||||
try:
|
||||
for element in self.processLine(line, date):
|
||||
ip = element[1]
|
||||
unixTime = element[2]
|
||||
fail = element[3]
|
||||
for (_, ip, unixTime, fail) in self.processLine(line, date):
|
||||
logSys.debug("Processing line with time:%s and ip:%s",
|
||||
unixTime, ip)
|
||||
# ensure the time is not in the future, e. g. by some estimated (assumed) time:
|
||||
|
@ -841,11 +838,9 @@ class Filter(JailThread):
|
|||
failList = list()
|
||||
|
||||
ll = logSys.getEffectiveLevel()
|
||||
returnRawHost = self.returnRawHost
|
||||
cidr = IPAddr.CIDR_UNSPEC
|
||||
if self.__useDns == "raw":
|
||||
returnRawHost = True
|
||||
cidr = IPAddr.CIDR_RAW
|
||||
defcidr = IPAddr.CIDR_UNSPEC
|
||||
if self.__useDns == "raw" or self.returnRawHost:
|
||||
defcidr = IPAddr.CIDR_RAW
|
||||
|
||||
if self.__lineBufferSize > 1:
|
||||
self.__lineBuffer.append(tupleLine)
|
||||
|
@ -908,7 +903,8 @@ class Filter(JailThread):
|
|||
if not self.checkAllRegex or self.__lineBufferSize > 1:
|
||||
self.__lineBuffer, buf = failRegex.getUnmatchedTupleLines(), None
|
||||
# merge data if multi-line failure:
|
||||
raw = returnRawHost
|
||||
cidr = defcidr
|
||||
raw = (defcidr == IPAddr.CIDR_RAW)
|
||||
if preGroups:
|
||||
currFail, fail = fail, preGroups.copy()
|
||||
fail.update(currFail)
|
||||
|
@ -927,49 +923,50 @@ class Filter(JailThread):
|
|||
# failure-id:
|
||||
fid = fail.get('fid')
|
||||
# ip-address or host:
|
||||
host = fail.get('ip4')
|
||||
if host is not None:
|
||||
ip = fail.get('ip4')
|
||||
if ip is not None:
|
||||
cidr = int(fail.get('cidr') or IPAddr.FAM_IPv4)
|
||||
raw = True
|
||||
else:
|
||||
host = fail.get('ip6')
|
||||
if host is not None:
|
||||
ip = fail.get('ip6')
|
||||
if ip is not None:
|
||||
cidr = int(fail.get('cidr') or IPAddr.FAM_IPv6)
|
||||
raw = True
|
||||
if host is None:
|
||||
host = fail.get('dns')
|
||||
if host is None:
|
||||
# first try to check we have mlfid case (cache connection id):
|
||||
if fid is None and mlfid is None:
|
||||
# if no failure-id also (obscure case, wrong regex), throw error inside getFailID:
|
||||
fid = failRegex.getFailID()
|
||||
host = fid
|
||||
cidr = IPAddr.CIDR_RAW
|
||||
raw = True
|
||||
else:
|
||||
ip = fail.get('dns')
|
||||
if ip is None:
|
||||
# first try to check we have mlfid case (cache connection id):
|
||||
if fid is None and mlfid is None:
|
||||
# if no failure-id also (obscure case, wrong regex), throw error inside getFailID:
|
||||
fid = failRegex.getFailID()
|
||||
ip = fid
|
||||
raw = True
|
||||
# if mlfid case (not failure):
|
||||
if host is None:
|
||||
if ip is None:
|
||||
if ll <= 7: logSys.log(7, "No failure-id by mlfid %r in regex %s: %s",
|
||||
mlfid, failRegexIndex, fail.get('mlfforget', "waiting for identifier"))
|
||||
fail['mlfpending'] = 1; # mark failure is pending
|
||||
if not self.checkAllRegex and self.ignorePending: return failList
|
||||
ips = [None]
|
||||
fids = [None]
|
||||
# if raw - add single ip or failure-id,
|
||||
# otherwise expand host to multiple ips using dns (or ignore it if not valid):
|
||||
elif raw:
|
||||
ip = IPAddr(host, cidr)
|
||||
# check host equal failure-id, if not - failure with complex id:
|
||||
if fid is not None and fid != host:
|
||||
ip = IPAddr(fid, IPAddr.CIDR_RAW)
|
||||
ips = [ip]
|
||||
# check ip/host equal failure-id, if not - failure with complex id:
|
||||
if fid is None or fid == ip:
|
||||
fid = IPAddr(ip, cidr)
|
||||
else:
|
||||
fail['ip'] = IPAddr(ip, cidr)
|
||||
fid = IPAddr(fid, defcidr)
|
||||
fids = [fid]
|
||||
# otherwise, try to use dns conversion:
|
||||
else:
|
||||
ips = DNSUtils.textToIp(host, self.__useDns)
|
||||
fids = DNSUtils.textToIp(ip, self.__useDns)
|
||||
# if checkAllRegex we must make a copy (to be sure next RE doesn't change merged/cached failure):
|
||||
if self.checkAllRegex and mlfid is not None:
|
||||
fail = fail.copy()
|
||||
# append failure with match to the list:
|
||||
for ip in ips:
|
||||
failList.append([failRegexIndex, ip, date, fail])
|
||||
for fid in fids:
|
||||
failList.append([failRegexIndex, fid, date, fail])
|
||||
if not self.checkAllRegex:
|
||||
break
|
||||
except RegexException as e: # pragma: no cover - unsure if reachable
|
||||
|
|
|
@ -257,6 +257,8 @@ class IPAddr(object):
|
|||
FAM_IPv6 = CIDR_RAW - socket.AF_INET6
|
||||
|
||||
def __new__(cls, ipstr, cidr=CIDR_UNSPEC):
|
||||
if cidr == IPAddr.CIDR_UNSPEC and isinstance(ipstr, (tuple, list)):
|
||||
cidr = IPAddr.CIDR_RAW
|
||||
if cidr == IPAddr.CIDR_RAW: # don't cache raw
|
||||
ip = super(IPAddr, cls).__new__(cls)
|
||||
ip.__init(ipstr, cidr)
|
||||
|
|
|
@ -295,7 +295,7 @@ class Jail(object):
|
|||
):
|
||||
try:
|
||||
#logSys.debug('restored ticket: %s', ticket)
|
||||
if self.filter.inIgnoreIPList(ticket.getIP(), log_ignore=True): continue
|
||||
if self.filter.inIgnoreIPList(ticket.getID(), log_ignore=True): continue
|
||||
# mark ticked was restored from database - does not put it again into db:
|
||||
ticket.restored = True
|
||||
# correct start time / ban time (by the same end of ban):
|
||||
|
|
|
@ -372,7 +372,7 @@ class ObserverThread(JailThread):
|
|||
# check jail active :
|
||||
if not jail.isAlive() or not jail.getBanTimeExtra("increment"):
|
||||
return
|
||||
ip = ticket.getIP()
|
||||
ip = ticket.getID()
|
||||
unixTime = ticket.getTime()
|
||||
logSys.debug("[%s] Observer: failure found %s", jail.name, ip)
|
||||
# increase retry count for known (bad) ip, corresponding banCount of it (one try will count than 2, 3, 5, 9 ...) :
|
||||
|
@ -435,7 +435,7 @@ class ObserverThread(JailThread):
|
|||
if not jail.isAlive() or not jail.database:
|
||||
return banTime
|
||||
be = jail.getBanTimeExtra()
|
||||
ip = ticket.getIP()
|
||||
ip = ticket.getID()
|
||||
orgBanTime = banTime
|
||||
# check ip was already banned (increment time of ban):
|
||||
try:
|
||||
|
@ -474,7 +474,7 @@ class ObserverThread(JailThread):
|
|||
return
|
||||
try:
|
||||
oldbtime = btime
|
||||
ip = ticket.getIP()
|
||||
ip = ticket.getID()
|
||||
logSys.debug("[%s] Observer: ban found %s, %s", jail.name, ip, btime)
|
||||
# if not permanent and ban time was not set - check time should be increased:
|
||||
if btime != -1 and ticket.getBanTime() is None:
|
||||
|
@ -514,7 +514,7 @@ class ObserverThread(JailThread):
|
|||
"""
|
||||
try:
|
||||
btime = ticket.getBanTime()
|
||||
ip = ticket.getIP()
|
||||
ip = ticket.getID()
|
||||
logSys.debug("[%s] Observer: prolong %s, %s", jail.name, ip, btime)
|
||||
# prolong ticket via actions that expected this:
|
||||
jail.actions._prolongBan(ticket)
|
||||
|
|
|
@ -33,7 +33,7 @@ logSys = getLogger(__name__)
|
|||
|
||||
|
||||
class Ticket(object):
|
||||
__slots__ = ('_ip', '_flags', '_banCount', '_banTime', '_time', '_data', '_retry', '_lastReset')
|
||||
__slots__ = ('_id', '_flags', '_banCount', '_banTime', '_time', '_data', '_retry', '_lastReset')
|
||||
|
||||
MAX_TIME = 0X7FFFFFFFFFFF ;# 4461763-th year
|
||||
|
||||
|
@ -48,7 +48,7 @@ class Ticket(object):
|
|||
@param matches (log) lines caused the ticket
|
||||
"""
|
||||
|
||||
self.setIP(ip)
|
||||
self.setID(ip)
|
||||
self._flags = 0;
|
||||
self._banCount = 0;
|
||||
self._banTime = None;
|
||||
|
@ -65,7 +65,7 @@ class Ticket(object):
|
|||
|
||||
def __str__(self):
|
||||
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._id, self._time,
|
||||
self._banTime, self._banCount,
|
||||
self._data['failures'], self._data.get('matches', []))
|
||||
|
||||
|
@ -74,7 +74,7 @@ class Ticket(object):
|
|||
|
||||
def __eq__(self, other):
|
||||
try:
|
||||
return self._ip == other._ip and \
|
||||
return self._id == other._id and \
|
||||
round(self._time, 2) == round(other._time, 2) and \
|
||||
self._data == other._data
|
||||
except AttributeError:
|
||||
|
@ -86,18 +86,17 @@ class Ticket(object):
|
|||
if v is not None:
|
||||
setattr(self, n, v)
|
||||
|
||||
|
||||
def setIP(self, value):
|
||||
def setID(self, value):
|
||||
# guarantee using IPAddr instead of unicode, str for the IP
|
||||
if isinstance(value, basestring):
|
||||
value = IPAddr(value)
|
||||
self._ip = value
|
||||
self._id = value
|
||||
|
||||
def getID(self):
|
||||
return self._data.get('fid', self._ip)
|
||||
return self._id
|
||||
|
||||
def getIP(self):
|
||||
return self._ip
|
||||
return self._data.get('ip', self._id)
|
||||
|
||||
def setTime(self, value):
|
||||
self._time = value
|
||||
|
|
|
@ -100,23 +100,23 @@ class AddFailure(unittest.TestCase):
|
|||
self.assertFalse(self.__banManager._inBanList(ticket))
|
||||
|
||||
def testBanTimeIncr(self):
|
||||
ticket = BanTicket(self.__ticket.getIP(), self.__ticket.getTime())
|
||||
ticket = BanTicket(self.__ticket.getID(), self.__ticket.getTime())
|
||||
## increase twice and at end permanent, check time/count increase:
|
||||
c = 0
|
||||
for i in (1000, 2000, -1):
|
||||
self.__banManager.addBanTicket(self.__ticket); c += 1
|
||||
ticket.setBanTime(i)
|
||||
self.assertFalse(self.__banManager.addBanTicket(ticket)); # no incr of c (already banned)
|
||||
self.assertEqual(str(self.__banManager.getTicketByID(ticket.getIP())),
|
||||
"BanTicket: ip=%s time=%s bantime=%s bancount=%s #attempts=0 matches=[]" % (ticket.getIP(), ticket.getTime(), i, c))
|
||||
self.assertEqual(str(self.__banManager.getTicketByID(ticket.getID())),
|
||||
"BanTicket: ip=%s time=%s bantime=%s bancount=%s #attempts=0 matches=[]" % (ticket.getID(), ticket.getTime(), i, c))
|
||||
## after permanent, it should remain permanent ban time (-1):
|
||||
self.__banManager.addBanTicket(self.__ticket); c += 1
|
||||
ticket.setBanTime(-1)
|
||||
self.assertFalse(self.__banManager.addBanTicket(ticket)); # no incr of c (already banned)
|
||||
ticket.setBanTime(1000)
|
||||
self.assertFalse(self.__banManager.addBanTicket(ticket)); # no incr of c (already banned)
|
||||
self.assertEqual(str(self.__banManager.getTicketByID(ticket.getIP())),
|
||||
"BanTicket: ip=%s time=%s bantime=%s bancount=%s #attempts=0 matches=[]" % (ticket.getIP(), ticket.getTime(), -1, c))
|
||||
self.assertEqual(str(self.__banManager.getTicketByID(ticket.getID())),
|
||||
"BanTicket: ip=%s time=%s bantime=%s bancount=%s #attempts=0 matches=[]" % (ticket.getID(), ticket.getTime(), -1, c))
|
||||
|
||||
def testUnban(self):
|
||||
btime = self.__banManager.getBanTime()
|
||||
|
|
|
@ -192,7 +192,7 @@ class DatabaseTest(LogCaptureTestCase):
|
|||
ticket.setAttempt(3)
|
||||
self.assertEqual(bans[0], ticket)
|
||||
# second ban found also:
|
||||
self.assertEqual(bans[1].getIP(), "1.2.3.8")
|
||||
self.assertEqual(bans[1].getID(), "1.2.3.8")
|
||||
# updated ?
|
||||
self.assertEqual(self.db.updateDb(Fail2BanDb.__version__), Fail2BanDb.__version__)
|
||||
# check current bans (should find 2 tickets after upgrade):
|
||||
|
@ -312,7 +312,7 @@ class DatabaseTest(LogCaptureTestCase):
|
|||
for i, ticket in enumerate(tickets):
|
||||
DefLogSys.debug('readtickets[%d]: %r', i, readtickets[i].getData())
|
||||
DefLogSys.debug(' == tickets[%d]: %r', i, ticket.getData())
|
||||
self.assertEqual(readtickets[i].getIP(), ticket.getIP())
|
||||
self.assertEqual(readtickets[i].getID(), ticket.getID())
|
||||
self.assertEqual(len(readtickets[i].getMatches()), len(ticket.getMatches()))
|
||||
|
||||
self.pruneLog('[test-phase 2] simulate errors')
|
||||
|
@ -354,10 +354,10 @@ class DatabaseTest(LogCaptureTestCase):
|
|||
def testDelBan(self):
|
||||
tickets = self._testAdd3Bans()
|
||||
# delete single IP:
|
||||
self.db.delBan(self.jail, tickets[0].getIP())
|
||||
self.db.delBan(self.jail, tickets[0].getID())
|
||||
self.assertEqual(len(self.db.getBans(jail=self.jail)), 2)
|
||||
# delete two IPs:
|
||||
self.db.delBan(self.jail, tickets[1].getIP(), tickets[2].getIP())
|
||||
self.db.delBan(self.jail, tickets[1].getID(), tickets[2].getID())
|
||||
self.assertEqual(len(self.db.getBans(jail=self.jail)), 0)
|
||||
|
||||
def testFlushBans(self):
|
||||
|
@ -398,7 +398,7 @@ class DatabaseTest(LogCaptureTestCase):
|
|||
# should retrieve 2 matches only, but count of all attempts:
|
||||
self.db.maxMatches = maxMatches;
|
||||
ticket = self.db.getBansMerged("127.0.0.1")
|
||||
self.assertEqual(ticket.getIP(), "127.0.0.1")
|
||||
self.assertEqual(ticket.getID(), "127.0.0.1")
|
||||
self.assertEqual(ticket.getAttempt(), len(failures))
|
||||
self.assertEqual(len(ticket.getMatches()), maxMatches)
|
||||
self.assertEqual(ticket.getMatches(), matches2find[-maxMatches:])
|
||||
|
@ -456,13 +456,13 @@ class DatabaseTest(LogCaptureTestCase):
|
|||
|
||||
# All for IP 127.0.0.1
|
||||
ticket = self.db.getBansMerged("127.0.0.1")
|
||||
self.assertEqual(ticket.getIP(), "127.0.0.1")
|
||||
self.assertEqual(ticket.getID(), "127.0.0.1")
|
||||
self.assertEqual(ticket.getAttempt(), 70)
|
||||
self.assertEqual(ticket.getMatches(), ["abc\n", "123\n", "ABC\n"])
|
||||
|
||||
# All for IP 127.0.0.1 for single jail
|
||||
ticket = self.db.getBansMerged("127.0.0.1", jail=self.jail)
|
||||
self.assertEqual(ticket.getIP(), "127.0.0.1")
|
||||
self.assertEqual(ticket.getID(), "127.0.0.1")
|
||||
self.assertEqual(ticket.getAttempt(), 30)
|
||||
self.assertEqual(ticket.getMatches(), ["abc\n", "123\n"])
|
||||
|
||||
|
@ -490,8 +490,8 @@ class DatabaseTest(LogCaptureTestCase):
|
|||
tickets = self.db.getBansMerged()
|
||||
self.assertEqual(len(tickets), 2)
|
||||
self.assertSortedEqual(
|
||||
list(set(ticket.getIP() for ticket in tickets)),
|
||||
[ticket.getIP() for ticket in tickets])
|
||||
list(set(ticket.getID() for ticket in tickets)),
|
||||
[ticket.getID() for ticket in tickets])
|
||||
|
||||
tickets = self.db.getBansMerged(jail=jail2)
|
||||
self.assertEqual(len(tickets), 1)
|
||||
|
@ -510,7 +510,7 @@ class DatabaseTest(LogCaptureTestCase):
|
|||
tickets = self.db.getCurrentBans(jail=self.jail)
|
||||
self.assertEqual(len(tickets), 2)
|
||||
ticket = self.db.getCurrentBans(jail=None, ip="127.0.0.1");
|
||||
self.assertEqual(ticket.getIP(), "127.0.0.1")
|
||||
self.assertEqual(ticket.getID(), "127.0.0.1")
|
||||
|
||||
# positive case (1 ticket not yet expired):
|
||||
tickets = self.db.getCurrentBans(jail=self.jail, forbantime=15,
|
||||
|
|
|
@ -355,23 +355,35 @@ class Fail2banRegexTest(LogCaptureTestCase):
|
|||
self.assertLogged('kevin')
|
||||
self.pruneLog()
|
||||
# multiple id combined to a tuple (id, tuple_id):
|
||||
self.assertTrue(_test_exec('-o', 'id',
|
||||
self.assertTrue(_test_exec('-o', 'id', '-d', '{^LN-BEG}EPOCH',
|
||||
'1591983743.667 192.0.2.1 192.0.2.2',
|
||||
r'^\s*<F-ID/> <F-TUPLE_ID>\S+</F-TUPLE_ID>'))
|
||||
self.assertLogged(str(('192.0.2.1', '192.0.2.2')))
|
||||
self.pruneLog()
|
||||
# multiple id combined to a tuple, id first - (id, tuple_id_1, tuple_id_2):
|
||||
self.assertTrue(_test_exec('-o', 'id',
|
||||
self.assertTrue(_test_exec('-o', 'id', '-d', '{^LN-BEG}EPOCH',
|
||||
'1591983743.667 left 192.0.2.3 right',
|
||||
r'^\s*<F-TUPLE_ID_1>\S+</F-TUPLE_ID_1> <F-ID/> <F-TUPLE_ID_2>\S+</F-TUPLE_ID_2>'))
|
||||
self.assertLogged(str(('192.0.2.3', 'left', 'right')))
|
||||
self.pruneLog()
|
||||
# id had higher precedence as ip-address:
|
||||
self.assertTrue(_test_exec('-o', 'id',
|
||||
self.assertTrue(_test_exec('-o', 'id', '-d', '{^LN-BEG}EPOCH',
|
||||
'1591983743.667 left [192.0.2.4]:12345 right',
|
||||
r'^\s*<F-TUPLE_ID_1>\S+</F-TUPLE_ID_1> <F-ID><ADDR>:<F-PORT/></F-ID> <F-TUPLE_ID_2>\S+</F-TUPLE_ID_2>'))
|
||||
self.assertLogged(str(('[192.0.2.4]:12345', 'left', 'right')))
|
||||
self.pruneLog()
|
||||
# ip is not id anymore (if IP-address deviates from ID):
|
||||
self.assertTrue(_test_exec('-o', 'ip', '-d', '{^LN-BEG}EPOCH',
|
||||
'1591983743.667 left [192.0.2.4]:12345 right',
|
||||
r'^\s*<F-TUPLE_ID_1>\S+</F-TUPLE_ID_1> <F-ID><ADDR>:<F-PORT/></F-ID> <F-TUPLE_ID_2>\S+</F-TUPLE_ID_2>'))
|
||||
self.assertNotLogged(str(('[192.0.2.4]:12345', 'left', 'right')))
|
||||
self.assertLogged('192.0.2.4')
|
||||
self.pruneLog()
|
||||
self.assertTrue(_test_exec('-o', 'ID:<fid> | IP:<ip>', '-d', '{^LN-BEG}EPOCH',
|
||||
'1591983743.667 left [192.0.2.4]:12345 right',
|
||||
r'^\s*<F-TUPLE_ID_1>\S+</F-TUPLE_ID_1> <F-ID><ADDR>:<F-PORT/></F-ID> <F-TUPLE_ID_2>\S+</F-TUPLE_ID_2>'))
|
||||
self.assertLogged('ID:'+str(('[192.0.2.4]:12345', 'left', 'right'))+' | IP:192.0.2.4')
|
||||
self.pruneLog()
|
||||
# row with id :
|
||||
self.assertTrue(_test_exec('-o', 'row', STR_00, RE_00_ID))
|
||||
self.assertLogged("['kevin'", "'ip4': '192.0.2.0'", "'fid': 'kevin'", all=True)
|
||||
|
@ -393,6 +405,43 @@ class Fail2banRegexTest(LogCaptureTestCase):
|
|||
self.assertLogged('192.0.2.0, kevin, inet4')
|
||||
self.pruneLog()
|
||||
|
||||
def testStalledIPByNoFailFrmtOutput(self):
|
||||
opts = (
|
||||
'-c', CONFIG_DIR,
|
||||
"-d", r"^(?:%a )?%b %d %H:%M:%S(?:\.%f)?(?: %ExY)?",
|
||||
)
|
||||
log = (
|
||||
'May 27 00:16:33 host sshd[2364]: User root not allowed because account is locked\n'
|
||||
'May 27 00:16:33 host sshd[2364]: Received disconnect from 192.0.2.76 port 58846:11: Bye Bye [preauth]'
|
||||
)
|
||||
_test = lambda *args: _test_exec(*(opts + args))
|
||||
# with MLFID from prefregex and IP after failure obtained from F-NOFAIL RE:
|
||||
self.assertTrue(_test('-o', 'IP:<ip>', log, 'sshd'))
|
||||
self.assertLogged('IP:192.0.2.76')
|
||||
self.pruneLog()
|
||||
# test diverse ID/IP constellations:
|
||||
def _test_variants(flt="sshd", prefix=""):
|
||||
# with different ID/IP from failregex (ID/User from first, IP from second message):
|
||||
self.assertTrue(_test('-o', 'ID:"<fid>" | IP:<ip> | U:<F-USER>', log,
|
||||
flt+'[failregex="'
|
||||
'^'+prefix+'<F-ID>User <F-USER>\S+</F-USER></F-ID> not allowed\n'
|
||||
'^'+prefix+'Received disconnect from <ADDR>'
|
||||
'"]'))
|
||||
self.assertLogged('ID:"User root" | IP:192.0.2.76 | U:root')
|
||||
self.pruneLog()
|
||||
# with different ID/IP from failregex (User from first, ID and IP from second message):
|
||||
self.assertTrue(_test('-o', 'ID:"<fid>" | IP:<ip> | U:<F-USER>', log,
|
||||
flt+'[failregex="'
|
||||
'^'+prefix+'User <F-USER>\S+</F-USER> not allowed\n'
|
||||
'^'+prefix+'Received disconnect from <F-ID><ADDR> port \d+</F-ID>'
|
||||
'"]'))
|
||||
self.assertLogged('ID:"192.0.2.76 port 58846" | IP:192.0.2.76 | U:root')
|
||||
self.pruneLog()
|
||||
# first with sshd and prefregex:
|
||||
_test_variants()
|
||||
# the same without prefregex and MLFID directly in failregex (no merge with prefregex groups):
|
||||
_test_variants('common', prefix="\s*\S+ sshd\[<F-MLFID>\d+</F-MLFID>\]:\s+")
|
||||
|
||||
def testNoDateTime(self):
|
||||
# datepattern doesn't match:
|
||||
self.assertTrue(_test_exec('-d', '{^LN-BEG}EPOCH', '-o', 'Found-ID:<F-ID>', STR_00_NODT, RE_00_ID))
|
||||
|
|
|
@ -150,8 +150,8 @@ class AddFailure(unittest.TestCase):
|
|||
self.__failManager.setMaxRetry(5)
|
||||
#ticket = FailTicket('193.168.0.128', None)
|
||||
ticket = self.__failManager.toBan()
|
||||
self.assertEqual(ticket.getIP(), "193.168.0.128")
|
||||
self.assertTrue(isinstance(ticket.getIP(), (str, IPAddr)))
|
||||
self.assertEqual(ticket.getID(), "193.168.0.128")
|
||||
self.assertTrue(isinstance(ticket.getID(), (str, IPAddr)))
|
||||
|
||||
# finish with rudimentary tests of the ticket
|
||||
# verify consistent str
|
||||
|
@ -180,9 +180,9 @@ class AddFailure(unittest.TestCase):
|
|||
def testWindow(self):
|
||||
self._addDefItems()
|
||||
ticket = self.__failManager.toBan()
|
||||
self.assertNotEqual(ticket.getIP(), "100.100.10.10")
|
||||
self.assertNotEqual(ticket.getID(), "100.100.10.10")
|
||||
ticket = self.__failManager.toBan()
|
||||
self.assertNotEqual(ticket.getIP(), "100.100.10.10")
|
||||
self.assertNotEqual(ticket.getID(), "100.100.10.10")
|
||||
self.assertRaises(FailManagerEmpty, self.__failManager.toBan)
|
||||
|
||||
def testBgService(self):
|
||||
|
|
|
@ -141,7 +141,7 @@ def _ticket_tuple(ticket):
|
|||
"""
|
||||
attempts = ticket.getAttempt()
|
||||
date = ticket.getTime()
|
||||
ip = ticket.getIP()
|
||||
ip = ticket.getID()
|
||||
matches = ticket.getMatches()
|
||||
return (ip, attempts, date, matches)
|
||||
|
||||
|
@ -1460,7 +1460,7 @@ def get_monitor_failures_journal_testcase(Filter_): # pragma: systemd no cover
|
|||
self.assertTrue(ticket)
|
||||
|
||||
attempts = ticket.getAttempt()
|
||||
ip = ticket.getIP()
|
||||
ip = ticket.getID()
|
||||
ticket.getMatches()
|
||||
|
||||
self.assertEqual(ip, test_ip)
|
||||
|
@ -1646,7 +1646,7 @@ def get_monitor_failures_journal_testcase(Filter_): # pragma: systemd no cover
|
|||
self.waitForTicks(1)
|
||||
self.waitFailTotal(6, 10)
|
||||
self.assertTrue(Utils.wait_for(lambda: len(self.jail) == 2, 10))
|
||||
self.assertSortedEqual([self.jail.getFailTicket().getIP(), self.jail.getFailTicket().getIP()],
|
||||
self.assertSortedEqual([self.jail.getFailTicket().getID(), self.jail.getFailTicket().getID()],
|
||||
["192.0.2.1", "192.0.2.2"])
|
||||
|
||||
cls = MonitorJournalFailures
|
||||
|
|
|
@ -367,14 +367,14 @@ class BanTimeIncrDB(LogCaptureTestCase):
|
|||
# this old ticket should be removed now:
|
||||
restored_tickets = self.db.getCurrentBans(fromtime=stime, correctBanTime=False)
|
||||
self.assertEqual(len(restored_tickets), 2)
|
||||
self.assertEqual(restored_tickets[0].getIP(), ip)
|
||||
self.assertEqual(restored_tickets[0].getID(), ip)
|
||||
|
||||
# purge remove 1st ip
|
||||
self.db._purgeAge = -48*60*60
|
||||
self.db.purge()
|
||||
restored_tickets = self.db.getCurrentBans(fromtime=stime, correctBanTime=False)
|
||||
self.assertEqual(len(restored_tickets), 1)
|
||||
self.assertEqual(restored_tickets[0].getIP(), ip+'1')
|
||||
self.assertEqual(restored_tickets[0].getID(), ip+'1')
|
||||
|
||||
# this should purge all bans, bips and logs - nothing should be found now
|
||||
self.db._purgeAge = -240*60*60
|
||||
|
|
|
@ -39,6 +39,7 @@ class TicketTests(unittest.TestCase):
|
|||
|
||||
# Ticket
|
||||
t = Ticket('193.168.0.128', tm, matches)
|
||||
self.assertEqual(t.getID(), '193.168.0.128')
|
||||
self.assertEqual(t.getIP(), '193.168.0.128')
|
||||
self.assertEqual(t.getTime(), tm)
|
||||
self.assertEqual(t.getMatches(), matches2)
|
||||
|
@ -65,6 +66,7 @@ class TicketTests(unittest.TestCase):
|
|||
matches = ['first', 'second']
|
||||
ft = FailTicket('193.168.0.128', tm, matches)
|
||||
ft.setBanTime(60*60)
|
||||
self.assertEqual(ft.getID(), '193.168.0.128')
|
||||
self.assertEqual(ft.getIP(), '193.168.0.128')
|
||||
self.assertEqual(ft.getTime(), tm)
|
||||
self.assertEqual(ft.getMatches(), matches2)
|
||||
|
@ -116,6 +118,17 @@ class TicketTests(unittest.TestCase):
|
|||
self.assertEqual(ft2.getTime(), ft.getTime())
|
||||
self.assertEqual(ft2.getBanTime(), ft.getBanTime())
|
||||
|
||||
def testDiffIDAndIPTicket(self):
|
||||
tm = MyTime.time()
|
||||
# different ID (string) and IP:
|
||||
t = Ticket('123-456-678', tm, data={'ip':'192.0.2.1'})
|
||||
self.assertEqual(t.getID(), '123-456-678')
|
||||
self.assertEqual(t.getIP(), '192.0.2.1')
|
||||
# different ID (tuple) and IP:
|
||||
t = Ticket(('192.0.2.1', '5000'), tm, data={'ip':'192.0.2.1'})
|
||||
self.assertEqual(t.getID(), ('192.0.2.1', '5000'))
|
||||
self.assertEqual(t.getIP(), '192.0.2.1')
|
||||
|
||||
def testTicketFlags(self):
|
||||
flags = ('restored', 'banned')
|
||||
ticket = Ticket('test', 0)
|
||||
|
|
Loading…
Reference in New Issue