mirror of https://github.com/fail2ban/fail2ban
- 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
parent
6779814d91
commit
174ce7027a
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Reference in New Issue