From 728aaec6b851b45afa5dcf030e229fda29a66373 Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Thu, 9 May 2013 22:27:16 -0400 Subject: [PATCH 01/18] ENH: point to the status of master branch on travis --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 04f8b349..65183b92 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ the website: http://www.fail2ban.org Code status: ------------ -* [![tests status](https://secure.travis-ci.org/fail2ban/fail2ban.png)](https://travis-ci.org/fail2ban/fail2ban) travis-ci.org (master branch) +* [![tests status](https://secure.travis-ci.org/fail2ban/fail2ban.png?branch=master)](https://travis-ci.org/fail2ban/fail2ban) travis-ci.org (master branch) * [![Coverage Status](https://coveralls.io/repos/fail2ban/fail2ban/badge.png?branch=master)](https://coveralls.io/r/fail2ban/fail2ban) From 90d6a4a6cd8c2de9004239039c82dba80e562384 Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Thu, 9 May 2013 22:46:59 -0400 Subject: [PATCH 02/18] ENH: consistent operation of formatExceptionInfo + unittest for it --- common/helpers.py | 12 ++++++----- fail2ban-testcases | 3 +++ server/asyncserver.py | 4 ++-- testcases/misctestcase.py | 44 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 7 deletions(-) create mode 100644 testcases/misctestcase.py diff --git a/common/helpers.py b/common/helpers.py index dba4c9d6..c0cf052e 100644 --- a/common/helpers.py +++ b/common/helpers.py @@ -17,11 +17,7 @@ # 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 -# Author: Arturo 'Buanzo' Busleiman -# - -__author__ = "Cyril Jaquier" +__author__ = "Cyril Jaquier, Arturo 'Buanzo' Busleiman" __copyright__ = "Copyright (c) 2009 Cyril Jaquier" __license__ = "GPL" @@ -33,6 +29,12 @@ def formatExceptionInfo(): excName = cla.__name__ try: excArgs = exc.__dict__["args"] + # Assure that we always return a string, without unneeded + # 'decorations' with python <= 2.5 where args would be a tuple + if isinstance(excArgs, tuple) and len(excArgs) == 1: + excArgs = excArgs[0] + excArgs = str(excArgs) except KeyError: + # And always provide a string output excArgs = str(exc) return (excName, excArgs) diff --git a/fail2ban-testcases b/fail2ban-testcases index e00cc908..4e6689ea 100755 --- a/fail2ban-testcases +++ b/fail2ban-testcases @@ -36,6 +36,7 @@ from testcases import servertestcase from testcases import datedetectortestcase from testcases import actiontestcase from testcases import sockettestcase +from testcases import misctestcase from testcases.utils import FormatterWithTraceBack from server.mytime import MyTime @@ -150,6 +151,8 @@ tests.addTest(unittest.makeSuite(clientreadertestcase.JailReaderTest)) tests.addTest(unittest.makeSuite(clientreadertestcase.JailsReaderTest)) # CSocket and AsyncServer tests.addTest(unittest.makeSuite(sockettestcase.Socket)) +# Misc helpers +tests.addTest(unittest.makeSuite(misctestcase.HelpersTest)) # Filter if not opts.no_network: diff --git a/server/asyncserver.py b/server/asyncserver.py index 87f91633..62a5dd8b 100644 --- a/server/asyncserver.py +++ b/server/asyncserver.py @@ -70,8 +70,8 @@ class RequestHandler(asynchat.async_chat): self.close_when_done() def handle_error(self): - e1,e2 = helpers.formatExceptionInfo() - logSys.error("Unexpected communication error: "+e2) + e1, e2 = helpers.formatExceptionInfo() + logSys.error("Unexpected communication error: %s" % str(e2)) logSys.error(traceback.format_exc().splitlines()) self.close() diff --git a/testcases/misctestcase.py b/testcases/misctestcase.py new file mode 100644 index 00000000..9b053c13 --- /dev/null +++ b/testcases/misctestcase.py @@ -0,0 +1,44 @@ +# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +__author__ = "Yaroslav Halchenko" +__copyright__ = "Copyright (c) 2013 Yaroslav Halchenko" +__license__ = "GPL" + +import unittest +from common.helpers import formatExceptionInfo + +class HelpersTest(unittest.TestCase): + + def testFormatExceptionInfoBasic(self): + try: + raise ValueError("Very bad exception") + except: + name, args = formatExceptionInfo() + self.assertEqual(name, "ValueError") + self.assertEqual(args, "Very bad exception") + + def testFormatExceptionConvertArgs(self): + try: + raise ValueError("Very bad", None) + except: + name, args = formatExceptionInfo() + self.assertEqual(name, "ValueError") + # might be fragile due to ' vs " + self.assertEqual(args, "('Very bad', None)") From 26715d5e5e4b1671ce6cbfabe13a6495a9f64b75 Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Thu, 9 May 2013 23:08:20 -0400 Subject: [PATCH 03/18] ENH: basic test for setup.py itself (when applicable, should greatly improve coverage ;) ) --- fail2ban-testcases | 1 + testcases/misctestcase.py | 38 +++++++++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/fail2ban-testcases b/fail2ban-testcases index 4e6689ea..94b8208e 100755 --- a/fail2ban-testcases +++ b/fail2ban-testcases @@ -153,6 +153,7 @@ tests.addTest(unittest.makeSuite(clientreadertestcase.JailsReaderTest)) tests.addTest(unittest.makeSuite(sockettestcase.Socket)) # Misc helpers tests.addTest(unittest.makeSuite(misctestcase.HelpersTest)) +tests.addTest(unittest.makeSuite(misctestcase.SetupTest)) # Filter if not opts.no_network: diff --git a/testcases/misctestcase.py b/testcases/misctestcase.py index 9b053c13..e3c20882 100644 --- a/testcases/misctestcase.py +++ b/testcases/misctestcase.py @@ -21,7 +21,12 @@ __author__ = "Yaroslav Halchenko" __copyright__ = "Copyright (c) 2013 Yaroslav Halchenko" __license__ = "GPL" -import unittest +import os, sys, unittest +import tempfile +import shutil + +from glob import glob + from common.helpers import formatExceptionInfo class HelpersTest(unittest.TestCase): @@ -42,3 +47,34 @@ class HelpersTest(unittest.TestCase): self.assertEqual(name, "ValueError") # might be fragile due to ' vs " self.assertEqual(args, "('Very bad', None)") + + +class SetupTest(unittest.TestCase): + + def setUp(self): + setup = os.path.join(os.path.dirname(__file__), '..', 'setup.py') + self.setup = os.path.exists(setup) and setup or None + if not self.setup and sys.version_info >= (2,7): # running not out of the source + raise unittest.SkipTest( + "Seems to be running not out of source distribution" + " -- cannot locate setup.py") + + def testSetupInstallRoot(self): + if not self.setup: return # if verbose skip didn't work out + tmp = tempfile.mkdtemp() + os.system("%s install --root=%s >/dev/null" % (self.setup, tmp)) + + def addpath(l): + return [os.path.join(tmp, x) for x in l] + + self.assertEqual(sorted(glob('%s/*' % tmp)), + addpath(['etc', 'usr', 'var'])) + + # Assure presence of some files we expect to see in the installation + for f in ('etc/fail2ban/fail2ban.conf', + 'etc/fail2ban/jail.conf'): + self.assertTrue(os.path.exists(os.path.join(tmp, f)), + msg="Can't find %s" % f) + + # clean up + shutil.rmtree(tmp) From e70d01bc1010a2ad617b3c2d5ecbc8d8bdb35459 Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Thu, 9 May 2013 23:16:03 -0400 Subject: [PATCH 04/18] TST: cover few more lines in fail2banreader.py --- testcases/clientreadertestcase.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/testcases/clientreadertestcase.py b/testcases/clientreadertestcase.py index fad16f04..faa8dcd6 100644 --- a/testcases/clientreadertestcase.py +++ b/testcases/clientreadertestcase.py @@ -160,6 +160,15 @@ class JailsReaderTest(unittest.TestCase): self.assertEqual(opts['socket'], '/var/run/fail2ban/fail2ban.sock') self.assertEqual(opts['pidfile'], '/var/run/fail2ban/fail2ban.pid') + configurator.getOptions() + configurator.convertToProtocol() + commands = configurator.getConfigStream() + # and there is logging information left to be passed into the + # server + self.assertEqual(commands, + [['set', 'loglevel', 3], + ['set', 'logtarget', '/var/log/fail2ban.log']]) + # and if we force change configurator's fail2ban's baseDir # there should be an error message (test visually ;) -- # otherwise just a code smoke test) From dc05eee0f502e94b10c3d6cdf0cda1fcc7f93b49 Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Thu, 9 May 2013 23:42:51 -0400 Subject: [PATCH 05/18] TST: Some primarily smoke tests for tests utils --- fail2ban-testcases | 1 + testcases/misctestcase.py | 54 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/fail2ban-testcases b/fail2ban-testcases index 94b8208e..5ad7494e 100755 --- a/fail2ban-testcases +++ b/fail2ban-testcases @@ -154,6 +154,7 @@ tests.addTest(unittest.makeSuite(sockettestcase.Socket)) # Misc helpers tests.addTest(unittest.makeSuite(misctestcase.HelpersTest)) tests.addTest(unittest.makeSuite(misctestcase.SetupTest)) +tests.addTest(unittest.makeSuite(misctestcase.TestsUtilsTest)) # Filter if not opts.no_network: diff --git a/testcases/misctestcase.py b/testcases/misctestcase.py index e3c20882..89be2b17 100644 --- a/testcases/misctestcase.py +++ b/testcases/misctestcase.py @@ -21,12 +21,14 @@ __author__ = "Yaroslav Halchenko" __copyright__ = "Copyright (c) 2013 Yaroslav Halchenko" __license__ = "GPL" +import logging import os, sys, unittest import tempfile import shutil from glob import glob +from utils import mbasename, TraceBack, FormatterWithTraceBack from common.helpers import formatExceptionInfo class HelpersTest(unittest.TestCase): @@ -78,3 +80,55 @@ class SetupTest(unittest.TestCase): # clean up shutil.rmtree(tmp) + +class TestsUtilsTest(unittest.TestCase): + + def testmbasename(self): + self.assertEqual(mbasename("sample.py"), 'sample') + self.assertEqual(mbasename("/long/path/sample.py"), 'sample') + # this one would include only the directory for the __init__ and base files + self.assertEqual(mbasename("/long/path/__init__.py"), 'path.__init__') + self.assertEqual(mbasename("/long/path/base.py"), 'path.base') + self.assertEqual(mbasename("/long/path/base"), 'path.base') + + def testTraceBack(self): + # pretty much just a smoke test since tests runners swallow all the detail + + for compress in True, False: + tb = TraceBack(compress=compress) + + def func_raise(): + raise ValueError() + + def deep_function(i): + if i: deep_function(i-1) + else: func_raise() + + try: + print deep_function(3) + except ValueError: + s = tb() + self.assertTrue('>' in s) + self.assertTrue(':' in s) + + + def testFormatterWithTraceBack(self): + from StringIO import StringIO + strout = StringIO() + Formatter = FormatterWithTraceBack + + # and both types of traceback at once + fmt = ' %(tb)s | %(tbc)s : %(message)s' + logSys = logging.getLogger("fail2ban_tests") + out = logging.StreamHandler(strout) + out.setFormatter(Formatter(fmt)) + logSys.addHandler(out) + logSys.error("XXX") + + s = strout.getvalue() + self.assertTrue(s.rstrip().endswith(': XXX')) + pindex = s.index('|') + + # in this case compressed and not should be the same (?) + self.assertTrue(pindex > 10) # we should have some traceback + self.assertEqual(s[:pindex], s[pindex+1:pindex*2 + 1]) From 281d310b7eb45bca49c0a57a32a6c430f6bfddac Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Fri, 10 May 2013 00:02:49 -0400 Subject: [PATCH 06/18] ENH: actually tune up TraceBack to determine "unittest" portions of the stack across all python releases before for 2.7 it would spit out "suite" and other components of unittest module --- testcases/misctestcase.py | 2 +- testcases/utils.py | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/testcases/misctestcase.py b/testcases/misctestcase.py index 89be2b17..c9449407 100644 --- a/testcases/misctestcase.py +++ b/testcases/misctestcase.py @@ -108,7 +108,7 @@ class TestsUtilsTest(unittest.TestCase): print deep_function(3) except ValueError: s = tb() - self.assertTrue('>' in s) + self.assertFalse('>' in s) # There is only "fail2ban-testcases" in this case, no true traceback self.assertTrue(':' in s) diff --git a/testcases/utils.py b/testcases/utils.py index 6b894193..87aab915 100644 --- a/testcases/utils.py +++ b/testcases/utils.py @@ -61,11 +61,12 @@ class TraceBack(object): def __call__(self): ftb = traceback.extract_stack(limit=100)[:-2] - entries = [[mbasename(x[0]), str(x[1])] for x in ftb] - entries = [ e for e in entries - if not e[0] in ['unittest', 'logging.__init__' ]] + entries = [[mbasename(x[0]), dirname(x[0]), str(x[1])] for x in ftb] + entries = [ [e[0], e[2]] for e in entries + if not (e[0] in ['unittest', 'logging.__init__'] + or e[1].endswith('/unittest'))] - # lets make it more consize + # lets make it more concise entries_out = [entries[0]] for entry in entries[1:]: if entry[0] == entries_out[-1][0]: From bdc86e5f1d1ff9dd09f46e11491671d515e3b4a2 Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Fri, 10 May 2013 11:17:04 -0400 Subject: [PATCH 07/18] ENH: use the same python executable for setup.py test This doesn't anyhow resolve gh-161 which was revealed consistently on Debian sytem after adding this testSetupInstallRoot --- testcases/misctestcase.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testcases/misctestcase.py b/testcases/misctestcase.py index c9449407..4a6b4241 100644 --- a/testcases/misctestcase.py +++ b/testcases/misctestcase.py @@ -64,7 +64,8 @@ class SetupTest(unittest.TestCase): def testSetupInstallRoot(self): if not self.setup: return # if verbose skip didn't work out tmp = tempfile.mkdtemp() - os.system("%s install --root=%s >/dev/null" % (self.setup, tmp)) + os.system("%s %s install --root=%s >/dev/null" + % (sys.executable, self.setup, tmp)) def addpath(l): return [os.path.join(tmp, x) for x in l] From 8161038987608d58c5202977048a8b3f6a462ad6 Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Fri, 10 May 2013 11:40:12 -0400 Subject: [PATCH 08/18] ENH: strengthen detection of working pyinotify Even though import might work -- pyinotify might be dysfunctional. Check by creating/deleting a dummy WatchManager upon import --- server/filterpyinotify.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/server/filterpyinotify.py b/server/filterpyinotify.py index 4c270d2e..cea82711 100644 --- a/server/filterpyinotify.py +++ b/server/filterpyinotify.py @@ -23,19 +23,28 @@ __author__ = "Cyril Jaquier, Lee Clemens, Yaroslav Halchenko" __copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2012 Lee Clemens, 2012 Yaroslav Halchenko" __license__ = "GPL" +import time, logging, pyinotify + from distutils.version import LooseVersion +from os.path import dirname, sep as pathsep from failmanager import FailManagerEmpty from filter import FileFilter from mytime import MyTime -import time, logging, pyinotify if not hasattr(pyinotify, '__version__') \ or LooseVersion(pyinotify.__version__) < '0.8.3': raise ImportError("Fail2Ban requires pyinotify >= 0.8.3") -from os.path import dirname, sep as pathsep +# Verify that pyinotify is functional on this system +# Even though imports -- might be dysfunctional, e.g. as on kfreebsd +try: + manager = pyinotify.WatchManager() + del manager +except Exception, e: + raise ImportError("Pyinotify is probably not functional on this system: %s" + % str(e)) # Gets the instance of the logger. logSys = logging.getLogger("fail2ban.filter") From 8af3ffb332a3e0aecabe09e97c6805169f72421e Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Fri, 10 May 2013 16:54:53 +0100 Subject: [PATCH 09/18] BF: Fix for filterpoll incorrectly checking for jailless state --- server/filterpoll.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/filterpoll.py b/server/filterpoll.py index 8a6a88e7..20cde0ca 100644 --- a/server/filterpoll.py +++ b/server/filterpoll.py @@ -104,7 +104,8 @@ class FilterPoll(FileFilter): time.sleep(self.getSleepTime()) else: time.sleep(self.getSleepTime()) - logSys.debug((self.jail and self.jail.getName() or "jailless") + + logSys.debug( + (self.jail is not None and self.jail.getName() or "jailless") + " filter terminated") return True @@ -130,7 +131,7 @@ class FilterPoll(FileFilter): self.__file404Cnt[filename] += 1 if self.__file404Cnt[filename] > 2: logSys.warn("Too many errors. Setting the jail idle") - if self.jail: + if self.jail is not None: self.jail.setIdle(True) else: logSys.warn("No jail is assigned to %s" % self) From 90b8433ac5113d17b458005ee77dc89d692cbd52 Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Sun, 12 May 2013 21:42:59 -0400 Subject: [PATCH 10/18] DOC: inline commends with ';' are in effect only if ';' follows as space --- README.Solaris | 2 +- config/fail2ban.conf | 2 +- config/jail.conf | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.Solaris b/README.Solaris index 49056062..10a5f88c 100644 --- a/README.Solaris +++ b/README.Solaris @@ -71,7 +71,7 @@ OPT: Create /etc/fail2ban/fail2ban.local containing: # Fail2Ban main configuration file # -# Comments: use '#' for comment lines and ';' for inline comments +# Comments: use '#' for comment lines and ';' (following a space) for inline comments # # Changes: in most of the cases you should not modify this # file, but provide customizations in fail2ban.local file, e.g.: diff --git a/config/fail2ban.conf b/config/fail2ban.conf index 1888eddb..4094c8cd 100644 --- a/config/fail2ban.conf +++ b/config/fail2ban.conf @@ -1,6 +1,6 @@ # Fail2Ban main configuration file # -# Comments: use '#' for comment lines and ';' for inline comments +# Comments: use '#' for comment lines and ';' (following a space) for inline comments # # Changes: in most of the cases you should not modify this # file, but provide customizations in fail2ban.local file, e.g.: diff --git a/config/jail.conf b/config/jail.conf index 33453ab5..ec5b32ef 100644 --- a/config/jail.conf +++ b/config/jail.conf @@ -1,6 +1,6 @@ # Fail2Ban jail specifications file # -# Comments: use '#' for comment lines and ';' for inline comments +# Comments: use '#' for comment lines and ';' (following a space) for inline comments # # Changes: in most of the cases you should not modify this # file, but provide customizations in jail.local file, e.g.: From 571ff33fde2c43b47c9a22ebdff020bdf77a3c17 Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Sun, 12 May 2013 22:19:17 -0400 Subject: [PATCH 11/18] ENH: issue a warning if jail name is longer than 19 symbols (Close #222) --- fail2ban-testcases | 1 + server/jail.py | 6 +++++- testcases/servertestcase.py | 10 ++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/fail2ban-testcases b/fail2ban-testcases index 5ad7494e..0bd70259 100755 --- a/fail2ban-testcases +++ b/fail2ban-testcases @@ -140,6 +140,7 @@ else: # pragma: no cover # Server #tests.addTest(unittest.makeSuite(servertestcase.StartStop)) tests.addTest(unittest.makeSuite(servertestcase.Transmitter)) +tests.addTest(unittest.makeSuite(servertestcase.JailTests)) tests.addTest(unittest.makeSuite(actiontestcase.ExecuteAction)) # FailManager tests.addTest(unittest.makeSuite(failmanagertestcase.AddFailure)) diff --git a/server/jail.py b/server/jail.py index dee64e7f..5e60ec7f 100644 --- a/server/jail.py +++ b/server/jail.py @@ -38,7 +38,7 @@ class Jail: _BACKENDS = ['pyinotify', 'gamin', 'polling'] def __init__(self, name, backend = "auto"): - self.__name = name + self.setName(name) self.__queue = Queue.Queue() self.__filter = None logSys.info("Creating new jail '%s'" % self.__name) @@ -102,6 +102,10 @@ class Jail: self.__filter = FilterPyinotify(self) def setName(self, name): + if len(name) >= 20: + logSys.warning("Jail name %r might be too long and some commands " + "might not function correctly. Please shorten" + % name) self.__name = name def getName(self): diff --git a/testcases/servertestcase.py b/testcases/servertestcase.py index ff3f2e88..0a5593e3 100644 --- a/testcases/servertestcase.py +++ b/testcases/servertestcase.py @@ -26,6 +26,7 @@ __license__ = "GPL" import unittest, socket, time, tempfile, os from server.server import Server +from server.jail import Jail from common.exceptions import UnknownJailException class StartStop(unittest.TestCase): @@ -507,3 +508,12 @@ class TransmitterLogging(TransmitterBase): self.setGetTest("loglevel", "-1", -1) self.setGetTest("loglevel", "0", 0) self.setGetTestNOK("loglevel", "Bird") + + +class JailTests(unittest.TestCase): + + def testLongName(self): + # Just a smoke test for now + longname = "veryveryverylongname" + jail = Jail(longname) + self.assertEqual(jail.getName(), longname) From 21474884e0d02902f147d3ccc67af3607c5e739d Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Sun, 12 May 2013 22:55:02 -0400 Subject: [PATCH 12/18] ENH: now we know that logging handlers closing was still buggy in 2.6.2 --- server/server.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/server/server.py b/server/server.py index a8cf1c2f..773fdf0b 100644 --- a/server/server.py +++ b/server/server.py @@ -377,10 +377,11 @@ class Server: handler.flush() handler.close() except (ValueError, KeyError): # pragma: no cover - if sys.version_info >= (2,6): - raise - # is known to be thrown after logging was shutdown once + # Is known to be thrown after logging was shutdown once # with older Pythons -- seems to be safe to ignore there + # At least it was still failing on 2.6.2-0ubuntu1 (jaunty) + if sys.version_info >= (2,6,3): + raise # tell the handler to use this format hdlr.setFormatter(formatter) logging.getLogger("fail2ban").addHandler(hdlr) From f345c4d7dc4c3261d0b1bb04b92356b438b48320 Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Sun, 12 May 2013 23:22:50 -0400 Subject: [PATCH 13/18] ENH: include explicit list of new files which should not be there upon "install --root" that is to figure out what gets there on failing travis tests: e.g. https://travis-ci.org/fail2ban/fail2ban/jobs/7112324 --- testcases/misctestcase.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/testcases/misctestcase.py b/testcases/misctestcase.py index 4a6b4241..c63d4cae 100644 --- a/testcases/misctestcase.py +++ b/testcases/misctestcase.py @@ -50,6 +50,15 @@ class HelpersTest(unittest.TestCase): # might be fragile due to ' vs " self.assertEqual(args, "('Very bad', None)") +# based on +# http://stackoverflow.com/questions/2186525/use-a-glob-to-find-files-recursively-in-python +def recursive_glob(treeroot, pattern): + import fnmatch, os + results = [] + for base, dirs, files in os.walk(treeroot): + goodfiles = fnmatch.filter(dirs + files, pattern) + results.extend(os.path.join(base, f) for f in goodfiles) + return results class SetupTest(unittest.TestCase): @@ -70,8 +79,23 @@ class SetupTest(unittest.TestCase): def addpath(l): return [os.path.join(tmp, x) for x in l] - self.assertEqual(sorted(glob('%s/*' % tmp)), - addpath(['etc', 'usr', 'var'])) + def strippath(l): + return [x[len(tmp)+1:] for x in l] + + got = strippath(sorted(glob('%s/*' % tmp))) + need = ['etc', 'usr', 'var'] + + if got != need: + files = {} + for missing in set(got).difference(need): + missing_full = os.path.join(tmp, missing) + files[missing] = os.path.exists(missing_full) \ + and strippath(recursive_glob(missing_full, '*')) or None + + self.assertEqual( + got, need, + msg="Got: %s Needed: %s under %s. Files under new paths: %s" + % (got, need, tmp, files)) # Assure presence of some files we expect to see in the installation for f in ('etc/fail2ban/fail2ban.conf', From 1b301d723d4b2b8e66ca083d62645d60ec5d264e Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Sun, 12 May 2013 23:27:32 -0400 Subject: [PATCH 14/18] ENH: also print the failing traceback line in case of failure Also to troubleshoot https://travis-ci.org/fail2ban/fail2ban/jobs/7112324 --- testcases/misctestcase.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testcases/misctestcase.py b/testcases/misctestcase.py index c63d4cae..d63f8afc 100644 --- a/testcases/misctestcase.py +++ b/testcases/misctestcase.py @@ -133,8 +133,8 @@ class TestsUtilsTest(unittest.TestCase): print deep_function(3) except ValueError: s = tb() - self.assertFalse('>' in s) # There is only "fail2ban-testcases" in this case, no true traceback - self.assertTrue(':' in s) + self.assertFalse('>' in s, msg="'>' present in %r" % s) # There is only "fail2ban-testcases" in this case, no true traceback + self.assertTrue(':' in s, msg="no ':' in %r" % s) def testFormatterWithTraceBack(self): From 6aed705f3dd3567256666304c72e2eecedf5f0a3 Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Sun, 12 May 2013 23:42:01 -0400 Subject: [PATCH 15/18] BF: (travis) if tests ran under coverage -- there is a traceback parts to report (thus > would be present) --- testcases/misctestcase.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/testcases/misctestcase.py b/testcases/misctestcase.py index d63f8afc..84eafa01 100644 --- a/testcases/misctestcase.py +++ b/testcases/misctestcase.py @@ -133,7 +133,13 @@ class TestsUtilsTest(unittest.TestCase): print deep_function(3) except ValueError: s = tb() - self.assertFalse('>' in s, msg="'>' present in %r" % s) # There is only "fail2ban-testcases" in this case, no true traceback + + # if we run it through 'coverage' (e.g. on travis) then we + # would get a traceback + if 'coverage' in s: + self.assertTrue('>' in s, msg="no '>' in %r" % s) + else: + self.assertFalse('>' in s, msg="'>' present in %r" % s) # There is only "fail2ban-testcases" in this case, no true traceback self.assertTrue(':' in s, msg="no ':' in %r" % s) From 04bf9eceb64059afce466dc5b78bfe1c33a9c25b Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Sun, 12 May 2013 23:42:57 -0400 Subject: [PATCH 16/18] BF: (travis) relax the test for needed to be presented installed directories -- allow new on travis scripts install into user's home by default --- testcases/misctestcase.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/testcases/misctestcase.py b/testcases/misctestcase.py index 84eafa01..08c8fc30 100644 --- a/testcases/misctestcase.py +++ b/testcases/misctestcase.py @@ -85,7 +85,11 @@ class SetupTest(unittest.TestCase): got = strippath(sorted(glob('%s/*' % tmp))) need = ['etc', 'usr', 'var'] - if got != need: + # if anything is missing + if set(need).difference(got): + # below code was actually to print out not missing but + # rather files in 'excess'. Left in place in case we + # decide to revert to such more strict test files = {} for missing in set(got).difference(need): missing_full = os.path.join(tmp, missing) From a7f41af67182c9baae351f9261b5a149ebb5af9b Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Mon, 13 May 2013 11:00:44 -0400 Subject: [PATCH 17/18] All the (version) updates for the release of 0.8.9 --- ChangeLog | 4 ++-- README.md | 6 +++--- common/version.py | 6 +++--- man/fail2ban-client.1 | 7 +++++-- man/fail2ban-regex.1 | 4 ++-- man/fail2ban-server.1 | 4 ++-- 6 files changed, 17 insertions(+), 14 deletions(-) diff --git a/ChangeLog b/ChangeLog index b8d39b3a..e3ed79a7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,10 +4,10 @@ |_| \__,_|_|_/___|_.__/\__,_|_||_| ================================================================================ -Fail2Ban (version 0.8.9) 2013/04/XX +Fail2Ban (version 0.8.9) 2013/05/13 ================================================================================ -ver. 0.8.9 (2013/05/XX) - wanna-be-stable +ver. 0.8.9 (2013/05/13) - wanna-be-stable ---------- Originally targeted as a bugfix release, it incorporated many new diff --git a/README.md b/README.md index 65183b92..91deaf19 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ / _|__ _(_) |_ ) |__ __ _ _ _ | _/ _` | | |/ /| '_ \/ _` | ' \ |_| \__,_|_|_/___|_.__/\__,_|_||_| - v0.8.8 2012/07/31 + v0.8.9 2013/05/13 ## Fail2Ban: ban hosts that cause multiple authentication errors @@ -30,8 +30,8 @@ Optional: To install, just do: - tar xvfj fail2ban-0.8.8.tar.bz2 - cd fail2ban-0.8.8 + tar xvfj fail2ban-0.8.9.tar.bz2 + cd fail2ban-0.8.9 python setup.py install This will install Fail2Ban into /usr/share/fail2ban. The executable scripts are diff --git a/common/version.py b/common/version.py index df3b97c3..e6f948cd 100644 --- a/common/version.py +++ b/common/version.py @@ -18,10 +18,10 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Author: Cyril Jaquier -# +# __author__ = "Cyril Jaquier, Yaroslav Halchenko" -__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2012 Yaroslav Halchenko" +__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2013 Yaroslav Halchenko" __license__ = "GPL" -version = "0.8.8" +version = "0.8.9" diff --git a/man/fail2ban-client.1 b/man/fail2ban-client.1 index 431e690f..d7d620bc 100644 --- a/man/fail2ban-client.1 +++ b/man/fail2ban-client.1 @@ -1,12 +1,12 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.10. -.TH FAIL2BAN-CLIENT "1" "March 2013" "fail2ban-client v0.8.8" "User Commands" +.TH FAIL2BAN-CLIENT "1" "May 2013" "fail2ban-client v0.8.9" "User Commands" .SH NAME fail2ban-client \- configure and control the server .SH SYNOPSIS .B fail2ban-client [\fIOPTIONS\fR] \fI\fR .SH DESCRIPTION -Fail2Ban v0.8.8 reads log file that contains password failure report +Fail2Ban v0.8.9 reads log file that contains password failure report and bans the corresponding IP addresses using firewall rules. .SH OPTIONS .TP @@ -62,6 +62,9 @@ server .TP \fBping\fR tests if the server is alive +.TP +\fBhelp\fR +return this output .IP LOGGING .TP diff --git a/man/fail2ban-regex.1 b/man/fail2ban-regex.1 index 09b9d6b0..a42d96d5 100644 --- a/man/fail2ban-regex.1 +++ b/man/fail2ban-regex.1 @@ -1,12 +1,12 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.10. -.TH FAIL2BAN-REGEX "1" "March 2013" "fail2ban-regex v0.8.8" "User Commands" +.TH FAIL2BAN-REGEX "1" "May 2013" "fail2ban-regex v0.8.9" "User Commands" .SH NAME fail2ban-regex \- test Fail2ban "failregex" option .SH SYNOPSIS .B fail2ban-regex [\fIOPTIONS\fR] \fI \fR[\fIIGNOREREGEX\fR] .SH DESCRIPTION -Fail2Ban v0.8.8 reads log file that contains password failure report +Fail2Ban v0.8.9 reads log file that contains password failure report and bans the corresponding IP addresses using firewall rules. .PP This tools can test regular expressions for "fail2ban". diff --git a/man/fail2ban-server.1 b/man/fail2ban-server.1 index 3f6b013f..43e9d6d4 100644 --- a/man/fail2ban-server.1 +++ b/man/fail2ban-server.1 @@ -1,12 +1,12 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.10. -.TH FAIL2BAN-SERVER "1" "March 2013" "fail2ban-server v0.8.8" "User Commands" +.TH FAIL2BAN-SERVER "1" "May 2013" "fail2ban-server v0.8.9" "User Commands" .SH NAME fail2ban-server \- start the server .SH SYNOPSIS .B fail2ban-server [\fIOPTIONS\fR] .SH DESCRIPTION -Fail2Ban v0.8.8 reads log file that contains password failure report +Fail2Ban v0.8.9 reads log file that contains password failure report and bans the corresponding IP addresses using firewall rules. .PP Only use this command for debugging purpose. Start the server with From 152c619dc401e29089b7a2220eff6240758f3eb3 Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Mon, 13 May 2013 11:24:07 -0400 Subject: [PATCH 18/18] BF: add missing files to MANIFEST (I think we shoult not rely on sdist anyways -- 'git tag' tarballs are more thorough ;) ) --- MANIFEST | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/MANIFEST b/MANIFEST index 0537abae..5491c7d5 100644 --- a/MANIFEST +++ b/MANIFEST @@ -47,6 +47,26 @@ testcases/files/testcase-usedns.log testcases/files/logs/bsd/syslog-plain.txt testcases/files/logs/bsd/syslog-v.txt testcases/files/logs/bsd/syslog-vv.txt +testcases/files/logs/apache-overflows +testcases/files/logs/assp +testcases/files/logs/asterisk +testcases/files/logs/dovecot +testcases/files/logs/exim +testcases/files/logs/lighttpd +testcases/files/logs/mysqld.log +testcases/files/logs/named-refused +testcases/files/logs/pam-generic +testcases/files/logs/postfix +testcases/files/logs/proftpd +testcases/files/logs/pure-ftpd +testcases/files/logs/roundcube-auth +testcases/files/logs/sasl +testcases/files/logs/sogo-auth +testcases/files/logs/sshd +testcases/files/logs/sshd-ddos +testcases/files/logs/vsftpd +testcases/files/logs/webmin-auth +testcases/files/logs/wu-ftpd testcases/banmanagertestcase.py testcases/failmanagertestcase.py testcases/clientreadertestcase.py @@ -60,6 +80,8 @@ testcases/files/testcase01.log testcases/files/testcase02.log testcases/files/testcase03.log testcases/files/testcase04.log +testcases/misctestcase.py +testcases/utils.py setup.py setup.cfg common/__init__.py @@ -101,6 +123,9 @@ config/filter.d/dropbear.conf config/filter.d/lighttpd-auth.conf config/filter.d/recidive.conf config/filter.d/roundcube-auth.conf +config/filter.d/assp.conf +config/filter.d/mysqld-auth.conf +config/filter.d/sogo-auth.conf config/action.d/bsd-ipfw.conf config/action.d/dummy.conf config/action.d/iptables-blocktype.conf @@ -153,3 +178,7 @@ files/cacti/README files/nagios/check_fail2ban files/nagios/f2ban.txt files/bash-completion +files/fail2ban-tmpfiles.conf +files/fail2ban.service +files/ipmasq-ZZZzzz_fail2ban.rul +files/nagios/README