- 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
_tent/ipv6_via_aInfo
Cyril Jaquier 2008-03-04 00:17:56 +00:00
parent 6779814d91
commit 174ce7027a
6 changed files with 51 additions and 69 deletions

View File

@ -15,6 +15,7 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# Author: Yaroslav Halchenko # Author: Yaroslav Halchenko
# Modified: Cyril Jaquier
# $Revision$ # $Revision$
__author__ = 'Yaroslav Halhenko' __author__ = 'Yaroslav Halhenko'
@ -23,8 +24,11 @@ __date__ = '$Date: $'
__copyright__ = 'Copyright (c) 2007 Yaroslav Halchenko' __copyright__ = 'Copyright (c) 2007 Yaroslav Halchenko'
__license__ = 'GPL' __license__ = 'GPL'
import logging, os
from ConfigParser import SafeConfigParser from ConfigParser import SafeConfigParser
from ConfigParser import NoOptionError, NoSectionError
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.client.config")
class SafeConfigParserWithIncludes(SafeConfigParser): class SafeConfigParserWithIncludes(SafeConfigParser):
""" """
@ -38,10 +42,10 @@ class SafeConfigParserWithIncludes(SafeConfigParser):
Example: Example:
[INCLUDES] [INCLUDES]
files_before = 1.conf before = 1.conf
3.conf 3.conf
files_after = 1.conf after = 1.conf
It is a simple implementation, so just basic care is taken about It is a simple implementation, so just basic care is taken about
recursion. Includes preserve right order, ie new files are recursion. Includes preserve right order, ie new files are
@ -55,35 +59,42 @@ files_after = 1.conf
""" """
SECTION_NAME = "INCLUDES"
#@staticmethod #@staticmethod
def getIncludedFiles(filename, sectionName='INCLUDES', def getIncludes(resource, seen = []):
defaults={}, 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 (recursively) with the original one as well
Simple loops are taken care about Simple loops are taken care about
""" """
filenames = []
#print "Opening file " + filename # Use a short class name ;)
d = defaults.copy() # so that we do not poison our defaults SCPWI = SafeConfigParserWithIncludes
parser = SafeConfigParser(defaults = d)
parser.read(filename) parser = SafeConfigParser()
newFiles = [ ('files_before', []), ('files_after', []) ] parser.read(resource)
if sectionName in parser.sections():
resourceDir = os.path.dirname(resource)
newFiles = [ ('before', []), ('after', []) ]
if SCPWI.SECTION_NAME in parser.sections():
for option_name, option_list in newFiles: for option_name, option_list in newFiles:
if option_name in parser.options(sectionName): if option_name in parser.options(SCPWI.SECTION_NAME):
newFileNames = parser.get(sectionName, option_name) newResources = parser.get(SCPWI.SECTION_NAME, option_name)
for newFileName in newFileNames.split('\n'): for newResource in newResources.split('\n'):
if newFileName in seen: continue if os.path.isabs(newResource):
option_list += SafeConfigParserWithIncludes.\ r = newResource
getIncludedFiles(newFileName, else:
defaults=defaults, r = "%s/%s" % (resourceDir, newResource)
seen=seen + [filename]) if r in seen:
continue
s = seen + [resource]
option_list += SCPWI.getIncludes(r, s)
# combine lists # combine lists
filenames = newFiles[0][1] + [filename] + newFiles[1][1] return newFiles[0][1] + [resource] + newFiles[1][1]
#print "Includes list for " + filename + " is " + `filenames` #print "Includes list for " + resource + " is " + `resources`
return filenames getIncludes = staticmethod(getIncludes)
getIncludedFiles = staticmethod(getIncludedFiles)
def read(self, filenames): def read(self, filenames):
@ -91,8 +102,7 @@ files_after = 1.conf
if not isinstance(filenames, list): if not isinstance(filenames, list):
filenames = [ filenames ] filenames = [ filenames ]
for filename in filenames: for filename in filenames:
fileNamesFull += SafeConfigParserWithIncludes.\ fileNamesFull += SafeConfigParserWithIncludes.getIncludes(filename)
getIncludedFiles(filename, defaults=self._defaults) logSys.debug("Reading files: %s" % fileNamesFull)
#print "Opening config files " + `fileNamesFull`
return SafeConfigParser.read(self, fileNamesFull) return SafeConfigParser.read(self, fileNamesFull)

View File

@ -36,9 +36,7 @@ class ConfigReader(SafeConfigParserWithIncludes):
BASE_DIRECTORY = "/etc/fail2ban/" BASE_DIRECTORY = "/etc/fail2ban/"
def __init__(self): def __init__(self):
SafeConfigParserWithIncludes.__init__(self, SafeConfigParserWithIncludes.__init__(self)
{'configpath' : \
ConfigReader.BASE_DIRECTORY} )
self.__opts = None self.__opts = None
#@staticmethod #@staticmethod

View File

@ -9,7 +9,7 @@
[INCLUDES] [INCLUDES]
# Load customizations if any available # Load customizations if any available
files_after = %(configpath)s/filter.d/common.local after = common.local
[DEFAULT] [DEFAULT]

View File

@ -9,7 +9,7 @@
# Read common prefixes. If any customizations available -- read them from # Read common prefixes. If any customizations available -- read them from
# common.local # common.local
files_before = %(configpath)s/filter.d/common.conf before = common.conf
[Definition] [Definition]

View File

@ -222,7 +222,7 @@ class Fail2banRegex:
try: try:
self.__filter.addFailRegex(regex.getFailRegex()) self.__filter.addFailRegex(regex.getFailRegex())
try: try:
ret = self.__filter.findFailure(line) ret = self.__filter.processLine(line)
if not len(ret) == 0: if not len(ret) == 0:
if found == True: if found == True:
ret[0].append(True) ret[0].append(True)

View File

@ -235,9 +235,6 @@ class Filter(JailThread):
def processLine(self, line): def processLine(self, line):
if not self._isActive():
# The jail has been stopped
return
try: try:
# Decode line to UTF-8 # Decode line to UTF-8
l = line.decode('utf-8') l = line.decode('utf-8')
@ -246,25 +243,27 @@ class Filter(JailThread):
timeMatch = self.dateDetector.matchTime(l) timeMatch = self.dateDetector.matchTime(l)
if not timeMatch: if not timeMatch:
# There is no valid time in this line # There is no valid time in this line
return return []
# Lets split into time part and log part of the line # Lets split into time part and log part of the line
timeLine = timeMatch.group() timeLine = timeMatch.group()
# Lets leave the beginning in as well, so if there is no # Lets leave the beginning in as well, so if there is no
# anchore at the beginning of the time regexp, we don't # anchore at the beginning of the time regexp, we don't
# at least allow injection. Should be harmless otherwise # at least allow injection. Should be harmless otherwise
logLine = l[:timeMatch.start()] + l[timeMatch.end():] 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] ip = element[0]
unixTime = element[1] unixTime = element[1]
if unixTime < MyTime.time() - self.__findTime: if unixTime < MyTime.time() - self.getFindTime():
break break
if self.inIgnoreIPList(ip): if self.inIgnoreIPList(ip):
logSys.debug("Ignore "+ip) logSys.debug("Ignore %s" % ip)
continue continue
logSys.debug("Found "+ip) logSys.debug("Found %s" % ip)
self.failManager.addFailure(FailTicket(ip, unixTime)) self.failManager.addFailure(FailTicket(ip, unixTime))
## ##
# Returns true if the line should be ignored. # Returns true if the line should be ignored.
# #
@ -409,32 +408,7 @@ class FileFilter(Filter):
if not self._isActive(): if not self._isActive():
# The jail has been stopped # The jail has been stopped
break break
try: self.processLineAndAdd(line)
# 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))
# Read a new line. # Read a new line.
line = container.readline() line = container.readline()
container.close() container.close()