mirror of https://github.com/fail2ban/fail2ban
Introduced new filter option `prefregex` for pre-filtering using single regular expression;
Some filters extended with user name; [filter.d/pam-generic.conf]: grave fix injection on user name to host fixed; test-cases in testSampleRegexsFactory can now check the captured groups (using additionally fields in failJSON structure)pull/1698/head
parent
9d15a792a5
commit
4ff8d051f4
|
@ -16,7 +16,12 @@ _ttys_re=\S*
|
|||
__pam_re=\(?%(__pam_auth)s(?:\(\S+\))?\)?:?
|
||||
_daemon = \S+
|
||||
|
||||
failregex = ^%(__prefix_line)s%(__pam_re)s\s+authentication failure; logname=\S* uid=\S* euid=\S* tty=%(_ttys_re)s ruser=\S* rhost=<HOST>(?:\s+user=.*)?\s*$
|
||||
prefregex = ^%(__prefix_line)s%(__pam_re)s\s+authentication failure; logname=\S* uid=\S* euid=\S* tty=%(_ttys_re)s <F-CONTENT>.+</F-CONTENT>$
|
||||
|
||||
failregex = ^ruser=<F-USER>\S*</F-USER> rhost=<HOST>\s*$
|
||||
^ruser= rhost=<HOST>\s+user=<F-USER>\S*</F-USER>\s*$
|
||||
^ruser= rhost=<HOST>\s+user=<F-USER>.*?</F-USER>\s*$
|
||||
^ruser=<F-USER>.*?</F-USER> rhost=<HOST>\s*$
|
||||
|
||||
ignoreregex =
|
||||
|
||||
|
|
|
@ -32,23 +32,23 @@ __prefix_line_ml2 = %(__suff)s$<SKIPLINES>^(?P=__prefix)%(__pref)s
|
|||
|
||||
mode = %(normal)s
|
||||
|
||||
normal = ^%(__prefix_line_sl)s[aA]uthentication (?:failure|error|failed) for .* from <HOST>( via \S+)?\s*%(__suff)s$
|
||||
^%(__prefix_line_sl)sUser not known to the underlying authentication module for .* from <HOST>\s*%(__suff)s$
|
||||
^%(__prefix_line_sl)sFailed \S+ for (?P<cond_inv>invalid user )?(?P<user>(?P<cond_user>\S+)|(?(cond_inv)(?:(?! from ).)*?|[^:]+)) from <HOST>%(__on_port_opt)s(?: ssh\d*)?(?(cond_user): |(?:(?:(?! from ).)*)$)
|
||||
^%(__prefix_line_sl)sROOT LOGIN REFUSED.* FROM <HOST>\s*%(__suff)s$
|
||||
^%(__prefix_line_sl)s[iI](?:llegal|nvalid) user .*? from <HOST>%(__on_port_opt)s\s*$
|
||||
^%(__prefix_line_sl)sUser .+ from <HOST> not allowed because not listed in AllowUsers\s*%(__suff)s$
|
||||
^%(__prefix_line_sl)sUser .+ from <HOST> not allowed because listed in DenyUsers\s*%(__suff)s$
|
||||
^%(__prefix_line_sl)sUser .+ from <HOST> not allowed because not in any group\s*%(__suff)s$
|
||||
normal = ^%(__prefix_line_sl)s[aA]uthentication (?:failure|error|failed) for <F-USER>.*</F-USER> from <HOST>( via \S+)?\s*%(__suff)s$
|
||||
^%(__prefix_line_sl)sUser not known to the underlying authentication module for <F-USER>.*</F-USER> from <HOST>\s*%(__suff)s$
|
||||
^%(__prefix_line_sl)sFailed \S+ for (?P<cond_inv>invalid user )?<F-USER>(?P<cond_user>\S+)|(?(cond_inv)(?:(?! from ).)*?|[^:]+)</F-USER> from <HOST>%(__on_port_opt)s(?: ssh\d*)?(?(cond_user): |(?:(?:(?! from ).)*)$)
|
||||
^%(__prefix_line_sl)s<F-USER>ROOT</F-USER> LOGIN REFUSED.* FROM <HOST>\s*%(__suff)s$
|
||||
^%(__prefix_line_sl)s[iI](?:llegal|nvalid) user <F-USER>.*?</F-USER> from <HOST>%(__on_port_opt)s\s*$
|
||||
^%(__prefix_line_sl)sUser <F-USER>.+</F-USER> from <HOST> not allowed because not listed in AllowUsers\s*%(__suff)s$
|
||||
^%(__prefix_line_sl)sUser <F-USER>.+</F-USER> from <HOST> not allowed because listed in DenyUsers\s*%(__suff)s$
|
||||
^%(__prefix_line_sl)sUser <F-USER>.+</F-USER> from <HOST> not allowed because not in any group\s*%(__suff)s$
|
||||
^%(__prefix_line_sl)srefused connect from \S+ \(<HOST>\)\s*%(__suff)s$
|
||||
^%(__prefix_line_sl)sReceived disconnect from <HOST>%(__on_port_opt)s:\s*3: .*: Auth fail%(__suff)s$
|
||||
^%(__prefix_line_sl)sUser .+ from <HOST> not allowed because a group is listed in DenyGroups\s*%(__suff)s$
|
||||
^%(__prefix_line_sl)sUser .+ from <HOST> not allowed because none of user's groups are listed in AllowGroups\s*%(__suff)s$
|
||||
^%(__prefix_line_sl)spam_unix\(sshd:auth\):\s+authentication failure;\s*logname=\S*\s*uid=\d*\s*euid=\d*\s*tty=\S*\s*ruser=\S*\s*rhost=<HOST>\s.*%(__suff)s$
|
||||
^%(__prefix_line_sl)s(error: )?maximum authentication attempts exceeded for .* from <HOST>%(__on_port_opt)s(?: ssh\d*)? \[preauth\]$
|
||||
^%(__prefix_line_ml1)sUser .+ not allowed because account is locked%(__prefix_line_ml2)sReceived disconnect from <HOST>: 11: .+%(__suff)s$
|
||||
^%(__prefix_line_ml1)sDisconnecting: Too many authentication failures for .+?%(__prefix_line_ml2)sConnection closed by <HOST>%(__suff)s$
|
||||
^%(__prefix_line_ml1)sConnection from <HOST>%(__on_port_opt)s%(__prefix_line_ml2)sDisconnecting: Too many authentication failures for .+%(__suff)s$
|
||||
^%(__prefix_line_sl)sUser <F-USER>.+</F-USER> from <HOST> not allowed because a group is listed in DenyGroups\s*%(__suff)s$
|
||||
^%(__prefix_line_sl)sUser <F-USER>.+</F-USER> from <HOST> not allowed because none of user's groups are listed in AllowGroups\s*%(__suff)s$
|
||||
^%(__prefix_line_sl)spam_unix\(sshd:auth\):\s+authentication failure;\s*logname=\S*\s*uid=\d*\s*euid=\d*\s*tty=\S*\s*ruser=<F-USER>\S*</F-USER>\s*rhost=<HOST>\s.*%(__suff)s$
|
||||
^%(__prefix_line_sl)s(error: )?maximum authentication attempts exceeded for <F-USER>.*</F-USER> from <HOST>%(__on_port_opt)s(?: ssh\d*)? \[preauth\]$
|
||||
^%(__prefix_line_ml1)sUser <F-USER>.+</F-USER> not allowed because account is locked%(__prefix_line_ml2)sReceived disconnect from <HOST>: 11: .+%(__suff)s$
|
||||
^%(__prefix_line_ml1)sDisconnecting: Too many authentication failures for <F-USER>.+?</F-USER>%(__prefix_line_ml2)sConnection closed by <HOST>%(__suff)s$
|
||||
^%(__prefix_line_ml1)sConnection from <HOST>%(__on_port_opt)s%(__prefix_line_ml2)sDisconnecting: Too many authentication failures for <F-USER>.+</F-USER>%(__suff)s$
|
||||
|
||||
ddos = ^%(__prefix_line_sl)sDid not receive identification string from <HOST>%(__suff)s$
|
||||
^%(__prefix_line_sl)sReceived disconnect from <HOST>%(__on_port_opt)s:\s*14: No supported authentication methods available%(__suff)s$
|
||||
|
|
|
@ -271,7 +271,7 @@ class Fail2banRegex(object):
|
|||
def readRegex(self, value, regextype):
|
||||
assert(regextype in ('fail', 'ignore'))
|
||||
regex = regextype + 'regex'
|
||||
if os.path.isfile(value) or os.path.isfile(value + '.conf'):
|
||||
if regextype == 'fail' and (os.path.isfile(value) or os.path.isfile(value + '.conf')):
|
||||
if os.path.basename(os.path.dirname(value)) == 'filter.d':
|
||||
## within filter.d folder - use standard loading algorithm to load filter completely (with .local etc.):
|
||||
basedir = os.path.dirname(os.path.dirname(value))
|
||||
|
@ -291,43 +291,51 @@ class Fail2banRegex(object):
|
|||
return False
|
||||
reader.getOptions(None)
|
||||
readercommands = reader.convert()
|
||||
regex_values = [
|
||||
RegexStat(m[3])
|
||||
for m in filter(
|
||||
lambda x: x[0] == 'set' and x[2] == "add%sregex" % regextype,
|
||||
readercommands)
|
||||
] + [
|
||||
RegexStat(m)
|
||||
for mm in filter(
|
||||
lambda x: x[0] == 'multi-set' and x[2] == "add%sregex" % regextype,
|
||||
readercommands)
|
||||
for m in mm[3]
|
||||
]
|
||||
# Read out and set possible value of maxlines
|
||||
for command in readercommands:
|
||||
if command[2] == "maxlines":
|
||||
maxlines = int(command[3])
|
||||
|
||||
regex_values = {}
|
||||
for opt in readercommands:
|
||||
if opt[0] == 'multi-set':
|
||||
optval = opt[3]
|
||||
elif opt[0] == 'set':
|
||||
optval = [opt[3]]
|
||||
else:
|
||||
continue
|
||||
for optval in optval:
|
||||
try:
|
||||
self.setMaxLines(maxlines)
|
||||
except ValueError:
|
||||
output( "ERROR: Invalid value for maxlines (%(maxlines)r) " \
|
||||
"read from %(value)s" % locals() )
|
||||
if opt[2] == "prefregex":
|
||||
self._filter.prefRegex = optval
|
||||
elif opt[2] == "addfailregex":
|
||||
stor = regex_values.get('fail')
|
||||
if not stor: stor = regex_values['fail'] = list()
|
||||
stor.append(RegexStat(optval))
|
||||
#self._filter.addFailRegex(optval)
|
||||
elif opt[2] == "addignoreregex":
|
||||
stor = regex_values.get('ignore')
|
||||
if not stor: stor = regex_values['ignore'] = list()
|
||||
stor.append(RegexStat(optval))
|
||||
#self._filter.addIgnoreRegex(optval)
|
||||
elif opt[2] == "maxlines":
|
||||
self.setMaxLines(optval)
|
||||
elif opt[2] == "datepattern":
|
||||
self.setDatePattern(optval)
|
||||
elif opt[2] == "addjournalmatch":
|
||||
self.setJournalMatch(optval)
|
||||
except ValueError as e: # pragma: no cover
|
||||
output( "ERROR: Invalid value for %s (%r) " \
|
||||
"read from %s: %s" % (opt[2], optval, value, e) )
|
||||
return False
|
||||
elif command[2] == 'addjournalmatch':
|
||||
journalmatch = command[3:]
|
||||
self.setJournalMatch(journalmatch)
|
||||
elif command[2] == 'datepattern':
|
||||
datepattern = command[3]
|
||||
self.setDatePattern(datepattern)
|
||||
|
||||
else:
|
||||
output( "Use %11s line : %s" % (regex, shortstr(value)) )
|
||||
regex_values = [RegexStat(value)]
|
||||
regex_values = {regextype: [RegexStat(value)]}
|
||||
|
||||
setattr(self, "_" + regex, regex_values)
|
||||
for regex in regex_values:
|
||||
getattr(
|
||||
self._filter,
|
||||
'add%sRegex' % regextype.title())(regex.getFailRegex())
|
||||
for regextype, regex_values in regex_values.iteritems():
|
||||
regex = regextype + 'regex'
|
||||
setattr(self, "_" + regex, regex_values)
|
||||
for regex in regex_values:
|
||||
getattr(
|
||||
self._filter,
|
||||
'add%sRegex' % regextype.title())(regex.getFailRegex())
|
||||
return True
|
||||
|
||||
def testIgnoreRegex(self, line):
|
||||
|
|
|
@ -37,6 +37,7 @@ logSys = getLogger(__name__)
|
|||
class FilterReader(DefinitionInitConfigReader):
|
||||
|
||||
_configOpts = {
|
||||
"prefregex": ["string", None],
|
||||
"ignoreregex": ["string", None],
|
||||
"failregex": ["string", ""],
|
||||
"maxlines": ["int", None],
|
||||
|
@ -72,8 +73,8 @@ class FilterReader(DefinitionInitConfigReader):
|
|||
# We warn when multiline regex is used without maxlines > 1
|
||||
# therefore keep sure we set this option first.
|
||||
stream.insert(0, ["set", self._jailName, "maxlines", value])
|
||||
elif opt == 'datepattern':
|
||||
stream.append(["set", self._jailName, "datepattern", value])
|
||||
elif opt in ('datepattern', 'prefregex'):
|
||||
stream.append(["set", self._jailName, opt, value])
|
||||
# Do not send a command if the match is empty.
|
||||
elif opt == 'journalmatch':
|
||||
if value is None: continue
|
||||
|
|
|
@ -189,40 +189,45 @@ class Regex:
|
|||
# method of this object.
|
||||
# @param a list of tupples. The tupples are ( prematch, datematch, postdatematch )
|
||||
|
||||
def search(self, tupleLines):
|
||||
def search(self, tupleLines, orgLines=None):
|
||||
self._matchCache = self._regexObj.search(
|
||||
"\n".join("".join(value[::2]) for value in tupleLines) + "\n")
|
||||
if self.hasMatched():
|
||||
# Find start of the first line where the match was found
|
||||
try:
|
||||
self._matchLineStart = self._matchCache.string.rindex(
|
||||
"\n", 0, self._matchCache.start() +1 ) + 1
|
||||
except ValueError:
|
||||
self._matchLineStart = 0
|
||||
# Find end of the last line where the match was found
|
||||
try:
|
||||
self._matchLineEnd = self._matchCache.string.index(
|
||||
"\n", self._matchCache.end() - 1) + 1
|
||||
except ValueError:
|
||||
self._matchLineEnd = len(self._matchCache.string)
|
||||
if self._matchCache:
|
||||
if orgLines is None: orgLines = tupleLines
|
||||
# if single-line:
|
||||
if len(orgLines) <= 1:
|
||||
self._matchedTupleLines = orgLines
|
||||
self._unmatchedTupleLines = []
|
||||
else:
|
||||
# Find start of the first line where the match was found
|
||||
try:
|
||||
matchLineStart = self._matchCache.string.rindex(
|
||||
"\n", 0, self._matchCache.start() +1 ) + 1
|
||||
except ValueError:
|
||||
matchLineStart = 0
|
||||
# Find end of the last line where the match was found
|
||||
try:
|
||||
matchLineEnd = self._matchCache.string.index(
|
||||
"\n", self._matchCache.end() - 1) + 1
|
||||
except ValueError:
|
||||
matchLineEnd = len(self._matchCache.string)
|
||||
|
||||
lineCount1 = self._matchCache.string.count(
|
||||
"\n", 0, self._matchLineStart)
|
||||
lineCount2 = self._matchCache.string.count(
|
||||
"\n", 0, self._matchLineEnd)
|
||||
self._matchedTupleLines = tupleLines[lineCount1:lineCount2]
|
||||
self._unmatchedTupleLines = tupleLines[:lineCount1]
|
||||
|
||||
n = 0
|
||||
for skippedLine in self.getSkippedLines():
|
||||
for m, matchedTupleLine in enumerate(
|
||||
self._matchedTupleLines[n:]):
|
||||
if "".join(matchedTupleLine[::2]) == skippedLine:
|
||||
self._unmatchedTupleLines.append(
|
||||
self._matchedTupleLines.pop(n+m))
|
||||
n += m
|
||||
break
|
||||
self._unmatchedTupleLines.extend(tupleLines[lineCount2:])
|
||||
lineCount1 = self._matchCache.string.count(
|
||||
"\n", 0, matchLineStart)
|
||||
lineCount2 = self._matchCache.string.count(
|
||||
"\n", 0, matchLineEnd)
|
||||
self._matchedTupleLines = orgLines[lineCount1:lineCount2]
|
||||
self._unmatchedTupleLines = orgLines[:lineCount1]
|
||||
n = 0
|
||||
for skippedLine in self.getSkippedLines():
|
||||
for m, matchedTupleLine in enumerate(
|
||||
self._matchedTupleLines[n:]):
|
||||
if "".join(matchedTupleLine[::2]) == skippedLine:
|
||||
self._unmatchedTupleLines.append(
|
||||
self._matchedTupleLines.pop(n+m))
|
||||
n += m
|
||||
break
|
||||
self._unmatchedTupleLines.extend(orgLines[lineCount2:])
|
||||
|
||||
# Checks if the previous call to search() matched.
|
||||
#
|
||||
|
@ -234,6 +239,13 @@ class Regex:
|
|||
else:
|
||||
return False
|
||||
|
||||
##
|
||||
# Returns all matched groups.
|
||||
#
|
||||
|
||||
def getGroups(self):
|
||||
return self._matchCache.groupdict()
|
||||
|
||||
##
|
||||
# Returns skipped lines.
|
||||
#
|
||||
|
@ -332,13 +344,6 @@ class FailRegex(Regex):
|
|||
if not [grp for grp in FAILURE_ID_GROPS if grp in self._regexObj.groupindex]:
|
||||
raise RegexException("No failure-id group in '%s'" % self._regex)
|
||||
|
||||
##
|
||||
# Returns all matched groups.
|
||||
#
|
||||
|
||||
def getGroups(self):
|
||||
return self._matchCache.groupdict()
|
||||
|
||||
##
|
||||
# Returns the matched failure id.
|
||||
#
|
||||
|
|
|
@ -65,6 +65,8 @@ class Filter(JailThread):
|
|||
self.jail = jail
|
||||
## The failures manager.
|
||||
self.failManager = FailManager()
|
||||
## Regular expression pre-filtering matching the failures.
|
||||
self.__prefRegex = None
|
||||
## The regular expression list matching the failures.
|
||||
self.__failRegex = list()
|
||||
## The regular expression list with expressions to ignore.
|
||||
|
@ -129,6 +131,16 @@ class Filter(JailThread):
|
|||
self.delLogPath(path)
|
||||
delattr(self, '_reload_logs')
|
||||
|
||||
@property
|
||||
def prefRegex(self):
|
||||
return self.__prefRegex
|
||||
@prefRegex.setter
|
||||
def prefRegex(self, value):
|
||||
if value:
|
||||
self.__prefRegex = Regex(value, useDns=self.__useDns)
|
||||
else:
|
||||
self.__prefRegex = None
|
||||
|
||||
##
|
||||
# Add a regular expression which matches the failure.
|
||||
#
|
||||
|
@ -582,13 +594,30 @@ class Filter(JailThread):
|
|||
date, MyTime.time(), self.getFindTime())
|
||||
return failList
|
||||
|
||||
self.__lineBuffer = (
|
||||
self.__lineBuffer + [tupleLine[:3]])[-self.__lineBufferSize:]
|
||||
logSys.log(5, "Looking for failregex match of %r" % self.__lineBuffer)
|
||||
if self.__lineBufferSize > 1:
|
||||
orgBuffer = self.__lineBuffer = (
|
||||
self.__lineBuffer + [tupleLine[:3]])[-self.__lineBufferSize:]
|
||||
else:
|
||||
orgBuffer = self.__lineBuffer = [tupleLine[:3]]
|
||||
logSys.log(5, "Looking for failregex match of %r", self.__lineBuffer)
|
||||
|
||||
# Pre-filter fail regex (if available):
|
||||
preGroups = {}
|
||||
if self.__prefRegex:
|
||||
failRegex = self.__prefRegex.search(self.__lineBuffer)
|
||||
if not self.__prefRegex.hasMatched():
|
||||
return failList
|
||||
logSys.log(7, "Pre-filter matched %s", failRegex)
|
||||
preGroups = self.__prefRegex.getGroups()
|
||||
repl = preGroups.get('content')
|
||||
# Content replacement:
|
||||
if repl:
|
||||
del preGroups['content']
|
||||
self.__lineBuffer = [('', '', repl)]
|
||||
|
||||
# Iterates over all the regular expressions.
|
||||
for failRegexIndex, failRegex in enumerate(self.__failRegex):
|
||||
failRegex.search(self.__lineBuffer)
|
||||
failRegex.search(self.__lineBuffer, orgBuffer)
|
||||
if failRegex.hasMatched():
|
||||
# The failregex matched.
|
||||
logSys.log(7, "Matched %s", failRegex)
|
||||
|
@ -617,7 +646,11 @@ class Filter(JailThread):
|
|||
# retrieve failure-id, host, etc from failure match:
|
||||
raw = returnRawHost
|
||||
try:
|
||||
fail = failRegex.getGroups()
|
||||
if preGroups:
|
||||
fail = preGroups.copy()
|
||||
fail.update(failRegex.getGroups())
|
||||
else:
|
||||
fail = failRegex.getGroups()
|
||||
# failure-id:
|
||||
fid = fail.get('fid')
|
||||
# ip-address or host:
|
||||
|
|
|
@ -379,6 +379,14 @@ class Server:
|
|||
def getIgnoreCommand(self, name):
|
||||
return self.__jails[name].filter.getIgnoreCommand()
|
||||
|
||||
def setPrefRegex(self, name, value):
|
||||
flt = self.__jails[name].filter
|
||||
logSys.debug(" prefregex: %r", value)
|
||||
flt.prefRegex = value
|
||||
|
||||
def getPrefRegex(self, name):
|
||||
return self.__jails[name].filter.prefRegex
|
||||
|
||||
def addFailRegex(self, name, value, multiple=False):
|
||||
flt = self.__jails[name].filter
|
||||
if not multiple: value = (value,)
|
||||
|
|
|
@ -221,6 +221,10 @@ class Transmitter:
|
|||
value = command[2:]
|
||||
self.__server.delJournalMatch(name, value)
|
||||
return self.__server.getJournalMatch(name)
|
||||
elif command[1] == "prefregex":
|
||||
value = command[2]
|
||||
self.__server.setPrefRegex(name, value)
|
||||
return self.__server.getPrefRegex(name)
|
||||
elif command[1] == "addfailregex":
|
||||
value = command[2]
|
||||
self.__server.addFailRegex(name, value, multiple=multiple)
|
||||
|
@ -341,6 +345,8 @@ class Transmitter:
|
|||
return self.__server.getIgnoreIP(name)
|
||||
elif command[1] == "ignorecommand":
|
||||
return self.__server.getIgnoreCommand(name)
|
||||
elif command[1] == "prefregex":
|
||||
return self.__server.getPrefRegex(name)
|
||||
elif command[1] == "failregex":
|
||||
return self.__server.getFailRegex(name)
|
||||
elif command[1] == "ignoreregex":
|
||||
|
|
|
@ -1,17 +1,23 @@
|
|||
# failJSON: { "time": "2005-02-07T15:10:42", "match": true , "host": "192.168.1.1" }
|
||||
# failJSON: { "time": "2005-02-07T15:10:42", "match": true , "host": "192.168.1.1", "user": "sample-user" }
|
||||
Feb 7 15:10:42 example pure-ftpd: (pam_unix) authentication failure; logname= uid=0 euid=0 tty=pure-ftpd ruser=sample-user rhost=192.168.1.1
|
||||
# failJSON: { "time": "2005-05-12T09:47:54", "match": true , "host": "71-13-115-12.static.mdsn.wi.charter.com" }
|
||||
# failJSON: { "time": "2005-05-12T09:47:54", "match": true , "host": "71-13-115-12.static.mdsn.wi.charter.com", "user": "root" }
|
||||
May 12 09:47:54 vaio sshd[16004]: (pam_unix) authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=71-13-115-12.static.mdsn.wi.charter.com user=root
|
||||
# failJSON: { "time": "2005-05-12T09:48:03", "match": true , "host": "71-13-115-12.static.mdsn.wi.charter.com" }
|
||||
May 12 09:48:03 vaio sshd[16021]: (pam_unix) authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=71-13-115-12.static.mdsn.wi.charter.com
|
||||
# failJSON: { "time": "2005-05-15T18:02:12", "match": true , "host": "66.232.129.62" }
|
||||
# failJSON: { "time": "2005-05-15T18:02:12", "match": true , "host": "66.232.129.62", "user": "mark" }
|
||||
May 15 18:02:12 localhost proftpd: (pam_unix) authentication failure; logname= uid=0 euid=0 tty= ruser= rhost=66.232.129.62 user=mark
|
||||
|
||||
# linux-pam messages before commit f0f9c4479303b5a9c37667cf07f58426dc081676 (release 0.99.2.0 ) - nolonger supported
|
||||
# failJSON: { "time": "2004-11-25T17:12:13", "match": false }
|
||||
Nov 25 17:12:13 webmail pop(pam_unix)[4920]: authentication failure; logname= uid=0 euid=0 tty= ruser= rhost=192.168.10.3 user=mailuser
|
||||
|
||||
# failJSON: { "time": "2005-07-19T18:11:26", "match": true , "host": "www.google.com" }
|
||||
# failJSON: { "time": "2005-07-19T18:11:26", "match": true , "host": "www.google.com", "user": "an8767" }
|
||||
Jul 19 18:11:26 srv2 vsftpd: pam_unix(vsftpd:auth): authentication failure; logname= uid=0 euid=0 tty=ftp ruser=an8767 rhost=www.google.com
|
||||
# failJSON: { "time": "2005-07-19T18:11:26", "match": true , "host": "www.google.com" }
|
||||
Jul 19 18:11:26 srv2 vsftpd: pam_unix: authentication failure; logname= uid=0 euid=0 tty=ftp ruser=an8767 rhost=www.google.com
|
||||
|
||||
|
||||
# failJSON: { "time": "2005-07-19T18:11:50", "match": true , "host": "192.0.2.1", "user": "test rhost=192.0.2.151", "desc": "Injecting on username"}
|
||||
Jul 19 18:11:50 srv2 daemon: pam_unix(auth): authentication failure; logname= uid=0 euid=0 tty=xxx ruser=test rhost=192.0.2.151 rhost=192.0.2.1
|
||||
# failJSON: { "time": "2005-07-19T18:11:52", "match": true , "host": "192.0.2.2", "user": "test rhost=192.0.2.152", "desc": "Injecting on username after host"}
|
||||
Jul 19 18:11:52 srv2 daemon: pam_unix(auth): authentication failure; logname= uid=0 euid=0 tty=xxx ruser= rhost=192.0.2.2 user=test rhost=192.0.2.152
|
||||
|
|
|
@ -102,7 +102,9 @@ def testSampleRegexsFactory(name, basedir):
|
|||
else:
|
||||
continue
|
||||
for optval in optval:
|
||||
if opt[2] == "addfailregex":
|
||||
if opt[2] == "prefregex":
|
||||
self.filter.prefRegex = optval
|
||||
elif opt[2] == "addfailregex":
|
||||
self.filter.addFailRegex(optval)
|
||||
elif opt[2] == "addignoreregex":
|
||||
self.filter.addIgnoreRegex(optval)
|
||||
|
@ -126,7 +128,7 @@ def testSampleRegexsFactory(name, basedir):
|
|||
# test regexp contains greedy catch-all before <HOST>, that is
|
||||
# not hard-anchored at end or has not precise sub expression after <HOST>:
|
||||
for fr in self.filter.getFailRegex():
|
||||
if RE_WRONG_GREED.search(fr): #pragma: no cover
|
||||
if RE_WRONG_GREED.search(fr): # pragma: no cover
|
||||
raise AssertionError("Following regexp of \"%s\" contains greedy catch-all before <HOST>, "
|
||||
"that is not hard-anchored at end or has not precise sub expression after <HOST>:\n%s" %
|
||||
(name, str(fr).replace(RE_HOST, '<HOST>')))
|
||||
|
@ -152,12 +154,12 @@ def testSampleRegexsFactory(name, basedir):
|
|||
if not ret:
|
||||
# Check line is flagged as none match
|
||||
self.assertFalse(faildata.get('match', True),
|
||||
"Line not matched when should have: %s:%i %r" %
|
||||
"Line not matched when should have: %s:%i, line:\n%s" %
|
||||
(logFile.filename(), logFile.filelineno(), line))
|
||||
elif ret:
|
||||
# Check line is flagged to match
|
||||
self.assertTrue(faildata.get('match', False),
|
||||
"Line matched when shouldn't have: %s:%i %r" %
|
||||
"Line matched when shouldn't have: %s:%i, line:\n%s" %
|
||||
(logFile.filename(), logFile.filelineno(), line))
|
||||
self.assertEqual(len(ret), 1, "Multiple regexs matched %r - %s:%i" %
|
||||
(map(lambda x: x[0], ret),logFile.filename(), logFile.filelineno()))
|
||||
|
@ -165,6 +167,12 @@ def testSampleRegexsFactory(name, basedir):
|
|||
# Verify timestamp and host as expected
|
||||
failregex, host, fail2banTime, lines, fail = ret[0]
|
||||
self.assertEqual(host, faildata.get("host", None))
|
||||
# Verify other captures:
|
||||
for k, v in faildata.iteritems():
|
||||
if k not in ("time", "match", "host", "desc"):
|
||||
fv = fail.get(k, None)
|
||||
self.assertEqual(fv, v, "Value of %s mismatch %r != %r on: %s:%i, line:\n%s" % (
|
||||
k, fv, v, logFile.filename(), logFile.filelineno(), line))
|
||||
|
||||
t = faildata.get("time", None)
|
||||
try:
|
||||
|
@ -177,7 +185,7 @@ def testSampleRegexsFactory(name, basedir):
|
|||
jsonTime += jsonTimeLocal.microsecond / 1000000
|
||||
|
||||
self.assertEqual(fail2banTime, jsonTime,
|
||||
"UTC Time mismatch fail2ban %s (%s) != failJson %s (%s) (diff %.3f seconds) on: %s:%i %r:" %
|
||||
"UTC Time mismatch %s (%s) != %s (%s) (diff %.3f seconds) on: %s:%i, line:\n%s" %
|
||||
(fail2banTime, time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime(fail2banTime)),
|
||||
jsonTime, time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime(jsonTime)),
|
||||
fail2banTime - jsonTime, logFile.filename(), logFile.filelineno(), line ) )
|
||||
|
|
Loading…
Reference in New Issue