From 4115b62a01112576bdb22c18f1445103bdda3ebe Mon Sep 17 00:00:00 2001 From: Cameron Norman Date: Fri, 11 Apr 2014 16:49:56 -0700 Subject: [PATCH 01/25] Update fail2ban.upstart It was actually a little problematic :) --- files/fail2ban.upstart | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/files/fail2ban.upstart b/files/fail2ban.upstart index 1780a810..19349ebd 100644 --- a/files/fail2ban.upstart +++ b/files/fail2ban.upstart @@ -3,11 +3,9 @@ description "fail2ban - ban hosts that cause multiple authentication errors" start on filesystem and started networking stop on deconfiguring-networking -expect fork +expect daemon respawn -exec /usr/bin/fail2ban-client -x -b start - -pre-stop exec /usr/bin/fail2ban-client stop +exec /usr/bin/fail2ban-server -x -b post-stop exec rm -f /var/run/fail2ban/fail2ban.pid From 0c8e72f45266ef1ce44cbd8acda304fb3e1bc689 Mon Sep 17 00:00:00 2001 From: Cameron Norman Date: Fri, 11 Apr 2014 17:09:08 -0700 Subject: [PATCH 02/25] Update fail2ban.upstart No longer directly exec the server, do not remove the PID file because it is unnecessary to do so. No longer respawns because Upstart can not track the process with the starter command. --- files/fail2ban.upstart | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/files/fail2ban.upstart b/files/fail2ban.upstart index 19349ebd..ccf267f0 100644 --- a/files/fail2ban.upstart +++ b/files/fail2ban.upstart @@ -3,9 +3,5 @@ description "fail2ban - ban hosts that cause multiple authentication errors" start on filesystem and started networking stop on deconfiguring-networking -expect daemon -respawn - -exec /usr/bin/fail2ban-server -x -b - -post-stop exec rm -f /var/run/fail2ban/fail2ban.pid +pre-start exec /usr/bin/fail2ban-client -x start +post-stop exec /usr/bin/fail2ban-client stop From 7d112430caabfb6f5922545adca77b1a210a3185 Mon Sep 17 00:00:00 2001 From: Jason Martin Date: Wed, 16 Apr 2014 21:21:41 -0700 Subject: [PATCH 03/25] Block brute-force attempts against the Monit gui --- config/filter.d/monit.conf | 18 ++++++++++++++++++ config/jail.conf | 6 ++++++ 2 files changed, 24 insertions(+) create mode 100644 config/filter.d/monit.conf diff --git a/config/filter.d/monit.conf b/config/filter.d/monit.conf new file mode 100644 index 00000000..f32eae61 --- /dev/null +++ b/config/filter.d/monit.conf @@ -0,0 +1,18 @@ +# Fail2Ban filter for monit.conf, looks for failed access attempts +# +# + +[INCLUDES] + +# Read common prefixes. If any customizations available -- read them from +# common.local +before = common.conf + +[Definition] +# Samples: +# [PDT Apr 16 20:59:11] error : Warning: Client '1.2.3.4' supplied unknown user 'foo' accessing monit httpd +# [PDT Apr 16 20:59:33] error : Warning: Client '1.2.3.4' supplied wrong password for user 'admin' accessing monit httpd + +failregex = Warning: Client '' supplied + +ignoreregex = diff --git a/config/jail.conf b/config/jail.conf index 96b3096f..7f7a7cbe 100644 --- a/config/jail.conf +++ b/config/jail.conf @@ -366,6 +366,12 @@ maxretry = 5 port = http,https logpath = /var/log/tomcat*/catalina.out +[monit] +#Ban clients brute-forcing the monit gui login +filter = monit +port = 2812 +logpath = /var/log/monit + [webmin-auth] From 6a740f684a52f4ccc310e75d17b18ec610c70bb0 Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Fri, 18 Apr 2014 23:27:30 +0100 Subject: [PATCH 04/25] ENH: Move traceback formatter to from tests.utils to helpers Now allows for tests to be removed from package if desired --- bin/fail2ban-regex | 2 +- bin/fail2ban-testcases | 3 +- fail2ban/helpers.py | 83 ++++++++++++++++++++++++++++++++- fail2ban/tests/misctestcase.py | 3 +- fail2ban/tests/utils.py | 84 ++-------------------------------- 5 files changed, 91 insertions(+), 84 deletions(-) diff --git a/bin/fail2ban-regex b/bin/fail2ban-regex index ef198dcb..f1d5bdb9 100755 --- a/bin/fail2ban-regex +++ b/bin/fail2ban-regex @@ -45,7 +45,7 @@ from fail2ban.client.filterreader import FilterReader from fail2ban.server.filter import Filter from fail2ban.server.failregex import RegexException -from fail2ban.tests.utils import FormatterWithTraceBack +from fail2ban.helpers import FormatterWithTraceBack # Gets the instance of the logger. logSys = logging.getLogger("fail2ban") diff --git a/bin/fail2ban-testcases b/bin/fail2ban-testcases index b3bddf1c..0e2fdb4b 100755 --- a/bin/fail2ban-testcases +++ b/bin/fail2ban-testcases @@ -34,7 +34,8 @@ if os.path.exists("fail2ban/__init__.py"): sys.path.insert(0, ".") from fail2ban.version import version -from fail2ban.tests.utils import FormatterWithTraceBack, gatherTests +from fail2ban.tests.utils import gatherTests +from fail2ban.helpers import FormatterWithTraceBack from fail2ban.server.mytime import MyTime from optparse import OptionParser, Option diff --git a/fail2ban/helpers.py b/fail2ban/helpers.py index 74ea7a7a..2579381d 100644 --- a/fail2ban/helpers.py +++ b/fail2ban/helpers.py @@ -20,9 +20,90 @@ __author__ = "Cyril Jaquier, Arturo 'Buanzo' Busleiman, Yaroslav Halchenko" __license__ = "GPL" +import sys +import os +import traceback +import re +import logging def formatExceptionInfo(): """ Consistently format exception information """ - import sys cla, exc = sys.exc_info()[:2] return (cla.__name__, str(exc)) + +# +# Following "traceback" functions are adopted from PyMVPA distributed +# under MIT/Expat and copyright by PyMVPA developers (i.e. me and +# Michael). Hereby I re-license derivative work on these pieces under GPL +# to stay in line with the main Fail2Ban license +# +def mbasename(s): + """Custom function to include directory name if filename is too common + + Also strip .py at the end + """ + base = os.path.basename(s) + if base.endswith('.py'): + base = base[:-3] + if base in set(['base', '__init__']): + base = os.path.basename(os.path.dirname(s)) + '.' + base + return base + +class TraceBack(object): + """Customized traceback to be included in debug messages + """ + + def __init__(self, compress=False): + """Initialize TrackBack metric + + Parameters + ---------- + compress : bool + if True then prefix common with previous invocation gets + replaced with ... + """ + self.__prev = "" + self.__compress = compress + + def __call__(self): + ftb = traceback.extract_stack(limit=100)[:-2] + entries = [ + [mbasename(x[0]), os.path.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 concise + entries_out = [entries[0]] + for entry in entries[1:]: + if entry[0] == entries_out[-1][0]: + entries_out[-1][1] += ',%s' % entry[1] + else: + entries_out.append(entry) + sftb = '>'.join(['%s:%s' % (mbasename(x[0]), + x[1]) for x in entries_out]) + if self.__compress: + # lets remove part which is common with previous invocation + prev_next = sftb + common_prefix = os.path.commonprefix((self.__prev, sftb)) + common_prefix2 = re.sub('>[^>]*$', '', common_prefix) + + if common_prefix2 != "": + sftb = '...' + sftb[len(common_prefix2):] + self.__prev = prev_next + + return sftb + +class FormatterWithTraceBack(logging.Formatter): + """Custom formatter which expands %(tb) and %(tbc) with tracebacks + + TODO: might need locking in case of compressed tracebacks + """ + def __init__(self, fmt, *args, **kwargs): + logging.Formatter.__init__(self, fmt=fmt, *args, **kwargs) + compress = '%(tbc)s' in fmt + self._tb = TraceBack(compress=compress) + + def format(self, record): + record.tbc = record.tb = self._tb() + return logging.Formatter.format(self, record) diff --git a/fail2ban/tests/misctestcase.py b/fail2ban/tests/misctestcase.py index 284b684b..ca84eba7 100644 --- a/fail2ban/tests/misctestcase.py +++ b/fail2ban/tests/misctestcase.py @@ -32,8 +32,7 @@ import datetime from glob import glob from StringIO import StringIO -from .utils import mbasename, TraceBack, FormatterWithTraceBack -from ..helpers import formatExceptionInfo +from ..helpers import formatExceptionInfo, mbasename, TraceBack, FormatterWithTraceBack from ..server.datetemplate import DatePatternRegex diff --git a/fail2ban/tests/utils.py b/fail2ban/tests/utils.py index 85c1d929..7727632e 100644 --- a/fail2ban/tests/utils.py +++ b/fail2ban/tests/utils.py @@ -22,90 +22,17 @@ __author__ = "Yaroslav Halchenko" __copyright__ = "Copyright (c) 2013 Yaroslav Halchenko" __license__ = "GPL" -import logging, os, re, traceback, time, unittest -from os.path import basename, dirname +import logging +import os +import re +import time +import unittest from StringIO import StringIO from ..server.mytime import MyTime logSys = logging.getLogger(__name__) -# -# Following "traceback" functions are adopted from PyMVPA distributed -# under MIT/Expat and copyright by PyMVPA developers (i.e. me and -# Michael). Hereby I re-license derivative work on these pieces under GPL -# to stay in line with the main Fail2Ban license -# -def mbasename(s): - """Custom function to include directory name if filename is too common - - Also strip .py at the end - """ - base = basename(s) - if base.endswith('.py'): - base = base[:-3] - if base in set(['base', '__init__']): - base = basename(dirname(s)) + '.' + base - return base - -class TraceBack(object): - """Customized traceback to be included in debug messages - """ - - def __init__(self, compress=False): - """Initialize TrackBack metric - - Parameters - ---------- - compress : bool - if True then prefix common with previous invocation gets - replaced with ... - """ - self.__prev = "" - self.__compress = compress - - def __call__(self): - ftb = traceback.extract_stack(limit=100)[:-2] - 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 concise - entries_out = [entries[0]] - for entry in entries[1:]: - if entry[0] == entries_out[-1][0]: - entries_out[-1][1] += ',%s' % entry[1] - else: - entries_out.append(entry) - sftb = '>'.join(['%s:%s' % (mbasename(x[0]), - x[1]) for x in entries_out]) - if self.__compress: - # lets remove part which is common with previous invocation - prev_next = sftb - common_prefix = os.path.commonprefix((self.__prev, sftb)) - common_prefix2 = re.sub('>[^>]*$', '', common_prefix) - - if common_prefix2 != "": - sftb = '...' + sftb[len(common_prefix2):] - self.__prev = prev_next - - return sftb - -class FormatterWithTraceBack(logging.Formatter): - """Custom formatter which expands %(tb) and %(tbc) with tracebacks - - TODO: might need locking in case of compressed tracebacks - """ - def __init__(self, fmt, *args, **kwargs): - logging.Formatter.__init__(self, fmt=fmt, *args, **kwargs) - compress = '%(tbc)s' in fmt - self._tb = TraceBack(compress=compress) - - def format(self, record): - record.tbc = record.tb = self._tb() - return logging.Formatter.format(self, record) - def mtimesleep(): # no sleep now should be necessary since polling tracks now not only # mtime but also ino and size @@ -146,7 +73,6 @@ def gatherTests(regexps=None, no_network=False): if not regexps: # pragma: no cover tests = unittest.TestSuite() else: # pragma: no cover - import re class FilteredTestSuite(unittest.TestSuite): _regexps = [re.compile(r) for r in regexps] def addTest(self, suite): From 72bfd1433032c8790d2f5add267076798eed7ce5 Mon Sep 17 00:00:00 2001 From: Jason Martin Date: Sat, 19 Apr 2014 12:58:03 -0700 Subject: [PATCH 05/25] Tidy up filter.d/monit.conf, make regex more complete. Add ChangeLog / THANKS entry. Add test cases. --- ChangeLog | 1 + THANKS | 1 + config/filter.d/monit.conf | 13 ++----------- fail2ban/tests/files/logs/monit | 6 ++++++ 4 files changed, 10 insertions(+), 11 deletions(-) create mode 100644 fail2ban/tests/files/logs/monit diff --git a/ChangeLog b/ChangeLog index 66df9639..7113d431 100644 --- a/ChangeLog +++ b/ChangeLog @@ -21,6 +21,7 @@ ver. 0.9.1 (2014/xx/xx) - better, faster, stronger * Nginx filter to support missing server_name. Closes gh-676 - New features: + - Added monit filter thanks Jason H Martin. - Enhancements diff --git a/THANKS b/THANKS index 2c084dee..27165492 100644 --- a/THANKS +++ b/THANKS @@ -48,6 +48,7 @@ Ivo Truxa John Thoe Jacques Lav!gnotte Ioan Indreias +Jason H Martin Jonathan Kamens Jonathan Lanning Jonathan Underwood diff --git a/config/filter.d/monit.conf b/config/filter.d/monit.conf index f32eae61..04d01b20 100644 --- a/config/filter.d/monit.conf +++ b/config/filter.d/monit.conf @@ -2,17 +2,8 @@ # # -[INCLUDES] - -# Read common prefixes. If any customizations available -- read them from -# common.local -before = common.conf - [Definition] -# Samples: -# [PDT Apr 16 20:59:11] error : Warning: Client '1.2.3.4' supplied unknown user 'foo' accessing monit httpd -# [PDT Apr 16 20:59:33] error : Warning: Client '1.2.3.4' supplied wrong password for user 'admin' accessing monit httpd -failregex = Warning: Client '' supplied +failregex = \]\s*error\s*:\s*Warning:\s+Client '' supplied unknown user '\w+' accessing monit httpd$ + \]\s*error\s*:\s*Warning:\s+Client '' supplied wrong password for user '\w+' accessing monit httpd$ -ignoreregex = diff --git a/fail2ban/tests/files/logs/monit b/fail2ban/tests/files/logs/monit new file mode 100644 index 00000000..a923b6e2 --- /dev/null +++ b/fail2ban/tests/files/logs/monit @@ -0,0 +1,6 @@ +# failJSON: { "time": "2005-04-16T21:05:29", "match": true , "host": "69.93.127.111" } +[PDT Apr 16 21:05:29] error : Warning: Client '69.93.127.111' supplied unknown user 'foo' accessing monit httpd + +# failJSON: { "time": "2005-04-16T20:59:33", "match": true , "host": "97.113.189.111" } +[PDT Apr 16 20:59:33] error : Warning: Client '97.113.189.111' supplied wrong password for user 'admin' accessing monit httpd + From 0ef5027234fa6eaa9716abc9dd5ddcc5e67a3fe5 Mon Sep 17 00:00:00 2001 From: Cameron Norman Date: Sat, 19 Apr 2014 14:12:20 -0700 Subject: [PATCH 06/25] Change Upstart job to track PID of the server This only works correctly if the client does not fork itself when starting the server (which forks twice further). --- files/fail2ban.upstart | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/files/fail2ban.upstart b/files/fail2ban.upstart index ccf267f0..8a7ba10c 100644 --- a/files/fail2ban.upstart +++ b/files/fail2ban.upstart @@ -3,5 +3,13 @@ description "fail2ban - ban hosts that cause multiple authentication errors" start on filesystem and started networking stop on deconfiguring-networking -pre-start exec /usr/bin/fail2ban-client -x start +expect daemon +respawn + +pre-start script + [ -d /var/run/fail2ban ] || mkdir -p /var/run/fail2ban +end script + +exec /usr/bin/fail2ban-client -x start + post-stop exec /usr/bin/fail2ban-client stop From 39ad5b7474158c6fdb4b94935fe97f53e59fe69e Mon Sep 17 00:00:00 2001 From: Cameron Norman Date: Sat, 19 Apr 2014 15:10:19 -0700 Subject: [PATCH 07/25] Update Upstart job: uses stop command in pre-stop, removes PID file in post-stop --- files/fail2ban.upstart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/files/fail2ban.upstart b/files/fail2ban.upstart index 8a7ba10c..baabb22b 100644 --- a/files/fail2ban.upstart +++ b/files/fail2ban.upstart @@ -12,4 +12,6 @@ end script exec /usr/bin/fail2ban-client -x start -post-stop exec /usr/bin/fail2ban-client stop +pre-stop exec /usr/bin/fail2ban-client stop + +post-stop exec rm -f /var/run/fail2ban/fail2ban.pid From 9c2a0cb40395e6d860867cbb23fbbab14cbde2b9 Mon Sep 17 00:00:00 2001 From: Cameron Norman Date: Sun, 20 Apr 2014 11:37:07 -0700 Subject: [PATCH 08/25] Added foreground and background options to fail2ban-client --- bin/fail2ban-client | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/bin/fail2ban-client b/bin/fail2ban-client index 8737c49d..8d757cfe 100755 --- a/bin/fail2ban-client +++ b/bin/fail2ban-client @@ -51,6 +51,7 @@ class Fail2banClient: self.__conf["conf"] = "/etc/fail2ban" self.__conf["dump"] = False self.__conf["force"] = False + self.__conf["background"] = True self.__conf["verbose"] = 1 self.__conf["interactive"] = False self.__conf["socket"] = None @@ -83,6 +84,8 @@ class Fail2banClient: print " -v increase verbosity" print " -q decrease verbosity" print " -x force execution of the server (remove socket file)" + print " -b start server in background (default)" + print " -f start server in foreground" print " -h, --help display this help message" print " -V, --version print the version" print @@ -125,6 +128,10 @@ class Fail2banClient: self.__conf["force"] = True elif opt[0] == "-i": self.__conf["interactive"] = True + elif opt[0] == "-b": + self.__conf["background"] = True + elif opt[0] == "-f": + self.__conf["background"] = False elif opt[0] in ["-h", "--help"]: self.dispUsage() sys.exit(0) @@ -194,7 +201,8 @@ class Fail2banClient: # Start the server self.__startServerAsync(self.__conf["socket"], self.__conf["pidfile"], - self.__conf["force"]) + self.__conf["force"], + self.__conf["background"]) try: # Wait for the server to start self.__waitOnServer() @@ -242,14 +250,12 @@ class Fail2banClient: # # Start the Fail2ban server in daemon mode. - def __startServerAsync(self, socket, pidfile, force = False): + def __startServerAsync(self, socket, pidfile, force = False, background = True): # Forks the current process. pid = os.fork() if pid == 0: args = list() args.append(self.SERVER) - # Start in background mode. - args.append("-b") # Set the socket path. args.append("-s") args.append(socket) @@ -259,6 +265,12 @@ class Fail2banClient: # Force the execution if needed. if force: args.append("-x") + # Start in foreground mode if requested. + if background: + args.append("-b") + else: + args.append("-f") + try: # Use the current directory. exe = os.path.abspath(os.path.join(sys.path[0], self.SERVER)) From 1f53eb2d28ee48f4a0eb0b77f027cdebcb8a8b6e Mon Sep 17 00:00:00 2001 From: Cameron Norman Date: Sun, 20 Apr 2014 11:39:04 -0700 Subject: [PATCH 09/25] Updated man page for new options --- man/fail2ban-client.1 | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/man/fail2ban-client.1 b/man/fail2ban-client.1 index ec79d725..32580e20 100644 --- a/man/fail2ban-client.1 +++ b/man/fail2ban-client.1 @@ -34,6 +34,12 @@ decrease verbosity \fB\-x\fR force execution of the server (remove socket file) .TP +\fB\-b\fR +start the server in background mode (default) +.TP +\fB\-f\fR +start the server in foreground mode (note that the client forks once itself) +.TP \fB\-h\fR, \fB\-\-help\fR display this help message .TP From 7818b0cb2ab6122c72ba1f9206b6e543c3860eaf Mon Sep 17 00:00:00 2001 From: Cameron Norman Date: Sun, 20 Apr 2014 16:03:04 -0700 Subject: [PATCH 10/25] Added f and b to cmdOpts. f = start server in foreground; b = start server in background (default). --- bin/fail2ban-client | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/fail2ban-client b/bin/fail2ban-client index 8d757cfe..1acb3842 100755 --- a/bin/fail2ban-client +++ b/bin/fail2ban-client @@ -324,7 +324,7 @@ class Fail2banClient: # Reads the command line options. try: - cmdOpts = 'hc:s:p:xdviqV' + cmdOpts = 'hc:s:p:xfbdviqV' cmdLongOpts = ['help', 'version'] optList, args = getopt.getopt(self.__argv[1:], cmdOpts, cmdLongOpts) except getopt.GetoptError: From bbcbefd494a7120c4c8ac5341e859ae318d7a22f Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Tue, 22 Apr 2014 19:17:25 +0100 Subject: [PATCH 11/25] BF: bantime < 0 database should return all bans, as they are persistent --- ChangeLog | 1 + fail2ban/server/database.py | 13 ++++++++----- fail2ban/tests/databasetestcase.py | 13 +++++++++++-- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index bb1fd707..e8626930 100644 --- a/ChangeLog +++ b/ChangeLog @@ -20,6 +20,7 @@ ver. 0.9.1 (2014/xx/xx) - better, faster, stronger * Handle case when no sqlite library is available for persistent database * Only reban once per IP from database on fail2ban restart * Nginx filter to support missing server_name. Closes gh-676 + * Database now returns persistent bans on restart (bantime < 0) - New features: diff --git a/fail2ban/server/database.py b/fail2ban/server/database.py index 93186222..54cca4d3 100644 --- a/fail2ban/server/database.py +++ b/fail2ban/server/database.py @@ -380,7 +380,7 @@ class Fail2BanDb(object): if jail is not None: query += " AND jail=?" queryArgs.append(jail.name) - if bantime is not None: + if bantime is not None and bantime >= 0: query += " AND timeofban > ?" queryArgs.append(MyTime.time() - bantime) if ip is not None: @@ -399,7 +399,8 @@ class Fail2BanDb(object): Jail that the ban belongs to. Default `None`; all jails. bantime : int Ban time in seconds, such that bans returned would still be - valid now. Default `None`; no limit. + valid now. Negative values are equivalent to `None`. + Default `None`; no limit. ip : str IP Address to filter bans by. Default `None`; all IPs. @@ -427,7 +428,8 @@ class Fail2BanDb(object): Jail that the ban belongs to. Default `None`; all jails. bantime : int Ban time in seconds, such that bans returned would still be - valid now. Default `None`; no limit. + valid now. Negative values are equivalent to `None`. + Default `None`; no limit. ip : str IP Address to filter bans by. Default `None`; all IPs. @@ -438,7 +440,8 @@ class Fail2BanDb(object): in a list. When `ip` argument passed, a single `Ticket` is returned. """ - if bantime is None: + cacheKey = None + if bantime is None or bantime < 0: cacheKey = (ip, jail) if cacheKey in self._bansMergedCache: return self._bansMergedCache[cacheKey] @@ -468,7 +471,7 @@ class Fail2BanDb(object): ticket.setAttempt(failures) tickets.append(ticket) - if bantime is None: + if cacheKey: self._bansMergedCache[cacheKey] = tickets if ip is None else ticket return tickets if ip is None else ticket diff --git a/fail2ban/tests/databasetestcase.py b/fail2ban/tests/databasetestcase.py index 84101c50..2cf8577e 100644 --- a/fail2ban/tests/databasetestcase.py +++ b/fail2ban/tests/databasetestcase.py @@ -177,10 +177,15 @@ class DatabaseTest(unittest.TestCase): if Fail2BanDb is None: # pragma: no cover return self.testAddJail() - ticket = FailTicket("127.0.0.1", MyTime.time() - 40, ["abc\n"]) - self.db.addBan(self.jail, ticket) + self.db.addBan( + self.jail, FailTicket("127.0.0.1", MyTime.time() - 60, ["abc\n"])) + self.db.addBan( + self.jail, FailTicket("127.0.0.1", MyTime.time() - 40, ["abc\n"])) self.assertEqual(len(self.db.getBans(jail=self.jail,bantime=50)), 1) self.assertEqual(len(self.db.getBans(jail=self.jail,bantime=20)), 0) + # Negative values are for persistent bans, and such all bans should + # be returned + self.assertEqual(len(self.db.getBans(jail=self.jail,bantime=-1)), 2) def testGetBansMerged(self): if Fail2BanDb is None: # pragma: no cover @@ -251,6 +256,10 @@ class DatabaseTest(unittest.TestCase): self.assertEqual(len(tickets), 1) tickets = self.db.getBansMerged(bantime=5) self.assertEqual(len(tickets), 0) + # Negative values are for persistent bans, and such all bans should + # be returned + tickets = self.db.getBansMerged(bantime=-1) + self.assertEqual(len(tickets), 2) def testPurge(self): if Fail2BanDb is None: # pragma: no cover From 73cb3e3eec59b8be1a6781454bbe7be940603066 Mon Sep 17 00:00:00 2001 From: Cameron Norman Date: Tue, 22 Apr 2014 20:20:07 -0700 Subject: [PATCH 12/25] Added more specific help message to fail2ban-client with -f option --- bin/fail2ban-client | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/fail2ban-client b/bin/fail2ban-client index 1acb3842..289d7b39 100755 --- a/bin/fail2ban-client +++ b/bin/fail2ban-client @@ -85,7 +85,7 @@ class Fail2banClient: print " -q decrease verbosity" print " -x force execution of the server (remove socket file)" print " -b start server in background (default)" - print " -f start server in foreground" + print " -f start server in foreground (note that the client forks once itself)" print " -h, --help display this help message" print " -V, --version print the version" print From 9c3cb31862f8e0b31d12f0d02a9d114979cf3cd6 Mon Sep 17 00:00:00 2001 From: Jason Martin Date: Tue, 22 Apr 2014 21:29:52 -0700 Subject: [PATCH 13/25] Even stricter monit regex, now covers entire line --- config/filter.d/monit.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/filter.d/monit.conf b/config/filter.d/monit.conf index 04d01b20..1fcd980b 100644 --- a/config/filter.d/monit.conf +++ b/config/filter.d/monit.conf @@ -4,6 +4,6 @@ [Definition] -failregex = \]\s*error\s*:\s*Warning:\s+Client '' supplied unknown user '\w+' accessing monit httpd$ - \]\s*error\s*:\s*Warning:\s+Client '' supplied wrong password for user '\w+' accessing monit httpd$ +failregex = ^\[[A-Z]+\s+\]\s*error\s*:\s*Warning:\s+Client '' supplied unknown user '\w+' accessing monit httpd$ + ^\[[A-Z]+\s+\]\s*error\s*:\s*Warning:\s+Client '' supplied wrong password for user '\w+' accessing monit httpd$ From 2a14e48f0ba3a6c27af15af47e9c243ebb9373e1 Mon Sep 17 00:00:00 2001 From: Cameron Norman Date: Tue, 22 Apr 2014 21:55:51 -0700 Subject: [PATCH 14/25] A few final touches on the Upstart job (a) use static-network-up, since it is more generic than the started networking event (b) do not hook into network deconfiguration to speed up shutdown (c) expect fork, per the use of the "-f" option (d) use a variable for the run directory to make changing it simpler (e) handle the situation of a left over socket file (f) use the -f option to be able to track the PID --- files/fail2ban.upstart | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/files/fail2ban.upstart b/files/fail2ban.upstart index baabb22b..18fafebd 100644 --- a/files/fail2ban.upstart +++ b/files/fail2ban.upstart @@ -1,17 +1,20 @@ description "fail2ban - ban hosts that cause multiple authentication errors" -start on filesystem and started networking -stop on deconfiguring-networking +start on filesystem and static-network-up +stop on runlevel [016] -expect daemon +expect fork respawn +env RUNDIR=/var/run/fail2ban + pre-start script - [ -d /var/run/fail2ban ] || mkdir -p /var/run/fail2ban + test -d $RUNDIR || mkdir -p $RUNDIR + test ! -e $RUNDIR/fail2ban.sock || rm -f $RUNDIR/fail2ban.sock end script -exec /usr/bin/fail2ban-client -x start +exec /usr/bin/fail2ban-client -f -x start pre-stop exec /usr/bin/fail2ban-client stop -post-stop exec rm -f /var/run/fail2ban/fail2ban.pid +post-stop exec rm -f $RUNDIR/fail2ban.pid From bc10b64c69be90f9b5bc487d04c376b9432a40ac Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Sun, 27 Apr 2014 13:35:55 +0100 Subject: [PATCH 15/25] ENH: Match non "Bye Bye" for sshd locked accounts failregex --- ChangeLog | 1 + config/filter.d/sshd.conf | 2 +- fail2ban/tests/files/logs/sshd | 7 +++++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index bbe32054..40188554 100644 --- a/ChangeLog +++ b/ChangeLog @@ -29,6 +29,7 @@ ver. 0.9.1 (2014/xx/xx) - better, faster, stronger - Enhancements * Fail2ban-regex - add print-all-matched option. Closes gh-652 * Suppress fail2ban-client warnings for non-critical config options + * Match non "Bye Bye" disconnect messages for sshd locked account regex ver. 0.9.0 (2014/03/14) - beta ---------- diff --git a/config/filter.d/sshd.conf b/config/filter.d/sshd.conf index 059052fc..195744f2 100644 --- a/config/filter.d/sshd.conf +++ b/config/filter.d/sshd.conf @@ -30,7 +30,7 @@ failregex = ^%(__prefix_line)s(?:error: PAM: )?[aA]uthentication (?:failure|erro ^%(__prefix_line)sReceived disconnect from : 3: \S+: Auth fail$ ^%(__prefix_line)sUser .+ from not allowed because a group is listed in DenyGroups\s*$ ^%(__prefix_line)sUser .+ from not allowed because none of user's groups are listed in AllowGroups\s*$ - ^(?P<__prefix>%(__prefix_line)s)User .+ not allowed because account is locked(?P=__prefix)(?:error: )?Received disconnect from : 11: Bye Bye \[preauth\]$ + ^(?P<__prefix>%(__prefix_line)s)User .+ not allowed because account is locked(?P=__prefix)(?:error: )?Received disconnect from : 11: .+ \[preauth\]$ ^(?P<__prefix>%(__prefix_line)s)Disconnecting: Too many authentication failures for .+? \[preauth\](?P=__prefix)(?:error: )?Connection closed by \[preauth\]$ ^(?P<__prefix>%(__prefix_line)s)Connection from port \d+(?P=__prefix)Disconnecting: Too many authentication failures for .+? \[preauth\]$ diff --git a/fail2ban/tests/files/logs/sshd b/fail2ban/tests/files/logs/sshd index e2246cf8..b9d1b9b4 100644 --- a/fail2ban/tests/files/logs/sshd +++ b/fail2ban/tests/files/logs/sshd @@ -136,3 +136,10 @@ Jul 13 18:44:28 mdop sshd[4931]: Received disconnect from 89.24.13.192: 3: com.j Feb 12 04:09:18 localhost sshd[26713]: Connection from 115.249.163.77 port 51353 # failJSON: { "time": "2005-02-12T04:09:21", "match": true , "host": "115.249.163.77", "desc": "from gh-457" } Feb 12 04:09:21 localhost sshd[26713]: Disconnecting: Too many authentication failures for root [preauth] + +# failJSON: { "match": false } +Apr 27 13:02:04 host sshd[29116]: User root not allowed because account is locked +# failJSON: { "match": false } +Apr 27 13:02:04 host sshd[29116]: input_userauth_request: invalid user root [preauth] +# failJSON: { "time": "2005-04-27T13:02:04", "match": true , "host": "1.2.3.4", "desc": "No Bye-Bye" } +Apr 27 13:02:04 host sshd[29116]: Received disconnect from 1.2.3.4: 11: Normal Shutdown, Thank you for playing [preauth] From b486014b3597957430ffaa86d1d537b280434148 Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Sat, 3 May 2014 12:09:48 +0100 Subject: [PATCH 16/25] TST: Add Python 3.4 for TravisCI This reverts commit 233aa043f356ad2a9439cfec3db31c6be06d0104. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 41eeca27..9a92a7f6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ python: - "2.7" - "3.2" - "3.3" + - "3.4" - "pypy" before_install: - if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then sudo apt-get update -qq; fi From cf3a6015f09b39cd668f1f202b695ea23c52fcb8 Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Sat, 3 May 2014 12:44:03 +0100 Subject: [PATCH 17/25] BF: Avoid closing "/dev/urandom" for Python 3.4.0 Upstream bug: http://bugs.python.org/issue21207 Closes gh-687 --- fail2ban/server/server.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/fail2ban/server/server.py b/fail2ban/server/server.py index 1bf8dcbb..735ce0a9 100644 --- a/fail2ban/server/server.py +++ b/fail2ban/server/server.py @@ -523,11 +523,19 @@ class Server: except (AttributeError, ValueError): maxfd = 256 # default maximum - for fd in range(0, maxfd): - try: - os.close(fd) - except OSError: # ERROR (ignore) - pass + # urandom should not be closed in Python 3.4.0. Fixed in 3.4.1 + # http://bugs.python.org/issue21207 + if sys.version_info[0:3] == (3, 4, 0): # pragma: no cover + urandom_fd = os.open("/dev/urandom", os.O_RDONLY) + for fd in range(0, maxfd): + try: + if not os.path.sameopenfile(urandom_fd, fd): + os.close(fd) + except OSError: # ERROR (ignore) + pass + os.close(urandom_fd) + else: + os.closerange(0, maxfd) # Redirect the standard file descriptors to /dev/null. os.open("/dev/null", os.O_RDONLY) # standard input (0) From 1e8402cb9951f6447da42f054ea894b1c1cbee1c Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Sat, 3 May 2014 12:51:15 +0100 Subject: [PATCH 18/25] DOC: ChangeLog entry for Python 3.4.0 persistent "/dev/urandom" fix --- ChangeLog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ChangeLog b/ChangeLog index bbe32054..d3101944 100644 --- a/ChangeLog +++ b/ChangeLog @@ -22,6 +22,8 @@ ver. 0.9.1 (2014/xx/xx) - better, faster, stronger * Nginx filter to support missing server_name. Closes gh-676 * fail2ban-regex assertion error caused by miscount missed lines with multiline regex + * Fix actions failing to execute for Python 3.4.0. Work around for + http://bugs.python.org/issue21207 - New features: From b3266ba44d3311a35bd18a3055a77dc27f577085 Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Sat, 3 May 2014 14:28:13 +0100 Subject: [PATCH 19/25] BF: Tags not fully recursively substituted Note: recursive check ignored for "matches", as tags would be escaped, and hence shouldn't match "<%s>" as "" would become "\". This therefore maintains advantage of delayed call for {ip,jail,}matches. Fixes gh-713 --- fail2ban/server/action.py | 11 +++++++++-- fail2ban/tests/actiontestcase.py | 13 ++++++++++--- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/fail2ban/server/action.py b/fail2ban/server/action.py index 0098c546..d1883338 100644 --- a/fail2ban/server/action.py +++ b/fail2ban/server/action.py @@ -371,8 +371,11 @@ class CommandAction(ActionBase): within the values recursively replaced. """ t = re.compile(r'<([^ >]+)>') - for tag, value in tags.iteritems(): - value = str(value) + for tag in tags.iterkeys(): + if tag.endswith('matches'): + # Escapped so wont match + continue + value = str(tags[tag]) m = t.search(value) done = [] #logSys.log(5, 'TAG: %s, value: %s' % (tag, value)) @@ -383,6 +386,9 @@ class CommandAction(ActionBase): # recursive definitions are bad #logSys.log(5, 'recursion fail tag: %s value: %s' % (tag, value) ) return False + elif found_tag.endswith('matches'): + # Escapped so wont match + continue else: if tags.has_key(found_tag): value = value.replace('<%s>' % found_tag , tags[found_tag]) @@ -441,6 +447,7 @@ class CommandAction(ActionBase): `query` string with tags replaced. """ string = query + aInfo = cls.substituteRecursiveTags(aInfo) for tag in aInfo: if "<%s>" % tag in query: value = str(aInfo[tag]) # assure string diff --git a/fail2ban/tests/actiontestcase.py b/fail2ban/tests/actiontestcase.py index cb004b4d..f1ea77ce 100644 --- a/fail2ban/tests/actiontestcase.py +++ b/fail2ban/tests/actiontestcase.py @@ -100,17 +100,24 @@ class CommandActionTest(LogCaptureTestCase): {'ipjailmatches': "some >char< should \< be[ escap}ed&\n"}), "some \\>char\\< should \\\\\\< be\\[ escap\\}ed\\&\n") + + # Recursive + aInfo["ABC"] = "" + self.assertEqual( + self.__action.replaceTag("Text text ABC", aInfo), + "Text 890 text 890 ABC") + # Callable self.assertEqual( - self.__action.replaceTag("09 11", - CallingMap(callme=lambda: str(10))), + self.__action.replaceTag("09 11", + CallingMap(matches=lambda: str(10))), "09 10 11") # As tag not present, therefore callable should not be called # Will raise ValueError if it is self.assertEqual( self.__action.replaceTag("abc", - CallingMap(callme=lambda: int("a"))), "abc") + CallingMap(matches=lambda: int("a"))), "abc") def testExecuteActionBan(self): self.__action.actionstart = "touch /tmp/fail2ban.test" From 65269365ee1bb1c958eddfd0c9db9c35a29491e9 Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Mon, 5 May 2014 23:16:18 -0400 Subject: [PATCH 20/25] minor --- ChangeLog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index d3101944..150ea99d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -22,7 +22,7 @@ ver. 0.9.1 (2014/xx/xx) - better, faster, stronger * Nginx filter to support missing server_name. Closes gh-676 * fail2ban-regex assertion error caused by miscount missed lines with multiline regex - * Fix actions failing to execute for Python 3.4.0. Work around for + * Fix actions failing to execute for Python 3.4.0. Workaround for http://bugs.python.org/issue21207 - New features: From 904b362215e587c1038e556681a787cea61eeccc Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Fri, 9 May 2014 20:25:44 +0100 Subject: [PATCH 21/25] DOC: ChangeLog update for recursive tag bug fix Also minor typo fixes in comments --- ChangeLog | 2 ++ fail2ban/server/action.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index bbe32054..ec6a3b28 100644 --- a/ChangeLog +++ b/ChangeLog @@ -22,6 +22,8 @@ ver. 0.9.1 (2014/xx/xx) - better, faster, stronger * Nginx filter to support missing server_name. Closes gh-676 * fail2ban-regex assertion error caused by miscount missed lines with multiline regex + * Recursive action tags now fully processed. Fixes issue with bsd-ipfw + action - New features: diff --git a/fail2ban/server/action.py b/fail2ban/server/action.py index d1883338..736386b1 100644 --- a/fail2ban/server/action.py +++ b/fail2ban/server/action.py @@ -373,7 +373,7 @@ class CommandAction(ActionBase): t = re.compile(r'<([^ >]+)>') for tag in tags.iterkeys(): if tag.endswith('matches'): - # Escapped so wont match + # Escapped so won't match continue value = str(tags[tag]) m = t.search(value) @@ -387,7 +387,7 @@ class CommandAction(ActionBase): #logSys.log(5, 'recursion fail tag: %s value: %s' % (tag, value) ) return False elif found_tag.endswith('matches'): - # Escapped so wont match + # Escapped so won't match continue else: if tags.has_key(found_tag): From 1e586fb0e94248cd0185e2df9a33dbbef9734299 Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Sun, 11 May 2014 14:49:49 +0100 Subject: [PATCH 22/25] ENH: explicitly define tags which should be escaped --- fail2ban/server/action.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/fail2ban/server/action.py b/fail2ban/server/action.py index 736386b1..fefe2c2c 100644 --- a/fail2ban/server/action.py +++ b/fail2ban/server/action.py @@ -194,6 +194,8 @@ class CommandAction(ActionBase): timeout """ + _escapedTags = set(('matches', 'ipmatches', 'ipjailmatches')) + def __init__(self, jail, name): super(CommandAction, self).__init__(jail, name) self.timeout = 60 @@ -351,8 +353,8 @@ class CommandAction(ActionBase): if not self.executeCmd(stopCmd, self.timeout): raise RuntimeError("Error stopping action") - @staticmethod - def substituteRecursiveTags(tags): + @classmethod + def substituteRecursiveTags(cls, tags): """Sort out tag definitions within other tags. so: becomes: @@ -372,8 +374,8 @@ class CommandAction(ActionBase): """ t = re.compile(r'<([^ >]+)>') for tag in tags.iterkeys(): - if tag.endswith('matches'): - # Escapped so won't match + if tag in cls._escapedTags: + # Escaped so won't match continue value = str(tags[tag]) m = t.search(value) @@ -386,8 +388,8 @@ class CommandAction(ActionBase): # recursive definitions are bad #logSys.log(5, 'recursion fail tag: %s value: %s' % (tag, value) ) return False - elif found_tag.endswith('matches'): - # Escapped so won't match + elif found_tag in cls._escapedTags: + # Escaped so won't match continue else: if tags.has_key(found_tag): @@ -451,7 +453,7 @@ class CommandAction(ActionBase): for tag in aInfo: if "<%s>" % tag in query: value = str(aInfo[tag]) # assure string - if tag.endswith('matches'): + if tag in cls._escapedTags: # That one needs to be escaped since its content is # out of our control value = cls.escapeTag(value) From 2bf0b4a50c9d79507115b4eddd2da66ef9b7945e Mon Sep 17 00:00:00 2001 From: sebres Date: Wed, 14 May 2014 22:26:22 +0100 Subject: [PATCH 23/25] strptime bug fix: if gmtoff is None we have 1 hour increment of time (through utctimetuple), compare: >>>> datetime.datetime.fromtimestamp(time.mktime(datetime.datetime.now().timetuple())).strftime("%Y-%m-%d %H:%M:%S") '2014-04-29 17:26:31' >>>> datetime.datetime.fromtimestamp(time.mktime(datetime.datetime.now().utctimetuple())).strftime("%Y-%m-%d %H:%M:%S") '2014-04-29 18:26:37' --- fail2ban/server/strptime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fail2ban/server/strptime.py b/fail2ban/server/strptime.py index 5517e6b0..cf02dad5 100644 --- a/fail2ban/server/strptime.py +++ b/fail2ban/server/strptime.py @@ -190,5 +190,5 @@ def reGroupDictStrptime(found_dict): if gmtoff is not None: return calendar.timegm(date_result.utctimetuple()) else: - return time.mktime(date_result.utctimetuple()) + return time.mktime(date_result.timetuple()) From 8843423c8f66ff46d9636fd1594187fd5dfa3da1 Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Wed, 14 May 2014 23:01:14 +0100 Subject: [PATCH 24/25] TST: Fix tests due to @sebres fix and based from gh-349 reverts --- fail2ban/tests/datedetectortestcase.py | 2 +- fail2ban/tests/files/logs/dovecot | 8 ++++---- fail2ban/tests/files/logs/selinux-ssh | 16 ++++++++-------- fail2ban/tests/filtertestcase.py | 26 +++++++++++++------------- fail2ban/tests/samplestestcase.py | 2 +- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/fail2ban/tests/datedetectortestcase.py b/fail2ban/tests/datedetectortestcase.py index 55f9a823..726e73f8 100644 --- a/fail2ban/tests/datedetectortestcase.py +++ b/fail2ban/tests/datedetectortestcase.py @@ -131,7 +131,7 @@ class DateDetectorTest(unittest.TestCase): # see https://github.com/fail2ban/fail2ban/pull/130 # yoh: unfortunately this test is not really effective to reproduce the # situation but left in place to assure consistent behavior - mu = time.mktime(datetime.datetime(2012, 10, 11, 2, 37, 17).utctimetuple()) + mu = time.mktime(datetime.datetime(2012, 10, 11, 2, 37, 17).timetuple()) logdate = self.__datedetector.getTime('2012/10/11 02:37:17 [error] 18434#0') self.assertNotEqual(logdate, None) ( logTime, logMatch ) = logdate diff --git a/fail2ban/tests/files/logs/dovecot b/fail2ban/tests/files/logs/dovecot index 5c3acb93..6ca31b7c 100644 --- a/fail2ban/tests/files/logs/dovecot +++ b/fail2ban/tests/files/logs/dovecot @@ -1,12 +1,12 @@ -# failJSON: { "time": "2010-09-16T06:51:00", "match": true , "host": "80.187.101.33" } +# failJSON: { "time": "2010-09-16T07:51:00", "match": true , "host": "80.187.101.33" } @400000004c91b044077a9e94 imap-login: Info: Aborted login (auth failed, 1 attempts): user=, method=CRAM-MD5, rip=80.187.101.33, lip=80.254.129.240, TLS -# failJSON: { "time": "2010-09-16T06:51:00", "match": true , "host": "176.61.140.224" } +# failJSON: { "time": "2010-09-16T07:51:00", "match": true , "host": "176.61.140.224" } @400000004c91b044077a9e94 dovecot-auth: pam_unix(dovecot:auth): authentication failure; logname= uid=0 euid=0 tty=dovecot ruser=web rhost=176.61.140.224 # Above example with injected rhost into ruser -- should not match for 1.2.3.4 -# failJSON: { "time": "2010-09-16T06:51:00", "match": true , "host": "192.0.43.10" } +# failJSON: { "time": "2010-09-16T07:51:00", "match": true , "host": "192.0.43.10" } @400000004c91b044077a9e94 dovecot-auth: pam_unix(dovecot:auth): authentication failure; logname= uid=0 euid=0 tty=dovecot ruser=rhost=1.2.3.4 rhost=192.0.43.10 -# failJSON: { "time": "2010-09-16T06:51:00", "match": true , "host": "176.61.140.225" } +# failJSON: { "time": "2010-09-16T07:51:00", "match": true , "host": "176.61.140.225" } @400000004c91b044077a9e94 dovecot-auth: pam_unix(dovecot:auth): authentication failure; logname= uid=0 euid=0 tty=dovecot ruser=root rhost=176.61.140.225 user=root # failJSON: { "time": "2004-12-12T11:19:11", "match": true , "host": "190.210.136.21" } diff --git a/fail2ban/tests/files/logs/selinux-ssh b/fail2ban/tests/files/logs/selinux-ssh index b6db443b..f9e1b828 100644 --- a/fail2ban/tests/files/logs/selinux-ssh +++ b/fail2ban/tests/files/logs/selinux-ssh @@ -1,25 +1,25 @@ -# failJSON: { "time": "2013-07-09T01:45:16", "match": false , "host": "173.242.116.187" } +# failJSON: { "time": "2013-07-09T02:45:16", "match": false , "host": "173.242.116.187" } type=USER_LOGIN msg=audit(1373330716.415:4063): user pid=11998 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=login acct="root" exe="/usr/sbin/sshd" hostname=? addr=173.242.116.187 terminal=ssh res=failed' -# failJSON: { "time": "2013-07-09T01:45:17", "match": false , "host": "173.242.116.187" } +# failJSON: { "time": "2013-07-09T02:45:17", "match": false , "host": "173.242.116.187" } type=USER_LOGIN msg=audit(1373330717.000:4068): user pid=12000 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=login acct=28756E6B6E6F776E207573657229 exe="/usr/sbin/sshd" hostname=? addr=173.242.116.187 terminal=ssh res=failed' -# failJSON: { "time": "2013-07-09T01:45:17", "match": true , "host": "173.242.116.187" } +# failJSON: { "time": "2013-07-09T02:45:17", "match": true , "host": "173.242.116.187" } type=USER_ERR msg=audit(1373330717.000:4070): user pid=12000 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=PAM:bad_ident acct="?" exe="/usr/sbin/sshd" hostname=173.242.116.187 addr=173.242.116.187 terminal=ssh res=failed' -# failJSON: { "time": "2013-07-09T01:45:17", "match": false , "host": "173.242.116.187" } +# failJSON: { "time": "2013-07-09T02:45:17", "match": false , "host": "173.242.116.187" } type=USER_LOGIN msg=audit(1373330717.000:4073): user pid=12000 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=login acct=28696E76616C6964207573657229 exe="/usr/sbin/sshd" hostname=? addr=173.242.116.187 terminal=ssh res=failed' -# failJSON: { "time": "2013-06-30T01:02:08", "match": false , "host": "113.240.248.18" } +# failJSON: { "time": "2013-06-30T02:02:08", "match": false , "host": "113.240.248.18" } type=USER_LOGIN msg=audit(1372546928.000:52008): user pid=21569 uid=0 auid=0 ses=76 subj=unconfined_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=login acct="sshd" exe="/usr/sbin/sshd" hostname=? addr=113.240.248.18 terminal=ssh res=failed' -# failJSON: { "time": "2013-06-30T02:58:20", "match": true , "host": "113.240.248.18" } +# failJSON: { "time": "2013-06-30T03:58:20", "match": true , "host": "113.240.248.18" } type=USER_ERR msg=audit(1372557500.000:61747): user pid=23684 uid=0 auid=0 ses=76 subj=unconfined_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=PAM:bad_ident acct="?" exe="/usr/sbin/sshd" hostname=113.240.248.18 addr=113.240.248.18 terminal=ssh res=failed' -# failJSON: { "time": "2013-06-30T03:58:20", "match": false , "host": "113.240.248.18" } +# failJSON: { "time": "2013-06-30T04:58:20", "match": false , "host": "113.240.248.18" } type=USER_LOGIN msg=audit(1372557500.000:61750): user pid=23684 uid=0 auid=0 ses=76 subj=unconfined_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=login acct=28696E76616C6964207573657229 exe="/usr/sbin/sshd" hostname=? addr=113.240.248.18 terminal=ssh res=failed' -# failJSON: { "time": "2013-07-06T17:48:00", "match": true , "host": "194.228.20.113" } +# failJSON: { "time": "2013-07-06T18:48:00", "match": true , "host": "194.228.20.113" } type=USER_AUTH msg=audit(1373129280.000:9): user pid=1277 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=pubkey acct="root" exe="/usr/sbin/sshd" hostname=? addr=194.228.20.113 terminal=ssh res=failed' # failJSON: { "time": "2013-10-30T07:57:43", "match": true , "host": "192.168.3.100" } diff --git a/fail2ban/tests/filtertestcase.py b/fail2ban/tests/filtertestcase.py index a0f715cd..c02e8616 100644 --- a/fail2ban/tests/filtertestcase.py +++ b/fail2ban/tests/filtertestcase.py @@ -794,7 +794,7 @@ class GetFailures(unittest.TestCase): FILENAME_MULTILINE = os.path.join(TEST_FILES_DIR, "testcase-multiline.log") # so that they could be reused by other tests - FAILURES_01 = ('193.168.0.128', 3, 1124017199.0, + FAILURES_01 = ('193.168.0.128', 3, 1124013599.0, [u'Aug 14 11:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128']*3) def setUp(self): @@ -844,7 +844,7 @@ class GetFailures(unittest.TestCase): def testGetFailures02(self): - output = ('141.3.81.106', 4, 1124017139.0, + output = ('141.3.81.106', 4, 1124013539.0, [u'Aug 14 11:%d:59 i60p295 sshd[12365]: Failed publickey for roehl from ::ffff:141.3.81.106 port 51332 ssh2' % m for m in 53, 54, 57, 58]) @@ -854,7 +854,7 @@ class GetFailures(unittest.TestCase): _assert_correct_last_attempt(self, self.filter, output) def testGetFailures03(self): - output = ('203.162.223.135', 7, 1124017144.0) + output = ('203.162.223.135', 7, 1124013544.0) self.filter.addLogPath(GetFailures.FILENAME_03) self.filter.addFailRegex("error,relay=,.*550 User unknown") @@ -862,7 +862,7 @@ class GetFailures(unittest.TestCase): _assert_correct_last_attempt(self, self.filter, output) def testGetFailures04(self): - output = [('212.41.96.186', 4, 1124017200.0), + output = [('212.41.96.186', 4, 1124013600.0), ('212.41.96.185', 4, 1124017198.0)] self.filter.addLogPath(GetFailures.FILENAME_04) @@ -877,11 +877,11 @@ class GetFailures(unittest.TestCase): def testGetFailuresUseDNS(self): # We should still catch failures with usedns = no ;-) - output_yes = ('93.184.216.119', 2, 1124017139.0, + output_yes = ('93.184.216.119', 2, 1124013539.0, [u'Aug 14 11:54:59 i60p295 sshd[12365]: Failed publickey for roehl from example.com port 51332 ssh2', u'Aug 14 11:58:59 i60p295 sshd[12365]: Failed publickey for roehl from ::ffff:93.184.216.119 port 51332 ssh2']) - output_no = ('93.184.216.119', 1, 1124017139.0, + output_no = ('93.184.216.119', 1, 1124013539.0, [u'Aug 14 11:58:59 i60p295 sshd[12365]: Failed publickey for roehl from ::ffff:93.184.216.119 port 51332 ssh2']) # Actually no exception would be raised -- it will be just set to 'no' @@ -904,7 +904,7 @@ class GetFailures(unittest.TestCase): def testGetFailuresMultiRegex(self): - output = ('141.3.81.106', 8, 1124017141.0) + output = ('141.3.81.106', 8, 1124013541.0) self.filter.addLogPath(GetFailures.FILENAME_02) self.filter.addFailRegex("Failed .* from ") @@ -923,8 +923,8 @@ class GetFailures(unittest.TestCase): self.assertRaises(FailManagerEmpty, self.filter.failManager.toBan) def testGetFailuresMultiLine(self): - output = [("192.0.43.10", 2, 1124017199.0), - ("192.0.43.11", 1, 1124017198.0)] + output = [("192.0.43.10", 2, 1124013599.0), + ("192.0.43.11", 1, 1124013598.0)] self.filter.addLogPath(GetFailures.FILENAME_MULTILINE) self.filter.addFailRegex("^.*rsyncd\[(?P\d+)\]: connect from .+ \(\)$^.+ rsyncd\[(?P=pid)\]: rsync error: .*$") self.filter.setMaxLines(100) @@ -942,7 +942,7 @@ class GetFailures(unittest.TestCase): self.assertEqual(sorted(foundList), sorted(output)) def testGetFailuresMultiLineIgnoreRegex(self): - output = [("192.0.43.10", 2, 1124017199.0)] + output = [("192.0.43.10", 2, 1124013599.0)] self.filter.addLogPath(GetFailures.FILENAME_MULTILINE) self.filter.addFailRegex("^.*rsyncd\[(?P\d+)\]: connect from .+ \(\)$^.+ rsyncd\[(?P=pid)\]: rsync error: .*$") self.filter.addIgnoreRegex("rsync error: Received SIGINT") @@ -956,9 +956,9 @@ class GetFailures(unittest.TestCase): self.assertRaises(FailManagerEmpty, self.filter.failManager.toBan) def testGetFailuresMultiLineMultiRegex(self): - output = [("192.0.43.10", 2, 1124017199.0), - ("192.0.43.11", 1, 1124017198.0), - ("192.0.43.15", 1, 1124017198.0)] + output = [("192.0.43.10", 2, 1124013599.0), + ("192.0.43.11", 1, 1124013598.0), + ("192.0.43.15", 1, 1124013598.0)] self.filter.addLogPath(GetFailures.FILENAME_MULTILINE) self.filter.addFailRegex("^.*rsyncd\[(?P\d+)\]: connect from .+ \(\)$^.+ rsyncd\[(?P=pid)\]: rsync error: .*$") self.filter.addFailRegex("^.* sendmail\[.*, msgid=<(?P[^>]+).*relay=\[\].*$^.+ spamd: result: Y \d+ .*,mid=<(?P=msgid)>(,bayes=[.\d]+)?(,autolearn=\S+)?\s*$") diff --git a/fail2ban/tests/samplestestcase.py b/fail2ban/tests/samplestestcase.py index 3529fcc2..132ade7b 100644 --- a/fail2ban/tests/samplestestcase.py +++ b/fail2ban/tests/samplestestcase.py @@ -129,7 +129,7 @@ def testSampleRegexsFactory(name): jsonTimeLocal = datetime.datetime.strptime(t, "%Y-%m-%dT%H:%M:%S.%f") - jsonTime = time.mktime(jsonTimeLocal.utctimetuple()) + jsonTime = time.mktime(jsonTimeLocal.timetuple()) jsonTime += jsonTimeLocal.microsecond / 1000000 From 1c20fd88d4d4131c6a4ed8a0abe89534097b823f Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Wed, 14 May 2014 23:04:48 +0100 Subject: [PATCH 25/25] DOC: Update docs in reference to time zone related fix --- ChangeLog | 1 + THANKS | 1 + 2 files changed, 2 insertions(+) diff --git a/ChangeLog b/ChangeLog index 515c79d2..69cbe909 100644 --- a/ChangeLog +++ b/ChangeLog @@ -27,6 +27,7 @@ ver. 0.9.1 (2014/xx/xx) - better, faster, stronger * Database now returns persistent bans on restart (bantime < 0) * Recursive action tags now fully processed. Fixes issue with bsd-ipfw action + * Correct times for non-timezone date times formats - Thanks sebres - New features: - Added monit filter thanks Jason H Martin. diff --git a/THANKS b/THANKS index 27165492..2c5b65bf 100644 --- a/THANKS +++ b/THANKS @@ -86,6 +86,7 @@ Rolf Fokkens Roman Gelfand Russell Odom Sebastian Arcus +sebres Sireyessire silviogarbes Stefan Tatschner