ENH: Pass date time straight from systemd backend

Removes need to reparse the date time back from the ISO format
pull/527/head
Steven Hiscocks 2013-12-28 18:02:16 +00:00
parent 087af27c65
commit c80297045e
5 changed files with 83 additions and 60 deletions

View File

@ -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:

View File

@ -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:

View File

@ -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)

View File

@ -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()

View File

@ -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",