partially cherry pick from branch 'multi-set', prepare for conditional config parameters logic:

- new readers logic (group some by multiple parameters 'set' -> 'multi-set';
- prevent to add 'known/' parameters twice (by merge section etc);
- test cases fixed;
ipv6-support
sebres 2016-05-03 17:52:46 +02:00
parent 2c5ba07f60
commit 1f2e507135
12 changed files with 201 additions and 146 deletions

View File

@ -35,18 +35,18 @@ logSys = getLogger(__name__)
class ActionReader(DefinitionInitConfigReader): class ActionReader(DefinitionInitConfigReader):
_configOpts = [ _configOpts = {
["string", "actionstart", None], "actionstart": ["string", None],
["string", "actionstart6", None], "actionstart6": ["string", None],
["string", "actionstop", None], "actionstop": ["string", None],
["string", "actionstop6", None], "actionstop6": ["string", None],
["string", "actioncheck", None], "actioncheck": ["string", None],
["string", "actioncheck6", None], "actioncheck6": ["string", None],
["string", "actionban", None], "actionban": ["string", None],
["string", "actionban6", None], "actionban6": ["string", None],
["string", "actionunban", None], "actionunban": ["string", None],
["string", "actionunban6", None], "actionunban6": ["string", None],
] }
def __init__(self, file_, jailName, initOpts, **kwargs): def __init__(self, file_, jailName, initOpts, **kwargs):
self._name = initOpts.get("actname", file_) self._name = initOpts.get("actname", file_)
@ -70,30 +70,16 @@ class ActionReader(DefinitionInitConfigReader):
head = ["set", self._jailName] head = ["set", self._jailName]
stream = list() stream = list()
stream.append(head + ["addaction", self._name]) stream.append(head + ["addaction", self._name])
head.extend(["action", self._name]) multi = []
for opt in self._opts: for opt, optval in self._opts.iteritems():
if opt == "actionstart": if opt in self._configOpts:
stream.append(head + ["actionstart", self._opts[opt]]) multi.append([opt, optval])
elif opt == "actionstart6":
stream.append(head + ["actionstart6", self._opts[opt]])
elif opt == "actionstop":
stream.append(head + ["actionstop", self._opts[opt]])
elif opt == "actionstop6":
stream.append(head + ["actionstop6", self._opts[opt]])
elif opt == "actioncheck":
stream.append(head + ["actioncheck", self._opts[opt]])
elif opt == "actioncheck6":
stream.append(head + ["actioncheck6", self._opts[opt]])
elif opt == "actionban":
stream.append(head + ["actionban", self._opts[opt]])
elif opt == "actionban6":
stream.append(head + ["actionban6", self._opts[opt]])
elif opt == "actionunban":
stream.append(head + ["actionunban", self._opts[opt]])
elif opt == "actionunban6":
stream.append(head + ["actionunban6", self._opts[opt]])
if self._initOpts: if self._initOpts:
for p in self._initOpts: for opt, optval in self._initOpts.iteritems():
stream.append(head + [p, self._initOpts[p]]) multi.append([opt, optval])
if len(multi) > 1:
stream.append(["multi-set", self._jailName, "action", self._name, multi])
elif len(multi):
stream.append(["set", self._jailName, "action", self._name] + multi[0])
return stream return stream

View File

@ -231,7 +231,7 @@ after = 1.conf
# save previous known values, for possible using in local interpolations later: # save previous known values, for possible using in local interpolations later:
sk = {} sk = {}
for k, v in s2.iteritems(): for k, v in s2.iteritems():
if not k.startswith('known/'): if not k.startswith('known/') and k != '__name__':
sk['known/'+k] = v sk['known/'+k] = v
s2.update(sk) s2.update(sk)
# merge section # merge section
@ -256,7 +256,7 @@ after = 1.conf
alls = self.get_sections() alls = self.get_sections()
sk = {} sk = {}
for k, v in options.iteritems(): for k, v in options.iteritems():
if pref == '' or not k.startswith(pref): if pref == '' or (not k.startswith(pref) and k != '__name__'):
sk[pref+k] = v sk[pref+k] = v
alls[section].update(sk) alls[section].update(sk)

View File

@ -203,40 +203,47 @@ class ConfigReaderUnshared(SafeConfigParserWithIncludes):
# #
# Read the given option in the configuration file. Default values # Read the given option in the configuration file. Default values
# are used... # are used...
# Each optionValues entry is composed of an array with: # Each options entry is composed of an array with:
# 0 -> the type of the option # [[type, name, default], ...]
# 1 -> the name of the option # Or it is a dict:
# 2 -> the default value for the option # {name: [type, default], ...}
def getOptions(self, sec, options, pOptions=None): def getOptions(self, sec, options, pOptions=None):
values = dict() values = dict()
for option in options: for optname in options:
try: if isinstance(options, (list,tuple)):
if option[0] == "bool": if len(optname) > 2:
v = self.getboolean(sec, option[1]) opttype, optname, optvalue = optname
elif option[0] == "int":
v = self.getint(sec, option[1])
else: else:
v = self.get(sec, option[1]) (opttype, optname), optvalue = optname, None
if not pOptions is None and option[1] in pOptions: else:
opttype, optvalue = options[optname]
try:
if opttype == "bool":
v = self.getboolean(sec, optname)
elif opttype == "int":
v = self.getint(sec, optname)
else:
v = self.get(sec, optname)
if not pOptions is None and optname in pOptions:
continue continue
values[option[1]] = v values[optname] = v
except NoSectionError, e: except NoSectionError, e:
# No "Definition" section or wrong basedir # No "Definition" section or wrong basedir
logSys.error(e) logSys.error(e)
values[option[1]] = option[2] values[optname] = optvalue
# TODO: validate error handling here. # TODO: validate error handling here.
except NoOptionError: except NoOptionError:
if not option[2] is None: if not optvalue is None:
logSys.warning("'%s' not defined in '%s'. Using default one: %r" logSys.warning("'%s' not defined in '%s'. Using default one: %r"
% (option[1], sec, option[2])) % (optname, sec, optvalue))
values[option[1]] = option[2] values[optname] = optvalue
elif logSys.getEffectiveLevel() <= logLevel: elif logSys.getEffectiveLevel() <= logLevel:
logSys.log(logLevel, "Non essential option '%s' not defined in '%s'.", option[1], sec) logSys.log(logLevel, "Non essential option '%s' not defined in '%s'.", optname, sec)
except ValueError: except ValueError:
logSys.warning("Wrong value for '" + option[1] + "' in '" + sec + logSys.warning("Wrong value for '" + optname + "' in '" + sec +
"'. Using default one: '" + repr(option[2]) + "'") "'. Using default one: '" + repr(optvalue) + "'")
values[option[1]] = option[2] values[optname] = optvalue
return values return values
@ -286,7 +293,8 @@ class DefinitionInitConfigReader(ConfigReader):
if self.has_section("Init"): if self.has_section("Init"):
for opt in self.options("Init"): for opt in self.options("Init"):
v = self.get("Init", opt) v = self.get("Init", opt)
self._initOpts['known/'+opt] = v if not opt.startswith('known/') and opt != '__name__':
self._initOpts['known/'+opt] = v
if not opt in self._initOpts: if not opt in self._initOpts:
self._initOpts[opt] = v self._initOpts[opt] = v

View File

@ -291,7 +291,14 @@ class Fail2banRegex(object):
RegexStat(m[3]) RegexStat(m[3])
for m in filter( for m in filter(
lambda x: x[0] == 'set' and x[2] == "add%sregex" % regextype, lambda x: x[0] == 'set' and x[2] == "add%sregex" % regextype,
readercommands)] readercommands)
] + [
RegexStat(m)
for mm in filter(
lambda x: x[0] == 'multi-set' and x[2] == "add%sregex" % regextype,
readercommands)
for m in mm[3]
]
# Read out and set possible value of maxlines # Read out and set possible value of maxlines
for command in readercommands: for command in readercommands:
if command[2] == "maxlines": if command[2] == "maxlines":

