From e442503133c86f5ef78dcc63cf1b90014213a22f Mon Sep 17 00:00:00 2001 From: Lee Clemens Date: Fri, 30 Dec 2011 00:18:52 -0500 Subject: [PATCH 01/14] Added pyinotify backend --- MANIFEST | 1 + config/jail.conf | 17 +++-- server/filterpyinotify.py | 148 ++++++++++++++++++++++++++++++++++++++ server/jail.py | 39 ++++++++-- 4 files changed, 194 insertions(+), 11 deletions(-) create mode 100644 server/filterpyinotify.py diff --git a/MANIFEST b/MANIFEST index 4c60f8e4..eef145b6 100644 --- a/MANIFEST +++ b/MANIFEST @@ -20,6 +20,7 @@ client/configurator.py client/csocket.py server/asyncserver.py server/filter.py +server/filterpyinotify.py server/filtergamin.py server/filterpoll.py server/iso8601.py diff --git a/config/jail.conf b/config/jail.conf index fec6b1bd..cdef1cb3 100644 --- a/config/jail.conf +++ b/config/jail.conf @@ -25,14 +25,17 @@ findtime = 600 # "maxretry" is the number of failures before a host get banned. maxretry = 3 -# "backend" specifies the backend used to get files modification. Available -# options are "gamin", "polling" and "auto". This option can be overridden in -# each jail too (use "gamin" for a jail and "polling" for another). +# "backend" specifies the backend used to get files modification. +# Available options are "pyinotify", "gamin", "polling" and "auto". +# This option can be overridden in each jail as well. # -# gamin: requires Gamin (a file alteration monitor) to be installed. If Gamin -# is not installed, Fail2ban will use polling. -# polling: uses a polling algorithm which does not require external libraries. -# auto: will choose Gamin if available and polling otherwise. +# pyinotify: requires pyinotify (a file alteration monitor) to be installed. +# If pyinotify is not installed, Fail2ban will use auto. +# gamin: requires Gamin (a file alteration monitor) to be installed. +# If Gamin is not installed, Fail2ban will use auto. +# polling: uses a polling algorithm which does not require external libraries. +# auto: will try to use the following backends, in order: +# pyinotify, gamin, polling. backend = auto diff --git a/server/filterpyinotify.py b/server/filterpyinotify.py new file mode 100644 index 00000000..c5e70e22 --- /dev/null +++ b/server/filterpyinotify.py @@ -0,0 +1,148 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + +# This file is part of Fail2Ban. +# +# Fail2Ban is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Fail2Ban is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Fail2Ban; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +# Author: Cyril Jaquier +# +# $Revision$ + +__author__ = "Cyril Jaquier" +__version__ = "$Revision$" +__date__ = "$Date$" +__copyright__ = "Copyright (c) 2004 Cyril Jaquier" +__license__ = "GPL" + +from failmanager import FailManagerEmpty +from filter import FileFilter +from mytime import MyTime + +import time, logging, pyinotify + + + +# Gets the instance of the logger. +logSys = logging.getLogger("fail2ban.filter") + +## +# Log reader class. +# +# This class reads a log file and detects login failures or anything else +# that matches a given regular expression. This class is instantiated by +# a Jail object. + +class ProcessPyinotify(pyinotify.ProcessEvent): + def __init__(self, FileFilter, **kargs): + super(ProcessPyinotify, self).__init__(**kargs) + self.__FileFilter = FileFilter + pass + + # just need default, since using mask on watch to limit events + def process_default(self, event): + logSys.debug("PYINOTIFY: Callback for Event: %s" % event) + self.__FileFilter.callback(event.pathname) + + +class FilterPyinotify(FileFilter): + # Constructor. + # + # Initialize the filter object with default values. + # @param jail the jail object + def __init__(self, jail): + FileFilter.__init__(self, jail) + self.__modified = False + self.monitor = pyinotify.WatchManager() + self.watches = dict() + + + def callback(self, path): + self.getFailures(path) + try: + while True: + ticket = self.failManager.toBan() + self.jail.putFailTicket(ticket) + except FailManagerEmpty: + self.failManager.cleanup(MyTime.time()) + self.dateDetector.sortTemplate() + self.__modified = False + + ## + # Add a log file path + # + # @param path log file path + def addLogPath(self, path, tail=False): + if self.containsLogPath(path): + logSys.error(path + " already exists") + else: + wd = self.monitor.add_watch(path, pyinotify.IN_MODIFY) + self.watches[path] = wd[path] + FileFilter.addLogPath(self, path, tail) + logSys.info("Added logfile = %s" % path) + + ## + # Delete a log path + # + # @param path the log file to delete + + def delLogPath(self, path): + if not self.containsLogPath(path): + logSys.error(path + " is not monitored") + else: + self.monitor.rm_watch(self.watches[path]) + FileFilter.delLogPath(self, path) + logSys.info("Removed logfile = %s" % path) + + ## + # Main loop. + # + # This function is the main loop of the thread. It checks if the + # file has been modified and looks for failures. + # @return True when the thread exits nicely + + def run(self): + self.setActive(True) + self.notifier = pyinotify.ThreadedNotifier(self.monitor, + ProcessPyinotify(self)) + self.notifier.start() + while self._isActive(): + if not self.getIdle(): + self.notifier.process_events() + # Convert sleep seconds to millis + if self.notifier.check_events(): + self.notifier.read_events() + else: + time.sleep(self.getSleepTime()) + # Cleanup pyinotify + self.__cleanup() + logSys.debug(self.jail.getName() + ": filter terminated") + return True + + ## + # Call super.stop() and then stop the 'Notifier' + + def stop(self): + # Call super to set __isRunning + super(FilterPyinotify, self).stop() + # Now stop the Notifier, otherwise we're deadlocked + self.notifier.stop() + + ## + # Deallocates the resources used by pyinotify. + + def __cleanup(self): + del self.notifier + del self.monitor diff --git a/server/jail.py b/server/jail.py index eefe69e5..16bdd088 100644 --- a/server/jail.py +++ b/server/jail.py @@ -41,13 +41,37 @@ class Jail: self.__queue = Queue.Queue() self.__filter = None logSys.info("Creating new jail '%s'" % self.__name) - if backend == "polling": - self.__initPoller() - else: + self.__setBackend = False + if backend == "auto": + # Quick-escape for auto (default/fall-back condition) + self.__setBackend = False + elif backend == "pyinotify": + try: + self.__initPyinotify() + self.__setBackend = True + except ImportError: + self.__setBackend = False + elif backend == "gamin": try: self.__initGamin() + self.__setBackend = True except ImportError: - self.__initPoller() + self.__setBackend = False + elif backend == "polling": + self.__initPoller() + self.__setBackend = True + + if not self.__setBackend: + # If auto, or unrecognized, or failed using an explicit value + try: + self.__initPyinotify() + except ImportError: + try: + self.__initGamin() + except ImportError: + self.__initPoller() + self.__setBackend = True + self.__action = Actions(self) def __initPoller(self): @@ -62,6 +86,13 @@ class Jail: from filtergamin import FilterGamin self.__filter = FilterGamin(self) + def __initPyinotify(self): + # Try to import pyinotify + import pyinotify + logSys.info("Jail '%s' uses pyinotify" % self.__name) + from filterpyinotify import FilterPyinotify + self.__filter = FilterPyinotify(self) + def setName(self, name): self.__name = name From d1050350db96703cf15a36c99f842e45623d17e5 Mon Sep 17 00:00:00 2001 From: Lee Clemens Date: Fri, 30 Dec 2011 00:28:17 -0500 Subject: [PATCH 02/14] Added pyinotify backend --- server/filterpyinotify.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/server/filterpyinotify.py b/server/filterpyinotify.py index c5e70e22..9b7a6f09 100644 --- a/server/filterpyinotify.py +++ b/server/filterpyinotify.py @@ -31,9 +31,7 @@ from failmanager import FailManagerEmpty from filter import FileFilter from mytime import MyTime -import time, logging, pyinotify - - +import time, logging, pyinotify # Gets the instance of the logger. logSys = logging.getLogger("fail2ban.filter") @@ -45,28 +43,19 @@ logSys = logging.getLogger("fail2ban.filter") # that matches a given regular expression. This class is instantiated by # a Jail object. -class ProcessPyinotify(pyinotify.ProcessEvent): - def __init__(self, FileFilter, **kargs): - super(ProcessPyinotify, self).__init__(**kargs) - self.__FileFilter = FileFilter - pass - - # just need default, since using mask on watch to limit events - def process_default(self, event): - logSys.debug("PYINOTIFY: Callback for Event: %s" % event) - self.__FileFilter.callback(event.pathname) - - class FilterPyinotify(FileFilter): + ## # Constructor. # # Initialize the filter object with default values. # @param jail the jail object + def __init__(self, jail): FileFilter.__init__(self, jail) self.__modified = False + # Pyinotify watch manager self.monitor = pyinotify.WatchManager() - self.watches = dict() + logSys.debug("Created FilterPyinotify") def callback(self, path): @@ -84,12 +73,12 @@ class FilterPyinotify(FileFilter): # Add a log file path # # @param path log file path + def addLogPath(self, path, tail=False): if self.containsLogPath(path): logSys.error(path + " already exists") else: wd = self.monitor.add_watch(path, pyinotify.IN_MODIFY) - self.watches[path] = wd[path] FileFilter.addLogPath(self, path, tail) logSys.info("Added logfile = %s" % path) @@ -102,7 +91,6 @@ class FilterPyinotify(FileFilter): if not self.containsLogPath(path): logSys.error(path + " is not monitored") else: - self.monitor.rm_watch(self.watches[path]) FileFilter.delLogPath(self, path) logSys.info("Removed logfile = %s" % path) @@ -146,3 +134,15 @@ class FilterPyinotify(FileFilter): def __cleanup(self): del self.notifier del self.monitor + + +class ProcessPyinotify(pyinotify.ProcessEvent): + def __init__(self, FileFilter, **kargs): + super(ProcessPyinotify, self).__init__(**kargs) + self.__FileFilter = FileFilter + pass + + # just need default, since using mask on watch to limit events + def process_default(self, event): + logSys.debug("PYINOTIFY: Callback for Event: %s" % event) + self.__FileFilter.callback(event.pathname) From 4bf4d4ddb517a1980c72f2c2def3483d3cebe7d7 Mon Sep 17 00:00:00 2001 From: Lee Clemens Date: Fri, 30 Dec 2011 00:37:15 -0500 Subject: [PATCH 03/14] Removed wd assignment and irrelevant comment --- server/filterpyinotify.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/filterpyinotify.py b/server/filterpyinotify.py index 9b7a6f09..12317687 100644 --- a/server/filterpyinotify.py +++ b/server/filterpyinotify.py @@ -78,7 +78,7 @@ class FilterPyinotify(FileFilter): if self.containsLogPath(path): logSys.error(path + " already exists") else: - wd = self.monitor.add_watch(path, pyinotify.IN_MODIFY) + self.monitor.add_watch(path, pyinotify.IN_MODIFY) FileFilter.addLogPath(self, path, tail) logSys.info("Added logfile = %s" % path) @@ -109,7 +109,6 @@ class FilterPyinotify(FileFilter): while self._isActive(): if not self.getIdle(): self.notifier.process_events() - # Convert sleep seconds to millis if self.notifier.check_events(): self.notifier.read_events() else: From adca2b87e8e1557bd0bc2dc6a5df78d2c98858d0 Mon Sep 17 00:00:00 2001 From: Lee Clemens Date: Fri, 30 Dec 2011 17:20:58 -0500 Subject: [PATCH 04/14] Changed Copyright information --- server/filterpyinotify.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/server/filterpyinotify.py b/server/filterpyinotify.py index 12317687..1202eb41 100644 --- a/server/filterpyinotify.py +++ b/server/filterpyinotify.py @@ -18,13 +18,11 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Author: Cyril Jaquier -# -# $Revision$ -__author__ = "Cyril Jaquier" +__author__ = "Lee Clemens" __version__ = "$Revision$" __date__ = "$Date$" -__copyright__ = "Copyright (c) 2004 Cyril Jaquier" +__copyright__ = "Copyright (c) 2011 Lee Clemens" __license__ = "GPL" from failmanager import FailManagerEmpty From b0830385ed4e92fff7248cf664dbb02d576d2cef Mon Sep 17 00:00:00 2001 From: Lee Clemens Date: Sat, 31 Dec 2011 02:01:19 -0500 Subject: [PATCH 05/14] Update Free Software Foundation's address --- server/filterpyinotify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/filterpyinotify.py b/server/filterpyinotify.py index 1202eb41..11c5f911 100644 --- a/server/filterpyinotify.py +++ b/server/filterpyinotify.py @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with Fail2Ban; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Author: Cyril Jaquier From 4c130634980f2acc3b9d7352529d9f5a388435c1 Mon Sep 17 00:00:00 2001 From: Lee Clemens Date: Sat, 31 Dec 2011 03:33:58 -0500 Subject: [PATCH 06/14] Added addLogPath and delLogPath functionality (adds/removes paths from the WatchManager) --- server/filterpyinotify.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/server/filterpyinotify.py b/server/filterpyinotify.py index 11c5f911..ec6f53da 100644 --- a/server/filterpyinotify.py +++ b/server/filterpyinotify.py @@ -54,6 +54,7 @@ class FilterPyinotify(FileFilter): # Pyinotify watch manager self.monitor = pyinotify.WatchManager() logSys.debug("Created FilterPyinotify") + self.__watches = dict() def callback(self, path): @@ -76,7 +77,8 @@ class FilterPyinotify(FileFilter): if self.containsLogPath(path): logSys.error(path + " already exists") else: - self.monitor.add_watch(path, pyinotify.IN_MODIFY) + wd = self.monitor.add_watch(path, pyinotify.IN_MODIFY) + self.__watches.update(wd) FileFilter.addLogPath(self, path, tail) logSys.info("Added logfile = %s" % path) @@ -89,8 +91,14 @@ class FilterPyinotify(FileFilter): if not self.containsLogPath(path): logSys.error(path + " is not monitored") else: - FileFilter.delLogPath(self, path) - logSys.info("Removed logfile = %s" % path) + wdInt = self.__watches[path] + wd = self.monitor.rm_watch(wdInt) + if wd[wdInt]: + del self.__watches[path] + FileFilter.delLogPath(self, path) + logSys.info("Removed logfile = %s" % path) + else: + logSys.error("Failed to remove watch on path: %s", path) ## # Main loop. From 5c4ec6e7fb83022d7305c7fa83a71c2a637421e9 Mon Sep 17 00:00:00 2001 From: Lee Clemens Date: Sat, 31 Dec 2011 03:37:43 -0500 Subject: [PATCH 07/14] Renamed Notifier and Watch Manager to start with __ prefix --- server/filterpyinotify.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/server/filterpyinotify.py b/server/filterpyinotify.py index ec6f53da..c5ca6b99 100644 --- a/server/filterpyinotify.py +++ b/server/filterpyinotify.py @@ -52,7 +52,7 @@ class FilterPyinotify(FileFilter): FileFilter.__init__(self, jail) self.__modified = False # Pyinotify watch manager - self.monitor = pyinotify.WatchManager() + self.__monitor = pyinotify.WatchManager() logSys.debug("Created FilterPyinotify") self.__watches = dict() @@ -77,7 +77,7 @@ class FilterPyinotify(FileFilter): if self.containsLogPath(path): logSys.error(path + " already exists") else: - wd = self.monitor.add_watch(path, pyinotify.IN_MODIFY) + wd = self.__monitor.add_watch(path, pyinotify.IN_MODIFY) self.__watches.update(wd) FileFilter.addLogPath(self, path, tail) logSys.info("Added logfile = %s" % path) @@ -92,7 +92,7 @@ class FilterPyinotify(FileFilter): logSys.error(path + " is not monitored") else: wdInt = self.__watches[path] - wd = self.monitor.rm_watch(wdInt) + wd = self.__monitor.rm_watch(wdInt) if wd[wdInt]: del self.__watches[path] FileFilter.delLogPath(self, path) @@ -109,14 +109,14 @@ class FilterPyinotify(FileFilter): def run(self): self.setActive(True) - self.notifier = pyinotify.ThreadedNotifier(self.monitor, + self.__notifier = pyinotify.ThreadedNotifier(self.__monitor, ProcessPyinotify(self)) - self.notifier.start() + self.__notifier.start() while self._isActive(): if not self.getIdle(): - self.notifier.process_events() - if self.notifier.check_events(): - self.notifier.read_events() + self.__notifier.process_events() + if self.__notifier.check_events(): + self.__notifier.read_events() else: time.sleep(self.getSleepTime()) # Cleanup pyinotify @@ -131,14 +131,14 @@ class FilterPyinotify(FileFilter): # Call super to set __isRunning super(FilterPyinotify, self).stop() # Now stop the Notifier, otherwise we're deadlocked - self.notifier.stop() + self.__notifier.stop() ## # Deallocates the resources used by pyinotify. def __cleanup(self): - del self.notifier - del self.monitor + del self.__notifier + del self.__monitor class ProcessPyinotify(pyinotify.ProcessEvent): From aa4514bffa36d296de0f9dd8dfc9193e178d33d4 Mon Sep 17 00:00:00 2001 From: Lee Clemens Date: Sat, 31 Dec 2011 03:39:25 -0500 Subject: [PATCH 08/14] Removed PYINOTIFY prefix to debug statement for Callback for Event --- server/filterpyinotify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/filterpyinotify.py b/server/filterpyinotify.py index c5ca6b99..94e2328c 100644 --- a/server/filterpyinotify.py +++ b/server/filterpyinotify.py @@ -149,5 +149,5 @@ class ProcessPyinotify(pyinotify.ProcessEvent): # just need default, since using mask on watch to limit events def process_default(self, event): - logSys.debug("PYINOTIFY: Callback for Event: %s" % event) + logSys.debug("Callback for Event: %s" % event) self.__FileFilter.callback(event.pathname) From 02894623cfcabef6773051ab43bd6ba10b2b351c Mon Sep 17 00:00:00 2001 From: Lee Clemens Date: Tue, 3 Jan 2012 20:14:24 -0500 Subject: [PATCH 09/14] Moved dict() above debug statement --- server/filterpyinotify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/filterpyinotify.py b/server/filterpyinotify.py index 94e2328c..2f466960 100644 --- a/server/filterpyinotify.py +++ b/server/filterpyinotify.py @@ -53,8 +53,8 @@ class FilterPyinotify(FileFilter): self.__modified = False # Pyinotify watch manager self.__monitor = pyinotify.WatchManager() - logSys.debug("Created FilterPyinotify") self.__watches = dict() + logSys.debug("Created FilterPyinotify") def callback(self, path): From c9945db756f974a446a6d759cfe2fd92086b2cff Mon Sep 17 00:00:00 2001 From: Lee Clemens Date: Tue, 3 Jan 2012 21:30:55 -0500 Subject: [PATCH 10/14] Updated TODO --- TODO | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TODO b/TODO index 2b17c916..ce40ebcf 100644 --- a/TODO +++ b/TODO @@ -19,8 +19,6 @@ Legend: - Add timeout to external commands (signal alarm, watchdog thread, etc) -- New backend: pyinotify - - Uniformize filters and actions name. Use the software name (openssh, postfix, proftp) @@ -52,3 +50,5 @@ Legend: # better return values in function # refactoring in server.py, actions.py, filter.py + +* New backend: pyinotify From f9ccc0543f674dd5ab0ca362bfe41fb660396833 Mon Sep 17 00:00:00 2001 From: Lee Clemens Date: Sun, 8 Jan 2012 14:27:00 -0500 Subject: [PATCH 11/14] Added pyinotify requirements to README --- README | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README b/README index 4b5048ed..556e2fac 100644 --- a/README +++ b/README @@ -22,7 +22,12 @@ Required: >=python-2.3 (http://www.python.org) Optional: - >=gamin-0.0.21 (http://www.gnome.org/~veillard/gamin) + pyinotify: + >=linux-2.6.13 + >=python-2.4 + >=pyinotify-0.8.3 (https://github.com/seb-m/pyinotify) + Gamin: + >=gamin-0.0.21 (http://www.gnome.org/~veillard/gamin) To install, just do: From 7413817f9acb239c2e4fd088d7a35d7819c8998f Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Sun, 8 Jan 2012 21:27:12 -0500 Subject: [PATCH 12/14] RF: for pyinotify's filter -- adjusted authors/copyright and avoided super() for old-style class --- server/filterpyinotify.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/server/filterpyinotify.py b/server/filterpyinotify.py index 2f466960..66d43e53 100644 --- a/server/filterpyinotify.py +++ b/server/filterpyinotify.py @@ -17,12 +17,10 @@ # along with Fail2Ban; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# Author: Cyril Jaquier +# Original author: Cyril Jaquier -__author__ = "Lee Clemens" -__version__ = "$Revision$" -__date__ = "$Date$" -__copyright__ = "Copyright (c) 2011 Lee Clemens" +__author__ = "Cyril Jaquier, Lee Clemens, Yaroslav Halchenko" +__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2012 Lee Clemens, 2012 Yaroslav Halchenko" __license__ = "GPL" from failmanager import FailManagerEmpty @@ -143,7 +141,11 @@ class FilterPyinotify(FileFilter): class ProcessPyinotify(pyinotify.ProcessEvent): def __init__(self, FileFilter, **kargs): - super(ProcessPyinotify, self).__init__(**kargs) + #super(ProcessPyinotify, self).__init__(**kargs) + # for some reason root class _ProcessEvent is old-style (is + # not derived from object), so to play safe let's avoid super + # for now, and call superclass directly + pyinotify.ProcessEvent.__init__(self, **kargs) self.__FileFilter = FileFilter pass From f90d53fc2616d38b7ee2d65ba13ee7c0f1463b0c Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Sun, 8 Jan 2012 21:29:43 -0500 Subject: [PATCH 13/14] RF: traverse known backends in a loop should now be - easier to comprehend - more scalable happens new backends come - more informative with all the info and debug messages NB not sure why __setBackend was instance's attribute, now there is a method _setBackend --- server/jail.py | 82 +++++++++++++++++++++++++------------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/server/jail.py b/server/jail.py index ff7f4e25..4307db78 100644 --- a/server/jail.py +++ b/server/jail.py @@ -18,13 +18,9 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Author: Cyril Jaquier -# -# $Revision$ -__author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" -__copyright__ = "Copyright (c) 2004 Cyril Jaquier" +__author__ = "Cyril Jaquier, Lee Celemens, Yaroslav Halchenko" +__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2012 Lee Clemens, 2012 Yaroslav Halchenko" __license__ = "GPL" import Queue, logging @@ -35,58 +31,62 @@ from actions import Actions logSys = logging.getLogger("fail2ban.jail") class Jail: - + + _BACKENDS = ('pyinotify', 'gamin', 'pull') + """Known backends. Each backend should have corresponding + __initBackend method + """ + def __init__(self, name, backend = "auto"): self.__name = name self.__queue = Queue.Queue() self.__filter = None logSys.info("Creating new jail '%s'" % self.__name) - self.__setBackend = False - if backend == "auto": - # Quick-escape for auto (default/fall-back condition) - self.__setBackend = False - elif backend == "pyinotify": - try: - self.__initPyinotify() - self.__setBackend = True - except ImportError: - self.__setBackend = False - elif backend == "gamin": - try: - self.__initGamin() - self.__setBackend = True - except ImportError: - self.__setBackend = False - elif backend == "polling": - self.__initPoller() - self.__setBackend = True + self._setBackend(backend) - if not self.__setBackend: - # If auto, or unrecognized, or failed using an explicit value - try: - self.__initPyinotify() - except ImportError: - try: - self.__initGamin() - except ImportError: - self.__initPoller() - self.__setBackend = True + def _setBackend(self, backend): + backend = backend.lower() # to assure consistent matching - self.__action = Actions(self) - - def __initPoller(self): + backends = self._BACKENDS + if backend != 'auto': + # we have got strict specification of the backend to use + if not (backend in self._BACKENDS): + raise ValueError("Unknown backend %s. Must be among %s or 'auto'" + % (backend, backends)) + # so explore starting from it till the 'end' + backends = backends[backends.index(backend):] + + for b in backends: + initmethod = getattr(self, '_init%s' % b.capitalize()) + try: + initmethod() + if backend != 'auto' and b != backend: + logSys.warning("Could only initiated %r backend whenever " + "%r was requested" % (b, backend)) + else: + logSys.info("Initiated %r backend" % b) + self.__action = Actions(self) + return # we are done + except ImportError, e: + logSys.debug( + "Backend %r failed to initialize due to %s" % (b, e)) + raise RuntimeError( + "We should have initialized at least 'polling' backend") + + + def _initPoller(self): logSys.info("Jail '%s' uses poller" % self.__name) from filterpoll import FilterPoll self.__filter = FilterPoll(self) - def __initGamin(self): + def _initGamin(self): # Try to import gamin import gamin logSys.info("Jail '%s' uses Gamin" % self.__name) from filtergamin import FilterGamin self.__filter = FilterGamin(self) - def __initPyinotify(self): + def _initPyinotify(self): # Try to import pyinotify import pyinotify logSys.info("Jail '%s' uses pyinotify" % self.__name) From b7adb290887644a23927d5ffd27bf34ef2ba0d1a Mon Sep 17 00:00:00 2001 From: Lee Clemens Date: Mon, 9 Jan 2012 19:30:49 -0500 Subject: [PATCH 14/14] Spelling, Changed docstring to comment --- server/jail.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/server/jail.py b/server/jail.py index 4307db78..dd179a30 100644 --- a/server/jail.py +++ b/server/jail.py @@ -19,7 +19,7 @@ # Author: Cyril Jaquier -__author__ = "Cyril Jaquier, Lee Celemens, Yaroslav Halchenko" +__author__ = "Cyril Jaquier, Lee Clemens, Yaroslav Halchenko" __copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2012 Lee Clemens, 2012 Yaroslav Halchenko" __license__ = "GPL" @@ -32,10 +32,8 @@ logSys = logging.getLogger("fail2ban.jail") class Jail: - _BACKENDS = ('pyinotify', 'gamin', 'pull') - """Known backends. Each backend should have corresponding - __initBackend method - """ + #Known backends. Each backend should have corresponding __initBackend method + _BACKENDS = ('pyinotify', 'gamin', 'polling') def __init__(self, name, backend = "auto"): self.__name = name