diff --git a/fail2ban/client/configparserinc.py b/fail2ban/client/configparserinc.py index 35fa7498..a0e02228 100644 --- a/fail2ban/client/configparserinc.py +++ b/fail2ban/client/configparserinc.py @@ -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 ] diff --git a/fail2ban/client/configreader.py b/fail2ban/client/configreader.py index 04502504..6e46e349 100644 --- a/fail2ban/client/configreader.py +++ b/fail2ban/client/configreader.py @@ -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: diff --git a/fail2ban/client/jailreader.py b/fail2ban/client/jailreader.py index 2bef2c4f..58ae2a7f 100644 --- a/fail2ban/client/jailreader.py +++ b/fail2ban/client/jailreader.py @@ -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) diff --git a/fail2ban/tests/config/filter.d/test.conf b/fail2ban/tests/config/filter.d/test.conf index f09d3467..9d08ef09 100644 --- a/fail2ban/tests/config/filter.d/test.conf +++ b/fail2ban/tests/config/filter.d/test.conf @@ -1,6 +1,13 @@ #[INCLUDES] #before = common.conf -[Definition] -failregex = failure test 1 (filter.d/test.conf) +[DEFAULT] +_daemon = default +[Definition] +where = conf +failregex = failure <_daemon> (filter.d/test.%(where)s) + +[Init] +# test parameter, should be overriden in jail by "filter=test[one=1,...]" +one = *1* diff --git a/fail2ban/tests/config/filter.d/test.local b/fail2ban/tests/config/filter.d/test.local index 1b6cf55e..a954f81e 100644 --- a/fail2ban/tests/config/filter.d/test.local +++ b/fail2ban/tests/config/filter.d/test.local @@ -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) + failure %(_daemon)s (filter.d/test.) +# 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 \ No newline at end of file diff --git a/fail2ban/tests/config/jail.conf b/fail2ban/tests/config/jail.conf index 659e3fd3..64c1b830 100644 --- a/fail2ban/tests/config/jail.conf +++ b/fail2ban/tests/config/jail.conf @@ -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) + failure %(known/_daemon)s %(known/three)s (jail.local) [missinglogfiles] enabled = true