View File

@ -37,10 +37,10 @@ logSys = getLogger(__name__)
class FilterReader(DefinitionInitConfigReader): class FilterReader(DefinitionInitConfigReader):
_configOpts = [ _configOpts = {
["string", "ignoreregex", None], "ignoreregex": ["string", None],
["string", "failregex", ""], "failregex": ["string", ""],
] }
def setFile(self, fileName): def setFile(self, fileName):
self.__file = fileName self.__file = fileName
@ -64,16 +64,16 @@ class FilterReader(DefinitionInitConfigReader):
if not len(opts): if not len(opts):
return stream return stream
for opt, value in opts.iteritems(): for opt, value in opts.iteritems():
if opt == "failregex": if opt in ("failregex", "ignoreregex"):
multi = []
for regex in value.split('\n'): for regex in value.split('\n'):
# Do not send a command if the rule is empty. # Do not send a command if the rule is empty.
if regex != '': if regex != '':
stream.append(["set", self._jailName, "addfailregex", regex]) multi.append(regex)
elif opt == "ignoreregex": if len(multi) > 1:
for regex in value.split('\n'): stream.append(["multi-set", self._jailName, "add" + opt, multi])
# Do not send a command if the rule is empty. elif len(multi):
if regex != '': stream.append(["set", self._jailName, "add" + opt, multi[0]])
stream.append(["set", self._jailName, "addignoreregex", regex])
if self._initOpts: if self._initOpts:
if 'maxlines' in self._initOpts: if 'maxlines' in self._initOpts:
# We warn when multiline regex is used without maxlines > 1 # We warn when multiline regex is used without maxlines > 1

