* 'ainfo-copy' of https://github.com/kwirk/fail2ban:
  TST: actions modifying aInfo test more robust
  TST: Test for actions modifying (un)ban aInfo
  BF: aInfo could be modified by actions, causing unexpected behaviour
pull/747/head
Yaroslav Halchenko 11 years ago
commit 0adb10f653

@ -45,7 +45,7 @@ messages['ban'] = {}
messages['ban']['head'] = \ messages['ban']['head'] = \
"""Hi, """Hi,
The IP %(ip)s has just been banned for %(bantime)s seconds The IP %(ip)s has just been banned for %(bantime)i seconds
by Fail2Ban after %(failures)i attempts against %(jailname)s. by Fail2Ban after %(failures)i attempts against %(jailname)s.
""" """
messages['ban']['tail'] = \ messages['ban']['tail'] = \

@ -69,6 +69,9 @@ class CallingMap(MutableMapping):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.data = dict(*args, **kwargs) self.data = dict(*args, **kwargs)
def __repr__(self):
return "%s(%r)" % (self.__class__.__name__, self.data)
def __getitem__(self, key): def __getitem__(self, key):
value = self.data[key] value = self.data[key]
if callable(value): if callable(value):
@ -88,6 +91,9 @@ class CallingMap(MutableMapping):
def __len__(self): def __len__(self):
return len(self.data) return len(self.data)
def copy(self):
return self.__class__(self.data.copy())
class ActionBase(object): class ActionBase(object):
"""An abstract base class for actions in Fail2Ban. """An abstract base class for actions in Fail2Ban.

@ -274,11 +274,12 @@ class Actions(JailThread, Mapping):
logSys.notice("[%s] Ban %s" % (self._jail.name, aInfo["ip"])) logSys.notice("[%s] Ban %s" % (self._jail.name, aInfo["ip"]))
for name, action in self._actions.iteritems(): for name, action in self._actions.iteritems():
try: try:
action.ban(aInfo) action.ban(aInfo.copy())
except Exception as e: except Exception as e:
logSys.error( logSys.error(
"Failed to execute ban jail '%s' action '%s': %s", "Failed to execute ban jail '%s' action '%s' "
self._jail.name, name, e, "info '%r': %s",
self._jail.name, name, aInfo, e,
exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) exc_info=logSys.getEffectiveLevel()<=logging.DEBUG)
return True return True
else: else:
@ -322,11 +323,12 @@ class Actions(JailThread, Mapping):
logSys.notice("[%s] Unban %s" % (self._jail.name, aInfo["ip"])) logSys.notice("[%s] Unban %s" % (self._jail.name, aInfo["ip"]))
for name, action in self._actions.iteritems(): for name, action in self._actions.iteritems():
try: try:
action.unban(aInfo) action.unban(aInfo.copy())
except Exception as e: except Exception as e:
logSys.error( logSys.error(
"Failed to execute unban jail '%s' action '%s': %s", "Failed to execute unban jail '%s' action '%s' "
self._jail.name, name, e, "info '%r': %s",
self._jail.name, name, aInfo, e,
exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) exc_info=logSys.getEffectiveLevel()<=logging.DEBUG)
@property @property

@ -29,6 +29,7 @@ import os
import tempfile import tempfile
from ..server.actions import Actions from ..server.actions import Actions
from ..server.ticket import FailTicket
from .dummyjail import DummyJail from .dummyjail import DummyJail
from .utils import LogCaptureTestCase from .utils import LogCaptureTestCase
@ -140,3 +141,28 @@ class ExecuteActions(LogCaptureTestCase):
self.__actions.stop() self.__actions.stop()
self.__actions.join() self.__actions.join()
self.assertTrue(self._is_logged("Failed to stop")) self.assertTrue(self._is_logged("Failed to stop"))
def testBanActionsAInfo(self):
# Action which deletes IP address from aInfo
self.__actions.add(
"action1",
os.path.join(TEST_FILES_DIR, "action.d/action_modifyainfo.py"),
{})
self.__actions.add(
"action2",
os.path.join(TEST_FILES_DIR, "action.d/action_modifyainfo.py"),
{})
self.__jail.putFailTicket(FailTicket("1.2.3.4", 0))
self.__actions._Actions__checkBan()
# Will fail if modification of aInfo from first action propagates
# to second action, as both delete same key
self.assertFalse(self._is_logged("Failed to execute ban"))
self.assertTrue(self._is_logged("action1 ban deleted aInfo IP"))
self.assertTrue(self._is_logged("action2 ban deleted aInfo IP"))
self.__actions._Actions__flushBan()
# Will fail if modification of aInfo from first action propagates
# to second action, as both delete same key
self.assertFalse(self._is_logged("Failed to execute unban"))
self.assertTrue(self._is_logged("action1 unban deleted aInfo IP"))
self.assertTrue(self._is_logged("action2 unban deleted aInfo IP"))

@ -0,0 +1,14 @@
from fail2ban.server.action import ActionBase
class TestAction(ActionBase):
def ban(self, aInfo):
del aInfo['ip']
self._logSys.info("%s ban deleted aInfo IP", self._name)
def unban(self, aInfo):
del aInfo['ip']
self._logSys.info("%s unban deleted aInfo IP", self._name)
Action = TestAction
Loading…
Cancel
Save