From 174ce7027a2fe311ff94b549d5acc123dc22bfed Mon Sep 17 00:00:00 2001 From: Cyril Jaquier Date: Tue, 4 Mar 2008 00:17:56 +0000 Subject: [PATCH] - Fixed fail2ban-regex. It support "includes" in configuration files. - Modified "includes" to be more generic. We will probably support URL in the future. - Small refactoring. git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/branches/FAIL2BAN-0_8@656 a942ae1a-1317-0410-a47c-b1dcaea8d605 --- client/configparserinc.py | 66 +++++++++++++++++++++---------------- client/configreader.py | 4 +-- config/filter.d/common.conf | 2 +- config/filter.d/sshd.conf | 2 +- fail2ban-regex | 2 +- server/filter.py | 44 +++++-------------------- 6 files changed, 51 insertions(+), 69 deletions(-) diff --git a/client/configparserinc.py b/client/configparserinc.py index 44289c4e..2ece4231 100644 --- a/client/configparserinc.py +++ b/client/configparserinc.py @@ -15,6 +15,7 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Author: Yaroslav Halchenko +# Modified: Cyril Jaquier # $Revision$ __author__ = 'Yaroslav Halhenko' @@ -23,8 +24,11 @@ __date__ = '$Date: $' __copyright__ = 'Copyright (c) 2007 Yaroslav Halchenko' __license__ = 'GPL' +import logging, os from ConfigParser import SafeConfigParser -from ConfigParser import NoOptionError, NoSectionError + +# Gets the instance of the logger. +logSys = logging.getLogger("fail2ban.client.config") class SafeConfigParserWithIncludes(SafeConfigParser): """ @@ -38,10 +42,10 @@ class SafeConfigParserWithIncludes(SafeConfigParser): Example: [INCLUDES] -files_before = 1.conf +before = 1.conf 3.conf -files_after = 1.conf +after = 1.conf It is a simple implementation, so just basic care is taken about recursion. Includes preserve right order, ie new files are @@ -55,35 +59,42 @@ files_after = 1.conf """ + SECTION_NAME = "INCLUDES" + #@staticmethod - def getIncludedFiles(filename, sectionName='INCLUDES', - defaults={}, seen=[]): + def getIncludes(resource, seen = []): """ - Given 1 config filename returns list of included files + Given 1 config resource returns list of included files (recursively) with the original one as well Simple loops are taken care about """ - filenames = [] - #print "Opening file " + filename - d = defaults.copy() # so that we do not poison our defaults - parser = SafeConfigParser(defaults = d) - parser.read(filename) - newFiles = [ ('files_before', []), ('files_after', []) ] - if sectionName in parser.sections(): + + # Use a short class name ;) + SCPWI = SafeConfigParserWithIncludes + + parser = SafeConfigParser() + parser.read(resource) + + resourceDir = os.path.dirname(resource) + + newFiles = [ ('before', []), ('after', []) ] + if SCPWI.SECTION_NAME in parser.sections(): for option_name, option_list in newFiles: - if option_name in parser.options(sectionName): - newFileNames = parser.get(sectionName, option_name) - for newFileName in newFileNames.split('\n'): - if newFileName in seen: continue - option_list += SafeConfigParserWithIncludes.\ - getIncludedFiles(newFileName, - defaults=defaults, - seen=seen + [filename]) + if option_name in parser.options(SCPWI.SECTION_NAME): + newResources = parser.get(SCPWI.SECTION_NAME, option_name) + for newResource in newResources.split('\n'): + if os.path.isabs(newResource): + r = newResource + else: + r = "%s/%s" % (resourceDir, newResource) + if r in seen: + continue + s = seen + [resource] + option_list += SCPWI.getIncludes(r, s) # combine lists - filenames = newFiles[0][1] + [filename] + newFiles[1][1] - #print "Includes list for " + filename + " is " + `filenames` - return filenames - getIncludedFiles = staticmethod(getIncludedFiles) + return newFiles[0][1] + [resource] + newFiles[1][1] + #print "Includes list for " + resource + " is " + `resources` + getIncludes = staticmethod(getIncludes) def read(self, filenames): @@ -91,8 +102,7 @@ files_after = 1.conf if not isinstance(filenames, list): filenames = [ filenames ] for filename in filenames: - fileNamesFull += SafeConfigParserWithIncludes.\ - getIncludedFiles(filename, defaults=self._defaults) - #print "Opening config files " + `fileNamesFull` + fileNamesFull += SafeConfigParserWithIncludes.getIncludes(filename) + logSys.debug("Reading files: %s" % fileNamesFull) return SafeConfigParser.read(self, fileNamesFull) diff --git a/client/configreader.py b/client/configreader.py index 8df1db34..f8e9ade8 100644 --- a/client/configreader.py +++ b/client/configreader.py @@ -36,9 +36,7 @@ class ConfigReader(SafeConfigParserWithIncludes): BASE_DIRECTORY = "/etc/fail2ban/" def __init__(self): - SafeConfigParserWithIncludes.__init__(self, - {'configpath' : \ - ConfigReader.BASE_DIRECTORY} ) + SafeConfigParserWithIncludes.__init__(self) self.__opts = None #@staticmethod diff --git a/config/filter.d/common.conf b/config/filter.d/common.conf index 40e3a360..ee084229 100644 --- a/config/filter.d/common.conf +++ b/config/filter.d/common.conf @@ -9,7 +9,7 @@ [INCLUDES] # Load customizations if any available -files_after = %(configpath)s/filter.d/common.local +after = common.local [DEFAULT] diff --git a/config/filter.d/sshd.conf b/config/filter.d/sshd.conf index 374a32f3..84d09614 100644 --- a/config/filter.d/sshd.conf +++ b/config/filter.d/sshd.conf @@ -9,7 +9,7 @@ # Read common prefixes. If any customizations available -- read them from # common.local -files_before = %(configpath)s/filter.d/common.conf +before = common.conf [Definition] diff --git a/fail2ban-regex b/fail2ban-regex index 3729701d..2e786d15 100755 --- a/fail2ban-regex +++ b/fail2ban-regex @@ -222,7 +222,7 @@ class Fail2banRegex: try: self.__filter.addFailRegex(regex.getFailRegex()) try: - ret = self.__filter.findFailure(line) + ret = self.__filter.processLine(line) if not len(ret) == 0: if found == True: ret[0].append(True) diff --git a/server/filter.py b/server/filter.py index c869695e..dd9115d5 100644 --- a/server/filter.py +++ b/server/filter.py @@ -235,9 +235,6 @@ class Filter(JailThread): def processLine(self, line): - if not self._isActive(): - # The jail has been stopped - return try: # Decode line to UTF-8 l = line.decode('utf-8') @@ -246,25 +243,27 @@ class Filter(JailThread): timeMatch = self.dateDetector.matchTime(l) if not timeMatch: # There is no valid time in this line - return + return [] # Lets split into time part and log part of the line timeLine = timeMatch.group() # Lets leave the beginning in as well, so if there is no # anchore at the beginning of the time regexp, we don't # at least allow injection. Should be harmless otherwise logLine = l[:timeMatch.start()] + l[timeMatch.end():] - for element in self.findFailure(timeLine, logLine): + return self.findFailure(timeLine, logLine) + + def processLineAndAdd(self, line): + for element in self.processLine(line): ip = element[0] unixTime = element[1] - if unixTime < MyTime.time() - self.__findTime: + if unixTime < MyTime.time() - self.getFindTime(): break if self.inIgnoreIPList(ip): - logSys.debug("Ignore "+ip) + logSys.debug("Ignore %s" % ip) continue - logSys.debug("Found "+ip) + logSys.debug("Found %s" % ip) self.failManager.addFailure(FailTicket(ip, unixTime)) - ## # Returns true if the line should be ignored. # @@ -409,32 +408,7 @@ class FileFilter(Filter): if not self._isActive(): # The jail has been stopped break - try: - # Decode line to UTF-8 - line = line.decode('utf-8') - except UnicodeDecodeError: - pass - timeMatch = self.dateDetector.matchTime(line) - if not timeMatch: - # There is no valid time in this line - line = container.readline() - continue - # Lets split into time part and log part of the line - timeLine = timeMatch.group() - # Lets leave the beginning in as well, so if there is no - # anchore at the beginning of the time regexp, we don't - # at least allow injection. Should be harmless otherwise - logLine = line[:timeMatch.start()] + line[timeMatch.end():] - for element in self.findFailure(timeLine, logLine): - ip = element[0] - unixTime = element[1] - if unixTime < MyTime.time() - self.getFindTime(): - break - if self.inIgnoreIPList(ip): - logSys.debug("Ignore "+ip) - continue - logSys.debug("Found "+ip) - self.failManager.addFailure(FailTicket(ip, unixTime)) + self.processLineAndAdd(line) # Read a new line. line = container.readline() container.close()