View File

@ -190,11 +190,11 @@ class JailReader(ConfigReader):
""" """
stream = [] stream = []
for opt in self.__opts: for opt, value in self.__opts.iteritems():
if opt == "logpath" and \ if opt == "logpath" and \
self.__opts.get('backend', None) != "systemd": self.__opts.get('backend', None) != "systemd":
found_files = 0 found_files = 0
for path in self.__opts[opt].split("\n"): for path in value.split("\n"):
path = path.rsplit(" ", 1) path = path.rsplit(" ", 1)
path, tail = path if len(path) > 1 else (path[0], "head") path, tail = path if len(path) > 1 else (path[0], "head")
pathList = JailReader._glob(path) pathList = JailReader._glob(path)
@ -208,32 +208,32 @@ class JailReader(ConfigReader):
raise ValueError( raise ValueError(
"Have not found any log file for %s jail" % self.__name) "Have not found any log file for %s jail" % self.__name)
elif opt == "logencoding": elif opt == "logencoding":
stream.append(["set", self.__name, "logencoding", self.__opts[opt]]) stream.append(["set", self.__name, "logencoding", value])
elif opt == "backend": elif opt == "backend":
backend = self.__opts[opt] backend = value
elif opt == "maxretry": elif opt == "maxretry":
stream.append(["set", self.__name, "maxretry", self.__opts[opt]]) stream.append(["set", self.__name, "maxretry", value])
elif opt == "ignoreip": elif opt == "ignoreip":
for ip in splitcommaspace(self.__opts[opt]): for ip in splitcommaspace(value):
stream.append(["set", self.__name, "addignoreip", ip]) stream.append(["set", self.__name, "addignoreip", ip])
elif opt == "findtime": elif opt == "findtime":
stream.append(["set", self.__name, "findtime", self.__opts[opt]]) stream.append(["set", self.__name, "findtime", value])
elif opt == "bantime": elif opt == "bantime":
stream.append(["set", self.__name, "bantime", self.__opts[opt]]) stream.append(["set", self.__name, "bantime", value])
elif opt == "usedns": elif opt == "usedns":
stream.append(["set", self.__name, "usedns", self.__opts[opt]]) stream.append(["set", self.__name, "usedns", value])
elif opt == "failregex": elif opt in ("failregex", "ignoreregex"):
for regex in self.__opts[opt].split('\n'): multi = []
for regex in value.split('\n'):
# Do not send a command if the rule is empty. # Do not send a command if the rule is empty.
if regex != '': if regex != '':
stream.append(["set", self.__name, "addfailregex", regex]) multi.append(regex)
if len(multi) > 1:
stream.append(["multi-set", self.__name, "add" + opt, multi])
elif len(multi):
stream.append(["set", self.__name, "add" + opt, multi[0]])
elif opt == "ignorecommand": elif opt == "ignorecommand":
stream.append(["set", self.__name, "ignorecommand", self.__opts[opt]]) stream.append(["set", self.__name, "ignorecommand", value])
elif opt == "ignoreregex":
for regex in self.__opts[opt].split('\n'):
# Do not send a command if the rule is empty.
if regex != '':
stream.append(["set", self.__name, "addignoreregex", regex])
if self.__filter: if self.__filter:
stream.extend(self.__filter.convert()) stream.extend(self.__filter.convert())
for action in self.__actions: for action in self.__actions:

View File

