diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 00000000..ba38f23d --- /dev/null +++ b/.pylintrc @@ -0,0 +1,37 @@ +# Custom pylint configuration for the Fail2Ban project +# +# Set your PYLINTRC environment variable to point to this file +# e.g. +# export PYLINTRC=$PWD/.pylintrc + +[FORMAT] +indent-string='\t' + +[BASIC] +# Fail2Ban uses non-conventional to Python world camel-casing +# These regexps were originally borrowed from 0.4.x series of +# PyMVPA which had similar conventions. + +# Regular expression which should only match correct module names +module-rgx=(([a-z][a-z0-9_]*)|([A-Z][a-zA-Z0-9_]+))$ + +attr-rgx=[a-z_][a-zA-Z0-9_]{2,30} + +# Regular expression which should only match correct class names +class-rgx=[A-Z_]+[a-zA-Z0-9]+$ + +# Regular expression which should only match correct function names +function-rgx=[a-z_]+[a-z_][a-zA-Z0-9]*$ + +# Regular expression which should only match correct method names +method-rgx=([a-z_]|__)[a-zA-Z0-9]*(__)?$ + +# Regular expression which should only match correct argument names +argument-rgx=[a-z][a-zA-Z0-9]*_*[a-zA-Z0-9]*_*[a-zA-Z0-9]*_?$ + +# Regular expression which should only match correct variable names +variable-rgx=([a-z_]+[a-zA-Z0-9]*_*[a-zA-Z0-9]*_*[a-zA-Z0-9]*_?||(__.*__))$||[A-Z] + +# Regular expression which should only match correct module level names +# Default: (([A-Z_][A-Z1-9_]*)|(__.*__))$ +const-rgx=([a-z_]+[a-zA-Z0-9]*_*[a-zA-Z0-9]*_*[a-zA-Z0-9]*_?|__.*__)$||[A-Z] diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 00000000..4c60f8e4 --- /dev/null +++ b/MANIFEST @@ -0,0 +1,127 @@ +README +ChangeLog +TODO +THANKS +COPYING +fail2ban-client +fail2ban-server +fail2ban-testcases +fail2ban-regex +client/configreader.py +client/configparserinc.py +client/jailreader.py +client/fail2banreader.py +client/jailsreader.py +client/beautifier.py +client/filterreader.py +client/actionreader.py +client/__init__.py +client/configurator.py +client/csocket.py +server/asyncserver.py +server/filter.py +server/filtergamin.py +server/filterpoll.py +server/iso8601.py +server/server.py +server/actions.py +server/faildata.py +server/failmanager.py +server/datedetector.py +server/jailthread.py +server/transmitter.py +server/action.py +server/ticket.py +server/jail.py +server/jails.py +server/__init__.py +server/banmanager.py +server/datetemplate.py +server/mytime.py +server/failregex.py +testcases/banmanagertestcase.py +testcases/failmanagertestcase.py +testcases/clientreadertestcase.py +testcases/filtertestcase.py +testcases/__init__.py +testcases/datedetectortestcase.py +testcases/actiontestcase.py +testcases/servertestcase.py +testcases/files/testcase01.log +testcases/files/testcase02.log +testcases/files/testcase03.log +testcases/files/testcase04.log +setup.py +setup.cfg +common/__init__.py +common/helpers.py +common/version.py +common/protocol.py +config/jail.conf +config/filter.d/common.conf +config/filter.d/apache-auth.conf +config/filter.d/apache-badbots.conf +config/filter.d/apache-nohome.conf +config/filter.d/apache-noscript.conf +config/filter.d/apache-overflows.conf +config/filter.d/courierlogin.conf +config/filter.d/couriersmtp.conf +config/filter.d/cyrus-imap.conf +config/filter.d/exim.conf +config/filter.d/gssftpd.conf +config/filter.d/lighttpd-fastcgi.conf +config/filter.d/named-refused.conf +config/filter.d/postfix.conf +config/filter.d/proftpd.conf +config/filter.d/pure-ftpd.conf +config/filter.d/qmail.conf +config/filter.d/pam-generic.conf +config/filter.d/php-url-fopen.conf +config/filter.d/sasl.conf +config/filter.d/sieve.conf +config/filter.d/sshd.conf +config/filter.d/sshd-ddos.conf +config/filter.d/vsftpd.conf +config/filter.d/webmin-auth.conf +config/filter.d/wuftpd.conf +config/filter.d/xinetd-fail.conf +config/action.d/complain.conf +config/action.d/dshield.conf +config/action.d/hostsdeny.conf +config/action.d/ipfw.conf +config/action.d/ipfilter.conf +config/action.d/iptables.conf +config/action.d/iptables-allports.conf +config/action.d/iptables-multiport.conf +config/action.d/iptables-multiport-log.conf +config/action.d/iptables-new.conf +config/action.d/mail.conf +config/action.d/mail-buffered.conf +config/action.d/mail-whois.conf +config/action.d/mail-whois-lines.conf +config/action.d/mynetwatchman.conf +config/action.d/sendmail.conf +config/action.d/sendmail-buffered.conf +config/action.d/sendmail-whois.conf +config/action.d/sendmail-whois-lines.conf +config/action.d/shorewall.conf +config/fail2ban.conf +man/fail2ban-client.1 +man/fail2ban-client.h2m +man/fail2ban-server.1 +man/fail2ban-server.h2m +man/fail2ban-regex.1 +man/fail2ban-regex.h2m +man/generate-man +files/gentoo-initd +files/gentoo-confd +files/redhat-initd +files/macosx-initd +files/solaris-fail2ban.xml +files/solaris-svc-fail2ban +files/suse-initd +files/cacti/fail2ban_stats.sh +files/cacti/cacti_host_template_fail2ban.xml +files/cacti/README +files/nagios/check_fail2ban +files/nagios/f2ban.txt diff --git a/README b/README index fb6f3d2b..cb4c1cd4 100644 --- a/README +++ b/README @@ -26,8 +26,8 @@ Optional: To install, just do: -> tar xvfj fail2ban-0.8.4.tar.bz2 -> cd fail2ban-0.8.4 +> tar xvfj fail2ban-0.8.5.tar.bz2 +> cd fail2ban-0.8.5 > python setup.py install This will install Fail2Ban into /usr/share/fail2ban. The executable scripts are @@ -64,15 +64,7 @@ Cyril Jaquier: Thanks: ------- -Kévin Drapel, Marvin Rouge, Sireyessire, Robert Edeker, Tom Pike, Iain Lea, -Andrey G. Grozin, Yaroslav Halchenko, Jonathan Kamens, Stephen Gildea, Markus -Hoffmann, Mark Edgington, Patrick Börjesson, kojiro, zugeschmiert, Tyler, Nick -Munger, Christoph Haas, Justin Shore, Joël Bertrand, René Berber, mEDI, Axel -Thimm, Eric Gerbier, Christian Rauch, Michael C. Haller, Jonathan Underwood, -Hanno 'Rince' Wagner, Daniel B. Cid, David Nutter, Raphaël Marichez, Guillaume -Delvit, Vaclav Misek, Adrien Clerc, Michael Hanselmann, Vincent Deffontaines, -Bill Heaton, Russell Odom, Christos Psonis, Arturo 'Buanzo' Busleiman and many -others. +See THANKS file. License: -------- diff --git a/THANKS b/THANKS new file mode 100644 index 00000000..ff20d07d --- /dev/null +++ b/THANKS @@ -0,0 +1,47 @@ +Fail2Ban is an open source project with many contributions from its +users community. Below is an alphabetically sorted partial list of the +contributors to the project. If you have been left off, please let us +know (preferably send a pull request on github with the "fix") and you +will be added + +Adrien Clerc +Andrey G. Grozin +Arturo 'Buanzo' Busleiman +Axel Thimm +Bill Heaton +Christian Rauch +Christoph Haas +Christos Psonis +Daniel B. Cid +David Nutter +Eric Gerbier +Guillaume Delvit +Hanno 'Rince' Wagner +Iain Lea +Jonathan Kamens +Jonathan Underwood +Joël Bertrand +Justin Shore +Kévin Drapel +kojiro +Mark Edgington +Markus Hoffmann +Marvin Rouge +mEDI +Michael C. Haller +Michael Hanselmann +NickMunger +Patrick Börjesson +Raphaël Marichez +René Berber +Robert Edeker +Russell Odom +Sireyessire +Stephen Gildea +Tom Pike +Tyler +Vaclav Misek +Vincent Deffontaines +Yaroslav Halchenko +Yehuda Katz +zugeschmiert diff --git a/TODO b/TODO index 4737c79a..2b17c916 100644 --- a/TODO +++ b/TODO @@ -4,7 +4,7 @@ |_| \__,_|_|_/___|_.__/\__,_|_||_| ================================================================================ -ToDo $Revision$ +ToDo ================================================================================ Legend: diff --git a/client/__init__.py b/client/__init__.py index dc25edd6..65ba9868 100644 --- a/client/__init__.py +++ b/client/__init__.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify diff --git a/client/actionreader.py b/client/actionreader.py index 51acaf07..29672bca 100644 --- a/client/actionreader.py +++ b/client/actionreader.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify @@ -87,4 +90,4 @@ class ActionReader(ConfigReader): stream.append(head + ["setcinfo", self.__file, p, self.__cInfo[p]]) return stream - \ No newline at end of file + diff --git a/client/beautifier.py b/client/beautifier.py index ab98b294..104a239e 100644 --- a/client/beautifier.py +++ b/client/beautifier.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify diff --git a/client/configparserinc.py b/client/configparserinc.py index c86d786c..12ac97a8 100644 --- a/client/configparserinc.py +++ b/client/configparserinc.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify diff --git a/client/configreader.py b/client/configreader.py index f8e9ade8..18ae9b9a 100644 --- a/client/configreader.py +++ b/client/configreader.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify diff --git a/client/configurator.py b/client/configurator.py index 8c3a69dd..526f218b 100644 --- a/client/configurator.py +++ b/client/configurator.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify @@ -75,4 +78,4 @@ class Configurator: for opt in self.__streams["jails"]: cmds.append(opt) return cmds - \ No newline at end of file + diff --git a/client/csocket.py b/client/csocket.py index 13bae5f8..4bfb0510 100644 --- a/client/csocket.py +++ b/client/csocket.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify diff --git a/client/fail2banreader.py b/client/fail2banreader.py index 99bc522e..f4f2728f 100644 --- a/client/fail2banreader.py +++ b/client/fail2banreader.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify @@ -55,4 +58,4 @@ class Fail2banReader(ConfigReader): elif opt == "logtarget": stream.append(["set", "logtarget", self.__opts[opt]]) return stream - \ No newline at end of file + diff --git a/client/filterreader.py b/client/filterreader.py index 4e546da2..b20c6f83 100644 --- a/client/filterreader.py +++ b/client/filterreader.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify @@ -71,4 +74,4 @@ class FilterReader(ConfigReader): if regex != '': stream.append(["set", self.__name, "addignoreregex", regex]) return stream - \ No newline at end of file + diff --git a/client/jailreader.py b/client/jailreader.py index a65f5eae..52d5f82c 100644 --- a/client/jailreader.py +++ b/client/jailreader.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify diff --git a/client/jailsreader.py b/client/jailsreader.py index 620e7b45..9e68dffe 100644 --- a/client/jailsreader.py +++ b/client/jailsreader.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify @@ -84,4 +87,4 @@ class JailsReader(ConfigReader): stream.append(["start", jail.getName()]) return stream - \ No newline at end of file + diff --git a/common/__init__.py b/common/__init__.py index dc25edd6..65ba9868 100644 --- a/common/__init__.py +++ b/common/__init__.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify diff --git a/common/helpers.py b/common/helpers.py index a8671592..5afc3723 100644 --- a/common/helpers.py +++ b/common/helpers.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify @@ -27,12 +30,12 @@ __license__ = "GPL" def formatExceptionInfo(): - """ Author: Arturo 'Buanzo' Busleiman """ - import sys - cla, exc = sys.exc_info()[:2] - excName = cla.__name__ - try: - excArgs = exc.__dict__["args"] - except KeyError: - excArgs = str(exc) - return (excName, excArgs) + """ Author: Arturo 'Buanzo' Busleiman """ + import sys + cla, exc = sys.exc_info()[:2] + excName = cla.__name__ + try: + excArgs = exc.__dict__["args"] + except KeyError: + excArgs = str(exc) + return (excName, excArgs) diff --git a/common/protocol.py b/common/protocol.py index 675ef5ec..7f0ec9e1 100644 --- a/common/protocol.py +++ b/common/protocol.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify diff --git a/common/version.py b/common/version.py index fc90ffc6..ed2f72db 100644 --- a/common/version.py +++ b/common/version.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify diff --git a/config/filter.d/common.conf b/config/filter.d/common.conf index b580c78d..18bf41c5 100644 --- a/config/filter.d/common.conf +++ b/config/filter.d/common.conf @@ -32,10 +32,16 @@ __daemon_re = [\[\(]?%(_daemon)s(?:\(\S+\))?[\]\)]?:? # EXAMPLES: sshd[31607], pop(pam_unix)[4920] __daemon_combs_re = (?:%(__pid_re)s?:\s+%(__daemon_re)s|%(__daemon_re)s%(__pid_re)s?:) +# Some messages have a kernel prefix with a timestamp +# EXAMPLES: kernel: [769570.846956] +__kernel_prefix = kernel: \[\d+\.\d+\] + +__hostname = \S+ + # # Common line prefixes (beginnings) which could be used in filters # # [hostname] [vserver tag] daemon_id spaces # this can be optional (for instance if we match named native log files) -__prefix_line = \s*(?:\S+ )?(?:@vserver_\S+ )?%(__daemon_combs_re)s?\s* +__prefix_line = \s*(?:%(__hostname)s )?(?:%(__kernel_prefix)s )?(?:@vserver_\S+ )?%(__daemon_combs_re)s?\s* diff --git a/config/filter.d/sasl.conf b/config/filter.d/sasl.conf index e316605f..4be847ee 100644 --- a/config/filter.d/sasl.conf +++ b/config/filter.d/sasl.conf @@ -14,7 +14,7 @@ # (?:::f{4,6}:)?(?P[\w\-.^_]+) # Values: TEXT # -failregex = (?i): warning: [-._\w]+\[\]: SASL (?:LOGIN|PLAIN|(?:CRAM|DIGEST)-MD5) authentication failed(: [ A-Za-z0-9+/]*={0,2})?$ +failregex = (?i): warning: [-._\w]+\[\]: SASL (?:LOGIN|PLAIN|(?:CRAM|DIGEST)-MD5) authentication failed(: [ A-Za-z0-9+/]*={0,2})?\s*$ # Option: ignoreregex # Notes.: regex to ignore. If this regex matches, the line is ignored. diff --git a/fail2ban-client b/fail2ban-client index d212fe7f..31dd48ca 100755 --- a/fail2ban-client +++ b/fail2ban-client @@ -1,4 +1,7 @@ #!/usr/bin/python +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify diff --git a/fail2ban-server b/fail2ban-server index 696f3e95..f66bbaf8 100755 --- a/fail2ban-server +++ b/fail2ban-server @@ -1,4 +1,7 @@ #!/usr/bin/python +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify diff --git a/fail2ban-testcases b/fail2ban-testcases index 87cf9e80..c05a64bd 100755 --- a/fail2ban-testcases +++ b/fail2ban-testcases @@ -1,4 +1,7 @@ #!/usr/bin/python +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify @@ -26,7 +29,7 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" -import unittest, logging, sys +import unittest, logging, sys, time, os from common.version import version from testcases import banmanagertestcase @@ -40,6 +43,11 @@ from server.mytime import MyTime # Set the time to a fixed, known value # Sun Aug 14 12:00:00 CEST 2005 + +# yoh: we need to adjust TZ to match the one used by Cyril so all the timestamps match +old_TZ = os.environ.get('TZ', None) +os.environ['TZ'] = 'Europe/Zurich' +time.tzset() MyTime.setTime(1124013600) # Gets the instance of the logger. @@ -74,3 +82,10 @@ tests.addTest(unittest.makeSuite(datedetectortestcase.DateDetectorTest)) # Tests runner testRunner = unittest.TextTestRunner() testRunner.run(tests) + +# Just for the sake of it reset the TZ +# yoh is planing to move all this into setup/teardown methods within tests +os.environ.pop('TZ') +if old_TZ: + os.environ['TZ'] = old_TZ +time.tzset() diff --git a/server/__init__.py b/server/__init__.py index dc25edd6..65ba9868 100644 --- a/server/__init__.py +++ b/server/__init__.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify diff --git a/server/action.py b/server/action.py index 8dafbf27..e8f6bd12 100644 --- a/server/action.py +++ b/server/action.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify diff --git a/server/actions.py b/server/actions.py index 06f15ec6..e438ab7a 100644 --- a/server/actions.py +++ b/server/actions.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify @@ -158,6 +161,7 @@ class Actions(JailThread): aInfo["ip"] = bTicket.getIP() aInfo["failures"] = bTicket.getAttempt() aInfo["time"] = bTicket.getTime() + aInfo["matches"] = "".join(bTicket.getMatches()) if self.__banManager.addBanTicket(bTicket): logSys.warn("[%s] Ban %s" % (self.jail.getName(), aInfo["ip"])) for action in self.__actions: @@ -198,6 +202,7 @@ class Actions(JailThread): aInfo["ip"] = ticket.getIP() aInfo["failures"] = ticket.getAttempt() aInfo["time"] = ticket.getTime() + aInfo["matches"] = "".join(ticket.getMatches()) logSys.warn("[%s] Unban %s" % (self.jail.getName(), aInfo["ip"])) for action in self.__actions: action.execActionUnban(aInfo) diff --git a/server/asyncserver.py b/server/asyncserver.py index 9d1c9c66..3558a2dd 100644 --- a/server/asyncserver.py +++ b/server/asyncserver.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify diff --git a/server/banmanager.py b/server/banmanager.py index 13c2a307..328ad032 100644 --- a/server/banmanager.py +++ b/server/banmanager.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify @@ -130,7 +133,7 @@ class BanManager: ip = ticket.getIP() #lastTime = ticket.getTime() lastTime = MyTime.time() - banTicket = BanTicket(ip, lastTime) + banTicket = BanTicket(ip, lastTime, ticket.getMatches()) banTicket.setAttempt(ticket.getAttempt()) return banTicket createBanTicket = staticmethod(createBanTicket) diff --git a/server/datedetector.py b/server/datedetector.py index b67b49ba..09567656 100644 --- a/server/datedetector.py +++ b/server/datedetector.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify diff --git a/server/datetemplate.py b/server/datetemplate.py index 711c6c99..a561abbe 100644 --- a/server/datetemplate.py +++ b/server/datetemplate.py @@ -1,4 +1,6 @@ -# -*- coding: utf-8 -*- +# emacs: -*- mode: python; coding: utf-8; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify @@ -30,6 +32,10 @@ import re, time from mytime import MyTime import iso8601 +import logging +logSys = logging.getLogger("fail2ban.datetemplate") + + class DateTemplate: def __init__(self): @@ -76,7 +82,7 @@ class DateEpoch(DateTemplate): dateMatch = self.matchDate(line) if dateMatch: # extract part of format which represents seconds since epoch - date = list(time.localtime(float(dateMatch.group()))) + date = list(MyTime.localtime(float(dateMatch.group()))) return date @@ -86,20 +92,20 @@ class DateEpoch(DateTemplate): # standard. class DateStrptime(DateTemplate): - + TABLE = dict() - TABLE["Jan"] = [] - TABLE["Feb"] = [u"Fév"] - TABLE["Mar"] = [u"Mär"] - TABLE["Apr"] = ["Avr"] - TABLE["May"] = ["Mai"] - TABLE["Jun"] = [] - TABLE["Jul"] = [] - TABLE["Aug"] = ["Aou"] - TABLE["Sep"] = [] - TABLE["Oct"] = ["Okt"] - TABLE["Nov"] = [] - TABLE["Dec"] = [u"Déc", "Dez"] + TABLE["Jan"] = ["Sty"] + TABLE["Feb"] = [u"Fév", "Lut"] + TABLE["Mar"] = [u"Mär", "Mar"] + TABLE["Apr"] = ["Avr", "Kwi"] + TABLE["May"] = ["Mai", "Maj"] + TABLE["Jun"] = ["Lip"] + TABLE["Jul"] = ["Sie"] + TABLE["Aug"] = ["Aou", "Wrz"] + TABLE["Sep"] = ["Sie"] + TABLE["Oct"] = [u"Paź"] + TABLE["Nov"] = ["Lis"] + TABLE["Dec"] = [u"Déc", "Dez", "Gru"] def __init__(self): DateTemplate.__init__(self) @@ -116,6 +122,8 @@ class DateStrptime(DateTemplate): for t in DateStrptime.TABLE: for m in DateStrptime.TABLE[t]: if date.find(m) >= 0: + logSys.debug(u"Replacing %r with %r in %r" % + (m, t, date)) return date.replace(m, t) return date convertLocale = staticmethod(convertLocale) @@ -145,6 +153,9 @@ class DateStrptime(DateTemplate): # If the date is greater than the current time, we suppose # that the log is not from this year but from the year before if time.mktime(date) > MyTime.time(): + logSys.debug( + u"Correcting deduced year from %d to %d since %f > %f" % + (date[0], date[0]-1, time.mktime(date), MyTime.time())) date[0] -= 1 elif date[1] == 1 and date[2] == 1: # If it is Jan 1st, it is either really Jan 1st or there @@ -169,7 +180,7 @@ class DateTai64n(DateTemplate): value = dateMatch.group() seconds_since_epoch = value[2:17] # convert seconds from HEX into local time stamp - date = list(time.localtime(int(seconds_since_epoch, 16))) + date = list(MyTime.localtime(int(seconds_since_epoch, 16))) return date diff --git a/server/faildata.py b/server/faildata.py index d68cd7c1..2b7959a7 100644 --- a/server/faildata.py +++ b/server/faildata.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify @@ -35,16 +38,24 @@ class FailData: self.__retry = 0 self.__lastTime = 0 self.__lastReset = 0 - + self.__matches = [] + def setRetry(self, value): self.__retry = value - + # keep only the last matches or reset entirely + self.__matches = self.__matches[-min(len(self.__matches, value)):] \ + if value else [] + def getRetry(self): return self.__retry - - def inc(self): + + def getMatches(self): + return self.__matches + + def inc(self, matches=None): self.__retry += 1 - + self.__matches += matches or [] + def setLastTime(self, value): if value > self.__lastTime: self.__lastTime = value diff --git a/server/failmanager.py b/server/failmanager.py index 95cd47c0..73e5f242 100644 --- a/server/failmanager.py +++ b/server/failmanager.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify @@ -88,19 +91,22 @@ class FailManager: self.__lock.acquire() ip = ticket.getIP() unixTime = ticket.getTime() + matches = ticket.getMatches() if self.__failList.has_key(ip): fData = self.__failList[ip] if fData.getLastReset() < unixTime - self.__maxTime: fData.setLastReset(unixTime) fData.setRetry(0) - fData.inc() + fData.inc(matches) fData.setLastTime(unixTime) else: fData = FailData() - fData.inc() + fData.inc(matches) fData.setLastReset(unixTime) fData.setLastTime(unixTime) self.__failList[ip] = fData + logSys.debug("Currently have failures from %d IPs: %s" + % (len(self.__failList), self.__failList.keys())) self.__failTotal += 1 finally: self.__lock.release() @@ -134,7 +140,7 @@ class FailManager: if data.getRetry() >= self.__maxRetry: self.__delFailure(ip) # Create a FailTicket from BanData - failTicket = FailTicket(ip, data.getLastTime()) + failTicket = FailTicket(ip, data.getLastTime(), data.getMatches()) failTicket.setAttempt(data.getRetry()) return failTicket raise FailManagerEmpty diff --git a/server/failregex.py b/server/failregex.py index 0a5a0de4..5bc1c401 100644 --- a/server/failregex.py +++ b/server/failregex.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify diff --git a/server/filter.py b/server/filter.py index 6f1e4c7d..38151d90 100644 --- a/server/filter.py +++ b/server/filter.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify @@ -278,7 +281,7 @@ class Filter(JailThread): logSys.debug("Ignore %s" % ip) continue logSys.debug("Found %s" % ip) - self.failManager.addFailure(FailTicket(ip, unixTime)) + self.failManager.addFailure(FailTicket(ip, unixTime, [line])) ## # Returns true if the line should be ignored. diff --git a/server/filtergamin.py b/server/filtergamin.py index d2b06fee..0489a2ef 100644 --- a/server/filtergamin.py +++ b/server/filtergamin.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify diff --git a/server/filterpoll.py b/server/filterpoll.py index 621816ed..618e4bac 100644 --- a/server/filterpoll.py +++ b/server/filterpoll.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify diff --git a/server/iso8601.py b/server/iso8601.py index 71dccfea..a42989ea 100644 --- a/server/iso8601.py +++ b/server/iso8601.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: + # Copyright (c) 2007 Michael Twomey # # Permission is hereby granted, free of charge, to any person obtaining a diff --git a/server/jail.py b/server/jail.py index 7b4a2719..eefe69e5 100644 --- a/server/jail.py +++ b/server/jail.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify diff --git a/server/jails.py b/server/jails.py index 8dbde9c8..1ff94e06 100644 --- a/server/jails.py +++ b/server/jails.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify diff --git a/server/jailthread.py b/server/jailthread.py index 44667ff0..ac102ba3 100644 --- a/server/jailthread.py +++ b/server/jailthread.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify diff --git a/server/mytime.py b/server/mytime.py index b6762c4f..b64a2b75 100644 --- a/server/mytime.py +++ b/server/mytime.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify @@ -76,4 +79,10 @@ class MyTime: else: return time.gmtime(MyTime.myTime) gmtime = staticmethod(gmtime) - \ No newline at end of file + + def localtime(x=None): + if MyTime.myTime == None or x is not None: + return time.localtime(x) + else: + return time.localtime(MyTime.myTime) + localtime = staticmethod(localtime) diff --git a/server/server.py b/server/server.py index 370696b2..a5b0bf09 100644 --- a/server/server.py +++ b/server/server.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify diff --git a/server/ticket.py b/server/ticket.py index e2caaeb0..81bffa9d 100644 --- a/server/ticket.py +++ b/server/ticket.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify @@ -31,12 +34,25 @@ logSys = logging.getLogger("fail2ban") class Ticket: - def __init__(self, ip, time): + def __init__(self, ip, time, matches=None): + """Ticket constructor + + @param ip the IP address + @param time the ban time + @param matches (log) lines caused the ticket + """ + self.__ip = ip self.__time = time self.__attempt = 0 self.__file = None + self.__matches = matches or [] + + def __str__(self): + return "%s: ip=%s time=%s #attempts=%d" % \ + (self.__class__, self.__ip, self.__time, self.__attempt) + def setIP(self, value): self.__ip = value @@ -61,11 +77,12 @@ class Ticket: def getAttempt(self): return self.__attempt + def getMatches(self): + return self.__matches + class FailTicket(Ticket): - - def __init__(self, ip, time): - Ticket.__init__(self, ip, time) + pass ## @@ -74,14 +91,4 @@ class FailTicket(Ticket): # This class extends the Ticket class. It is mainly used by the BanManager. class BanTicket(Ticket): - - ## - # Constructor. - # - # Call the Ticket (parent) constructor and initialize default - # values. - # @param ip the IP address - # @param time the ban time - - def __init__(self, ip, time): - Ticket.__init__(self, ip, time) + pass diff --git a/server/transmitter.py b/server/transmitter.py index cb9d668d..27e966a6 100644 --- a/server/transmitter.py +++ b/server/transmitter.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify @@ -261,4 +264,4 @@ class Transmitter: name = command[0] return self.__server.statusJail(name) raise Exception("Invalid command (no status)") - \ No newline at end of file + diff --git a/setup.py b/setup.py index 1e43771f..aff89545 100755 --- a/setup.py +++ b/setup.py @@ -1,4 +1,6 @@ #!/usr/bin/python +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : # This file is part of Fail2Ban. # diff --git a/testcases/__init__.py b/testcases/__init__.py index 76dba873..65ba9868 100644 --- a/testcases/__init__.py +++ b/testcases/__init__.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify @@ -22,4 +25,4 @@ __author__ = "Cyril Jaquier" __version__ = "$Revision$" __date__ = "$Date$" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" -__license__ = "GPL" \ No newline at end of file +__license__ = "GPL" diff --git a/testcases/actiontestcase.py b/testcases/actiontestcase.py index bc6f39ad..a537ab0c 100644 --- a/testcases/actiontestcase.py +++ b/testcases/actiontestcase.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify @@ -44,4 +47,4 @@ class ExecuteAction(unittest.TestCase): self.__action.setActionCheck("[ -e /tmp/fail2ban.test ]") self.assertTrue(self.__action.execActionBan(None)) - \ No newline at end of file + diff --git a/testcases/banmanagertestcase.py b/testcases/banmanagertestcase.py index 644cf6cb..4ff76f41 100644 --- a/testcases/banmanagertestcase.py +++ b/testcases/banmanagertestcase.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify @@ -53,4 +56,4 @@ class AddFailure(unittest.TestCase): def _testInListNOK(self): ticket = BanTicket('111.111.1.111', 1167605999.0) self.assertFalse(self.__banManager.inBanList(ticket)) - \ No newline at end of file + diff --git a/testcases/clientreadertestcase.py b/testcases/clientreadertestcase.py index 39ab80bf..74993914 100644 --- a/testcases/clientreadertestcase.py +++ b/testcases/clientreadertestcase.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify @@ -40,4 +43,4 @@ class JailReaderTest(unittest.TestCase): expected = ['mail-whois', {'name': 'SSH'}] result = JailReader.splitAction(action) self.assertEquals(expected, result) - \ No newline at end of file + diff --git a/testcases/datedetectortestcase.py b/testcases/datedetectortestcase.py index 43c7528d..70447b25 100644 --- a/testcases/datedetectortestcase.py +++ b/testcases/datedetectortestcase.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify @@ -64,4 +67,4 @@ class DateDetectorTest(unittest.TestCase): # # self.assertEqual(self.__datedetector.getTime(log), date) # self.assertEqual(self.__datedetector.getUnixTime(log), dateUnix) - \ No newline at end of file + diff --git a/testcases/failmanagertestcase.py b/testcases/failmanagertestcase.py index 95f25b44..3c1bce22 100644 --- a/testcases/failmanagertestcase.py +++ b/testcases/failmanagertestcase.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify diff --git a/testcases/filtertestcase.py b/testcases/filtertestcase.py index 76fe0302..c004c7d2 100644 --- a/testcases/filtertestcase.py +++ b/testcases/filtertestcase.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify @@ -25,6 +28,8 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" import unittest +import time + from server.filterpoll import FilterPoll from server.filter import FileFilter, DNSUtils from server.failmanager import FailManager @@ -94,26 +99,44 @@ class GetFailures(unittest.TestCase): def tearDown(self): """Call after every test case.""" - + + def _assertEqualEntries(self, found, output): + """Little helper to unify comparisons with the target entries + + and report helpful failure reports instead of millions of seconds ;) + """ + self.assertEqual(found[:2], output[:2]) + found_time, output_time = \ + time.localtime(found[2]),\ + time.localtime(output[2]) + self.assertEqual(found_time, output_time) + if len(found) > 3: # match matches + self.assertEqual(found[3], output[3]) + + def testGetFailures01(self): - output = ('193.168.0.128', 3, 1124013599.0) - + output = ('193.168.0.128', 3, 1124013599.0, + ['Aug 14 11:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128\n']*3) + self.__filter.addLogPath(GetFailures.FILENAME_01) self.__filter.addFailRegex("(?:(?:Authentication failure|Failed [-/\w+]+) for(?: [iI](?:llegal|nvalid) user)?|[Ii](?:llegal|nvalid) user|ROOT LOGIN REFUSED) .*(?: from|FROM) ") self.__filter.getFailures(GetFailures.FILENAME_01) - + ticket = self.__filter.failManager.toBan() attempts = ticket.getAttempt() date = ticket.getTime() ip = ticket.getIP() - found = (ip, attempts, date) - - self.assertEqual(found, output) + matches = ticket.getMatches() + found = (ip, attempts, date, matches) + + self._assertEqualEntries(found, output) def testGetFailures02(self): - output = ('141.3.81.106', 4, 1124013539.0) + output = ('141.3.81.106', 4, 1124013539.0, + ['Aug 14 11:%d:59 i60p295 sshd[12365]: Failed publickey for roehl from ::ffff:141.3.81.106 port 51332 ssh2\n' + % m for m in 53, 54, 57, 58]) self.__filter.addLogPath(GetFailures.FILENAME_02) self.__filter.addFailRegex("Failed .* from ") @@ -125,9 +148,10 @@ class GetFailures(unittest.TestCase): attempts = ticket.getAttempt() date = ticket.getTime() ip = ticket.getIP() - found = (ip, attempts, date) + matches = ticket.getMatches() + found = (ip, attempts, date, matches) - self.assertEqual(found, output) + self._assertEqualEntries(found, output) def testGetFailures03(self): output = ('203.162.223.135', 6, 1124013544.0) @@ -144,7 +168,7 @@ class GetFailures(unittest.TestCase): ip = ticket.getIP() found = (ip, attempts, date) - self.assertEqual(found, output) + self._assertEqualEntries(found, output) def testGetFailures04(self): output = [('212.41.96.186', 4, 1124013600.0), @@ -182,7 +206,7 @@ class GetFailures(unittest.TestCase): ip = ticket.getIP() found = (ip, attempts, date) - self.assertEqual(found, output) + self._assertEqualEntries(found, output) def testGetFailuresIgnoreRegex(self): output = ('141.3.81.106', 8, 1124013541.0) diff --git a/testcases/servertestcase.py b/testcases/servertestcase.py index 90ca92e4..ab32eb92 100644 --- a/testcases/servertestcase.py +++ b/testcases/servertestcase.py @@ -1,3 +1,6 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify @@ -124,4 +127,4 @@ class Transmitter(unittest.TestCase): jail = self.__server.jails[name] self.assertEqual(jail.getFilter().failManager.size(), 0) self.assertEqual(jail.getAction().banManager.size(), 2) - \ No newline at end of file +