mirror of https://github.com/fail2ban/fail2ban
bug fix in the config readers: mixing with the init section should affect only own init options (from init section only bypass default section);
the situation details: value of "_daemon" from default section "default" (with init section) falsely overwrites it from definition section "test" - the resulting value of "_daemon" should be "test" in all 3 resulting failregex's (as specified in test.local), fixed and covered now; additionally more complex cases covered also (all filter parameters in jail via "%(known/...)s", dynamical interpolation across all, etc);pull/1726/head
parent
4f1473724b
commit
57e9c25449
|
@ -32,7 +32,7 @@ from ..helpers import getLogger
|
|||
if sys.version_info >= (3,2):
|
||||
|
||||
# SafeConfigParser deprecated from Python 3.2 (renamed to ConfigParser)
|
||||
from configparser import ConfigParser as SafeConfigParser, \
|
||||
from configparser import ConfigParser as SafeConfigParser, NoSectionError, \
|
||||
BasicInterpolation
|
||||
|
||||
# And interpolation of __name__ was simply removed, thus we need to
|
||||
|
@ -60,7 +60,7 @@ if sys.version_info >= (3,2):
|
|||
parser, option, accum, rest, section, map, depth)
|
||||
|
||||
else: # pragma: no cover
|
||||
from ConfigParser import SafeConfigParser
|
||||
from ConfigParser import SafeConfigParser, NoSectionError
|
||||
|
||||
# Gets the instance of the logger.
|
||||
logSys = getLogger(__name__)
|
||||
|
@ -200,6 +200,18 @@ after = 1.conf
|
|||
def get_sections(self):
|
||||
return self._sections
|
||||
|
||||
def options(self, section, onlyOwn=False):
|
||||
"""Return a list of option names for the given section name."""
|
||||
try:
|
||||
opts = self._sections[section]
|
||||
except KeyError:
|
||||
raise NoSectionError(section)
|
||||
if not onlyOwn:
|
||||
# mix it with defaults:
|
||||
return set(opts.keys()) | set(self._defaults)
|
||||
# only own option names:
|
||||
return opts.keys()
|
||||
|
||||
def read(self, filenames, get_includes=True):
|
||||
if not isinstance(filenames, list):
|
||||
filenames = [ filenames ]
|
||||
|
|
|
@ -122,9 +122,9 @@ class ConfigReader():
|
|||
if self._cfg is not None:
|
||||
return self._cfg.merge_section(*args, **kwargs)
|
||||
|
||||
def options(self, *args):
|
||||
def options(self, section, onlyOwn=False):
|
||||
if self._cfg is not None:
|
||||
return self._cfg.options(*args)
|
||||
return self._cfg.options(section, onlyOwn)
|
||||
return {}
|
||||
|
||||
def get(self, sec, opt, raw=False, vars={}):
|
||||
|
@ -297,23 +297,35 @@ class DefinitionInitConfigReader(ConfigReader):
|
|||
self._create_unshared(self._file)
|
||||
return SafeConfigParserWithIncludes.read(self._cfg, self._file)
|
||||
|
||||
def getOptions(self, pOpts):
|
||||
def getOptions(self, pOpts, all=False):
|
||||
# overwrite static definition options with init values, supplied as
|
||||
# direct parameters from jail-config via action[xtra1="...", xtra2=...]:
|
||||
if not pOpts:
|
||||
pOpts = dict()
|
||||
if self._initOpts:
|
||||
if not pOpts:
|
||||
pOpts = dict()
|
||||
pOpts = _merge_dicts(pOpts, self._initOpts)
|
||||
self._opts = ConfigReader.getOptions(
|
||||
self, "Definition", self._configOpts, pOpts)
|
||||
self._pOpts = pOpts
|
||||
if self.has_section("Init"):
|
||||
for opt in self.options("Init"):
|
||||
v = self.get("Init", opt)
|
||||
if not opt.startswith('known/') and opt != '__name__':
|
||||
# get only own options (without options from default):
|
||||
getopt = lambda opt: self.get("Init", opt)
|
||||
for opt in self.options("Init", onlyOwn=True):
|
||||
if opt == '__name__': continue
|
||||
v = None
|
||||
if not opt.startswith('known/'):
|
||||
if v is None: v = getopt(opt)
|
||||
self._initOpts['known/'+opt] = v
|
||||
if not opt in self._initOpts:
|
||||
if opt not in self._initOpts:
|
||||
if v is None: v = getopt(opt)
|
||||
self._initOpts[opt] = v
|
||||
if all and self.has_section("Definition"):
|
||||
# merge with all definition options (and options from default),
|
||||
# bypass already converted option (so merge only new options):
|
||||
for opt in self.options("Definition"):
|
||||
if opt == '__name__' or opt in self._opts: continue
|
||||
self._opts[opt] = self.get("Definition", opt)
|
||||
|
||||
|
||||
def _convert_to_boolean(self, value):
|
||||
return value.lower() in ("1", "yes", "true", "on")
|
||||
|
@ -336,12 +348,12 @@ class DefinitionInitConfigReader(ConfigReader):
|
|||
|
||||
def getCombined(self, ignore=()):
|
||||
combinedopts = self._opts
|
||||
ignore = set(ignore).copy()
|
||||
if self._initOpts:
|
||||
combinedopts = _merge_dicts(self._opts, self._initOpts)
|
||||
combinedopts = _merge_dicts(combinedopts, self._initOpts)
|
||||
if not len(combinedopts):
|
||||
return {}
|
||||
# ignore conditional options:
|
||||
ignore = set(ignore).copy()
|
||||
for n in combinedopts:
|
||||
cond = SafeConfigParserWithIncludes.CONDITIONAL_RE.match(n)
|
||||
if cond:
|
||||
|
|
|
@ -139,11 +139,11 @@ class JailReader(ConfigReader):
|
|||
filterName, self.__name, filterOpt,
|
||||
share_config=self.share_config, basedir=self.getBaseDir())
|
||||
ret = self.__filter.read()
|
||||
# merge options from filter as 'known/...':
|
||||
self.__filter.getOptions(self.__opts)
|
||||
ConfigReader.merge_section(self, self.__name, self.__filter.getCombined(), 'known/')
|
||||
if not ret:
|
||||
raise JailDefError("Unable to read the filter %r" % filterName)
|
||||
# merge options from filter as 'known/...' (all options unfiltered):
|
||||
self.__filter.getOptions(self.__opts, all=True)
|
||||
ConfigReader.merge_section(self, self.__name, self.__filter.getCombined(), 'known/')
|
||||
else:
|
||||
self.__filter = None
|
||||
logSys.warning("No filter set for jail %s" % self.__name)
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
#[INCLUDES]
|
||||
#before = common.conf
|
||||
|
||||
[Definition]
|
||||
failregex = failure test 1 (filter.d/test.conf) <HOST>
|
||||
[DEFAULT]
|
||||
_daemon = default
|
||||
|
||||
[Definition]
|
||||
where = conf
|
||||
failregex = failure <_daemon> <one> (filter.d/test.%(where)s) <HOST>
|
||||
|
||||
[Init]
|
||||
# test parameter, should be overriden in jail by "filter=test[one=1,...]"
|
||||
one = *1*
|
||||
|
|
|
@ -2,6 +2,15 @@
|
|||
#before = common.conf
|
||||
|
||||
[Definition]
|
||||
# overwrite default daemon, additionally it should be accessible in jail with "%(known/_daemon)s":
|
||||
_daemon = test
|
||||
# interpolate previous regex (from test.conf) + new 2nd + dynamical substitution) of "two" an "where":
|
||||
failregex = %(known/failregex)s
|
||||
failure test 2 (filter.d/test.local) <HOST>
|
||||
failure %(_daemon)s <two> (filter.d/test.<where>) <HOST>
|
||||
# parameter "two" should be specified in jail by "filter=test[..., two=2]"
|
||||
|
||||
[Init]
|
||||
# this parameter can be used in jail with "%(known/three)s":
|
||||
three = 3
|
||||
# this parameter "where" does not overwrite "where" in definition of test.conf (dynamical values only):
|
||||
where = local
|
|
@ -15,9 +15,9 @@ ignoreip =
|
|||
|
||||
[test-known-interp]
|
||||
enabled = true
|
||||
filter = test
|
||||
filter = test[one=1,two=2]
|
||||
failregex = %(known/failregex)s
|
||||
failure test 3 (jail.local) <HOST>
|
||||
failure %(known/_daemon)s %(known/three)s (jail.local) <HOST>
|
||||
|
||||
[missinglogfiles]
|
||||
enabled = true
|
||||
|
|
Loading…
Reference in New Issue