From 77f451c4a38f60fabfa58e8b0a17d399bf7c7f05 Mon Sep 17 00:00:00 2001 From: sebres Date: Thu, 11 Aug 2016 18:34:18 +0200 Subject: [PATCH 1/4] introduces new command "fail2ban-python", as automatically created symlink to python executable, where fail2ban currently installed (resp. its modules are located); fixed pythonic filters and test scripts (running via "fail2ban-python" now); fixed test case "testSetupInstallRoot" not for default python (also using direct call, out of virtualenv); --- bin/fail2ban-testcases | 5 ++- .../ignorecommands/apache-fakegooglebot | 2 +- fail2ban/helpers.py | 18 +++++++++ .../tests/files/config/apache-auth/digest.py | 2 +- fail2ban/tests/files/ignorecommand.py | 2 +- fail2ban/tests/misctestcase.py | 6 ++- setup.py | 40 ++++++++++++++++++- 7 files changed, 69 insertions(+), 6 deletions(-) diff --git a/bin/fail2ban-testcases b/bin/fail2ban-testcases index 98b9118f..dcc55421 100755 --- a/bin/fail2ban-testcases +++ b/bin/fail2ban-testcases @@ -38,11 +38,14 @@ if os.path.exists("fail2ban/__init__.py"): from fail2ban.version import version from fail2ban.tests.utils import gatherTests -from fail2ban.helpers import FormatterWithTraceBack, getLogger +from fail2ban.helpers import updatePyExec, FormatterWithTraceBack, getLogger from fail2ban.server.mytime import MyTime from optparse import OptionParser, Option +# Update fail2ban-python env to current python version (where f2b-modules located/installed) +updatePyExec(os.path.dirname(__file__)) + def get_opt_parser(): # use module docstring for help output p = OptionParser( diff --git a/config/filter.d/ignorecommands/apache-fakegooglebot b/config/filter.d/ignorecommands/apache-fakegooglebot index fe4b6591..68a2cb5b 100755 --- a/config/filter.d/ignorecommands/apache-fakegooglebot +++ b/config/filter.d/ignorecommands/apache-fakegooglebot @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env fail2ban-python # Inspired by https://isc.sans.edu/forums/diary/When+Google+isnt+Google/15968/ # # Written in Python to reuse built-in Python batteries and not depend on diff --git a/fail2ban/helpers.py b/fail2ban/helpers.py index 7660e64a..99210b17 100644 --- a/fail2ban/helpers.py +++ b/fail2ban/helpers.py @@ -118,6 +118,24 @@ class FormatterWithTraceBack(logging.Formatter): return logging.Formatter.format(self, record) +def updatePyExec(bindir, executable=None): + """Update fail2ban-python link to current python version (where f2b-modules located/installed) + """ + bindir = os.path.realpath(bindir) + if executable is None: + executable = sys.executable + pypath = os.path.join(bindir, 'fail2ban-python') + # if not exists or point to another version - update link: + isfile = os.path.isfile(pypath) + if not isfile or os.path.realpath(pypath) != os.path.realpath(executable): + if isfile: + os.unlink(pypath) + os.symlink(executable, pypath) + # extend current environment path (e.g. if fail2ban not yet installed): + if bindir not in os.environ["PATH"].split(os.pathsep): + os.environ["PATH"] = os.environ["PATH"] + os.pathsep + bindir; + + def getLogger(name): """Get logging.Logger instance with Fail2Ban logger name convention """ diff --git a/fail2ban/tests/files/config/apache-auth/digest.py b/fail2ban/tests/files/config/apache-auth/digest.py index f4fcfcb9..03588594 100755 --- a/fail2ban/tests/files/config/apache-auth/digest.py +++ b/fail2ban/tests/files/config/apache-auth/digest.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env fail2ban-python import requests try: diff --git a/fail2ban/tests/files/ignorecommand.py b/fail2ban/tests/files/ignorecommand.py index 473980ec..7011b51b 100755 --- a/fail2ban/tests/files/ignorecommand.py +++ b/fail2ban/tests/files/ignorecommand.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env fail2ban-python import sys if sys.argv[1] == "10.0.0.1": exit(0) diff --git a/fail2ban/tests/misctestcase.py b/fail2ban/tests/misctestcase.py index df347e77..b910d94c 100644 --- a/fail2ban/tests/misctestcase.py +++ b/fail2ban/tests/misctestcase.py @@ -73,7 +73,7 @@ class HelpersTest(unittest.TestCase): def _getSysPythonVersion(): import subprocess, locale - sysVerCmd = "python -c 'import sys; print(tuple(sys.version_info))'" + sysVerCmd = "fail2ban-python -c 'import sys; print(tuple(sys.version_info))'" if sys.version_info >= (2,7): sysVer = subprocess.check_output(sysVerCmd, shell=True) else: @@ -144,6 +144,10 @@ class SetupTest(unittest.TestCase): 'etc/fail2ban/jail.conf'): self.assertTrue(os.path.exists(os.path.join(tmp, f)), msg="Can't find %s" % f) + self.assertEqual( + os.path.realpath(os.path.join(tmp, 'usr/local/bin/fail2ban-python')), + os.path.realpath(sys.executable)) + finally: # clean up shutil.rmtree(tmp) diff --git a/setup.py b/setup.py index 5579c981..fa66d4d6 100755 --- a/setup.py +++ b/setup.py @@ -38,12 +38,46 @@ except ImportError: # python 2.x from distutils.command.build_py import build_py from distutils.command.build_scripts import build_scripts +# all versions +from distutils.command.install_scripts import install_scripts + import os from os.path import isfile, join, isdir, realpath import sys import warnings from glob import glob + +def updatePyExec(bindir, executable=None): + """Update fail2ban-python link to current python version (where f2b-modules located/installed) + """ + bindir = os.path.realpath(bindir) + if executable is None: + executable = sys.executable + pypath = os.path.join(bindir, 'fail2ban-python') + # if not exists or point to another version - update link: + isfile = os.path.isfile(pypath) + if not isfile or os.path.realpath(pypath) != os.path.realpath(executable): + if isfile: + os.unlink(pypath) + os.symlink(executable, pypath) + + +# Wrapper to install python binding (to current python version): +class install_scripts_f2b(install_scripts): + + def get_outputs(self): + outputs = install_scripts.get_outputs(self) + fn = None + for fn in outputs: + if os.path.basename(fn) == 'fail2ban-server': + break + bindir = os.path.dirname(fn) + print('creating fail2ban-python binding -> %s' % (bindir,)) + updatePyExec(bindir) + return outputs + + if setuptools and "test" in sys.argv: import logging logSys = logging.getLogger("fail2ban") @@ -99,12 +133,16 @@ setup( url = "http://www.fail2ban.org", license = "GPL", platforms = "Posix", - cmdclass = {'build_py': build_py, 'build_scripts': build_scripts}, + cmdclass = { + 'build_py': build_py, 'build_scripts': build_scripts, + 'install_scripts': install_scripts_f2b + }, scripts = [ 'bin/fail2ban-client', 'bin/fail2ban-server', 'bin/fail2ban-regex', 'bin/fail2ban-testcases', + # 'bin/fail2ban-python', -- link (binary), will be installed via install_scripts_f2b wrapper ], packages = [ 'fail2ban', From 08af8de98171ad37a190dfaafccd629c83afe1a1 Mon Sep 17 00:00:00 2001 From: sebres Date: Thu, 11 Aug 2016 19:57:40 +0200 Subject: [PATCH 2/4] compatibility fix (virtualenv, running test cases in py3) --- MANIFEST | 1 + bin/fail2ban-testcases | 3 ++- fail2ban/helpers.py | 18 --------------- fail2ban/setup.py | 42 ++++++++++++++++++++++++++++++++++ fail2ban/tests/misctestcase.py | 33 ++++++++++++++++---------- setup.py | 18 ++++----------- 6 files changed, 70 insertions(+), 45 deletions(-) create mode 100644 fail2ban/setup.py diff --git a/MANIFEST b/MANIFEST index 36110c83..512b1d03 100644 --- a/MANIFEST +++ b/MANIFEST @@ -200,6 +200,7 @@ fail2ban/server/strptime.py fail2ban/server/ticket.py fail2ban/server/transmitter.py fail2ban/server/utils.py +fail2ban/setup.py fail2ban-testcases-all fail2ban-testcases-all-python3 fail2ban/tests/action_d/__init__.py diff --git a/bin/fail2ban-testcases b/bin/fail2ban-testcases index dcc55421..34d392e4 100755 --- a/bin/fail2ban-testcases +++ b/bin/fail2ban-testcases @@ -38,7 +38,8 @@ if os.path.exists("fail2ban/__init__.py"): from fail2ban.version import version from fail2ban.tests.utils import gatherTests -from fail2ban.helpers import updatePyExec, FormatterWithTraceBack, getLogger +from fail2ban.helpers import FormatterWithTraceBack, getLogger +from fail2ban.setup import updatePyExec from fail2ban.server.mytime import MyTime from optparse import OptionParser, Option diff --git a/fail2ban/helpers.py b/fail2ban/helpers.py index 99210b17..7660e64a 100644 --- a/fail2ban/helpers.py +++ b/fail2ban/helpers.py @@ -118,24 +118,6 @@ class FormatterWithTraceBack(logging.Formatter): return logging.Formatter.format(self, record) -def updatePyExec(bindir, executable=None): - """Update fail2ban-python link to current python version (where f2b-modules located/installed) - """ - bindir = os.path.realpath(bindir) - if executable is None: - executable = sys.executable - pypath = os.path.join(bindir, 'fail2ban-python') - # if not exists or point to another version - update link: - isfile = os.path.isfile(pypath) - if not isfile or os.path.realpath(pypath) != os.path.realpath(executable): - if isfile: - os.unlink(pypath) - os.symlink(executable, pypath) - # extend current environment path (e.g. if fail2ban not yet installed): - if bindir not in os.environ["PATH"].split(os.pathsep): - os.environ["PATH"] = os.environ["PATH"] + os.pathsep + bindir; - - def getLogger(name): """Get logging.Logger instance with Fail2Ban logger name convention """ diff --git a/fail2ban/setup.py b/fail2ban/setup.py new file mode 100644 index 00000000..87d50e66 --- /dev/null +++ b/fail2ban/setup.py @@ -0,0 +1,42 @@ +# 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__ = "Serg G. Brester" +__license__ = "GPL" + +import os +import sys + + +def updatePyExec(bindir, executable=None): + """Update fail2ban-python link to current python version (where f2b-modules located/installed) + """ + bindir = os.path.realpath(bindir) + if executable is None: + executable = sys.executable + pypath = os.path.join(bindir, 'fail2ban-python') + # if not exists or point to another version - update link: + isfile = os.path.isfile(pypath) + if not isfile or os.path.realpath(pypath) != os.path.realpath(executable): + if isfile: + os.unlink(pypath) + os.symlink(executable, pypath) + # extend current environment path (e.g. if fail2ban not yet installed): + if bindir not in os.environ["PATH"].split(os.pathsep): + os.environ["PATH"] = os.environ["PATH"] + os.pathsep + bindir; diff --git a/fail2ban/tests/misctestcase.py b/fail2ban/tests/misctestcase.py index b910d94c..7028eeb1 100644 --- a/fail2ban/tests/misctestcase.py +++ b/fail2ban/tests/misctestcase.py @@ -71,16 +71,21 @@ class HelpersTest(unittest.TestCase): self.assertEqual(splitwords(' 1\n 2, 3'), ['1', '2', '3']) +if sys.version_info >= (2,7): + def _sh_call(cmd): + import subprocess, locale + ret = subprocess.check_output(cmd, shell=True) + if sys.version_info >= (3,): + ret = ret.decode(locale.getpreferredencoding(), 'replace') + return str(ret).rstrip() +else: + def _sh_call(cmd): + import subprocess + ret = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout.read() + return str(ret).rstrip() + def _getSysPythonVersion(): - import subprocess, locale - sysVerCmd = "fail2ban-python -c 'import sys; print(tuple(sys.version_info))'" - if sys.version_info >= (2,7): - sysVer = subprocess.check_output(sysVerCmd, shell=True) - else: - sysVer = subprocess.Popen(sysVerCmd, shell=True, stdout=subprocess.PIPE).stdout.read() - if sys.version_info >= (3,): - sysVer = sysVer.decode(locale.getpreferredencoding(), 'replace') - return str(sysVer).rstrip() + return _sh_call("fail2ban-python -c 'import sys; print(tuple(sys.version_info))'") class SetupTest(unittest.TestCase): @@ -144,9 +149,13 @@ class SetupTest(unittest.TestCase): 'etc/fail2ban/jail.conf'): self.assertTrue(os.path.exists(os.path.join(tmp, f)), msg="Can't find %s" % f) - self.assertEqual( - os.path.realpath(os.path.join(tmp, 'usr/local/bin/fail2ban-python')), - os.path.realpath(sys.executable)) + # Because the install (test) path in virtual-env differs from some development-env, + # it is not a `tmp + '/usr/local/bin/'`, so search for it: + installedPath = _sh_call('find ' + tmp+ ' -name fail2ban-python').split('\n') + self.assertTrue(len(installedPath) > 0) + for installedPath in installedPath: + self.assertEqual( + os.path.realpath(installedPath), os.path.realpath(sys.executable)) finally: # clean up diff --git a/setup.py b/setup.py index fa66d4d6..be1fe685 100755 --- a/setup.py +++ b/setup.py @@ -47,20 +47,7 @@ import sys import warnings from glob import glob - -def updatePyExec(bindir, executable=None): - """Update fail2ban-python link to current python version (where f2b-modules located/installed) - """ - bindir = os.path.realpath(bindir) - if executable is None: - executable = sys.executable - pypath = os.path.join(bindir, 'fail2ban-python') - # if not exists or point to another version - update link: - isfile = os.path.isfile(pypath) - if not isfile or os.path.realpath(pypath) != os.path.realpath(executable): - if isfile: - os.unlink(pypath) - os.symlink(executable, pypath) +from fail2ban.setup import updatePyExec # Wrapper to install python binding (to current python version): @@ -78,6 +65,9 @@ class install_scripts_f2b(install_scripts): return outputs +# Update fail2ban-python env to current python version (where f2b-modules located/installed) +updatePyExec(os.path.join(os.path.dirname(__file__), 'bin')) + if setuptools and "test" in sys.argv: import logging logSys = logging.getLogger("fail2ban") From f390a82b597a7c6aa39fff33b6ef106ed0b45bdb Mon Sep 17 00:00:00 2001 From: sebres Date: Thu, 11 Aug 2016 21:37:16 +0200 Subject: [PATCH 3/4] python 2.6 compatibility minor fix - no skip + BaseException.message seems to be deprecated --- fail2ban/tests/utils.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/fail2ban/tests/utils.py b/fail2ban/tests/utils.py index 2838fa05..f6333fb8 100644 --- a/fail2ban/tests/utils.py +++ b/fail2ban/tests/utils.py @@ -104,9 +104,10 @@ def initTests(opts): # (prevent long sleeping during test cases ... less time goes to sleep): Utils.DEFAULT_SLEEP_TIME = 0.0025 Utils.DEFAULT_SLEEP_INTERVAL = 0.0005 - def F2B_SkipIfFast(): - raise unittest.SkipTest('Skip test because of "--fast"') - unittest.F2B.SkipIfFast = F2B_SkipIfFast + if sys.version_info >= (2,7): # no skip in previous version: + def F2B_SkipIfFast(): + raise unittest.SkipTest('Skip test because of "--fast"') + unittest.F2B.SkipIfFast = F2B_SkipIfFast else: # sleep intervals are large - use replacement for sleep to check time to sleep: _org_sleep = time.sleep @@ -333,8 +334,8 @@ if not hasattr(unittest.TestCase, 'assertRaisesRegexp'): try: fun(*args, **kwargs) except exccls as e: - if re.search(regexp, e.message) is None: - self.fail('\"%s\" does not match \"%s\"' % (regexp, e.message)) + if re.search(regexp, str(e)) is None: + self.fail('\"%s\" does not match \"%s\"' % (regexp, e)) else: self.fail('%s not raised' % getattr(exccls, '__name__')) unittest.TestCase.assertRaisesRegexp = assertRaisesRegexp From c8e7c1f7f4527da431b41bd7d23d1b51dd135b20 Mon Sep 17 00:00:00 2001 From: sebres Date: Fri, 12 Aug 2016 16:41:55 +0200 Subject: [PATCH 4/4] BF: prefer sys.argv[0] by retrieving of root resp. bin path: __file__ seems to be overwritten sometimes on some python versions (e.g. bug of 2.6 by running under cProfile, etc.) --- bin/fail2ban-testcases | 6 +++++- setup.py | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/bin/fail2ban-testcases b/bin/fail2ban-testcases index 34d392e4..7e5f7aca 100755 --- a/bin/fail2ban-testcases +++ b/bin/fail2ban-testcases @@ -45,7 +45,11 @@ from fail2ban.server.mytime import MyTime from optparse import OptionParser, Option # Update fail2ban-python env to current python version (where f2b-modules located/installed) -updatePyExec(os.path.dirname(__file__)) +bindir = os.path.dirname( + # __file__ seems to be overwritten sometimes on some python versions (e.g. bug of 2.6 by running under cProfile, etc.): + sys.argv[0] if os.path.basename(sys.argv[0]) == 'fail2ban-testcases' else __file__ +) +updatePyExec(bindir) def get_opt_parser(): # use module docstring for help output diff --git a/setup.py b/setup.py index be1fe685..11d0a20b 100755 --- a/setup.py +++ b/setup.py @@ -66,7 +66,11 @@ class install_scripts_f2b(install_scripts): # Update fail2ban-python env to current python version (where f2b-modules located/installed) -updatePyExec(os.path.join(os.path.dirname(__file__), 'bin')) +rootdir = os.path.realpath(os.path.dirname( + # __file__ seems to be overwritten sometimes on some python versions (e.g. bug of 2.6 by running under cProfile, etc.): + sys.argv[0] if os.path.basename(sys.argv[0]) == 'setup.py' else __file__ +)) +updatePyExec(os.path.join(rootdir, 'bin')) if setuptools and "test" in sys.argv: import logging