@ -263,8 +263,13 @@ class Server:
def getIgnoreCommand(self, name): def getIgnoreCommand(self, name):
return self.__jails[name].filter.getIgnoreCommand() return self.__jails[name].filter.getIgnoreCommand()
def addFailRegex(self, name, value): def addFailRegex(self, name, value, multiple=False):
self.__jails[name].filter.addFailRegex(value) flt = self.__jails[name].filter
if multiple:
for value in value:
flt.addFailRegex(value)
else:
flt.addFailRegex(value)
def delFailRegex(self, name, index): def delFailRegex(self, name, index):
self.__jails[name].filter.delFailRegex(index) self.__jails[name].filter.delFailRegex(index)
@ -272,8 +277,13 @@ class Server:
def getFailRegex(self, name): def getFailRegex(self, name):
return self.__jails[name].filter.getFailRegex() return self.__jails[name].filter.getFailRegex()
def addIgnoreRegex(self, name, value): def addIgnoreRegex(self, name, value, multiple=False):
self.__jails[name].filter.addIgnoreRegex(value) flt = self.__jails[name].filter
if multiple:
for value in value:
flt.addIgnoreRegex(value)
else:
flt.addIgnoreRegex(value)
def delIgnoreRegex(self, name, index): def delIgnoreRegex(self, name, index):
self.__jails[name].filter.delIgnoreRegex(index) self.__jails[name].filter.delIgnoreRegex(index)

View File

@ -99,6 +99,8 @@ class Transmitter:
return None return None
elif command[0] == "flushlogs": elif command[0] == "flushlogs":
return self.__server.flushLogs() return self.__server.flushLogs()
elif command[0] == "multi-set":
return self.__commandSet(command[1:], True)
elif command[0] == "set": elif command[0] == "set":
return self.__commandSet(command[1:]) return self.__commandSet(command[1:])
elif command[0] == "get": elif command[0] == "get":
@ -109,7 +111,7 @@ class Transmitter:
return version.version return version.version
raise Exception("Invalid command") raise Exception("Invalid command")
def __commandSet(self, command): def __commandSet(self, command, multiple=False):
name = command[0] name = command[0]
# Logging # Logging
if name == "loglevel": if name == "loglevel":
@ -196,7 +198,9 @@ class Transmitter:
return self.__server.getJournalMatch(name) return self.__server.getJournalMatch(name)
elif command[1] == "addfailregex": elif command[1] == "addfailregex":
value = command[2] value = command[2]
self.__server.addFailRegex(name, value) self.__server.addFailRegex(name, value, multiple=multiple)
if multiple:
return True
return self.__server.getFailRegex(name) return self.__server.getFailRegex(name)
elif command[1] == "delfailregex": elif command[1] == "delfailregex":
value = int(command[2]) value = int(command[2])
@ -204,7 +208,9 @@ class Transmitter:
return self.__server.getFailRegex(name) return self.__server.getFailRegex(name)
elif command[1] == "addignoreregex": elif command[1] == "addignoreregex":
value = command[2] value = command[2]
self.__server.addIgnoreRegex(name, value) self.__server.addIgnoreRegex(name, value, multiple=multiple)
if multiple:
return True
return self.__server.getIgnoreRegex(name) return self.__server.getIgnoreRegex(name)
elif command[1] == "delignoreregex": elif command[1] == "delignoreregex":
value = int(command[2]) value = int(command[2])
@ -254,15 +260,26 @@ class Transmitter:
return None return None
elif command[1] == "action": elif command[1] == "action":
actionname = command[2] actionname = command[2]
actionkey = command[3]
action = self.__server.getAction(name, actionname) action = self.__server.getAction(name, actionname)
if callable(getattr(action, actionkey, None)): if multiple:
actionvalue = json.loads(command[4]) if len(command)>4 else {} for cmd in command[3]:
return getattr(action, actionkey)(**actionvalue) actionkey = cmd[0]
if callable(getattr(action, actionkey, None)):
actionvalue = json.loads(cmd[1]) if len(cmd)>1 else {}
getattr(action, actionkey)(**actionvalue)
else:
actionvalue = cmd[1]
setattr(action, actionkey, actionvalue)
return True
else: else:
actionvalue = command[4] actionkey = command[3]
setattr(action, actionkey, actionvalue) if callable(getattr(action, actionkey, None)):
return getattr(action, actionkey) actionvalue = json.loads(command[4]) if len(command)>4 else {}
return getattr(action, actionkey)(**actionvalue)
else:
actionvalue = command[4]
setattr(action, actionkey, actionvalue)
return getattr(action, actionkey)
raise Exception("Invalid command (no set action or not yet implemented)") raise Exception("Invalid command (no set action or not yet implemented)")
def __commandGet(self, command): def __commandGet(self, command):

