RF: Change Jails and Actions to Mapping types

pull/549/head
Steven Hiscocks 2014-01-04 16:57:08 +00:00
parent a070284a18
commit 6e63f0ea5a
12 changed files with 332 additions and 373 deletions

View File

@ -103,7 +103,7 @@ class SMTPAction(ActionBase):
self.message_values = CallingMap( self.message_values = CallingMap(
jailname = self._jail.getName(), # Doesn't change jailname = self._jail.getName(), # Doesn't change
hostname = socket.gethostname, hostname = socket.gethostname,
bantime = self._jail.getAction().getBanTime, bantime = self._jail.actions.getBanTime,
) )
def _sendMessage(self, subject, text): def _sendMessage(self, subject, text):

View File

@ -29,7 +29,7 @@ __license__ = "GPL"
class DuplicateJailException(Exception): class DuplicateJailException(Exception):
pass pass
class UnknownJailException(Exception): class UnknownJailException(KeyError):
pass pass

View File

@ -102,7 +102,6 @@ protocol = [
["get <JAIL> usedns", "gets the usedns setting for <JAIL>"], ["get <JAIL> usedns", "gets the usedns setting for <JAIL>"],
["get <JAIL> maxretry", "gets the number of failures allowed for <JAIL>"], ["get <JAIL> maxretry", "gets the number of failures allowed for <JAIL>"],
["get <JAIL> maxlines", "gets the number of lines to buffer for <JAIL>"], ["get <JAIL> maxlines", "gets the number of lines to buffer for <JAIL>"],
["get <JAIL> addaction", "gets the last action which has been added for <JAIL>"],
["get <JAIL> actions", "gets a list of actions for <JAIL>"], ["get <JAIL> actions", "gets a list of actions for <JAIL>"],
["", "COMMAND ACTION INFORMATION",""], ["", "COMMAND ACTION INFORMATION",""],
["get <JAIL> action <ACT> actionstart", "gets the start command for the action <ACT> for <JAIL>"], ["get <JAIL> action <ACT> actionstart", "gets the start command for the action <ACT> for <JAIL>"],

View File

@ -79,12 +79,11 @@ class ActionBase(object):
place to create a python based action for fail2ban. This class can place to create a python based action for fail2ban. This class can
be inherited from to ease implementation, but is not required as be inherited from to ease implementation, but is not required as
long as the following required methods/properties are implemented: long as the following required methods/properties are implemented:
- __init__(jail, actionname) - __init__(jail, name)
- start() - start()
- stop() - stop()
- ban(aInfo) - ban(aInfo)
- unban(aInfo) - unban(aInfo)
- actionname
""" """
__metaclass__ = ABCMeta __metaclass__ = ABCMeta
@ -101,23 +100,17 @@ class ActionBase(object):
return False return False
return True return True
def __init__(self, jail, actionname): def __init__(self, jail, name):
"""Should initialise the action class with `jail` being the Jail """Should initialise the action class with `jail` being the Jail
object the action belongs to, `actionname` being the name assigned object the action belongs to, `name` being the name assigned
to the action, and `kwargs` being all other args that have been to the action, and `kwargs` being all other args that have been
specified with jail.conf or on the fail2ban-client. specified with jail.conf or on the fail2ban-client.
""" """
self._jail = jail self._jail = jail
self._actionname = actionname self._name = name
self._logSys = logging.getLogger( self._logSys = logging.getLogger(
'%s.%s' % (__name__, self.__class__.__name__)) '%s.%s' % (__name__, self.__class__.__name__))
@property
def actionname(self):
"""The name of the action, which should not change in the
lifetime of the action."""
return self._actionname
def start(self): def start(self):
"""Executed when the jail/action starts.""" """Executed when the jail/action starts."""
pass pass
@ -144,8 +137,8 @@ class CommandAction(ActionBase):
Fail2Ban uses. Fail2Ban uses.
""" """
def __init__(self, jail, actionname): def __init__(self, jail, name):
super(CommandAction, self).__init__(jail, actionname) super(CommandAction, self).__init__(jail, name)
self.timeout = 60 self.timeout = 60
## Command executed in order to initialize the system. ## Command executed in order to initialize the system.
self.actionstart = '' self.actionstart = ''
@ -172,7 +165,7 @@ class CommandAction(ActionBase):
def timeout(self, timeout): def timeout(self, timeout):
self._timeout = int(timeout) self._timeout = int(timeout)
self._logSys.debug("Set action %s timeout = %i" % self._logSys.debug("Set action %s timeout = %i" %
(self.actionname, self.timeout)) (self._name, self.timeout))
@property @property
def _properties(self): def _properties(self):

View File

@ -27,6 +27,7 @@ __license__ = "GPL"
import time, logging import time, logging
import os import os
import imp import imp
from collections import Mapping
from .banmanager import BanManager from .banmanager import BanManager
from .jailthread import JailThread from .jailthread import JailThread
@ -36,40 +37,61 @@ from .mytime import MyTime
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = logging.getLogger(__name__) logSys = logging.getLogger(__name__)
## class Actions(JailThread, Mapping):
# Execute commands. """Handles jail actions.
#
# This class reads the failures from the Jail queue and decide if an This class handles the actions of the jail. Creation, deletion or to
# action has to be taken. A BanManager take care of the banned IP actions must be done through this class. This class is based on the
# addresses. Mapping type, and the `add` method must be used to add new actions.
This class also starts and stops the actions, and fetches bans from
the jail executing these bans via the actions.
"""
class Actions(JailThread):
##
# Constructor.
#
# Initialize the filter object with default values.
# @param jail the jail object
def __init__(self, jail): def __init__(self, jail):
"""Initialise an empty Actions instance.
Parameters
----------
jail: Jail
The jail of which the actions belongs to.
"""
JailThread.__init__(self) JailThread.__init__(self)
## The jail which contains this action. ## The jail which contains this action.
self.jail = jail self._jail = jail
self.__actions = list() self._actions = dict()
## The ban manager. ## The ban manager.
self.__banManager = BanManager() self.__banManager = BanManager()
## def add(self, name, pythonModule=None, initOpts=None):
# Adds an action. """Adds a new action.
#
# @param name The action name Add a new action if not already present, defaulting to standard
`CommandAction`, or specified Python module.
def addAction(self, name, pythonModule=None, initOpts=None):
Parameters
----------
name : str
The name of the action.
pythonModule : str
Path to Python file which must contain `Action` class.
initOpts : dict
Options for Python Action, used as keyword arguments for
initialisation.
Raises
------
ValueError
If action name already exists.
RuntimeError
If external Python module does not have `Action` class
or does not implement necessary methods as per `ActionBase`
abstract class.
"""
# Check is action name already exists # Check is action name already exists
if name in [action.actionname for action in self.__actions]: if name in self._actions:
raise ValueError("Action %s already exists" % name) raise ValueError("Action %s already exists" % name)
if pythonModule is None: if pythonModule is None:
action = CommandAction(self.jail, name) action = CommandAction(self._jail, name)
else: else:
pythonModuleName = os.path.basename(pythonModule.strip(".py")) pythonModuleName = os.path.basename(pythonModule.strip(".py"))
customActionModule = imp.load_source( customActionModule = imp.load_source(
@ -79,53 +101,35 @@ class Actions(JailThread):
"%s module does not have 'Action' class" % pythonModule) "%s module does not have 'Action' class" % pythonModule)
elif not issubclass(customActionModule.Action, ActionBase): elif not issubclass(customActionModule.Action, ActionBase):
raise RuntimeError( raise RuntimeError(
"%s module %s does not implment required methods" % ( "%s module %s does not implement required methods" % (
pythonModule, customActionModule.Action.__name__)) pythonModule, customActionModule.Action.__name__))
action = customActionModule.Action(self.jail, name, **initOpts) action = customActionModule.Action(self._jail, name, **initOpts)
self.__actions.append(action) self._actions[name] = action
## def __getitem__(self, name):
# Removes an action. try:
# return self._actions[name]
# @param name The action name except KeyError:
raise KeyError("Invalid Action name: %s" % name)
def delAction(self, name):
for action in self.__actions: def __delitem__(self, name):
if action.actionname == name: try:
self.__actions.remove(action) del self._actions[name]
return except KeyError:
raise KeyError("Invalid Action name: %s" % name) raise KeyError("Invalid Action name: %s" % name)
## def __iter__(self):
# Returns an action. return iter(self._actions)
#
# Raises a KeyError exception if the action does not exist. def __len__(self):
# return len(self._actions)
# @param name the action name
# @return the action def __eq__(self, other): # Required for Threading
return False
def getAction(self, name):
for action in self.__actions: def __hash__(self): # Required for Threading
if action.actionname == name: return id(self)
return action
raise KeyError("Invalid Action name")
##
# Returns the last defined action.
#
# @return The last defined action.
def getLastAction(self):
return self.__actions[-1]
##
# Returns the list of actions
#
# @return list of actions
def getActions(self):
return self.__actions
## ##
# Set the ban time. # Set the ban time.
# #
@ -142,38 +146,52 @@ class Actions(JailThread):
def getBanTime(self): def getBanTime(self):
return self.__banManager.getBanTime() return self.__banManager.getBanTime()
##
# Remove a banned IP now, rather than waiting for it to expire, even if set to never expire.
#
# @return the IP string or 'None' if not unbanned.
def removeBannedIP(self, ip): def removeBannedIP(self, ip):
"""Removes banned IP calling actions' unban method
Remove a banned IP now, rather than waiting for it to expire,
even if set to never expire.
Parameters
----------
ip : str
The IP address to unban
Raises
------
ValueError
If `ip` is not banned
"""
# Find the ticket with the IP. # Find the ticket with the IP.
ticket = self.__banManager.getTicketByIP(ip) ticket = self.__banManager.getTicketByIP(ip)
if ticket is not None: if ticket is not None:
# Unban the IP. # Unban the IP.
self.__unBan(ticket) self.__unBan(ticket)
return ip else:
raise ValueError("IP %s is not banned" % ip) raise ValueError("IP %s is not banned" % ip)
##
# Main loop.
#
# This function is the main loop of the thread. It checks the Jail
# queue and executes commands when an IP address is banned.
# @return True when the thread exits nicely
def run(self): def run(self):
"""Main loop for Threading.
This function is the main loop of the thread. It checks the jail
queue and executes commands when an IP address is banned.
Returns
-------
bool
True when the thread exits nicely.
"""
self.setActive(True) self.setActive(True)
for action in self.__actions: for name, action in self._actions.iteritems():
try: try:
action.start() action.start()
except Exception as e: except Exception as e:
logSys.error("Failed to start jail '%s' action '%s': %s", logSys.error("Failed to start jail '%s' action '%s': %s",
self.jail.getName(), action.actionname, e) self._jail.getName(), name, e)
while self._isActive(): while self._isActive():
if not self.getIdle(): if not self.getIdle():
#logSys.debug(self.jail.getName() + ": action") #logSys.debug(self._jail.getName() + ": action")
ret = self.__checkBan() ret = self.__checkBan()
if not ret: if not ret:
self.__checkUnBan() self.__checkUnBan()
@ -181,24 +199,27 @@ class Actions(JailThread):
else: else:
time.sleep(self.getSleepTime()) time.sleep(self.getSleepTime())
self.__flushBan() self.__flushBan()
for action in self.__actions: for name, action in self._actions.iteritems():
try: try:
action.stop() action.stop()
except Exception as e: except Exception as e:
logSys.error("Failed to stop jail '%s' action '%s': %s", logSys.error("Failed to stop jail '%s' action '%s': %s",
self.jail.getName(), action.actionname, e) self._jail.getName(), name, e)
logSys.debug(self.jail.getName() + ": action terminated") logSys.debug(self._jail.getName() + ": action terminated")
return True return True
##
# Check for IP address to ban.
#
# Look in the Jail queue for FailTicket. If a ticket is available,
# it executes the "ban" command and add a ticket to the BanManager.
# @return True if an IP address get banned
def __checkBan(self): def __checkBan(self):
ticket = self.jail.getFailTicket() """Check for IP address to ban.
Look in the jail queue for FailTicket. If a ticket is available,
it executes the "ban" command and adds a ticket to the BanManager.
Returns
-------
bool
True if an IP address get banned.
"""
ticket = self._jail.getFailTicket()
if ticket != False: if ticket != False:
aInfo = CallingMap() aInfo = CallingMap()
bTicket = BanManager.createBanTicket(ticket) bTicket = BanManager.createBanTicket(ticket)
@ -206,83 +227,88 @@ class Actions(JailThread):
aInfo["failures"] = bTicket.getAttempt() aInfo["failures"] = bTicket.getAttempt()
aInfo["time"] = bTicket.getTime() aInfo["time"] = bTicket.getTime()
aInfo["matches"] = "\n".join(bTicket.getMatches()) aInfo["matches"] = "\n".join(bTicket.getMatches())
if self.jail.getDatabase() is not None: if self._jail.getDatabase() is not None:
aInfo["ipmatches"] = lambda: "\n".join( aInfo["ipmatches"] = lambda: "\n".join(
self.jail.getDatabase().getBansMerged( self._jail.getDatabase().getBansMerged(
ip=bTicket.getIP()).getMatches()) ip=bTicket.getIP()).getMatches())
aInfo["ipjailmatches"] = lambda: "\n".join( aInfo["ipjailmatches"] = lambda: "\n".join(
self.jail.getDatabase().getBansMerged( self._jail.getDatabase().getBansMerged(
ip=bTicket.getIP(), jail=self.jail).getMatches()) ip=bTicket.getIP(), jail=self._jail).getMatches())
aInfo["ipfailures"] = lambda: "\n".join( aInfo["ipfailures"] = lambda: "\n".join(
self.jail.getDatabase().getBansMerged( self._jail.getDatabase().getBansMerged(
ip=bTicket.getIP()).getAttempt()) ip=bTicket.getIP()).getAttempt())
aInfo["ipjailfailures"] = lambda: "\n".join( aInfo["ipjailfailures"] = lambda: "\n".join(
self.jail.getDatabase().getBansMerged( self._jail.getDatabase().getBansMerged(
ip=bTicket.getIP(), jail=self.jail).getAttempt()) ip=bTicket.getIP(), jail=self._jail).getAttempt())
if self.__banManager.addBanTicket(bTicket): if self.__banManager.addBanTicket(bTicket):
logSys.warning("[%s] Ban %s" % (self.jail.getName(), aInfo["ip"])) logSys.warning("[%s] Ban %s" % (self._jail.getName(), aInfo["ip"]))
for action in self.__actions: for name, action in self._actions.iteritems():
try: try:
action.ban(aInfo) action.ban(aInfo)
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': %s",
self.jail.getName(), action.actionname, e) self._jail.getName(), name, e)
return True return True
else: else:
logSys.info("[%s] %s already banned" % (self.jail.getName(), logSys.info("[%s] %s already banned" % (self._jail.getName(),
aInfo["ip"])) aInfo["ip"]))
return False return False
##
# Check for IP address to unban.
#
# Unban IP address which are outdated.
def __checkUnBan(self): def __checkUnBan(self):
"""Check for IP address to unban.
Unban IP addresses which are outdated.
"""
for ticket in self.__banManager.unBanList(MyTime.time()): for ticket in self.__banManager.unBanList(MyTime.time()):
self.__unBan(ticket) self.__unBan(ticket)
##
# Flush the ban list.
#
# Unban all IP address which are still in the banning list.
def __flushBan(self): def __flushBan(self):
"""Flush the ban list.
Unban all IP address which are still in the banning list.
"""
logSys.debug("Flush ban list") logSys.debug("Flush ban list")
for ticket in self.__banManager.flushBanList(): for ticket in self.__banManager.flushBanList():
self.__unBan(ticket) self.__unBan(ticket)
##
# Unbans host corresponding to the ticket.
#
# Executes the actions in order to unban the host given in the
# ticket.
def __unBan(self, ticket): def __unBan(self, ticket):
"""Unbans host corresponding to the ticket.
Executes the actions in order to unban the host given in the
ticket.
Parameters
----------
ticket : FailTicket
Ticket of failures of which to unban
"""
aInfo = dict() aInfo = dict()
aInfo["ip"] = ticket.getIP() aInfo["ip"] = ticket.getIP()
aInfo["failures"] = ticket.getAttempt() aInfo["failures"] = ticket.getAttempt()
aInfo["time"] = ticket.getTime() aInfo["time"] = ticket.getTime()
aInfo["matches"] = "".join(ticket.getMatches()) aInfo["matches"] = "".join(ticket.getMatches())
logSys.warning("[%s] Unban %s" % (self.jail.getName(), aInfo["ip"])) logSys.warning("[%s] Unban %s" % (self._jail.getName(), aInfo["ip"]))
for action in self.__actions: for name, action in self._actions.iteritems():
try: try:
action.unban(aInfo) action.unban(aInfo)
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': %s",
self.jail.getName(), action.actionname, e) self._jail.getName(), name, e)
##
# Get the status of the filter.
#
# Get some informations about the filter state such as the total
# number of failures.
# @return a list with tuple
def status(self): def status(self):
"""Get the status of the filter.
Get some informations about the filter state such as the total
number of failures.
Returns
-------
list
List of tuple pairs, each containing a description and value
for general status information.
"""
ret = [("Currently banned", self.__banManager.size()), ret = [("Currently banned", self.__banManager.size()),
("Total banned", self.__banManager.getBanTotal()), ("Total banned", self.__banManager.getBanTotal()),
("IP list", self.__banManager.getBanList())] ("IP list", self.__banManager.getBanList())]

View File

@ -71,7 +71,7 @@ class Jail:
"%r was requested" % (b, backend)) "%r was requested" % (b, backend))
else: else:
logSys.info("Initiated %r backend" % b) logSys.info("Initiated %r backend" % b)
self.__action = Actions(self) self.__actions = Actions(self)
return # we are done return # we are done
except ImportError, e: except ImportError, e:
logSys.debug( logSys.debug(
@ -124,11 +124,17 @@ class Jail:
def getDatabase(self): def getDatabase(self):
return self.__db return self.__db
def getFilter(self): @property
def filter(self):
"""The filter which the jail is using to monitor log files.
"""
return self.__filter return self.__filter
def getAction(self): @property
return self.__action def actions(self):
"""Actions object used to manage actions for jail.
"""
return self.__actions
def putFailTicket(self, ticket): def putFailTicket(self, ticket):
self.__queue.put(ticket) self.__queue.put(ticket)
@ -143,36 +149,36 @@ class Jail:
def start(self): def start(self):
self.__filter.start() self.__filter.start()
self.__action.start() self.__actions.start()
# Restore any previous valid bans from the database # Restore any previous valid bans from the database
if self.__db is not None: if self.__db is not None:
for ticket in self.__db.getBans( for ticket in self.__db.getBans(
jail=self, bantime=self.__action.getBanTime()): jail=self, bantime=self.__actions.getBanTime()):
self.__queue.put(ticket) self.__queue.put(ticket)
logSys.info("Jail '%s' started" % self.__name) logSys.info("Jail '%s' started" % self.__name)
def stop(self): def stop(self):
self.__filter.stop() self.__filter.stop()
self.__action.stop() self.__actions.stop()
self.__filter.join() self.__filter.join()
self.__action.join() self.__actions.join()
logSys.info("Jail '%s' stopped" % self.__name) logSys.info("Jail '%s' stopped" % self.__name)
def isAlive(self): def isAlive(self):
isAlive0 = self.__filter.isAlive() isAlive0 = self.__filter.isAlive()
isAlive1 = self.__action.isAlive() isAlive1 = self.__actions.isAlive()
return isAlive0 or isAlive1 return isAlive0 or isAlive1
def setIdle(self, value): def setIdle(self, value):
self.__filter.setIdle(value) self.__filter.setIdle(value)
self.__action.setIdle(value) self.__actions.setIdle(value)
def getIdle(self): def getIdle(self):
return self.__filter.getIdle() or self.__action.getIdle() return self.__filter.getIdle() or self.__actions.getIdle()
def getStatus(self): def getStatus(self):
fStatus = self.__filter.status() fStatus = self.__filter.status()
aStatus = self.__action.status() aStatus = self.__actions.status()
ret = [("filter", fStatus), ret = [("filter", fStatus),
("action", aStatus)] ("action", aStatus)]
return ret return ret

View File

@ -22,136 +22,83 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2013- Yaroslav Halchenko"
__license__ = "GPL" __license__ = "GPL"
from threading import Lock from threading import Lock
from collections import Mapping
from ..exceptions import DuplicateJailException, UnknownJailException from ..exceptions import DuplicateJailException, UnknownJailException
from .jail import Jail from .jail import Jail
##
# Handles the jails.
#
# This class handles the jails. Creation, deletion or access to a jail must be
# done through this class. This class is thread-safe which is not the case of
# the jail itself, including filter and actions.
class Jails: class Jails(Mapping):
"""Handles the jails.
##
# Constructor. This class handles the jails. Creation, deletion or access to a jail
must be done through this class. This class is thread-safe which is
not the case of the jail itself, including filter and actions. This
class is based on Mapping type, and the `add` method must be used to
add additional jails.
"""
def __init__(self): def __init__(self):
"""Initialise an empty Jails instance.
"""
self.__lock = Lock() self.__lock = Lock()
self.__jails = dict() self._jails = dict()
##
# Adds a jail.
#
# Adds a new jail which should use the given backend. Raises a
# <code>DuplicateJailException</code> if the jail is already defined.
# @param name The name of the jail
# @param backend The backend to use
def add(self, name, backend, db=None): def add(self, name, backend, db=None):
"""Adds a jail.
Adds a new jail if not already present which should use the
given backend.
Parameters
----------
name : str
The name of the jail.
backend : str
The backend to use.
Raises
------
DuplicateJailException
If jail name is already present.
"""
try: try:
self.__lock.acquire() self.__lock.acquire()
if self.__jails.has_key(name): if name in self._jails:
raise DuplicateJailException(name) raise DuplicateJailException(name)
else: else:
self.__jails[name] = Jail(name, backend, db) self._jails[name] = Jail(name, backend, db)
finally:
self.__lock.release()
##
# Removes a jail.
#
# Removes the jail <code>name</code>. Raise an <code>UnknownJailException</code>
# if the jail does not exist.
# @param name The name of the jail
def remove(self, name):
try:
self.__lock.acquire()
if self.__jails.has_key(name):
del self.__jails[name]
else:
raise UnknownJailException(name)
finally:
self.__lock.release()
##
# Returns a jail.
#
# Returns the jail <code>name</code>. Raise an <code>UnknownJailException</code>
# if the jail does not exist.
# @param name The name of the jail
def get(self, name):
try:
self.__lock.acquire()
if self.__jails.has_key(name):
jail = self.__jails[name]
return jail
else:
raise UnknownJailException(name)
finally:
self.__lock.release()
##
# Returns an action class instance.
#
# Returns the action object of the jail <code>name</code>. Raise an
# <code>UnknownJailException</code> if the jail does not exist.
# @param name The name of the jail
def getAction(self, name):
try:
self.__lock.acquire()
if self.__jails.has_key(name):
action = self.__jails[name].getAction()
return action
else:
raise UnknownJailException(name)
finally:
self.__lock.release()
##
# Returns a filter class instance.
#
# Returns the filter object of the jail <code>name</code>. Raise an
# <code>UnknownJailException</code> if the jail does not exist.
# @param name The name of the jail
def getFilter(self, name):
try:
self.__lock.acquire()
if self.__jails.has_key(name):
action = self.__jails[name].getFilter()
return action
else:
raise UnknownJailException(name)
finally:
self.__lock.release()
##
# Returns the jails.
#
# Returns a copy of the jails list.
def getAll(self):
try:
self.__lock.acquire()
return self.__jails.copy()
finally:
self.__lock.release()
##
# Returns the size of the jails.
#
# Returns the number of jails.
def size(self):
try:
self.__lock.acquire()
return len(self.__jails)
finally: finally:
self.__lock.release() self.__lock.release()
def __getitem__(self, name):
try:
self.__lock.acquire()
return self._jails[name]
except KeyError:
raise UnknownJailException(name)
finally:
self.__lock.release()
def __delitem__(self, name):
try:
self.__lock.acquire()
del self._jails[name]
except KeyError:
raise UnknownJailException(name)
finally:
self.__lock.release()
def __len__(self):
try:
self.__lock.acquire()
return len(self._jails)
finally:
self.__lock.release()
def __iter__(self):
try:
self.__lock.acquire()
return iter(self._jails)
finally:
self.__lock.release()

View File

@ -122,10 +122,10 @@ class Server:
def addJail(self, name, backend): def addJail(self, name, backend):
self.__jails.add(name, backend, self.__db) self.__jails.add(name, backend, self.__db)
if self.__db is not None: if self.__db is not None:
self.__db.addJail(self.__jails.get(name)) self.__db.addJail(self.__jails[name])
def delJail(self, name): def delJail(self, name):
self.__jails.remove(name) del self.__jails[name]
if self.__db is not None: if self.__db is not None:
self.__db.delJailName(name) self.__db.delJailName(name)
@ -133,7 +133,7 @@ class Server:
try: try:
self.__lock.acquire() self.__lock.acquire()
if not self.isAlive(name): if not self.isAlive(name):
self.__jails.get(name).start() self.__jails[name].start()
finally: finally:
self.__lock.release() self.__lock.release()
@ -142,7 +142,7 @@ class Server:
try: try:
self.__lock.acquire() self.__lock.acquire()
if self.isAlive(name): if self.isAlive(name):
self.__jails.get(name).stop() self.__jails[name].stop()
self.delJail(name) self.delJail(name)
finally: finally:
self.__lock.release() self.__lock.release()
@ -151,43 +151,43 @@ class Server:
logSys.info("Stopping all jails") logSys.info("Stopping all jails")
try: try:
self.__lock.acquire() self.__lock.acquire()
for jail in self.__jails.getAll(): for jail in self.__jails.keys():
self.stopJail(jail) self.stopJail(jail)
finally: finally:
self.__lock.release() self.__lock.release()
def isAlive(self, name): def isAlive(self, name):
return self.__jails.get(name).isAlive() return self.__jails[name].isAlive()
def setIdleJail(self, name, value): def setIdleJail(self, name, value):
self.__jails.get(name).setIdle(value) self.__jails[name].setIdle(value)
return True return True
def getIdleJail(self, name): def getIdleJail(self, name):
return self.__jails.get(name).getIdle() return self.__jails[name].getIdle()
# Filter # Filter
def addIgnoreIP(self, name, ip): def addIgnoreIP(self, name, ip):
self.__jails.getFilter(name).addIgnoreIP(ip) self.__jails[name].filter.addIgnoreIP(ip)
def delIgnoreIP(self, name, ip): def delIgnoreIP(self, name, ip):
self.__jails.getFilter(name).delIgnoreIP(ip) self.__jails[name].filter.delIgnoreIP(ip)
def getIgnoreIP(self, name): def getIgnoreIP(self, name):
return self.__jails.getFilter(name).getIgnoreIP() return self.__jails[name].filter.getIgnoreIP()
def addLogPath(self, name, fileName, tail=False): def addLogPath(self, name, fileName, tail=False):
filter_ = self.__jails.getFilter(name) filter_ = self.__jails[name].filter
if isinstance(filter_, FileFilter): if isinstance(filter_, FileFilter):
filter_.addLogPath(fileName, tail) filter_.addLogPath(fileName, tail)
def delLogPath(self, name, fileName): def delLogPath(self, name, fileName):
filter_ = self.__jails.getFilter(name) filter_ = self.__jails[name].filter
if isinstance(filter_, FileFilter): if isinstance(filter_, FileFilter):
filter_.delLogPath(fileName) filter_.delLogPath(fileName)
def getLogPath(self, name): def getLogPath(self, name):
filter_ = self.__jails.getFilter(name) filter_ = self.__jails[name].filter
if isinstance(filter_, FileFilter): if isinstance(filter_, FileFilter):
return [m.getFileName() return [m.getFileName()
for m in filter_.getLogPath()] for m in filter_.getLogPath()]
@ -196,17 +196,17 @@ class Server:
return [] return []
def addJournalMatch(self, name, match): # pragma: systemd no cover def addJournalMatch(self, name, match): # pragma: systemd no cover
filter_ = self.__jails.getFilter(name) filter_ = self.__jails[name].filter
if isinstance(filter_, JournalFilter): if isinstance(filter_, JournalFilter):
filter_.addJournalMatch(match) filter_.addJournalMatch(match)
def delJournalMatch(self, name, match): # pragma: systemd no cover def delJournalMatch(self, name, match): # pragma: systemd no cover
filter_ = self.__jails.getFilter(name) filter_ = self.__jails[name].filter
if isinstance(filter_, JournalFilter): if isinstance(filter_, JournalFilter):
filter_.delJournalMatch(match) filter_.delJournalMatch(match)
def getJournalMatch(self, name): # pragma: systemd no cover def getJournalMatch(self, name): # pragma: systemd no cover
filter_ = self.__jails.getFilter(name) filter_ = self.__jails[name].filter
if isinstance(filter_, JournalFilter): if isinstance(filter_, JournalFilter):
return filter_.getJournalMatch() return filter_.getJournalMatch()
else: else:
@ -214,112 +214,109 @@ class Server:
return [] return []
def setLogEncoding(self, name, encoding): def setLogEncoding(self, name, encoding):
filter_ = self.__jails.getFilter(name) filter_ = self.__jails[name].filter
if isinstance(filter_, FileFilter): if isinstance(filter_, FileFilter):
filter_.setLogEncoding(encoding) filter_.setLogEncoding(encoding)
def getLogEncoding(self, name): def getLogEncoding(self, name):
filter_ = self.__jails.getFilter(name) filter_ = self.__jails[name].filter
if isinstance(filter_, FileFilter): if isinstance(filter_, FileFilter):
return filter_.getLogEncoding() return filter_.getLogEncoding()
def setFindTime(self, name, value): def setFindTime(self, name, value):
self.__jails.getFilter(name).setFindTime(value) self.__jails[name].filter.setFindTime(value)
def getFindTime(self, name): def getFindTime(self, name):
return self.__jails.getFilter(name).getFindTime() return self.__jails[name].filter.getFindTime()
def setDatePattern(self, name, pattern): def setDatePattern(self, name, pattern):
self.__jails.getFilter(name).setDatePattern(pattern) self.__jails[name].filter.setDatePattern(pattern)
def getDatePattern(self, name): def getDatePattern(self, name):
return self.__jails.getFilter(name).getDatePattern() return self.__jails[name].filter.getDatePattern()
def setIgnoreCommand(self, name, value): def setIgnoreCommand(self, name, value):
self.__jails.getFilter(name).setIgnoreCommand(value) self.__jails[name].filter.setIgnoreCommand(value)
def getIgnoreCommand(self, name): def getIgnoreCommand(self, name):
return self.__jails.getFilter(name).getIgnoreCommand() return self.__jails[name].filter.getIgnoreCommand()
def addFailRegex(self, name, value): def addFailRegex(self, name, value):
self.__jails.getFilter(name).addFailRegex(value) self.__jails[name].filter.addFailRegex(value)
def delFailRegex(self, name, index): def delFailRegex(self, name, index):
self.__jails.getFilter(name).delFailRegex(index) self.__jails[name].filter.delFailRegex(index)
def getFailRegex(self, name): def getFailRegex(self, name):
return self.__jails.getFilter(name).getFailRegex() return self.__jails[name].filter.getFailRegex()
def addIgnoreRegex(self, name, value): def addIgnoreRegex(self, name, value):
self.__jails.getFilter(name).addIgnoreRegex(value) self.__jails[name].filter.addIgnoreRegex(value)
def delIgnoreRegex(self, name, index): def delIgnoreRegex(self, name, index):
self.__jails.getFilter(name).delIgnoreRegex(index) self.__jails[name].filter.delIgnoreRegex(index)
def getIgnoreRegex(self, name): def getIgnoreRegex(self, name):
return self.__jails.getFilter(name).getIgnoreRegex() return self.__jails[name].filter.getIgnoreRegex()
def setUseDns(self, name, value): def setUseDns(self, name, value):
self.__jails.getFilter(name).setUseDns(value) self.__jails[name].filter.setUseDns(value)
def getUseDns(self, name): def getUseDns(self, name):
return self.__jails.getFilter(name).getUseDns() return self.__jails[name].filter.getUseDns()
def setMaxRetry(self, name, value): def setMaxRetry(self, name, value):
self.__jails.getFilter(name).setMaxRetry(value) self.__jails[name].filter.setMaxRetry(value)
def getMaxRetry(self, name): def getMaxRetry(self, name):
return self.__jails.getFilter(name).getMaxRetry() return self.__jails[name].filter.getMaxRetry()
def setMaxLines(self, name, value): def setMaxLines(self, name, value):
self.__jails.getFilter(name).setMaxLines(value) self.__jails[name].filter.setMaxLines(value)
def getMaxLines(self, name): def getMaxLines(self, name):
return self.__jails.getFilter(name).getMaxLines() return self.__jails[name].filter.getMaxLines()
# Action # Action
def addAction(self, name, value, *args): def addAction(self, name, value, *args):
self.__jails.getAction(name).addAction(value, *args) self.__jails[name].actions.add(value, *args)
def getLastAction(self, name):
return self.__jails.getAction(name).getLastAction()
def getActions(self, name): def getActions(self, name):
return self.__jails.getAction(name).getActions() return self.__jails[name].actions
def delAction(self, name, value): def delAction(self, name, value):
self.__jails.getAction(name).delAction(value) del self.__jails[name].actions[value]
def getAction(self, name, value): def getAction(self, name, value):
return self.__jails.getAction(name).getAction(value) return self.__jails[name].actions[value]
def setBanTime(self, name, value): def setBanTime(self, name, value):
self.__jails.getAction(name).setBanTime(value) self.__jails[name].actions.setBanTime(value)
def setBanIP(self, name, value): def setBanIP(self, name, value):
return self.__jails.getFilter(name).addBannedIP(value) return self.__jails[name].filter.addBannedIP(value)
def setUnbanIP(self, name, value): def setUnbanIP(self, name, value):
return self.__jails.getAction(name).removeBannedIP(value) self.__jails[name].actions.removeBannedIP(value)
def getBanTime(self, name): def getBanTime(self, name):
return self.__jails.getAction(name).getBanTime() return self.__jails[name].actions.getBanTime()
# Status # Status
def status(self): def status(self):
try: try:
self.__lock.acquire() self.__lock.acquire()
jails = list(self.__jails.getAll()) jails = list(self.__jails)
jails.sort() jails.sort()
jailList = ", ".join(jails) jailList = ", ".join(jails)
ret = [("Number of jail", self.__jails.size()), ret = [("Number of jail", len(self.__jails)),
("Jail list", jailList)] ("Jail list", jailList)]
return ret return ret
finally: finally:
self.__lock.release() self.__lock.release()
def statusJail(self, name): def statusJail(self, name):
return self.__jails.get(name).getStatus() return self.__jails[name].getStatus()
# Logging # Logging
@ -447,7 +444,7 @@ class Server:
return "flushed" return "flushed"
def setDatabase(self, filename): def setDatabase(self, filename):
if self.__jails.size() == 0: if len(self.__jails) == 0:
if filename.lower() == "none": if filename.lower() == "none":
self.__db = None self.__db = None
else: else:

View File

@ -227,13 +227,14 @@ class Transmitter:
return self.__server.setBanIP(name,value) return self.__server.setBanIP(name,value)
elif command[1] == "unbanip": elif command[1] == "unbanip":
value = command[2] value = command[2]
return self.__server.setUnbanIP(name,value) self.__server.setUnbanIP(name, value)
return value
elif command[1] == "addaction": elif command[1] == "addaction":
args = [command[2]] args = [command[2]]
if len(command) > 3: if len(command) > 3:
args.extend([command[3], json.loads(command[4])]) args.extend([command[3], json.loads(command[4])])
self.__server.addAction(name, *args) self.__server.addAction(name, *args)
return self.__server.getLastAction(name).actionname return args[0]
elif command[1] == "delaction": elif command[1] == "delaction":
value = command[2] value = command[2]
self.__server.delAction(name, value) self.__server.delAction(name, value)
@ -300,10 +301,7 @@ class Transmitter:
elif command[1] == "bantime": elif command[1] == "bantime":
return self.__server.getBanTime(name) return self.__server.getBanTime(name)
elif command[1] == "actions": elif command[1] == "actions":
return [action.actionname return self.__server.getActions(name).keys()
for action in self.__server.getActions(name)]
elif command[1] == "addaction":
return self.__server.getLastAction(name).actionname
elif command[1] == "action": elif command[1] == "action":
actionname = command[2] actionname = command[2]
actionvalue = command[3] actionvalue = command[3]

View File

@ -47,8 +47,8 @@ class ExecuteActions(LogCaptureTestCase):
os.remove(self.__tmpfilename) os.remove(self.__tmpfilename)
def defaultActions(self): def defaultActions(self):
self.__actions.addAction('ip') self.__actions.add('ip')
self.__ip = self.__actions.getAction('ip') self.__ip = self.__actions['ip']
self.__ip.actionstart = 'echo ip start 64 >> "%s"' % self.__tmpfilename self.__ip.actionstart = 'echo ip start 64 >> "%s"' % self.__tmpfilename
self.__ip.actionban = 'echo ip ban <ip> >> "%s"' % self.__tmpfilename self.__ip.actionban = 'echo ip ban <ip> >> "%s"' % self.__tmpfilename
self.__ip.actionunban = 'echo ip unban <ip> >> "%s"' % self.__tmpfilename self.__ip.actionunban = 'echo ip unban <ip> >> "%s"' % self.__tmpfilename
@ -56,15 +56,15 @@ class ExecuteActions(LogCaptureTestCase):
self.__ip.actionstop = 'echo ip stop >> "%s"' % self.__tmpfilename self.__ip.actionstop = 'echo ip stop >> "%s"' % self.__tmpfilename
def testActionsManipulation(self): def testActionsManipulation(self):
self.__actions.addAction('test') self.__actions.add('test')
self.assertTrue(self.__actions.getAction('test')) self.assertTrue(self.__actions['test'])
self.assertTrue(self.__actions.getLastAction()) self.assertTrue('test' in self.__actions)
self.assertRaises(KeyError,self.__actions.getAction,*['nonexistant action']) self.assertFalse('nonexistant action' in self.__actions)
self.__actions.addAction('test1') self.__actions.add('test1')
self.__actions.delAction('test') del self.__actions['test']
self.__actions.delAction('test1') del self.__actions['test1']
self.assertRaises(KeyError, self.__actions.getAction, *['test']) self.assertFalse('test' in self.__actions)
self.assertRaises(IndexError,self.__actions.getLastAction) self.assertEqual(len(self.__actions), 0)
self.__actions.setBanTime(127) self.__actions.setBanTime(127)
self.assertEqual(self.__actions.getBanTime(),127) self.assertEqual(self.__actions.getBanTime(),127)
@ -85,7 +85,7 @@ class ExecuteActions(LogCaptureTestCase):
def testAddActionPython(self): def testAddActionPython(self):
self.__actions.addAction( self.__actions.add(
"Action", os.path.join(TEST_FILES_DIR, "action.d/action.py"), "Action", os.path.join(TEST_FILES_DIR, "action.d/action.py"),
{'opt1': 'value'}) {'opt1': 'value'})
@ -100,18 +100,18 @@ class ExecuteActions(LogCaptureTestCase):
self.assertTrue(self._is_logged("TestAction action stop")) self.assertTrue(self._is_logged("TestAction action stop"))
self.assertRaises(IOError, self.assertRaises(IOError,
self.__actions.addAction, "Action3", "/does/not/exist.py", {}) self.__actions.add, "Action3", "/does/not/exist.py", {})
# With optional argument # With optional argument
self.__actions.addAction( self.__actions.add(
"Action4", os.path.join(TEST_FILES_DIR, "action.d/action.py"), "Action4", os.path.join(TEST_FILES_DIR, "action.d/action.py"),
{'opt1': 'value', 'opt2': 'value2'}) {'opt1': 'value', 'opt2': 'value2'})
# With too many arguments # With too many arguments
self.assertRaises( self.assertRaises(
TypeError, self.__actions.addAction, "Action5", TypeError, self.__actions.add, "Action5",
os.path.join(TEST_FILES_DIR, "action.d/action.py"), os.path.join(TEST_FILES_DIR, "action.d/action.py"),
{'opt1': 'value', 'opt2': 'value2', 'opt3': 'value3'}) {'opt1': 'value', 'opt2': 'value2', 'opt3': 'value3'})
# Missing required argument # Missing required argument
self.assertRaises( self.assertRaises(
TypeError, self.__actions.addAction, "Action5", TypeError, self.__actions.add, "Action5",
os.path.join(TEST_FILES_DIR, "action.d/action.py"), {}) os.path.join(TEST_FILES_DIR, "action.d/action.py"), {})

View File

@ -3,8 +3,8 @@ from fail2ban.server.action import ActionBase
class TestAction(ActionBase): class TestAction(ActionBase):
def __init__(self, jail, actionname, opt1, opt2=None): def __init__(self, jail, name, opt1, opt2=None):
super(TestAction, self).__init__(jail, actionname) super(TestAction, self).__init__(jail, name)
self._logSys.debug("%s initialised" % self.__class__.__name__) self._logSys.debug("%s initialised" % self.__class__.__name__)
self.opt1 = opt1 self.opt1 = opt1
self.opt2 = opt2 self.opt2 = opt2

View File

@ -518,9 +518,6 @@ class Transmitter(TransmitterBase):
self.assertEqual( self.assertEqual(
self.transm.proceed(["set", self.jailName, "addaction", action]), self.transm.proceed(["set", self.jailName, "addaction", action]),
(0, action)) (0, action))
self.assertEqual(
self.transm.proceed(["get", self.jailName, "addaction"]),
(0, action))
self.assertEqual( self.assertEqual(
self.transm.proceed( self.transm.proceed(
["get", self.jailName, "actions"])[1][0], ["get", self.jailName, "actions"])[1][0],
@ -546,10 +543,6 @@ class Transmitter(TransmitterBase):
self.transm.proceed( self.transm.proceed(
["get", self.jailName, "action", action, "InvalidKey"])[0], ["get", self.jailName, "action", action, "InvalidKey"])[0],
1) 1)
self.assertEqual(
self.transm.proceed(
["get", self.jailName, "action", action, "actionname"]),
(0, action))
self.assertEqual( self.assertEqual(
self.transm.proceed( self.transm.proceed(
["set", self.jailName, "action", action, "timeout", "10"]), ["set", self.jailName, "action", action, "timeout", "10"]),
@ -575,7 +568,7 @@ class Transmitter(TransmitterBase):
self.assertEqual( self.assertEqual(
sorted(self.transm.proceed(["get", self.jailName, sorted(self.transm.proceed(["get", self.jailName,
"actionproperties", action])[1]), "actionproperties", action])[1]),
['actionname', 'opt1', 'opt2']) ['opt1', 'opt2'])
self.assertEqual( self.assertEqual(
self.transm.proceed(["get", self.jailName, "action", action, self.transm.proceed(["get", self.jailName, "action", action,
"opt1"]), "opt1"]),