mirror of https://github.com/fail2ban/fail2ban
ENH: Pass date time straight from systemd backend
Removes need to reparse the date time back from the ISO formatpull/527/head
parent
087af27c65
commit
c80297045e
|
@ -239,7 +239,9 @@ class Fail2banRegex(object):
|
||||||
if not self._datepattern_set:
|
if not self._datepattern_set:
|
||||||
self._filter.setDatePattern(pattern)
|
self._filter.setDatePattern(pattern)
|
||||||
self._datepattern_set = True
|
self._datepattern_set = True
|
||||||
print "Use datepattern : %s" % self._filter.getDatePattern()[1]
|
if pattern is not None:
|
||||||
|
print "Use datepattern : %s" % (
|
||||||
|
self._filter.getDatePattern()[1], )
|
||||||
|
|
||||||
def setMaxLines(self, v):
|
def setMaxLines(self, v):
|
||||||
if not self._maxlines_set:
|
if not self._maxlines_set:
|
||||||
|
@ -318,11 +320,11 @@ class Fail2banRegex(object):
|
||||||
return False
|
return False
|
||||||
return found
|
return found
|
||||||
|
|
||||||
def testRegex(self, line):
|
def testRegex(self, line, date=None):
|
||||||
orgLineBuffer = self._filter._Filter__lineBuffer
|
orgLineBuffer = self._filter._Filter__lineBuffer
|
||||||
fullBuffer = len(orgLineBuffer) >= self._filter.getMaxLines()
|
fullBuffer = len(orgLineBuffer) >= self._filter.getMaxLines()
|
||||||
try:
|
try:
|
||||||
line, ret = self._filter.processLine(line, checkAllRegex=True)
|
line, ret = self._filter.processLine(line, date, checkAllRegex=True)
|
||||||
for match in ret:
|
for match in ret:
|
||||||
# Append True/False flag depending if line was matched by
|
# Append True/False flag depending if line was matched by
|
||||||
# more than one regex
|
# more than one regex
|
||||||
|
@ -353,11 +355,16 @@ class Fail2banRegex(object):
|
||||||
def process(self, test_lines):
|
def process(self, test_lines):
|
||||||
|
|
||||||
for line_no, line in enumerate(test_lines):
|
for line_no, line in enumerate(test_lines):
|
||||||
line = line.strip('\r\n')
|
if isinstance(line, tuple):
|
||||||
if line.startswith('#') or not line:
|
line_datetimestripped, ret = fail2banRegex.testRegex(
|
||||||
# skip comment and empty lines
|
line[0], line[1])
|
||||||
continue
|
line = "".join(line[0])
|
||||||
line_datetimestripped, ret = fail2banRegex.testRegex(line)
|
else:
|
||||||
|
line = line.rstrip('\r\n')
|
||||||
|
if line.startswith('#') or not line:
|
||||||
|
# skip comment and empty lines
|
||||||
|
continue
|
||||||
|
line_datetimestripped, ret = fail2banRegex.testRegex(line)
|
||||||
is_ignored = fail2banRegex.testIgnoreRegex(line_datetimestripped)
|
is_ignored = fail2banRegex.testIgnoreRegex(line_datetimestripped)
|
||||||
|
|
||||||
if is_ignored:
|
if is_ignored:
|
||||||
|
@ -373,7 +380,7 @@ class Fail2banRegex(object):
|
||||||
self._line_stats.missed_lines_timeextracted.append(line_datetimestripped)
|
self._line_stats.missed_lines_timeextracted.append(line_datetimestripped)
|
||||||
self._line_stats.tested += 1
|
self._line_stats.tested += 1
|
||||||
|
|
||||||
if line_no % 10 == 0:
|
if line_no % 10 == 0 and self._filter.dateDetector is not None:
|
||||||
self._filter.dateDetector.sortTemplate()
|
self._filter.dateDetector.sortTemplate()
|
||||||
|
|
||||||
|
|
||||||
|
@ -439,12 +446,14 @@ class Fail2banRegex(object):
|
||||||
_ = print_failregexes("Ignoreregex", self._ignoreregex)
|
_ = print_failregexes("Ignoreregex", self._ignoreregex)
|
||||||
|
|
||||||
|
|
||||||
print "\nDate template hits:"
|
if self._filter.dateDetector is not None:
|
||||||
out = []
|
print "\nDate template hits:"
|
||||||
for template in self._filter.dateDetector.getTemplates():
|
out = []
|
||||||
if self._verbose or template.getHits():
|
for template in self._filter.dateDetector.getTemplates():
|
||||||
out.append("[%d] %s" % (template.getHits(), template.getName()))
|
if self._verbose or template.getHits():
|
||||||
pprint_list(out, "[# of hits] date format")
|
out.append("[%d] %s" % (
|
||||||
|
template.getHits(), template.getName()))
|
||||||
|
pprint_list(out, "[# of hits] date format")
|
||||||
|
|
||||||
print "\nLines: %s" % self._line_stats
|
print "\nLines: %s" % self._line_stats
|
||||||
|
|
||||||
|
@ -523,7 +532,7 @@ if __name__ == "__main__":
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
myjournal = journal.Reader(converters={'__CURSOR': lambda x: x})
|
myjournal = journal.Reader(converters={'__CURSOR': lambda x: x})
|
||||||
journalmatch = fail2banRegex._journalmatch
|
journalmatch = fail2banRegex._journalmatch
|
||||||
fail2banRegex.setDatePattern("ISO8601")
|
fail2banRegex.setDatePattern(None)
|
||||||
if journalmatch:
|
if journalmatch:
|
||||||
try:
|
try:
|
||||||
for element in journalmatch:
|
for element in journalmatch:
|
||||||
|
|
|
@ -136,7 +136,7 @@ class Beautifier:
|
||||||
elif inC[2] == "datepattern":
|
elif inC[2] == "datepattern":
|
||||||
msg = "Current date pattern set to: "
|
msg = "Current date pattern set to: "
|
||||||
if response is None:
|
if response is None:
|
||||||
msg = msg + "Default Detectors"
|
msg = msg + "Not set/required"
|
||||||
elif response[0] is None:
|
elif response[0] is None:
|
||||||
msg = msg + "%s" % response[1]
|
msg = msg + "%s" % response[1]
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -199,8 +199,10 @@ class Filter(JailThread):
|
||||||
# @param pattern the date template pattern
|
# @param pattern the date template pattern
|
||||||
|
|
||||||
def setDatePattern(self, pattern):
|
def setDatePattern(self, pattern):
|
||||||
dateDetector = DateDetector()
|
if pattern is None:
|
||||||
if pattern.upper() == "ISO8601":
|
self.dateDetector = None
|
||||||
|
return
|
||||||
|
elif pattern.upper() == "ISO8601":
|
||||||
template = DateISO8601()
|
template = DateISO8601()
|
||||||
template.setName("ISO8601")
|
template.setName("ISO8601")
|
||||||
elif pattern.upper() == "EPOCH":
|
elif pattern.upper() == "EPOCH":
|
||||||
|
@ -215,8 +217,8 @@ class Filter(JailThread):
|
||||||
template.setPattern(pattern[1:], anchor=True)
|
template.setPattern(pattern[1:], anchor=True)
|
||||||
else:
|
else:
|
||||||
template.setPattern(pattern, anchor=False)
|
template.setPattern(pattern, anchor=False)
|
||||||
dateDetector.appendTemplate(template)
|
self.dateDetector = DateDetector()
|
||||||
self.dateDetector = dateDetector
|
self.dateDetector.appendTemplate(template)
|
||||||
logSys.info("Date pattern set to `%r`: `%s`" %
|
logSys.info("Date pattern set to `%r`: `%s`" %
|
||||||
(pattern, template.getName()))
|
(pattern, template.getName()))
|
||||||
logSys.debug("Date pattern regex for %r: %s" %
|
logSys.debug("Date pattern regex for %r: %s" %
|
||||||
|
@ -228,17 +230,18 @@ class Filter(JailThread):
|
||||||
# @return pattern of the date template pattern
|
# @return pattern of the date template pattern
|
||||||
|
|
||||||
def getDatePattern(self):
|
def getDatePattern(self):
|
||||||
templates = self.dateDetector.getTemplates()
|
if self.dateDetector is not None:
|
||||||
if len(templates) > 1:
|
templates = self.dateDetector.getTemplates()
|
||||||
return None # Default Detectors in use
|
if len(templates) > 1:
|
||||||
elif len(templates) == 1:
|
return None, "Default Detectors"
|
||||||
if hasattr(templates[0], "getPattern"):
|
elif len(templates) == 1:
|
||||||
pattern = templates[0].getPattern()
|
if hasattr(templates[0], "getPattern"):
|
||||||
if templates[0].getRegex()[0] == "^":
|
pattern = templates[0].getPattern()
|
||||||
pattern = "^" + pattern
|
if templates[0].getRegex()[0] == "^":
|
||||||
else:
|
pattern = "^" + pattern
|
||||||
pattern = None
|
else:
|
||||||
return pattern, templates[0].getName()
|
pattern = None
|
||||||
|
return pattern, templates[0].getName()
|
||||||
|
|
||||||
##
|
##
|
||||||
# Set the maximum retry value.
|
# Set the maximum retry value.
|
||||||
|
@ -361,28 +364,32 @@ class Filter(JailThread):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def processLine(self, line, returnRawHost=False, checkAllRegex=False):
|
def processLine(self, line, date=None, returnRawHost=False,
|
||||||
|
checkAllRegex=False):
|
||||||
"""Split the time portion from log msg and return findFailures on them
|
"""Split the time portion from log msg and return findFailures on them
|
||||||
"""
|
"""
|
||||||
l = line.rstrip('\r\n')
|
if date:
|
||||||
logSys.log(7, "Working on line %r", line)
|
tupleLine = line
|
||||||
|
|
||||||
timeMatch = self.dateDetector.matchTime(l)
|
|
||||||
if timeMatch:
|
|
||||||
tupleLine = (
|
|
||||||
l[:timeMatch.start()],
|
|
||||||
l[timeMatch.start():timeMatch.end()],
|
|
||||||
l[timeMatch.end():])
|
|
||||||
else:
|
else:
|
||||||
tupleLine = (l, "", "")
|
l = line.rstrip('\r\n')
|
||||||
|
logSys.log(7, "Working on line %r", line)
|
||||||
|
|
||||||
|
timeMatch = self.dateDetector.matchTime(l)
|
||||||
|
if timeMatch:
|
||||||
|
tupleLine = (
|
||||||
|
l[:timeMatch.start()],
|
||||||
|
l[timeMatch.start():timeMatch.end()],
|
||||||
|
l[timeMatch.end():])
|
||||||
|
else:
|
||||||
|
tupleLine = (l, "", "")
|
||||||
|
|
||||||
return "".join(tupleLine[::2]), self.findFailure(
|
return "".join(tupleLine[::2]), self.findFailure(
|
||||||
tupleLine, returnRawHost, checkAllRegex)
|
tupleLine, date, returnRawHost, checkAllRegex)
|
||||||
|
|
||||||
def processLineAndAdd(self, line):
|
def processLineAndAdd(self, line, date=None):
|
||||||
"""Processes the line for failures and populates failManager
|
"""Processes the line for failures and populates failManager
|
||||||
"""
|
"""
|
||||||
for element in self.processLine(line)[1]:
|
for element in self.processLine(line, date)[1]:
|
||||||
failregex = element[0]
|
failregex = element[0]
|
||||||
ip = element[1]
|
ip = element[1]
|
||||||
unixTime = element[2]
|
unixTime = element[2]
|
||||||
|
@ -421,7 +428,8 @@ class Filter(JailThread):
|
||||||
# to find the logging time.
|
# to find the logging time.
|
||||||
# @return a dict with IP and timestamp.
|
# @return a dict with IP and timestamp.
|
||||||
|
|
||||||
def findFailure(self, tupleLine, returnRawHost=False, checkAllRegex=False):
|
def findFailure(self, tupleLine, date=None, returnRawHost=False,
|
||||||
|
checkAllRegex=False):
|
||||||
failList = list()
|
failList = list()
|
||||||
|
|
||||||
# Checks if we must ignore this line.
|
# Checks if we must ignore this line.
|
||||||
|
@ -432,7 +440,10 @@ class Filter(JailThread):
|
||||||
return failList
|
return failList
|
||||||
|
|
||||||
timeText = tupleLine[1]
|
timeText = tupleLine[1]
|
||||||
if timeText:
|
if date:
|
||||||
|
self.__lastTimeText = timeText
|
||||||
|
self.__lastDate = date
|
||||||
|
elif timeText:
|
||||||
|
|
||||||
dateTimeMatch = self.dateDetector.getTime(timeText)
|
dateTimeMatch = self.dateDetector.getTime(timeText)
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ __author__ = "Steven Hiscocks"
|
||||||
__copyright__ = "Copyright (c) 2013 Steven Hiscocks"
|
__copyright__ = "Copyright (c) 2013 Steven Hiscocks"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import logging, datetime
|
import logging, datetime, time
|
||||||
from distutils.version import LooseVersion
|
from distutils.version import LooseVersion
|
||||||
|
|
||||||
from systemd import journal
|
from systemd import journal
|
||||||
|
@ -57,7 +57,7 @@ class FilterSystemd(JournalFilter): # pragma: systemd no cover
|
||||||
# Initialise systemd-journal connection
|
# Initialise systemd-journal connection
|
||||||
self.__journal = journal.Reader(converters={'__CURSOR': lambda x: x})
|
self.__journal = journal.Reader(converters={'__CURSOR': lambda x: x})
|
||||||
self.__matches = []
|
self.__matches = []
|
||||||
self.setDatePattern("ISO8601")
|
self.setDatePattern(None)
|
||||||
logSys.debug("Created FilterSystemd")
|
logSys.debug("Created FilterSystemd")
|
||||||
|
|
||||||
|
|
||||||
|
@ -162,8 +162,7 @@ class FilterSystemd(JournalFilter): # pragma: systemd no cover
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def formatJournalEntry(logentry):
|
def formatJournalEntry(logentry):
|
||||||
logelements = [logentry.get('_SOURCE_REALTIME_TIMESTAMP',
|
logelements = [""]
|
||||||
logentry.get('__REALTIME_TIMESTAMP')).isoformat()]
|
|
||||||
if logentry.get('_HOSTNAME'):
|
if logentry.get('_HOSTNAME'):
|
||||||
logelements.append(logentry['_HOSTNAME'])
|
logelements.append(logentry['_HOSTNAME'])
|
||||||
if logentry.get('SYSLOG_IDENTIFIER'):
|
if logentry.get('SYSLOG_IDENTIFIER'):
|
||||||
|
@ -188,18 +187,22 @@ class FilterSystemd(JournalFilter): # pragma: systemd no cover
|
||||||
logelements.append(logentry.get('MESSAGE', ''))
|
logelements.append(logentry.get('MESSAGE', ''))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
logline = u" ".join(logelements) + u"\n"
|
logline = u" ".join(logelements)
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
# Python 2, so treat as string
|
# Python 2, so treat as string
|
||||||
logline = " ".join([str(logline) for logline in logelements]) + "\n"
|
logline = " ".join([str(logline) for logline in logelements])
|
||||||
except TypeError:
|
except TypeError:
|
||||||
# Python 3, one or more elements bytes
|
# Python 3, one or more elements bytes
|
||||||
logSys.warning("Error decoding log elements from journal: %s" %
|
logSys.warning("Error decoding log elements from journal: %s" %
|
||||||
repr(logelements))
|
repr(logelements))
|
||||||
logline = self._joinStrAndBytes(logelements) + "\n"
|
logline = self._joinStrAndBytes(logelements)
|
||||||
|
|
||||||
logSys.debug("Read systemd journal entry: %s" % repr(logline))
|
date = logentry.get('_SOURCE_REALTIME_TIMESTAMP',
|
||||||
return logline
|
logentry.get('__REALTIME_TIMESTAMP'))
|
||||||
|
logSys.debug("Read systemd journal entry: %r" %
|
||||||
|
"".join([date.isoformat(), logline]))
|
||||||
|
return (('', date.isoformat(), logline),
|
||||||
|
time.mktime(date.timetuple()) + date.microsecond/1.0E6)
|
||||||
|
|
||||||
##
|
##
|
||||||
# Main loop.
|
# Main loop.
|
||||||
|
@ -232,7 +235,7 @@ class FilterSystemd(JournalFilter): # pragma: systemd no cover
|
||||||
continue
|
continue
|
||||||
if logentry:
|
if logentry:
|
||||||
self.processLineAndAdd(
|
self.processLineAndAdd(
|
||||||
self.formatJournalEntry(logentry))
|
*self.formatJournalEntry(logentry))
|
||||||
self.__modified = True
|
self.__modified = True
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
@ -243,7 +246,6 @@ class FilterSystemd(JournalFilter): # pragma: systemd no cover
|
||||||
self.jail.putFailTicket(ticket)
|
self.jail.putFailTicket(ticket)
|
||||||
except FailManagerEmpty:
|
except FailManagerEmpty:
|
||||||
self.failManager.cleanup(MyTime.time())
|
self.failManager.cleanup(MyTime.time())
|
||||||
self.dateDetector.sortTemplate()
|
|
||||||
self.__modified = False
|
self.__modified = False
|
||||||
self.__journal.wait(self.getSleepTime())
|
self.__journal.wait(self.getSleepTime())
|
||||||
logSys.debug((self.jail is not None and self.jail.getName()
|
logSys.debug((self.jail is not None and self.jail.getName()
|
||||||
|
|
|
@ -203,7 +203,8 @@ class BasicFilter(unittest.TestCase):
|
||||||
self.assertEqual(self.filter.getUseDns(), 'no')
|
self.assertEqual(self.filter.getUseDns(), 'no')
|
||||||
|
|
||||||
def testGetSetDatePattern(self):
|
def testGetSetDatePattern(self):
|
||||||
self.assertEqual(self.filter.getDatePattern(), None)
|
self.assertEqual(self.filter.getDatePattern(),
|
||||||
|
(None, "Default Detectors"))
|
||||||
self.filter.setDatePattern("^%Y-%m-%d-%H%M%S.%f %z")
|
self.filter.setDatePattern("^%Y-%m-%d-%H%M%S.%f %z")
|
||||||
self.assertEqual(self.filter.getDatePattern(),
|
self.assertEqual(self.filter.getDatePattern(),
|
||||||
("^%Y-%m-%d-%H%M%S.%f %z",
|
("^%Y-%m-%d-%H%M%S.%f %z",
|
||||||
|
|
Loading…
Reference in New Issue