mirror of https://github.com/fail2ban/fail2ban
more cases covered, start in repair distinguish operations, on demand flag etc
parent
b7fe33483a
commit
125da61bda
|
@ -484,7 +484,7 @@ class CommandAction(ActionBase):
|
||||||
"""
|
"""
|
||||||
return self._start()
|
return self._start()
|
||||||
|
|
||||||
def _start(self, family='', forceStart=False):
|
def _start(self, family=None, forceStart=False):
|
||||||
"""Executes the "actionstart" command.
|
"""Executes the "actionstart" command.
|
||||||
|
|
||||||
Replace the tags in the action command with actions properties
|
Replace the tags in the action command with actions properties
|
||||||
|
@ -496,7 +496,7 @@ class CommandAction(ActionBase):
|
||||||
return True
|
return True
|
||||||
elif not forceStart and self.__started.get(family): # pragma: no cover - normally unreachable
|
elif not forceStart and self.__started.get(family): # pragma: no cover - normally unreachable
|
||||||
return True
|
return True
|
||||||
family = [family] if family != '' else self._families
|
family = [family] if family is not None else self._families
|
||||||
def _started(family, ret):
|
def _started(family, ret):
|
||||||
if ret:
|
if ret:
|
||||||
self._operationExecuted('<actionstop>', family, None)
|
self._operationExecuted('<actionstop>', family, None)
|
||||||
|
@ -570,14 +570,14 @@ class CommandAction(ActionBase):
|
||||||
"""
|
"""
|
||||||
return self._stop()
|
return self._stop()
|
||||||
|
|
||||||
def _stop(self, family=''):
|
def _stop(self, family=None):
|
||||||
"""Executes the "actionstop" command.
|
"""Executes the "actionstop" command.
|
||||||
|
|
||||||
Replaces the tags in the action command with actions properties
|
Replaces the tags in the action command with actions properties
|
||||||
and executes the resulting command.
|
and executes the resulting command.
|
||||||
"""
|
"""
|
||||||
# collect started families, if started on demand (conditional):
|
# collect started families, if started on demand (conditional):
|
||||||
if not family:
|
if family is None:
|
||||||
family = [f for (f,v) in self.__started.iteritems() if v]
|
family = [f for (f,v) in self.__started.iteritems() if v]
|
||||||
# if no started (on demand) actions:
|
# if no started (on demand) actions:
|
||||||
if not family: return True
|
if not family: return True
|
||||||
|
@ -812,26 +812,27 @@ class CommandAction(ActionBase):
|
||||||
realCmd = Utils.buildShellCmd(realCmd, varsDict)
|
realCmd = Utils.buildShellCmd(realCmd, varsDict)
|
||||||
return realCmd
|
return realCmd
|
||||||
|
|
||||||
def _invariantCheck(self, family='', beforeRepair=None):
|
def _invariantCheck(self, family=None, beforeRepair=None, forceStart=True):
|
||||||
"""Executes a substituted `actioncheck` command.
|
"""Executes a substituted `actioncheck` command.
|
||||||
"""
|
"""
|
||||||
# for started action/family only (avoid check not started inet4 if inet6 gets broken):
|
# for started action/family only (avoid check not started inet4 if inet6 gets broken):
|
||||||
if family != '' and family not in self.__started and not self.__started.get(''):
|
if not forceStart and family is not None and family not in self.__started:
|
||||||
return True
|
return 1
|
||||||
checkCmd = self._getOperation('<actioncheck>', family)
|
checkCmd = self._getOperation('<actioncheck>', family)
|
||||||
if not checkCmd or self.executeCmd(checkCmd, self.timeout):
|
if not checkCmd or self.executeCmd(checkCmd, self.timeout):
|
||||||
return True
|
return 1
|
||||||
# if don't need repair/restore - just return:
|
# if don't need repair/restore - just return:
|
||||||
if beforeRepair and not beforeRepair():
|
if beforeRepair and not beforeRepair():
|
||||||
return False
|
return -1
|
||||||
self._logSys.error(
|
self._logSys.error(
|
||||||
"Invariant check failed. Trying to restore a sane environment")
|
"Invariant check failed. Trying to restore a sane environment")
|
||||||
# try to find repair command, if exists - exec it:
|
# try to find repair command, if exists - exec it:
|
||||||
repairCmd = self._getOperation('<actionrepair>', family)
|
repairCmd = self._getOperation('<actionrepair>', family)
|
||||||
if repairCmd:
|
if repairCmd:
|
||||||
if not self.executeCmd(repairCmd, self.timeout):
|
if not self.executeCmd(repairCmd, self.timeout):
|
||||||
|
self.__started[family] = 0
|
||||||
self._logSys.critical("Unable to restore environment")
|
self._logSys.critical("Unable to restore environment")
|
||||||
return False
|
return 0
|
||||||
self.__started[family] = 1
|
self.__started[family] = 1
|
||||||
else:
|
else:
|
||||||
# no repair command, try to restart action...
|
# no repair command, try to restart action...
|
||||||
|
@ -843,11 +844,11 @@ class CommandAction(ActionBase):
|
||||||
self._stop(family)
|
self._stop(family)
|
||||||
except RuntimeError: # bypass error in stop (if start/check succeeded hereafter).
|
except RuntimeError: # bypass error in stop (if start/check succeeded hereafter).
|
||||||
pass
|
pass
|
||||||
self._start(family)
|
self._start(family, forceStart=forceStart or not self._startOnDemand)
|
||||||
if not self.executeCmd(checkCmd, self.timeout):
|
if self.__started.get(family) and not self.executeCmd(checkCmd, self.timeout):
|
||||||
self._logSys.critical("Unable to restore environment")
|
self._logSys.critical("Unable to restore environment")
|
||||||
return False
|
return 0
|
||||||
return True
|
return 1
|
||||||
|
|
||||||
def _processCmd(self, cmd, aInfo=None):
|
def _processCmd(self, cmd, aInfo=None):
|
||||||
"""Executes a command with preliminary checks and substitutions.
|
"""Executes a command with preliminary checks and substitutions.
|
||||||
|
@ -876,7 +877,7 @@ class CommandAction(ActionBase):
|
||||||
# conditional corresponding family of the given ip:
|
# conditional corresponding family of the given ip:
|
||||||
try:
|
try:
|
||||||
family = aInfo["family"]
|
family = aInfo["family"]
|
||||||
except KeyError:
|
except (KeyError, TypeError):
|
||||||
family = ''
|
family = ''
|
||||||
|
|
||||||
# invariant check:
|
# invariant check:
|
||||||
|
@ -887,8 +888,10 @@ class CommandAction(ActionBase):
|
||||||
self._logSys.error("Invariant check failed. Unban is impossible.")
|
self._logSys.error("Invariant check failed. Unban is impossible.")
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
ret = self._invariantCheck(family, _beforeRepair)
|
# check and repair if broken:
|
||||||
if not ret:
|
ret = self._invariantCheck(family, _beforeRepair, forceStart=(cmd != '<actionunban>'))
|
||||||
|
# if not sane (and not restored) return:
|
||||||
|
if ret != 1:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Replace static fields
|
# Replace static fields
|
||||||
|
|
|
@ -228,8 +228,12 @@ class ExecuteActions(LogCaptureTestCase):
|
||||||
all=True, wait=True)
|
all=True, wait=True)
|
||||||
|
|
||||||
# check should fail (so cause stop/start):
|
# check should fail (so cause stop/start):
|
||||||
self.pruneLog('[test-phase 1] simulate inconsistent env')
|
self.pruneLog('[test-phase 1a] simulate inconsistent irreparable env by unban')
|
||||||
act['actioncheck?family=inet6'] = act.actioncheck + '; exit 1'
|
act['actioncheck?family=inet6'] = act.actioncheck + '; exit 1'
|
||||||
|
self.__actions.removeBannedIP('2001:db8::1')
|
||||||
|
self.assertLogged('Invariant check failed. Unban is impossible.',
|
||||||
|
wait=True)
|
||||||
|
self.pruneLog('[test-phase 1b] simulate inconsistent irreparable env by flush')
|
||||||
self.__actions._Actions__flushBan()
|
self.__actions._Actions__flushBan()
|
||||||
self.assertLogged(
|
self.assertLogged(
|
||||||
"stdout: %r" % 'ip flush inet4',
|
"stdout: %r" % 'ip flush inet4',
|
||||||
|
@ -238,7 +242,7 @@ class ExecuteActions(LogCaptureTestCase):
|
||||||
'No flush occured, do consistency check',
|
'No flush occured, do consistency check',
|
||||||
'Invariant check failed. Trying to restore a sane environment',
|
'Invariant check failed. Trying to restore a sane environment',
|
||||||
"stdout: %r" % 'ip stop', # same for both families
|
"stdout: %r" % 'ip stop', # same for both families
|
||||||
'Unable to restore environment',
|
'Failed to flush bans',
|
||||||
all=True, wait=True)
|
all=True, wait=True)
|
||||||
|
|
||||||
# check succeeds:
|
# check succeeds:
|
||||||
|
@ -283,11 +287,12 @@ class ExecuteActions(LogCaptureTestCase):
|
||||||
all=True)
|
all=True)
|
||||||
|
|
||||||
def testActionsConsistencyCheckDiffFam(self):
|
def testActionsConsistencyCheckDiffFam(self):
|
||||||
# same as testActionsConsistencyCheck, but different start/stop commands for both families
|
# same as testActionsConsistencyCheck, but different start/stop commands for both families and repair on unban
|
||||||
act = self.defaultAction({'start':' <family>', 'check':' <family>', 'flush':' <family>', 'stop':' <family>'})
|
act = self.defaultAction({'start':' <family>', 'check':' <family>', 'flush':' <family>', 'stop':' <family>'})
|
||||||
# flush for inet6 is intentionally "broken" here - test no unhandled except and invariant check:
|
# flush for inet6 is intentionally "broken" here - test no unhandled except and invariant check:
|
||||||
act['actionflush?family=inet6'] = act.actionflush + '; exit 1'
|
act['actionflush?family=inet6'] = act.actionflush + '; exit 1'
|
||||||
act.actionstart_on_demand = True
|
act.actionstart_on_demand = True
|
||||||
|
act.actionrepair_on_unban = True
|
||||||
self.__actions.start()
|
self.__actions.start()
|
||||||
self.assertNotLogged("stdout: %r" % 'ip start')
|
self.assertNotLogged("stdout: %r" % 'ip start')
|
||||||
|
|
||||||
|
@ -301,8 +306,36 @@ class ExecuteActions(LogCaptureTestCase):
|
||||||
all=True, wait=True)
|
all=True, wait=True)
|
||||||
|
|
||||||
# check should fail (so cause stop/start):
|
# check should fail (so cause stop/start):
|
||||||
self.pruneLog('[test-phase 1] simulate inconsistent env')
|
|
||||||
act['actioncheck?family=inet6'] = act.actioncheck + '; exit 1'
|
act['actioncheck?family=inet6'] = act.actioncheck + '; exit 1'
|
||||||
|
self.pruneLog('[test-phase 1a] simulate inconsistent irreparable env by unban')
|
||||||
|
self.__actions.removeBannedIP('2001:db8::1')
|
||||||
|
self.assertLogged('Invariant check failed. Trying to restore a sane environment',
|
||||||
|
"stdout: %r" % 'ip stop inet6',
|
||||||
|
all=True, wait=True)
|
||||||
|
self.assertNotLogged(
|
||||||
|
"stdout: %r" % 'ip start inet6', # start on demand (not on repair)
|
||||||
|
"stdout: %r" % 'ip stop inet4', # family inet4 is not affected
|
||||||
|
"stdout: %r" % 'ip start inet4',
|
||||||
|
all=True)
|
||||||
|
|
||||||
|
self.pruneLog('[test-phase 1b] simulate inconsistent irreparable env by ban')
|
||||||
|
self.assertEqual(self.__actions.addBannedIP('2001:db8::1'), 1)
|
||||||
|
self.assertLogged('Invariant check failed. Trying to restore a sane environment',
|
||||||
|
"stdout: %r" % 'ip stop inet6',
|
||||||
|
"stdout: %r" % 'ip start inet6',
|
||||||
|
"stdout: %r" % 'ip check inet6',
|
||||||
|
'Unable to restore environment',
|
||||||
|
'Failed to execute ban',
|
||||||
|
all=True, wait=True)
|
||||||
|
self.assertNotLogged(
|
||||||
|
"stdout: %r" % 'ip stop inet4', # family inet4 is not affected
|
||||||
|
"stdout: %r" % 'ip start inet4',
|
||||||
|
all=True)
|
||||||
|
|
||||||
|
act['actioncheck?family=inet6'] = act.actioncheck
|
||||||
|
self.assertEqual(self.__actions.addBannedIP('2001:db8::2'), 1)
|
||||||
|
act['actioncheck?family=inet6'] = act.actioncheck + '; exit 1'
|
||||||
|
self.pruneLog('[test-phase 1c] simulate inconsistent irreparable env by flush')
|
||||||
self.__actions._Actions__flushBan()
|
self.__actions._Actions__flushBan()
|
||||||
self.assertLogged(
|
self.assertLogged(
|
||||||
"stdout: %r" % 'ip flush inet4',
|
"stdout: %r" % 'ip flush inet4',
|
||||||
|
@ -311,7 +344,7 @@ class ExecuteActions(LogCaptureTestCase):
|
||||||
'No flush occured, do consistency check',
|
'No flush occured, do consistency check',
|
||||||
'Invariant check failed. Trying to restore a sane environment',
|
'Invariant check failed. Trying to restore a sane environment',
|
||||||
"stdout: %r" % 'ip stop inet6',
|
"stdout: %r" % 'ip stop inet6',
|
||||||
'Unable to restore environment',
|
'Failed to flush bans in jail',
|
||||||
all=True, wait=True)
|
all=True, wait=True)
|
||||||
# start/stop should be called for inet6 only:
|
# start/stop should be called for inet6 only:
|
||||||
self.assertNotLogged(
|
self.assertNotLogged(
|
||||||
|
|
Loading…
Reference in New Issue