From 61949b4ff7b5cb9a11b8b912a0e83524c77af830 Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Wed, 24 Apr 2013 23:23:31 -0400 Subject: [PATCH 01/19] Moving README into a markup README.md for github's goodnesses --- README => README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename README => README.md (100%) diff --git a/README b/README.md similarity index 100% rename from README rename to README.md From ce912bb11cc8de476586a4ce6ef19432de0f6c3f Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Thu, 25 Apr 2013 01:07:39 -0300 Subject: [PATCH 02/19] updated README.md to hyperlink, add travis and coversall for some reason coversall says 'unknown' -- may be it requires a paid account for the badges? --- README.md | 62 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 2016e9e1..04f8b349 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,9 @@ / _|__ _(_) |_ ) |__ __ _ _ _ | _/ _` | | |/ /| '_ \/ _` | ' \ |_| \__,_|_|_/___|_.__/\__,_|_||_| + v0.8.8 2012/07/31 -================================================================================ -Fail2Ban (version 0.8.8) 2012/07/31 -================================================================================ +## Fail2Ban: ban hosts that cause multiple authentication errors Fail2Ban scans log files like /var/log/pwdfail and bans IP that makes too many password failures. It updates firewall rules to reject the IP address. These @@ -18,32 +17,29 @@ are available in fail2ban(1) manpage and on the website http://www.fail2ban.org Installation: ------------- +**It is possible that Fail2ban is already packaged for your distribution. In +this case, you should use it instead.** + Required: - >=python-2.4 (http://www.python.org) +- [Python >= 2.4](http://www.python.org) Optional: - 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) +- [pyinotify >= 0.8.3](https://github.com/seb-m/pyinotify) + - Linux >= 2.6.13 +- [gamin >= 0.0.21](http://www.gnome.org/~veillard/gamin) To install, just do: -> tar xvfj fail2ban-0.8.8.tar.bz2 -> cd fail2ban-0.8.8 -> python setup.py install + tar xvfj fail2ban-0.8.8.tar.bz2 + cd fail2ban-0.8.8 + python setup.py install This will install Fail2Ban into /usr/share/fail2ban. The executable scripts are -placed into /usr/bin. - -It is possible that Fail2ban is already packaged for your distribution. In -this case, you should use it. +placed into /usr/bin, and configuration under /etc/fail2ban. Fail2Ban should be correctly installed now. Just type: -> fail2ban-client -h + fail2ban-client -h to see if everything is alright. You should always use fail2ban-client and never call fail2ban-server directly. @@ -57,29 +53,35 @@ available commands are described in the fail2ban-client(1) manpage. Also see fail2ban(1) manpage for further references and find even more documentation on 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) + +* [![Coverage Status](https://coveralls.io/repos/fail2ban/fail2ban/badge.png?branch=master)](https://coveralls.io/r/fail2ban/fail2ban) + Contact: -------- -Website: http://www.fail2ban.org - -You need some new features, you found bugs? -visit https://github.com/fail2ban/fail2ban/issues +### You need some new features, you found bugs? +visit [Issues](https://github.com/fail2ban/fail2ban/issues) and if your issue is not yet known -- file a bug report. -You would like to troubleshoot or discuss? -join the mailing list -https://lists.sourceforge.net/lists/listinfo/fail2ban-users +### You would like to troubleshoot or discuss? +join the [mailing list](https://lists.sourceforge.net/lists/listinfo/fail2ban-users) -You just appreciate this program: -send kudos to the original author (Cyril Jaquier ) -or better to the mailing list -https://lists.sourceforge.net/lists/listinfo/fail2ban-users +### You would like to contribute (new filters/actions/code/documentation)? +send a pull request + +### You just appreciate this program: +send kudos to the original author ([Cyril Jaquier](mailto: Cyril Jaquier ) +or better to the [mailing list](https://lists.sourceforge.net/lists/listinfo/fail2ban-users) since Fail2Ban is "community-driven" for years now. Thanks: ------- -See THANKS file. +See [THANKS](https://github.com/fail2ban/fail2ban/blob/master/THANKS) file. License: -------- From c5287e3d9cb0d5336b471f9647bd7e369327fc97 Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Fri, 26 Apr 2013 22:51:26 -0400 Subject: [PATCH 03/19] BF: Rename mentioning of README to README.md (Fixes #187) --- MANIFEST | 2 +- setup.cfg | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/MANIFEST b/MANIFEST index 28063b83..364c0b08 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1,4 +1,4 @@ -README +README.md ChangeLog TODO THANKS diff --git a/setup.cfg b/setup.cfg index 74c22b25..02cd5d5d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -8,6 +8,6 @@ formats=bztar release = 1 packager = Yaroslav Halchenko , Daniel Black doc_files = DEVELOP - README + README.md THANKS doc/run-rootless.txt diff --git a/setup.py b/setup.py index 784999a2..1b6268bf 100755 --- a/setup.py +++ b/setup.py @@ -69,7 +69,7 @@ setup( '' ), ('/usr/share/doc/fail2ban', - ['README', 'DEVELOP', 'doc/run-rootless.txt'] + ['README.md', 'DEVELOP', 'doc/run-rootless.txt'] ) ] ) From a0bb5163df7abcb634af41aeb26442e0aead857a Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Sun, 28 Apr 2013 09:23:41 +1000 Subject: [PATCH 04/19] DOC: changelog entry for enhanced ssh filter --- ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog b/ChangeLog index e60eb12f..f1d912c4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -89,6 +89,7 @@ Borreli, blotus: * [7cd6dab] Added help command to fail2ban-client. * [c8c7b0b,23bbc60] Better logging of log file read errors. * [3665e6d] Added code coverage to development process. + * [41b9f7b,32d10e9] More complete ssh filter rules to match openssh source. Pascal Borreli * [a2b29b4] Fixed lots of typos in config files and documentation. hamilton5 From 63870341d8a136e0f49aac882a15cbb9e782d18c Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Sun, 28 Apr 2013 10:44:05 +1000 Subject: [PATCH 05/19] DOC: release documentation and distributor contacts --- DEVELOP | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/DEVELOP b/DEVELOP index 3e8e430a..a00a7ac6 100644 --- a/DEVELOP +++ b/DEVELOP @@ -262,6 +262,38 @@ Takes care about executing start/check/ban/unban/stop commands Releasing ========= +# Check distribution patches and see if they can be included + + * https://apps.fedoraproject.org/packages/fail2ban/sources + * http://sources.gentoo.org/cgi-bin/viewvc.cgi/gentoo-x86/net-analyzer/fail2ban/ + * http://svnweb.freebsd.org/ports/head/security/py-fail2ban/ + * https://build.opensuse.org/package/show?package=fail2ban&project=openSUSE%3AFactory + * http://sophie.zarb.org/sources/fail2ban (Mageia) + +# Check distribution outstanding bugs + + * https://github.com/fail2ban/fail2ban/issues?sort=updated&state=open + * http://bugs.debian.org/cgi-bin/pkgreport.cgi?dist=unstable;package=fail2ban + * http://bugs.sabayon.org/buglist.cgi?quicksearch=net-analyzer%2Ffail2ban + * https://bugs.gentoo.org/buglist.cgi?query_format=advanced&short_desc=fail2ban&bug_status=UNCONFIRMED&bug_status=CONFIRMED&bug_status=IN_PROGRESS&short_desc_type=allwords + * https://bugzilla.redhat.com/buglist.cgi?query_format=advanced&bug_status=NEW&bug_status=ASSIGNED&component=fail2ban&classification=Red%20Hat&classification=Fedora + * http://www.freebsd.org/cgi/query-pr-summary.cgi?text=fail2ban + +# Provide a release sample to distributors + + * Debian: Yaroslav Halchenko + http://packages.qa.debian.org/f/fail2ban.html + * FreeBSD: Christoph Theis theis@gmx.at>, Nick Hilliard + http://svnweb.freebsd.org/ports/head/security/py-fail2ban/Makefile?view=markup + * Fedora: Axel Thimm + https://apps.fedoraproject.org/packages/fail2ban + * Gentoo: netmon@gentoo.org + http://sources.gentoo.org/cgi-bin/viewvc.cgi/gentoo-x86/net-analyzer/fail2ban/metadata.xml?view=markup + * openSUSE: Stephan Kulow + https://build.opensuse.org/package/users?package=fail2ban&project=openSUSE%3AFactory + +# Wait for feedback from distributors + # Ensure the version is correct in ./common/version.py # Add/finalize the corresponding entry in the ChangeLog @@ -296,9 +328,10 @@ Releasing # Email users and development list of release -TODO notifying distributors etc. +# notify distributors -Post Release: +Post Release +============ Add the following to the top of the ChangeLog From 0ac8746d0566427c7c1bb3dddb6e7eb82e0ff809 Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Sun, 28 Apr 2013 11:03:44 +1000 Subject: [PATCH 06/19] ENH: Account for views in named filter. By Romain Riviere in gentoo bug #259458 --- config/filter.d/named-refused.conf | 2 +- testcases/files/logs/named-refused | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/config/filter.d/named-refused.conf b/config/filter.d/named-refused.conf index ebf7681a..64f7d685 100644 --- a/config/filter.d/named-refused.conf +++ b/config/filter.d/named-refused.conf @@ -26,7 +26,7 @@ __line_prefix=(?:\s\S+ %(__daemon_combs_re)s\s+)? # Notes.: regex to match the password failures messages in the logfile. # Values: TEXT # -failregex = %(__line_prefix)sclient #.+: query(?: \(cache\))? '.*' denied\s*$ +failregex = %(__line_prefix)sclient #\S+: (view (internal|external): )?query(?: \(cache\))? '.*' denied\s*$ # Option: ignoreregex # Notes.: regex to ignore. If this regex matches, the line is ignored. diff --git a/testcases/files/logs/named-refused b/testcases/files/logs/named-refused index 6608ae2f..130e7417 100644 --- a/testcases/files/logs/named-refused +++ b/testcases/files/logs/named-refused @@ -3,3 +3,4 @@ Jul 24 14:16:56 raid5 named[3935]: client 62.123.164.113#32768: query 'ricreig.c Jul 24 14:17:13 raid5 named[3935]: client 148.160.29.6#33081: query (cache) 'geo-mueller.de/NS/IN' denied Jul 24 14:20:25 raid5 named[3935]: client 148.160.29.6#33081: query (cache) 'shivaree.de/NS/IN' denied Jul 24 14:23:36 raid5 named[3935]: client 148.160.29.6#33081: query (cache) 'mietberatung.de/NS/IN' denied +Jul 24 14:23:36 raid5 named[3935]: client 62.109.4.89#9334: view external: query (cache) './NS/IN' denied From f2f523407653d634a49ab2b3d60dd1839621ee41 Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Sun, 28 Apr 2013 11:05:07 +1000 Subject: [PATCH 07/19] DOC: ChangeLog for named-refused entry --- ChangeLog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ChangeLog b/ChangeLog index e60eb12f..cec4a833 100644 --- a/ChangeLog +++ b/ChangeLog @@ -93,6 +93,8 @@ Borreli, blotus: * [a2b29b4] Fixed lots of typos in config files and documentation. hamilton5 * [7ede1e8] Update dovecot filter config. + Romain Riviere + * [0ac8746] Enhance named-refused filter for views. Special Kudos also go to Fabian Wenk, Arturo 'Buanzo' Busleiman, Tom Hendrikx and other TBN heroes supporting users on fail2ban-users From 6a4605a675303d95449f6d7b99f71cded67970d4 Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Sun, 28 Apr 2013 11:26:03 +1000 Subject: [PATCH 08/19] DOC: document
tag --- man/jail.conf.5 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/jail.conf.5 b/man/jail.conf.5 index 552b0ac0..8f281ce4 100644 --- a/man/jail.conf.5 +++ b/man/jail.conf.5 @@ -92,7 +92,7 @@ Commands specified in the [Definition] section are executed through a system she return 0, otherwise error would be logged. Moreover if \fBactioncheck\fR exits with non-0 status, it is taken as indication that firewall status has changed and fail2ban needs to reinitialize itself (i.e. issue \fBactionstop\fR and \fBactionstart\fR commands). Tags are enclosed in <>. All the elements of [Init] are tags that are replaced in all action commands. Tags can be added by the -\fBfail2ban-client\fR using the setctag command. +\fBfail2ban-client\fR using the setctag command. \fB
\fR is a tag that is always a new line (\\n). More than a single command is allowed to be specified. Each command needs to be on a separate line and indented with whitespaces without blank lines. The following example defines two commands to be executed. From 1d9abd1b39e2db7322986418436bf6849267edad Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Mon, 29 Apr 2013 12:37:16 +1000 Subject: [PATCH 09/19] ENH: allow recursive tag substitution in action files. --- server/action.py | 35 ++++++++++++++++++++++++++++++++++- testcases/actiontestcase.py | 21 +++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/server/action.py b/server/action.py index ce9651e8..3221f150 100644 --- a/server/action.py +++ b/server/action.py @@ -28,7 +28,7 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" import logging, os -import threading +import threading, re #from subprocess import call # Gets the instance of the logger. @@ -143,6 +143,9 @@ class Action: # @return True if the command succeeded def execActionStart(self): + if self.__cInfo: + if not Action.substituteRecursiveTags(self.__cInfo): + return False startCmd = Action.replaceTag(self.__actionStart, self.__cInfo) return Action.executeCmd(startCmd) @@ -242,6 +245,36 @@ class Action: stopCmd = Action.replaceTag(self.__actionStop, self.__cInfo) return Action.executeCmd(stopCmd) + ## + # Sort out tag definations within other tags + # + # so: becomes: + # a = 3 a = 3 + # b = _3 b = 3_3 + # @param tags, a dictionary + # @returns tags altered or False if there is a recursive defination + #@staticmethod + def substituteRecursiveTags(tags): + t = re.compile(r'<([^ >]+)>') + for tag, value in tags.iteritems(): + value = str(value) + m = t.search(value) + while m: + if m.group(1) == tag: + # recursive definations are bad + return False + else: + if tags.has_key(m.group(1)): + value = value[0:m.start()] + tags[m.group(1)] + value[m.end():] + m = t.search(value, m.start()) + else: + # TODO missing tag? to abort or not? there is the case maybe + m = t.search(value, m.start() + 1) + tags[tag] = value + return tags + substituteRecursiveTags = staticmethod(substituteRecursiveTags) + + #@staticmethod def escapeTag(tag): for c in '\\#&;`|*?~<>^()[]{}$\n\'"': if c in tag: diff --git a/testcases/actiontestcase.py b/testcases/actiontestcase.py index 50ae2816..281cb0b6 100644 --- a/testcases/actiontestcase.py +++ b/testcases/actiontestcase.py @@ -61,6 +61,27 @@ class ExecuteAction(unittest.TestCase): def _is_logged(self, s): return s in self._log.getvalue() + def testSubstituteRecursiveTags(self): + aInfo = { + 'HOST': "192.0.2.0", + 'ABC': "123 ", + 'xyz': "890 ", + } + # Recursion is bad + self.assertFalse(Action.substituteRecursiveTags({'A': ''})) + self.assertFalse(Action.substituteRecursiveTags({'A': '', 'B': ''})) + self.assertFalse(Action.substituteRecursiveTags({'A': '', 'B': '', 'C': ''})) + # missing tags are ok + self.assertEquals(Action.substituteRecursiveTags({'A': ''}), {'A': ''}) + self.assertEquals(Action.substituteRecursiveTags({'A': ' ','X':'fun'}), {'A': ' fun', 'X':'fun'}) + self.assertEquals(Action.substituteRecursiveTags({'A': ' ', 'B': 'cool'}), {'A': ' cool', 'B': 'cool'}) + # rest is just cool + self.assertEquals(Action.substituteRecursiveTags(aInfo), + { 'HOST': "192.0.2.0", + 'ABC': '123 192.0.2.0', + 'xyz': '890 123 192.0.2.0', + }) + def testReplaceTag(self): aInfo = { 'HOST': "192.0.2.0", From e5474e57aa9cdef95e9b2425de8c8eaf867c4a78 Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Mon, 29 Apr 2013 12:38:42 +1000 Subject: [PATCH 10/19] DOC: ChangeLog for recursive tag substition --- ChangeLog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ChangeLog b/ChangeLog index fde414f4..3240e8f3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -90,6 +90,8 @@ Borreli, blotus: * [c8c7b0b,23bbc60] Better logging of log file read errors. * [3665e6d] Added code coverage to development process. * [41b9f7b,32d10e9] More complete ssh filter rules to match openssh source. + * [1d9abd1] Action files can have tags in defination that refer to other + tags. Pascal Borreli * [a2b29b4] Fixed lots of typos in config files and documentation. hamilton5 From 945ad3d9e65348a78318ca026dfcb5b31dbe736e Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Mon, 29 Apr 2013 14:10:23 +1000 Subject: [PATCH 11/19] BF: ensure dates in email are in the C locale. Thanks iGeorgeX --- config/action.d/sendmail-whois-lines.conf | 6 +++--- config/action.d/sendmail-whois.conf | 6 +++--- config/action.d/sendmail.conf | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/config/action.d/sendmail-whois-lines.conf b/config/action.d/sendmail-whois-lines.conf index a4b53b97..30cd84b8 100644 --- a/config/action.d/sendmail-whois-lines.conf +++ b/config/action.d/sendmail-whois-lines.conf @@ -12,7 +12,7 @@ # Values: CMD # actionstart = printf %%b "Subject: [Fail2Ban] : started - Date: `date -u +"%%a, %%d %%h %%Y %%T +0000"` + Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"` From: Fail2Ban <> To: \n Hi,\n @@ -25,7 +25,7 @@ actionstart = printf %%b "Subject: [Fail2Ban] : started # Values: CMD # actionstop = printf %%b "Subject: [Fail2Ban] : stopped - Date: `date -u +"%%a, %%d %%h %%Y %%T +0000"` + Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"` From: Fail2Ban <> To: \n Hi,\n @@ -46,7 +46,7 @@ actioncheck = # Values: CMD # actionban = printf %%b "Subject: [Fail2Ban] : banned - Date: `date -u +"%%a, %%d %%h %%Y %%T +0000"` + Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"` From: Fail2Ban <> To: \n Hi,\n diff --git a/config/action.d/sendmail-whois.conf b/config/action.d/sendmail-whois.conf index f63fd36f..6b7b9383 100644 --- a/config/action.d/sendmail-whois.conf +++ b/config/action.d/sendmail-whois.conf @@ -12,7 +12,7 @@ # Values: CMD # actionstart = printf %%b "Subject: [Fail2Ban] : started - Date: `date -u +"%%a, %%d %%h %%Y %%T +0000"` + Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"` From: Fail2Ban <> To: \n Hi,\n @@ -25,7 +25,7 @@ actionstart = printf %%b "Subject: [Fail2Ban] : started # Values: CMD # actionstop = printf %%b "Subject: [Fail2Ban] : stopped - Date: `date -u +"%%a, %%d %%h %%Y %%T +0000"` + Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"` From: Fail2Ban <> To: \n Hi,\n @@ -46,7 +46,7 @@ actioncheck = # Values: CMD # actionban = printf %%b "Subject: [Fail2Ban] : banned - Date: `date -u +"%%a, %%d %%h %%Y %%T +0000"` + Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"` From: Fail2Ban <> To: \n Hi,\n diff --git a/config/action.d/sendmail.conf b/config/action.d/sendmail.conf index 569b3bfa..db619a87 100644 --- a/config/action.d/sendmail.conf +++ b/config/action.d/sendmail.conf @@ -12,7 +12,7 @@ # Values: CMD # actionstart = printf %%b "Subject: [Fail2Ban] : started - Date: `date -u +"%%a, %%d %%h %%Y %%T +0000"` + Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"` From: Fail2Ban <> To: \n Hi,\n @@ -25,7 +25,7 @@ actionstart = printf %%b "Subject: [Fail2Ban] : started # Values: CMD # actionstop = printf %%b "Subject: [Fail2Ban] : stopped - Date: `date -u +"%%a, %%d %%h %%Y %%T +0000"` + Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"` From: Fail2Ban <> To: \n Hi,\n @@ -46,7 +46,7 @@ actioncheck = # Values: CMD # actionban = printf %%b "Subject: [Fail2Ban] : banned - Date: `date -u +"%%a, %%d %%h %%Y %%T +0000"` + Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"` From: Fail2Ban <> To: \n Hi,\n From f91ad7e8788d44a37a88bd3960bde77625bed442 Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Mon, 29 Apr 2013 14:12:15 +1000 Subject: [PATCH 12/19] DOC: credits for gh-70 fix --- ChangeLog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ChangeLog b/ChangeLog index fde414f4..53de74c6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -44,6 +44,8 @@ Borreli, blotus: Daniel Black * [f0610c01] Allow more that a one word command when changing and Action via the fail2ban-client. Closes gh-134. + * [945ad3d9] Fix dates on email actions to work in different locals. Closes + gh-70. Thanks to iGeorgeX for the idea. blotus * [96eb8986] ' and " should also be escaped in action tags Closes gh-109 - New features: From 2403f395e9ed62dcac004fe7915c84c4fc660608 Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Mon, 29 Apr 2013 15:33:45 +1000 Subject: [PATCH 13/19] ENH: remove stats of config files and use results of SafeConfigParserWithIncludes.read to facilitate meaningful error messages --- client/configreader.py | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/client/configreader.py b/client/configreader.py index 243c843c..44ebdd3d 100644 --- a/client/configreader.py +++ b/client/configreader.py @@ -52,9 +52,9 @@ class ConfigReader(SafeConfigParserWithIncludes): return self._basedir def read(self, filename): - if not (os.path.exists(self._basedir) and os.access(self._basedir, os.R_OK | os.X_OK)): - raise ValueError("Base configuration directory %s either does not exist " - "or is not accessible" % self._basedir) + if not os.path.exists(self._basedir): + raise ValueError("Base configuration directory %s does not exist " + % self._basedir) basename = os.path.join(self._basedir, filename) logSys.debug("Reading configs for %s under %s " % (basename, self._basedir)) config_files = [ basename + ".conf", @@ -65,27 +65,19 @@ class ConfigReader(SafeConfigParserWithIncludes): # possible further customizations under a .conf.d directory config_dir = basename + '.d' - if os.path.exists(config_dir): - if os.path.isdir(config_dir) and os.access(config_dir, os.X_OK | os.R_OK): - # files must carry .conf suffix as well - config_files += sorted(glob.glob('%s/*.conf' % config_dir)) - else: - logSys.warn("%s exists but not a directory or not accessible" - % config_dir) + config_files += sorted(glob.glob('%s/*.conf' % config_dir)) - # check if files are accessible, warn if any is not accessible - # and remove it from the list - config_files_accessible = [] - for f in config_files: - if os.access(f, os.R_OK): - config_files_accessible.append(f) - else: - logSys.warn("%s exists but not accessible - skipping" % f) - - if len(config_files_accessible): + if len(config_files): # at least one config exists and accessible - SafeConfigParserWithIncludes.read(self, config_files_accessible) - return True + logSys.debug("Reading config files: " + ', '.join(config_files)) + config_files_read = SafeConfigParserWithIncludes.read(self, config_files) + missed = [ cf for cf in config_files if cf not in config_files_read ] + logSys.error("Error reading files: " + ', '.join(missed)) + if config_files_read: + return True + logSys.error("Found no accessible config files for %r under %s" % + ( filename, self.getBaseDir() )) + return False else: logSys.error("Found no accessible config files for %r " % filename + (["under %s" % self.getBaseDir(), From f21566049c9e64215dd10091e5d7a999bbffb7cc Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Mon, 29 Apr 2013 13:54:14 -0400 Subject: [PATCH 14/19] BF: pyinotify backend should also handle IN_MOVED_TO events --- server/filterpyinotify.py | 4 ++-- testcases/filtertestcase.py | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/server/filterpyinotify.py b/server/filterpyinotify.py index e86498b0..4c270d2e 100644 --- a/server/filterpyinotify.py +++ b/server/filterpyinotify.py @@ -66,7 +66,7 @@ class FilterPyinotify(FileFilter): def callback(self, event, origin=''): logSys.debug("%sCallback for Event: %s", origin, event) path = event.pathname - if event.mask & pyinotify.IN_CREATE: + if event.mask & ( pyinotify.IN_CREATE | pyinotify.IN_MOVED_TO ): # skip directories altogether if event.mask & pyinotify.IN_ISDIR: logSys.debug("Ignoring creation of directory %s", path) @@ -130,7 +130,7 @@ class FilterPyinotify(FileFilter): if not (path_dir in self.__watches): # we need to watch also the directory for IN_CREATE self.__watches.update( - self.__monitor.add_watch(path_dir, pyinotify.IN_CREATE)) + self.__monitor.add_watch(path_dir, pyinotify.IN_CREATE | pyinotify.IN_MOVED_TO)) logSys.debug("Added monitor for the parent directory %s", path_dir) self._addFileWatcher(path) diff --git a/testcases/filtertestcase.py b/testcases/filtertestcase.py index 927cb2fe..00946f90 100644 --- a/testcases/filtertestcase.py +++ b/testcases/filtertestcase.py @@ -475,6 +475,40 @@ def get_monitor_failures_testcase(Filter_): self.assertEqual(self.filter.failManager.getFailTotal(), 6) + def _test_move_into_file(self, interim_kill=False): + # if we move a new file into the location of an old (monitored) file + self.file1 = _copy_lines_between_files(GetFailures.FILENAME_01, self.name, + n=100) + # make sure that it is monitored first + self.assert_correct_last_attempt(GetFailures.FAILURES_01) + self.assertEqual(self.filter.failManager.getFailTotal(), 3) + + if interim_kill: + _killfile(None, self.name) + time.sleep(0.2) # let them know + + # now create a new one to override old one + self.file = _copy_lines_between_files(GetFailures.FILENAME_01, + self.name + '.new', n=100) + os.rename(self.name + '.new', self.name) + self.assert_correct_last_attempt(GetFailures.FAILURES_01) + self.assertEqual(self.filter.failManager.getFailTotal(), 6) + + # and to make sure that it now monitored for changes + _copy_lines_between_files(GetFailures.FILENAME_01, self.name, n=100) + self.assert_correct_last_attempt(GetFailures.FAILURES_01) + self.assertEqual(self.filter.failManager.getFailTotal(), 9) + + + def test_move_into_file(self): + self._test_move_into_file(interim_kill=False) + + def test_move_into_file_after_removed(self): + # exactly as above test + remove file explicitly + # to test against possible drop-out of the file from monitoring + self._test_move_into_file(interim_kill=True) + + def test_new_bogus_file(self): # to make sure that watching whole directory does not effect _copy_lines_between_files(GetFailures.FILENAME_01, self.name, n=100) From 4f59e14616d0ecf9eee3f5589a5464a89cf9885d Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Mon, 29 Apr 2013 14:00:25 -0400 Subject: [PATCH 15/19] Changelog entry for the previous commit and some untabify --- ChangeLog | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 53de74c6..a1b3d61c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -29,6 +29,8 @@ Borreli, blotus: * [ab044b75] delay check for the existence of config directory until read. * [3b4084d4] fixing up for handling of TAI64N timestamps. * [154aa38e] do not shutdown logging until all jails stop. + * [f2156604] pyinotify -- monitor IN_MOVED_TO events. Closes gh-184. + Thanks to Jon Foster for report and troubleshooting. Orion Poplawski * [e4aedfdc00] pyinotify - use bitwise op on masks and do not try tracking newly created directories. @@ -44,8 +46,8 @@ Borreli, blotus: Daniel Black * [f0610c01] Allow more that a one word command when changing and Action via the fail2ban-client. Closes gh-134. - * [945ad3d9] Fix dates on email actions to work in different locals. Closes - gh-70. Thanks to iGeorgeX for the idea. + * [945ad3d9] Fix dates on email actions to work in different locals. Closes + gh-70. Thanks to iGeorgeX for the idea. blotus * [96eb8986] ' and " should also be escaped in action tags Closes gh-109 - New features: @@ -96,8 +98,8 @@ Borreli, blotus: * [a2b29b4] Fixed lots of typos in config files and documentation. hamilton5 * [7ede1e8] Update dovecot filter config. - Romain Riviere - * [0ac8746] Enhance named-refused filter for views. + Romain Riviere + * [0ac8746] Enhance named-refused filter for views. Special Kudos also go to Fabian Wenk, Arturo 'Buanzo' Busleiman, Tom Hendrikx and other TBN heroes supporting users on fail2ban-users From d28f3fa285c0a8edd134052057a1c526bfaf3985 Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Tue, 30 Apr 2013 08:07:21 +1000 Subject: [PATCH 16/19] DOC: s/defination/definition/g learn to spell --- ChangeLog | 2 +- server/action.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3240e8f3..6e00364b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -90,7 +90,7 @@ Borreli, blotus: * [c8c7b0b,23bbc60] Better logging of log file read errors. * [3665e6d] Added code coverage to development process. * [41b9f7b,32d10e9] More complete ssh filter rules to match openssh source. - * [1d9abd1] Action files can have tags in defination that refer to other + * [1d9abd1] Action files can have tags in definition that refer to other tags. Pascal Borreli * [a2b29b4] Fixed lots of typos in config files and documentation. diff --git a/server/action.py b/server/action.py index 3221f150..22a96fa2 100644 --- a/server/action.py +++ b/server/action.py @@ -246,13 +246,13 @@ class Action: return Action.executeCmd(stopCmd) ## - # Sort out tag definations within other tags + # Sort out tag definitions within other tags # # so: becomes: # a = 3 a = 3 # b = _3 b = 3_3 # @param tags, a dictionary - # @returns tags altered or False if there is a recursive defination + # @returns tags altered or False if there is a recursive definition #@staticmethod def substituteRecursiveTags(tags): t = re.compile(r'<([^ >]+)>') @@ -261,7 +261,7 @@ class Action: m = t.search(value) while m: if m.group(1) == tag: - # recursive definations are bad + # recursive definitions are bad return False else: if tags.has_key(m.group(1)): From d7862266d6bbab8dac3ca6d9941bc118d42325cd Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Tue, 30 Apr 2013 08:14:50 +1000 Subject: [PATCH 17/19] DOC: missing cinfo tags are ok. Log error for self referencing definitions --- server/action.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server/action.py b/server/action.py index 22a96fa2..c2f1455b 100644 --- a/server/action.py +++ b/server/action.py @@ -145,6 +145,7 @@ class Action: def execActionStart(self): if self.__cInfo: if not Action.substituteRecursiveTags(self.__cInfo): + logSys.error("Cinfo/definitions contain self referencing definitions and cannot be resolved") return False startCmd = Action.replaceTag(self.__actionStart, self.__cInfo) return Action.executeCmd(startCmd) @@ -268,7 +269,9 @@ class Action: value = value[0:m.start()] + tags[m.group(1)] + value[m.end():] m = t.search(value, m.start()) else: - # TODO missing tag? to abort or not? there is the case maybe + # Missing tags are ok so we just continue on searching. + # cInfo can contain aInfo elements like and valid shell + # constructs like . m = t.search(value, m.start() + 1) tags[tag] = value return tags From 98aa0e23eb6c20ecac7e8e7533b08333fceda879 Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Tue, 30 Apr 2013 08:19:11 +1000 Subject: [PATCH 18/19] BF: log error only if there were missed config files that couldn't be read --- client/configreader.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/configreader.py b/client/configreader.py index 44ebdd3d..5d52d6ad 100644 --- a/client/configreader.py +++ b/client/configreader.py @@ -72,7 +72,8 @@ class ConfigReader(SafeConfigParserWithIncludes): logSys.debug("Reading config files: " + ', '.join(config_files)) config_files_read = SafeConfigParserWithIncludes.read(self, config_files) missed = [ cf for cf in config_files if cf not in config_files_read ] - logSys.error("Error reading files: " + ', '.join(missed)) + if missed: + logSys.error("Could not read config files: " + ', '.join(missed)) if config_files_read: return True logSys.error("Found no accessible config files for %r under %s" % From 8e63d4c6da41b8ed4c0423ae907445bd8ca5f03a Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Thu, 2 May 2013 23:25:43 -0400 Subject: [PATCH 19/19] ENH: "is None" instead of "== None" + tune ups in headers is None is generally faster than == and from looking at those places should be adequate. Also while at those files removed unneded duplicate author listing + expanded copyright/authors with myself where applicable --- client/beautifier.py | 4 ++-- client/configreader.py | 4 ++-- fail2ban-client | 4 ++-- server/action.py | 12 +++--------- server/datedetector.py | 13 ++----------- server/datetemplate.py | 2 +- server/failregex.py | 8 +------- server/filter.py | 18 ++++++------------ server/mytime.py | 12 +++--------- server/server.py | 2 +- 10 files changed, 23 insertions(+), 56 deletions(-) diff --git a/client/beautifier.py b/client/beautifier.py index 7e48016c..8e690656 100644 --- a/client/beautifier.py +++ b/client/beautifier.py @@ -56,10 +56,10 @@ class Beautifier: msg = "Jail started" elif inC[0] == "stop": if len(inC) == 1: - if response == None: + if response is None: msg = "Shutdown successful" else: - if response == None: + if response is None: msg = "Jail stopped" elif inC[0] == "add": msg = "Added jail " + response diff --git a/client/configreader.py b/client/configreader.py index 243c843c..e4b5ce07 100644 --- a/client/configreader.py +++ b/client/configreader.py @@ -113,7 +113,7 @@ class ConfigReader(SafeConfigParserWithIncludes): v = self.getint(sec, option[1]) else: v = self.get(sec, option[1]) - if not pOptions == None and option[1] in pOptions: + if not pOptions is None and option[1] in pOptions: continue values[option[1]] = v except NoSectionError, e: @@ -121,7 +121,7 @@ class ConfigReader(SafeConfigParserWithIncludes): logSys.error(e) values[option[1]] = option[2] except NoOptionError: - if not option[2] == None: + if not option[2] is None: logSys.warn("'%s' not defined in '%s'. Using default one: %r" % (option[1], sec, option[2])) values[option[1]] = option[2] diff --git a/fail2ban-client b/fail2ban-client index d8147f02..de8519f4 100755 --- a/fail2ban-client +++ b/fail2ban-client @@ -350,9 +350,9 @@ class Fail2banClient: # Set socket path self.__configurator.readEarly() conf = self.__configurator.getEarlyOptions() - if self.__conf["socket"] == None: + if self.__conf["socket"] is None: self.__conf["socket"] = conf["socket"] - if self.__conf["pidfile"] == None: + if self.__conf["pidfile"] is None: self.__conf["pidfile"] = conf["pidfile"] logSys.info("Using socket file " + self.__conf["socket"]) diff --git a/server/action.py b/server/action.py index ce9651e8..ca2cd332 100644 --- a/server/action.py +++ b/server/action.py @@ -17,14 +17,8 @@ # 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 -# -# $Revision$ - -__author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" -__copyright__ = "Copyright (c) 2004 Cyril Jaquier" +__author__ = "Cyril Jaquier and Fail2Ban Contributors" +__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2012 Yaroslav Halchenko" __license__ = "GPL" import logging, os @@ -304,7 +298,7 @@ class Action: return False # Replace tags - if not aInfo == None: + if not aInfo is None: realCmd = Action.replaceTag(cmd, aInfo) else: realCmd = cmd diff --git a/server/datedetector.py b/server/datedetector.py index a54e072d..65ee7abf 100644 --- a/server/datedetector.py +++ b/server/datedetector.py @@ -17,13 +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 -# -# $Revision$ - -__author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" +__author__ = "Cyril Jaquier and Fail2Ban Contributors" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" @@ -197,10 +191,7 @@ class DateDetector: def getUnixTime(self, line): date = self.getTime(line) - if date == None: - return None - else: - return time.mktime(date) + return date and time.mktime(date) ## # Sort the template lists using the hits score. This method is not called diff --git a/server/datetemplate.py b/server/datetemplate.py index 51b8bb1e..6d7a9f23 100644 --- a/server/datetemplate.py +++ b/server/datetemplate.py @@ -65,7 +65,7 @@ class DateTemplate: def matchDate(self, line): dateMatch = self.__cRegex.search(line) - if not dateMatch == None: + if not dateMatch is None: self.__hits += 1 return dateMatch diff --git a/server/failregex.py b/server/failregex.py index b194d472..957c77db 100644 --- a/server/failregex.py +++ b/server/failregex.py @@ -17,13 +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 -# -# $Revision$ - __author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" @@ -125,7 +119,7 @@ class FailRegex(Regex): def getHost(self): host = self._matchCache.group("host") - if host == None: + if host is None: # Gets a few information. s = self._matchCache.string r = self._matchCache.re diff --git a/server/filter.py b/server/filter.py index 88a5d861..754a2a08 100644 --- a/server/filter.py +++ b/server/filter.py @@ -17,14 +17,8 @@ # 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 -# -# $Revision$ - -__author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" -__copyright__ = "Copyright (c) 2004 Cyril Jaquier" +__author__ = "Cyril Jaquier and Fail2Ban Contributors" +__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2013 Yaroslav Halchenko" __license__ = "GPL" from failmanager import FailManagerEmpty @@ -360,7 +354,7 @@ class Filter(JailThread): if failRegex.hasMatched(): # The failregex matched. date = self.dateDetector.getUnixTime(timeLine) - if date == None: + if date is None: logSys.debug("Found a match for %r but no valid date/time " "found for %r. Please file a detailed issue on" " https://github.com/fail2ban/fail2ban/issues " @@ -473,7 +467,7 @@ class FileFilter(Filter): def getFailures(self, filename): container = self.getFileContainer(filename) - if container == None: + if container is None: logSys.error("Unable to get failures in " + filename) return False # Try to open log file. @@ -570,12 +564,12 @@ class FileContainer: self.__handler.seek(self.__pos) def readline(self): - if self.__handler == None: + if self.__handler is None: return "" return self.__handler.readline() def close(self): - if not self.__handler == None: + if not self.__handler is None: # Saves the last position. self.__pos = self.__handler.tell() # Closes the file. diff --git a/server/mytime.py b/server/mytime.py index 286f3d2c..8ae85184 100644 --- a/server/mytime.py +++ b/server/mytime.py @@ -17,13 +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 -# -# $Revision$ - __author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" @@ -61,7 +55,7 @@ class MyTime: #@staticmethod def time(): - if MyTime.myTime == None: + if MyTime.myTime is None: return time.time() else: return MyTime.myTime @@ -74,14 +68,14 @@ class MyTime: #@staticmethod def gmtime(): - if MyTime.myTime == None: + if MyTime.myTime is None: return time.gmtime() else: return time.gmtime(MyTime.myTime) gmtime = staticmethod(gmtime) def localtime(x=None): - if MyTime.myTime == None or x is not None: + if MyTime.myTime is None or x is not None: return time.localtime(x) else: return time.localtime(MyTime.myTime) diff --git a/server/server.py b/server/server.py index 5f1a52c1..d46ab9b3 100644 --- a/server/server.py +++ b/server/server.py @@ -388,7 +388,7 @@ class Server: hdlr.setFormatter(formatter) logging.getLogger("fail2ban").addHandler(hdlr) # Does not display this message at startup. - if not self.__logTarget == None: + if not self.__logTarget is None: logSys.info("Changed logging target to %s for Fail2ban v%s" % (target, version.version)) # Sets the logging target.