View File

@ -273,7 +273,15 @@ class JailReaderTest(LogCaptureTestCase):
# convert and get stream # convert and get stream
stream = jail.convert() stream = jail.convert()
# get action and retrieve agent from it, compare with agent saved in version: # get action and retrieve agent from it, compare with agent saved in version:
act = [o for o in stream if len(o) > 4 and (o[4] == 'agent' or o[4].endswith('badips.py'))] act = []
for cmd in stream:
if len(cmd) <= 4:
continue
# differentiate between set and multi-set (wrop it here to single set):
if cmd[0] == 'set' and (cmd[4] == 'agent' or cmd[4].endswith('badips.py')):
act.append(cmd)
elif cmd[0] == 'multi-set':
act.extend([['set'] + cmd[1:4] + o for o in cmd[4] if o[0] == 'agent'])
useragent = 'Fail2Ban/%s' % version useragent = 'Fail2Ban/%s' % version
self.assertEqual(len(act), 4) self.assertEqual(len(act), 4)
self.assertEqual(act[0], ['set', 'blocklisttest', 'action', 'blocklist_de', 'agent', useragent]) self.assertEqual(act[0], ['set', 'blocklisttest', 'action', 'blocklist_de', 'agent', useragent])
@ -309,23 +317,21 @@ class FilterReaderTest(unittest.TestCase):
self.__share_cfg = {} self.__share_cfg = {}
def testConvert(self): def testConvert(self):
output = [['set', 'testcase01', 'addfailregex', output = [['multi-set', 'testcase01', 'addfailregex', [
"^\\s*(?:\\S+ )?(?:kernel: \\[\\d+\\.\\d+\\] )?(?:@vserver_\\S+ )" "^\\s*(?:\\S+ )?(?:kernel: \\[\\d+\\.\\d+\\] )?(?:@vserver_\\S+ )"
"?(?:(?:\\[\\d+\\])?:\\s+[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?|" "?(?:(?:\\[\\d+\\])?:\\s+[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?|"
"[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?(?:\\[\\d+\\])?:)?\\s*(?:" "[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?(?:\\[\\d+\\])?:)?\\s*(?:"
"error: PAM: )?Authentication failure for .* from <HOST>\\s*$"], "error: PAM: )?Authentication failure for .* from <HOST>\\s*$",
['set', 'testcase01', 'addfailregex',
"^\\s*(?:\\S+ )?(?:kernel: \\[\\d+\\.\\d+\\] )?(?:@vserver_\\S+ )" "^\\s*(?:\\S+ )?(?:kernel: \\[\\d+\\.\\d+\\] )?(?:@vserver_\\S+ )"
"?(?:(?:\\[\\d+\\])?:\\s+[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?|" "?(?:(?:\\[\\d+\\])?:\\s+[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?|"
"[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?(?:\\[\\d+\\])?:)?\\s*(?:" "[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?(?:\\[\\d+\\])?:)?\\s*(?:"
"error: PAM: )?User not known to the underlying authentication mo" "error: PAM: )?User not known to the underlying authentication mo"
"dule for .* from <HOST>\\s*$"], "dule for .* from <HOST>\\s*$",
['set', 'testcase01', 'addfailregex',
"^\\s*(?:\\S+ )?(?:kernel: \\[\\d+\\.\\d+\\] )?(?:@vserver_\\S+ )" "^\\s*(?:\\S+ )?(?:kernel: \\[\\d+\\.\\d+\\] )?(?:@vserver_\\S+ )"
"?(?:(?:\\[\\d+\\])?:\\s+[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?|" "?(?:(?:\\[\\d+\\])?:\\s+[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?|"
"[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?(?:\\[\\d+\\])?:)?\\s*(?:" "[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?(?:\\[\\d+\\])?:)?\\s*(?:"
"error: PAM: )?User not known to the\\nunderlying authentication." "error: PAM: )?User not known to the\\nunderlying authentication."
"+$<SKIPLINES>^.+ module for .* from <HOST>\\s*$"], "+$<SKIPLINES>^.+ module for .* from <HOST>\\s*$"]],
['set', 'testcase01', 'addignoreregex', ['set', 'testcase01', 'addignoreregex',
"^.+ john from host 192.168.1.1\\s*$"], "^.+ john from host 192.168.1.1\\s*$"],
['set', 'testcase01', 'addjournalmatch', ['set', 'testcase01', 'addjournalmatch',
@ -493,9 +499,11 @@ class JailsReaderTest(LogCaptureTestCase):
self.assertEqual(sorted(comm_commands), self.assertEqual(sorted(comm_commands),
sorted([['add', 'emptyaction', 'auto'], sorted([['add', 'emptyaction', 'auto'],
['add', 'test-known-interp', 'auto'], ['add', 'test-known-interp', 'auto'],
['set', 'test-known-interp', 'addfailregex', 'failure test 1 (filter.d/test.conf) <HOST>'], ['multi-set', 'test-known-interp', 'addfailregex', [
['set', 'test-known-interp', 'addfailregex', 'failure test 2 (filter.d/test.local) <HOST>'], 'failure test 1 (filter.d/test.conf) <HOST>',
['set', 'test-known-interp', 'addfailregex', 'failure test 3 (jail.local) <HOST>'], 'failure test 2 (filter.d/test.local) <HOST>',
'failure test 3 (jail.local) <HOST>'
]],
['start', 'test-known-interp'], ['start', 'test-known-interp'],
['add', 'missinglogfiles', 'auto'], ['add', 'missinglogfiles', 'auto'],
['set', 'missinglogfiles', 'addfailregex', '<IP>'], ['set', 'missinglogfiles', 'addfailregex', '<IP>'],
@ -658,12 +666,16 @@ class JailsReaderTest(LogCaptureTestCase):
self.assertTrue('blocktype' in action._initOpts) self.assertTrue('blocktype' in action._initOpts)
# Verify that we have a call to set it up # Verify that we have a call to set it up
blocktype_present = False blocktype_present = False
target_command = ['set', jail_name, 'action', action_name, 'blocktype'] target_command = [jail_name, 'action', action_name]
for command in commands: for command in commands:
if (len(command) > 5 and if (len(command) > 4 and command[0] == 'multi-set' and
command[:5] == target_command): command[1:4] == target_command):
blocktype_present = True blocktype_present = ('blocktype' in [cmd[0] for cmd in command[4]])
continue elif (len(command) > 5 and command[0] == 'set' and
command[1:4] == target_command and command[4] == 'blocktype'): # pragma: no cover - because of multi-set
blocktype_present = True
if blocktype_present:
break
self.assertTrue( self.assertTrue(
blocktype_present, blocktype_present,
msg="Found no %s command among %s" msg="Found no %s command among %s"

View File

@ -71,14 +71,21 @@ def testSampleRegexsFactory(name):
filterConf.getOptions({}) filterConf.getOptions({})
for opt in filterConf.convert(): for opt in filterConf.convert():
if opt[2] == "addfailregex": if opt[0] == 'multi-set':
self.filter.addFailRegex(opt[3]) optval = opt[3]
elif opt[2] == "maxlines": elif opt[0] == 'set':
self.filter.setMaxLines(opt[3]) optval = [opt[3]]
elif opt[2] == "addignoreregex": else:
self.filter.addIgnoreRegex(opt[3]) continue
elif opt[2] == "datepattern": for optval in optval:
self.filter.setDatePattern(opt[3]) if opt[2] == "addfailregex":
self.filter.addFailRegex(optval)
elif opt[2] == "addignoreregex":
self.filter.addIgnoreRegex(optval)
elif opt[2] == "maxlines":
self.filter.setMaxLines(optval)
elif opt[2] == "datepattern":
self.filter.setDatePattern(optval)
self.assertTrue( self.assertTrue(
os.path.isfile(os.path.join(TEST_FILES_DIR, "logs", name)), os.path.isfile(os.path.join(TEST_FILES_DIR, "logs", name)),

View File

@ -1002,7 +1002,10 @@ class ServerConfigReaderTests(LogCaptureTestCase):
cmd[3] = os.path.join(TEST_FILES_DIR, 'logs', cmd[1]) cmd[3] = os.path.join(TEST_FILES_DIR, 'logs', cmd[1])
# add dummy regex to prevent too long compile of all regexp (we don't use it in this test at all): # add dummy regex to prevent too long compile of all regexp (we don't use it in this test at all):
# [todo sebres] remove `not hasattr(unittest, 'F2B') or `, after merge with "f2b-perfom-prepare-716" ... # [todo sebres] remove `not hasattr(unittest, 'F2B') or `, after merge with "f2b-perfom-prepare-716" ...
elif (not hasattr(unittest, 'F2B') or unittest.F2B.fast) and len(cmd) > 3 and cmd[0] == 'set' and cmd[2] == 'addfailregex': elif (not hasattr(unittest, 'F2B') or unittest.F2B.fast) and (
len(cmd) > 3 and cmd[0] in ('set', 'multi-set') and cmd[2] == 'addfailregex'
):
cmd[0] = "set"
cmd[3] = "DUMMY-REGEX <HOST>" cmd[3] = "DUMMY-REGEX <HOST>"
# command to server, use cmdHandler direct instead of `transm.proceed(cmd)`: # command to server, use cmdHandler direct instead of `transm.proceed(cmd)`:
try: try:
@ -1036,7 +1039,7 @@ class ServerConfigReaderTests(LogCaptureTestCase):
testJailsActions = ( testJailsActions = (
('j-w-iptables-mp', 'iptables-multiport[name=%(__name__)s, bantime="600", port="http,https", protocol="tcp", chain="INPUT"]', { ('j-w-iptables-mp', 'iptables-multiport[name=%(__name__)s, bantime="600", port="http,https", protocol="tcp", chain="INPUT"]', {
'ip4': '`iptables ', 'ip6': '`ip6tables ', 'ip4': ('`iptables ',), 'ip6': ('`ip6tables ',),
'start': ( 'start': (
"`iptables -w -N f2b-j-w-iptables-mp`", "`iptables -w -N f2b-j-w-iptables-mp`",
"`iptables -w -A f2b-j-w-iptables-mp -j RETURN`", "`iptables -w -A f2b-j-w-iptables-mp -j RETURN`",
@ -1073,7 +1076,7 @@ class ServerConfigReaderTests(LogCaptureTestCase):
), ),
}), }),
('j-w-iptables-ap', 'iptables-allports[name=%(__name__)s, bantime="600", protocol="tcp", chain="INPUT"]', { ('j-w-iptables-ap', 'iptables-allports[name=%(__name__)s, bantime="600", protocol="tcp", chain="INPUT"]', {
'ip4': '`iptables ', 'ip6': '`ip6tables ', 'ip4': ('`iptables ',), 'ip6': ('`ip6tables ',),
'start': ( 'start': (
"`iptables -w -N f2b-j-w-iptables-ap`", "`iptables -w -N f2b-j-w-iptables-ap`",
"`iptables -w -A f2b-j-w-iptables-ap -j RETURN`", "`iptables -w -A f2b-j-w-iptables-ap -j RETURN`",
@ -1110,7 +1113,7 @@ class ServerConfigReaderTests(LogCaptureTestCase):
), ),
}), }),
('j-w-iptables-ipset', 'iptables-ipset-proto6[name=%(__name__)s, bantime="600", port="http", protocol="tcp", chain="INPUT"]', { ('j-w-iptables-ipset', 'iptables-ipset-proto6[name=%(__name__)s, bantime="600", port="http", protocol="tcp", chain="INPUT"]', {
'ip4': ' f2b-j-w-iptables-ipset ', 'ip6': ' f2b-j-w-iptables-ipset6 ', 'ip4': (' f2b-j-w-iptables-ipset ',), 'ip6': (' f2b-j-w-iptables-ipset6 ',),
'start': ( 'start': (
"`ipset create f2b-j-w-iptables-ipset hash:ip timeout 600`", "`ipset create f2b-j-w-iptables-ipset hash:ip timeout 600`",
"`iptables -w -I INPUT -p tcp -m multiport --dports http -m set --match-set f2b-j-w-iptables-ipset src -j REJECT --reject-with icmp-port-unreachable`", "`iptables -w -I INPUT -p tcp -m multiport --dports http -m set --match-set f2b-j-w-iptables-ipset src -j REJECT --reject-with icmp-port-unreachable`",
@ -1174,22 +1177,22 @@ class ServerConfigReaderTests(LogCaptureTestCase):
logSys.debug('# === ban-ipv4 ==='); self.pruneLog() logSys.debug('# === ban-ipv4 ==='); self.pruneLog()
action.ban({'ip': IPAddr('192.0.2.1')}) action.ban({'ip': IPAddr('192.0.2.1')})
self.assertLogged(*tests['ip4-check']+tests['ip4-ban'], all=True) self.assertLogged(*tests['ip4-check']+tests['ip4-ban'], all=True)
self.assertNotLogged(tests['ip6']) self.assertNotLogged(*tests['ip6'], all=True)
# test unban ip4 : # test unban ip4 :
logSys.debug('# === unban ipv4 ==='); self.pruneLog() logSys.debug('# === unban ipv4 ==='); self.pruneLog()
action.unban({'ip': IPAddr('192.0.2.1')}) action.unban({'ip': IPAddr('192.0.2.1')})
self.assertLogged(*tests['ip4-check']+tests['ip4-unban'], all=True) self.assertLogged(*tests['ip4-check']+tests['ip4-unban'], all=True)
self.assertNotLogged(tests['ip6']) self.assertNotLogged(*tests['ip6'], all=True)
# test ban ip6 : # test ban ip6 :
logSys.debug('# === ban ipv6 ==='); self.pruneLog() logSys.debug('# === ban ipv6 ==='); self.pruneLog()
action.ban({'ip': IPAddr('2001:DB8::')}) action.ban({'ip': IPAddr('2001:DB8::')})
self.assertLogged(*tests['ip6-check']+tests['ip6-ban'], all=True) self.assertLogged(*tests['ip6-check']+tests['ip6-ban'], all=True)
self.assertNotLogged(tests['ip4']) self.assertNotLogged(*tests['ip4'], all=True)
# test unban ip6 : # test unban ip6 :
logSys.debug('# === unban ipv6 ==='); self.pruneLog() logSys.debug('# === unban ipv6 ==='); self.pruneLog()
action.unban({'ip': IPAddr('2001:DB8::')}) action.unban({'ip': IPAddr('2001:DB8::')})
self.assertLogged(*tests['ip6-check']+tests['ip6-unban'], all=True) self.assertLogged(*tests['ip6-check']+tests['ip6-unban'], all=True)
self.assertNotLogged(tests['ip4']) self.assertNotLogged(*tests['ip4'], all=True)
# test stop : # test stop :
logSys.debug('# === stop ==='); self.pruneLog() logSys.debug('# === stop ==='); self.pruneLog()
action.stop() action.stop()

View File

@ -264,7 +264,7 @@ class LogCaptureTestCase(unittest.TestCase):
---------- ----------
s : string or list/set/tuple of strings s : string or list/set/tuple of strings
Test should succeed if string (or any of the listed) is present in the log Test should succeed if string (or any of the listed) is present in the log
all : boolean, should find all in s all : boolean (default False) if True should fail if any of s not logged
""" """
logged = self._log.getvalue() logged = self._log.getvalue()
if not kwargs.get('all', False): if not kwargs.get('all', False):
@ -272,16 +272,15 @@ class LogCaptureTestCase(unittest.TestCase):
for s_ in s: for s_ in s:
if s_ in logged: if s_ in logged:
return return
# pragma: no cover if True: # pragma: no cover
self.fail("None among %r was found in the log: ===\n%s===" % (s, logged)) self.fail("None among %r was found in the log: ===\n%s===" % (s, logged))
else: else:
# each entry should be found: # each entry should be found:
for s_ in s: for s_ in s:
if s_ not in logged: if s_ not in logged: # pragma: no cover
# pragma: no cover
self.fail("%r was not found in the log: ===\n%s===" % (s_, logged)) self.fail("%r was not found in the log: ===\n%s===" % (s_, logged))
def assertNotLogged(self, *s): def assertNotLogged(self, *s, **kwargs):
"""Assert that strings were not logged """Assert that strings were not logged
Parameters Parameters
@ -289,13 +288,19 @@ class LogCaptureTestCase(unittest.TestCase):
s : string or list/set/tuple of strings s : string or list/set/tuple of strings
Test should succeed if the string (or at least one of the listed) is not Test should succeed if the string (or at least one of the listed) is not
present in the log present in the log
all : boolean (default False) if True should fail if any of s logged
""" """
logged = self._log.getvalue() logged = self._log.getvalue()
for s_ in s: if not kwargs.get('all', False):
if s_ not in logged: for s_ in s:
return if s_ not in logged:
# pragma: no cover return
self.fail("All of the %r were found present in the log: ===\n%s===" % (s, logged)) if True: # pragma: no cover
self.fail("All of the %r were found present in the log: ===\n%s===" % (s, logged))
else:
for s_ in s:
if s_ in logged: # pragma: no cover
self.fail("%r was found in the log: ===\n%s===" % (s_, logged))
def pruneLog(self): def pruneLog(self):
self._log.truncate(0) self._log.truncate(0)