mirror of https://github.com/fail2ban/fail2ban
implements the feature of automatic switch `backend = auto` to backend `systemd`, when:
- no files matching `logpath` found for this jail; - no `systemd_if_nologs = false` (`true` by default) is specified for the jail; - option `journalmatch` is set for the jail or its filter (otherwise it'd be too heavy to allow all auto-jails, even if they have never been foreseen for journal); - option `skip_if_nologs` will be ignored if we could switch backend to `systemd`; closes gh-3768pull/3927/merge
parent
5a2fd9b31c
commit
b2352f113e
|
@ -117,12 +117,13 @@ class JailReader(ConfigReader):
|
||||||
"logencoding": ["string", None],
|
"logencoding": ["string", None],
|
||||||
"logpath": ["string", None],
|
"logpath": ["string", None],
|
||||||
"skip_if_nologs": ["bool", False],
|
"skip_if_nologs": ["bool", False],
|
||||||
|
"systemd_if_nologs": ["bool", True],
|
||||||
"action": ["string", ""]
|
"action": ["string", ""]
|
||||||
}
|
}
|
||||||
_configOpts.update(FilterReader._configOpts)
|
_configOpts.update(FilterReader._configOpts)
|
||||||
|
|
||||||
_ignoreOpts = set(
|
_ignoreOpts = set(
|
||||||
['action', 'filter', 'enabled', 'backend', 'skip_if_nologs'] +
|
['action', 'filter', 'enabled', 'backend', 'skip_if_nologs', 'systemd_if_nologs'] +
|
||||||
list(FilterReader._configOpts.keys())
|
list(FilterReader._configOpts.keys())
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -239,7 +240,7 @@ class JailReader(ConfigReader):
|
||||||
return self.__opts
|
return self.__opts
|
||||||
return _merge_dicts(self.__opts, self.__filter.getCombined())
|
return _merge_dicts(self.__opts, self.__filter.getCombined())
|
||||||
|
|
||||||
def convert(self, allow_no_files=False):
|
def convert(self, allow_no_files=False, systemd_if_nologs=True):
|
||||||
"""Convert read before __opts to the commands stream
|
"""Convert read before __opts to the commands stream
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
|
@ -277,14 +278,25 @@ class JailReader(ConfigReader):
|
||||||
stream2.append(
|
stream2.append(
|
||||||
["set", self.__name, "addlogpath", p, tail])
|
["set", self.__name, "addlogpath", p, tail])
|
||||||
if not found_files:
|
if not found_files:
|
||||||
msg = "Have not found any log file for %s jail" % self.__name
|
msg = "Have not found any log file for '%s' jail." % self.__name
|
||||||
skip_if_nologs = self.__opts.get('skip_if_nologs', False)
|
skip_if_nologs = self.__opts.get('skip_if_nologs', False)
|
||||||
if not allow_no_files and not skip_if_nologs:
|
# if auto and we can switch to systemd backend (only possible if jail have journalmatch):
|
||||||
|
if backend.startswith("auto") and systemd_if_nologs and (
|
||||||
|
self.__opts.get('systemd_if_nologs', True) and
|
||||||
|
self.__opts.get('journalmatch', None) is not None
|
||||||
|
):
|
||||||
|
# switch backend to systemd:
|
||||||
|
backend = 'systemd'
|
||||||
|
msg += " Jail will monitor systemd journal."
|
||||||
|
skip_if_nologs = False
|
||||||
|
elif not allow_no_files and not skip_if_nologs:
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
logSys.warning(msg)
|
logSys.warning(msg)
|
||||||
if skip_if_nologs:
|
if skip_if_nologs:
|
||||||
self.__opts['config-error'] = msg
|
self.__opts['runtime-error'] = msg
|
||||||
stream = [['config-error', "Jail '%s' skipped, because of missing log files." % (self.__name,)]]
|
msg = "Jail '%s' skipped, because of missing log files." % (self.__name,)
|
||||||
|
logSys.warning(msg)
|
||||||
|
stream = [['config-error', msg]]
|
||||||
return stream
|
return stream
|
||||||
elif opt == "ignoreip":
|
elif opt == "ignoreip":
|
||||||
stream.append(["set", self.__name, "addignoreip"] + splitwords(value))
|
stream.append(["set", self.__name, "addignoreip"] + splitwords(value))
|
||||||
|
|
|
@ -88,7 +88,7 @@ class JailsReader(ConfigReader):
|
||||||
parse_status |= 2
|
parse_status |= 2
|
||||||
return ((ignoreWrong and parse_status & 1) or not (parse_status & 2))
|
return ((ignoreWrong and parse_status & 1) or not (parse_status & 2))
|
||||||
|
|
||||||
def convert(self, allow_no_files=False):
|
def convert(self, allow_no_files=False, systemd_if_nologs=True):
|
||||||
"""Convert read before __opts and jails to the commands stream
|
"""Convert read before __opts and jails to the commands stream
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
|
@ -101,11 +101,14 @@ class JailsReader(ConfigReader):
|
||||||
stream = list()
|
stream = list()
|
||||||
# Convert jails
|
# Convert jails
|
||||||
for jail in self.__jails:
|
for jail in self.__jails:
|
||||||
stream.extend(jail.convert(allow_no_files=allow_no_files))
|
stream.extend(jail.convert(allow_no_files, systemd_if_nologs))
|
||||||
# Start jails
|
# Start jails
|
||||||
for jail in self.__jails:
|
for jail in self.__jails:
|
||||||
if not jail.options.get('config-error'):
|
if not jail.options.get('config-error') and not jail.options.get('runtime-error'):
|
||||||
stream.append(["start", jail.getName()])
|
stream.append(["start", jail.getName()])
|
||||||
|
else:
|
||||||
|
# just delete rtm-errors (to check next time if cached)
|
||||||
|
jail.options.pop('runtime-error', None)
|
||||||
|
|
||||||
return stream
|
return stream
|
||||||
|
|
||||||
|
|
|
@ -719,51 +719,67 @@ class JailsReaderTest(LogCaptureTestCase):
|
||||||
jails = JailsReader(basedir=IMPERFECT_CONFIG, share_config=IMPERFECT_CONFIG_SHARE_CFG)
|
jails = JailsReader(basedir=IMPERFECT_CONFIG, share_config=IMPERFECT_CONFIG_SHARE_CFG)
|
||||||
self.assertTrue(jails.read())
|
self.assertTrue(jails.read())
|
||||||
self.assertFalse(jails.getOptions(ignoreWrong=False))
|
self.assertFalse(jails.getOptions(ignoreWrong=False))
|
||||||
self.assertRaises(ValueError, jails.convert)
|
self.assertRaises(ValueError, lambda: jails.convert(systemd_if_nologs=False))
|
||||||
comm_commands = jails.convert(allow_no_files=True)
|
|
||||||
self.maxDiff = None
|
|
||||||
self.assertSortedEqual(comm_commands,
|
|
||||||
[['add', 'emptyaction', 'auto'],
|
|
||||||
['add', 'test-known-interp', 'auto'],
|
|
||||||
['multi-set', 'test-known-interp', 'addfailregex', [
|
|
||||||
'failure test 1 (filter.d/test.conf) <HOST>',
|
|
||||||
'failure test 2 (filter.d/test.local) <HOST>',
|
|
||||||
'failure test 3 (jail.local) <HOST>'
|
|
||||||
]],
|
|
||||||
['start', 'test-known-interp'],
|
|
||||||
['add', 'missinglogfiles', 'auto'],
|
|
||||||
['set', 'missinglogfiles', 'addfailregex', '<IP>'],
|
|
||||||
['config-error', "Jail 'missinglogfiles_skip' skipped, because of missing log files."],
|
|
||||||
['add', 'brokenaction', 'auto'],
|
|
||||||
['set', 'brokenaction', 'addfailregex', '<IP>'],
|
|
||||||
['set', 'brokenaction', 'addaction', 'brokenaction'],
|
|
||||||
['multi-set', 'brokenaction', 'action', 'brokenaction', [
|
|
||||||
['actionban', 'hit with big stick <ip>'],
|
|
||||||
['actname', 'brokenaction'],
|
|
||||||
['name', 'brokenaction']
|
|
||||||
]],
|
|
||||||
['add', 'parse_to_end_of_jail.conf', 'auto'],
|
|
||||||
['set', 'parse_to_end_of_jail.conf', 'addfailregex', '<IP>'],
|
|
||||||
['set', 'tz_correct', 'addfailregex', '<IP>'],
|
|
||||||
['set', 'tz_correct', 'logtimezone', 'UTC+0200'],
|
|
||||||
['start', 'emptyaction'],
|
|
||||||
['start', 'missinglogfiles'],
|
|
||||||
['start', 'brokenaction'],
|
|
||||||
['start', 'parse_to_end_of_jail.conf'],
|
|
||||||
['add', 'tz_correct', 'auto'],
|
|
||||||
['start', 'tz_correct'],
|
|
||||||
['config-error',
|
|
||||||
"Jail 'brokenactiondef' skipped, because of wrong configuration: Invalid action definition 'joho[foo': unexpected option syntax"],
|
|
||||||
['config-error',
|
|
||||||
"Jail 'brokenfilterdef' skipped, because of wrong configuration: Invalid filter definition 'flt[test': unexpected option syntax"],
|
|
||||||
['config-error',
|
|
||||||
"Jail 'missingaction' skipped, because of wrong configuration: Unable to read action 'noactionfileforthisaction'"],
|
|
||||||
['config-error',
|
|
||||||
"Jail 'missingbitsjail' skipped, because of wrong configuration: Unable to read the filter 'catchallthebadies'"],
|
|
||||||
])
|
|
||||||
self.assertLogged("Errors in jail 'missingbitsjail'.")
|
self.assertLogged("Errors in jail 'missingbitsjail'.")
|
||||||
self.assertNotLogged("Skipping...")
|
self.assertNotLogged("Skipping...")
|
||||||
|
# check with allow no files (just to cover other jail problems), but without switch to systemd:
|
||||||
|
self.pruneLog('[test-phase] allow no files, no switch to systemd ...')
|
||||||
|
comm_commands = jails.convert(allow_no_files=True, systemd_if_nologs=False)
|
||||||
|
self.maxDiff = None
|
||||||
|
def _checkStream(comm_commands, backend='auto'):
|
||||||
|
self.assertSortedEqual(comm_commands,
|
||||||
|
[['add', 'emptyaction', 'auto'],
|
||||||
|
['add', 'test-known-interp', 'auto'],
|
||||||
|
['multi-set', 'test-known-interp', 'addfailregex', [
|
||||||
|
'failure test 1 (filter.d/test.conf) <HOST>',
|
||||||
|
'failure test 2 (filter.d/test.local) <HOST>',
|
||||||
|
'failure test 3 (jail.local) <HOST>'
|
||||||
|
]],
|
||||||
|
['start', 'test-known-interp'],
|
||||||
|
['add', 'missinglogfiles', backend], # can switch backend because have journalmatch
|
||||||
|
['set', 'missinglogfiles', 'addfailregex', '<IP>'],
|
||||||
|
['set', 'missinglogfiles', 'addjournalmatch', '_COMM=test'],
|
||||||
|
['config-error', "Jail 'missinglogfiles_skip' skipped, because of missing log files."],
|
||||||
|
['add', 'brokenaction', 'auto'],
|
||||||
|
['set', 'brokenaction', 'addfailregex', '<IP>'],
|
||||||
|
['set', 'brokenaction', 'addaction', 'brokenaction'],
|
||||||
|
['multi-set', 'brokenaction', 'action', 'brokenaction', [
|
||||||
|
['actionban', 'hit with big stick <ip>'],
|
||||||
|
['actname', 'brokenaction'],
|
||||||
|
['name', 'brokenaction']
|
||||||
|
]],
|
||||||
|
['add', 'parse_to_end_of_jail.conf', 'auto'],
|
||||||
|
['set', 'parse_to_end_of_jail.conf', 'addfailregex', '<IP>'],
|
||||||
|
['set', 'tz_correct', 'addfailregex', '<IP>'],
|
||||||
|
['set', 'tz_correct', 'logtimezone', 'UTC+0200'],
|
||||||
|
['start', 'emptyaction'],
|
||||||
|
['start', 'missinglogfiles'],
|
||||||
|
['start', 'brokenaction'],
|
||||||
|
['start', 'parse_to_end_of_jail.conf'],
|
||||||
|
['add', 'tz_correct', 'auto'],
|
||||||
|
['start', 'tz_correct'],
|
||||||
|
['config-error',
|
||||||
|
"Jail 'brokenactiondef' skipped, because of wrong configuration: Invalid action definition 'joho[foo': unexpected option syntax"],
|
||||||
|
['config-error',
|
||||||
|
"Jail 'brokenfilterdef' skipped, because of wrong configuration: Invalid filter definition 'flt[test': unexpected option syntax"],
|
||||||
|
['config-error',
|
||||||
|
"Jail 'missingaction' skipped, because of wrong configuration: Unable to read action 'noactionfileforthisaction'"],
|
||||||
|
['config-error',
|
||||||
|
"Jail 'missingbitsjail' skipped, because of wrong configuration: Unable to read the filter 'catchallthebadies'"],
|
||||||
|
])
|
||||||
|
_checkStream(comm_commands, backend='auto')
|
||||||
|
self.assertNotLogged("Have not found any log file for 'missinglogfiles' jail. Jail will monitor systemd journal.")
|
||||||
self.assertLogged("No file(s) found for glob /weapons/of/mass/destruction")
|
self.assertLogged("No file(s) found for glob /weapons/of/mass/destruction")
|
||||||
|
self.assertLogged("Jail 'missinglogfiles_skip' skipped, because of missing log files.")
|
||||||
|
# switch backend auto to systemd if no files found, note that jail "missinglogfiles_skip" will be skipped yet,
|
||||||
|
# because for this jail configured skip_if_nologs = true, all other jails shall switch to systemd with warning
|
||||||
|
self.pruneLog('[test-phase] auto -> systemd')
|
||||||
|
comm_commands = jails.convert(allow_no_files=True)
|
||||||
|
_checkStream(comm_commands, backend='systemd')
|
||||||
|
self.assertNotLogged("Errors in jail 'missingbitsjail'.")
|
||||||
|
self.assertLogged("Have not found any log file for 'missinglogfiles' jail. Jail will monitor systemd journal.")
|
||||||
|
self.assertLogged("No file(s) found for glob /weapons/of/mass/destruction")
|
||||||
|
self.assertLogged("Jail 'missinglogfiles_skip' skipped, because of missing log files.")
|
||||||
|
|
||||||
def testReadStockActionConf(self):
|
def testReadStockActionConf(self):
|
||||||
unittest.F2B.SkipIfCfgMissing(stock=True)
|
unittest.F2B.SkipIfCfgMissing(stock=True)
|
||||||
|
|
|
@ -21,6 +21,7 @@ failregex = %(known/failregex)s
|
||||||
|
|
||||||
[missinglogfiles]
|
[missinglogfiles]
|
||||||
enabled = true
|
enabled = true
|
||||||
|
journalmatch = _COMM=test ;# allow to switch to systemd (by backend = `auto` and no logs found)
|
||||||
logpath = /weapons/of/mass/destruction
|
logpath = /weapons/of/mass/destruction
|
||||||
|
|
||||||
[missinglogfiles_skip]
|
[missinglogfiles_skip]
|
||||||
|
|
Loading…
Reference in New Issue