diff --git a/CHANGELOG b/ChangeLog similarity index 89% rename from CHANGELOG rename to ChangeLog index 6d05c5c8..2702b195 100644 --- a/CHANGELOG +++ b/ChangeLog @@ -4,9 +4,46 @@ |_| \__,_|_|_/___|_.__/\__,_|_||_| ============================================================= -Fail2Ban (version 0.8.1) 2007/08/14 +Fail2Ban (version 0.8.2) 2008/03/06 ============================================================= +ver. 0.8.2 (2008/03/06) - stable +---------- +- Fixed named filter. Thanks to Yaroslav Halchenko +- Fixed wrong path for apache-auth in jail.conf. Thanks to + Vincent Deffontaines +- Fixed timezone bug with epoch date template. Thanks to + Michael Hanselmann +- Added "full line failregex" patch. Thanks to Yaroslav + Halchenko. It will be possible to create stronger failregex + against log injection +- Fixed ipfw action script. Thanks to Nick Munger +- Removed date from logging message when using SYSLOG. Thanks + to Iain Lea +- Fixed "ignore IPs". Only the first value was taken into + account. Thanks to Adrien Clerc +- Moved socket to /var/run/fail2ban. +- Rewrote the communication server. +- Refactoring. Reduced number of files. +- Removed Python 2.4. Minimum required version is now Python + 2.3. +- New log rotation detection algorithm. +- Print monitored files in status. +- Create a PID file in /var/run/fail2ban/. Thanks to Julien + Perez. +- Fixed "Feb 29" bug. Thanks to James Andrewartha who pointed + this out. Thanks to Yaroslav Halchenko for the fix. +- "reload " reloads a single jail and the parameters in + fail2ban.conf. +- Added Mac OS/X startup script. Thanks to Bill Heaton. +- Absorbed some Debian patches. Thanks to Yaroslav Halchenko. +- Replaced "echo" with "printf" in actions. Fix #1839673 +- Replaced "reject" with "drop" in shorwall action. Fix + #1854875 +- Fixed Debian bug #456567, #468477, #462060, #461426 +- readline is now optional in fail2ban-client (not needed in + fail2ban-server). + ver. 0.8.1 (2007/08/14) - stable ---------- - Fixed vulnerability in sshd.conf. Thanks to Daniel B. Cid diff --git a/PKG-INFO b/PKG-INFO index d05c8004..74670ebd 100644 --- a/PKG-INFO +++ b/PKG-INFO @@ -1,10 +1,10 @@ Metadata-Version: 1.0 Name: fail2ban -Version: 0.8.1 +Version: 0.8.2 Summary: Ban IPs that make too many password failure -Home-page: http://fail2ban.sourceforge.net +Home-page: http://www.fail2ban.org Author: Cyril Jaquier -Author-email: lostcontrol@users.sourceforge.net +Author-email: cyril.jaquier@fail2ban.org License: GPL Description: Fail2Ban scans log files like /var/log/pwdfail or diff --git a/README b/README index 5cece798..d9e1e3f0 100644 --- a/README +++ b/README @@ -4,7 +4,7 @@ |_| \__,_|_|_/___|_.__/\__,_|_||_| ============================================================= -Fail2Ban (version 0.8.1) 2007/08/14 +Fail2Ban (version 0.8.2) 2008/03/06 ============================================================= Fail2Ban scans log files like /var/log/pwdfail and bans IP @@ -21,15 +21,15 @@ Installation: ------------- Required: - >=python-2.4 (http://www.python.org) + >=python-2.3 (http://www.python.org) Optional: >=gamin-0.0.21 (http://www.gnome.org/~veillard/gamin) To install, just do: -> tar xvfj fail2ban-0.8.1.tar.bz2 -> cd fail2ban-0.8.1 +> tar xvfj fail2ban-0.8.2.tar.bz2 +> cd fail2ban-0.8.2 > python setup.py install This will install Fail2Ban into /usr/share/fail2ban. The @@ -62,7 +62,7 @@ appreciate this program, you can contact me at: Website: http://www.fail2ban.org -Cyril Jaquier: +Cyril Jaquier: Thanks: ------- @@ -75,7 +75,8 @@ 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 +Delvit, Vaclav Misek, Adrien Clerc, Michael Hanselmann, +Vincent Deffontaines, Bill Heaton and many others. License: -------- diff --git a/TODO b/TODO index 8be435f4..29e7c37f 100644 --- a/TODO +++ b/TODO @@ -4,7 +4,7 @@ |_| \__,_|_|_/___|_.__/\__,_|_||_| ============================================================= -ToDo $Revision: 557 $ +ToDo $Revision: 653 $ ============================================================= Legend: @@ -15,9 +15,6 @@ Legend: - Removed relative imports -- Discuss where Fail2ban should be installed (/usr/share, - /usr/lib/python/site-packages/, etc) - - Cleanup fail2ban-client and fail2ban-server. Move code to server/ and client/ @@ -45,12 +42,8 @@ Legend: - Add gettext support (I18N) -- Fix the cPickle issue with Python 2.5 - - Multiline log reading -- Improve communication. (asyncore, asynchat??) - - Improve execution of action. Why does subprocess.call deadlock with multi-jails? diff --git a/client/beautifier.py b/client/beautifier.py index a73d5709..b109b333 100644 --- a/client/beautifier.py +++ b/client/beautifier.py @@ -16,11 +16,11 @@ # Author: Cyril Jaquier # -# $Revision: 547 $ +# $Revision: 644 $ __author__ = "Cyril Jaquier" -__version__ = "$Revision: 547 $" -__date__ = "$Date: 2007-02-12 00:21:56 +0100 (Mon, 12 Feb 2007) $" +__version__ = "$Revision: 644 $" +__date__ = "$Date: 2008-01-15 00:12:21 +0100 (Tue, 15 Jan 2008) $" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" @@ -72,9 +72,14 @@ class Beautifier: ipList = "" for ip in response[1][1][2][1]: ipList += ip + " " + # Creates file list. + fileList = "" + for f in response[0][1][2][1]: + fileList += f + " " # Display information msg = "Status for the jail: " + inC[1] + "\n" msg = msg + "|- " + response[0][0] + "\n" + msg = msg + "| |- " + response[0][1][2][0] + ":\t" + fileList + "\n" msg = msg + "| |- " + response[0][1][0][0] + ":\t" + `response[0][1][0][1]` + "\n" msg = msg + "| `- " + response[0][1][1][0] + ":\t" + `response[0][1][1][1]` + "\n" msg = msg + "`- " + response[1][0] + "\n" diff --git a/client/configparserinc.py b/client/configparserinc.py new file mode 100644 index 00000000..59253c13 --- /dev/null +++ b/client/configparserinc.py @@ -0,0 +1,108 @@ +# This file is part of Fail2Ban. +# +# Fail2Ban is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Fail2Ban is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Fail2Ban; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +# Author: Yaroslav Halchenko +# Modified: Cyril Jaquier +# $Revision: 656 $ + +__author__ = 'Yaroslav Halhenko' +__revision__ = '$Revision: $' +__date__ = '$Date: $' +__copyright__ = 'Copyright (c) 2007 Yaroslav Halchenko' +__license__ = 'GPL' + +import logging, os +from ConfigParser import SafeConfigParser + +# Gets the instance of the logger. +logSys = logging.getLogger("fail2ban.client.config") + +class SafeConfigParserWithIncludes(SafeConfigParser): + """ + Class adds functionality to SafeConfigParser to handle included + other configuration files (or may be urls, whatever in the future) + + File should have section [includes] and only 2 options implemented + are 'files_before' and 'files_after' where files are listed 1 per + line. + + Example: + +[INCLUDES] +before = 1.conf + 3.conf + +after = 1.conf + + It is a simple implementation, so just basic care is taken about + recursion. Includes preserve right order, ie new files are + inserted to the list of read configs before original, and their + includes correspondingly so the list should follow the leaves of + the tree. + + I wasn't sure what would be the right way to implement generic (aka c++ + template) so we could base at any *configparser class... so I will + leave it for the future + + """ + + SECTION_NAME = "INCLUDES" + + #@staticmethod + def getIncludes(resource, seen = []): + """ + Given 1 config resource returns list of included files + (recursively) with the original one as well + Simple loops are taken care about + """ + + # Use a short class name ;) + SCPWI = SafeConfigParserWithIncludes + + parser = SafeConfigParser() + parser.read(resource) + + resourceDir = os.path.dirname(resource) + + newFiles = [ ('before', []), ('after', []) ] + if SCPWI.SECTION_NAME in parser.sections(): + for option_name, option_list in newFiles: + if option_name in parser.options(SCPWI.SECTION_NAME): + newResources = parser.get(SCPWI.SECTION_NAME, option_name) + for newResource in newResources.split('\n'): + if os.path.isabs(newResource): + r = newResource + else: + r = "%s/%s" % (resourceDir, newResource) + if r in seen: + continue + s = seen + [resource] + option_list += SCPWI.getIncludes(r, s) + # combine lists + return newFiles[0][1] + [resource] + newFiles[1][1] + #print "Includes list for " + resource + " is " + `resources` + getIncludes = staticmethod(getIncludes) + + + def read(self, filenames): + fileNamesFull = [] + if not isinstance(filenames, list): + filenames = [ filenames ] + for filename in filenames: + fileNamesFull += SafeConfigParserWithIncludes.getIncludes(filename) + logSys.debug("Reading files: %s" % fileNamesFull) + return SafeConfigParser.read(self, fileNamesFull) + diff --git a/client/configreader.py b/client/configreader.py index c2d544d6..ed672dc4 100644 --- a/client/configreader.py +++ b/client/configreader.py @@ -15,38 +15,40 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Author: Cyril Jaquier -# -# $Revision: 458 $ +# Modified by: Yaroslav Halchenko (SafeConfigParserWithIncludes) +# $Revision: 656 $ __author__ = "Cyril Jaquier" -__version__ = "$Revision: 458 $" -__date__ = "$Date: 2006-11-12 15:52:36 +0100 (Sun, 12 Nov 2006) $" +__version__ = "$Revision: 656 $" +__date__ = "$Date: 2008-03-04 01:17:56 +0100 (Tue, 04 Mar 2008) $" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" import logging, os -from ConfigParser import SafeConfigParser +from configparserinc import SafeConfigParserWithIncludes from ConfigParser import NoOptionError, NoSectionError # Gets the instance of the logger. logSys = logging.getLogger("fail2ban.client.config") -class ConfigReader(SafeConfigParser): +class ConfigReader(SafeConfigParserWithIncludes): BASE_DIRECTORY = "/etc/fail2ban/" def __init__(self): - SafeConfigParser.__init__(self) + SafeConfigParserWithIncludes.__init__(self) self.__opts = None - @staticmethod + #@staticmethod def setBaseDir(folderName): path = folderName.rstrip('/') ConfigReader.BASE_DIRECTORY = path + '/' + setBaseDir = staticmethod(setBaseDir) - @staticmethod + #@staticmethod def getBaseDir(): return ConfigReader.BASE_DIRECTORY + getBaseDir = staticmethod(getBaseDir) def read(self, filename): basename = ConfigReader.BASE_DIRECTORY + filename @@ -54,7 +56,7 @@ class ConfigReader(SafeConfigParser): bConf = basename + ".conf" bLocal = basename + ".local" if os.path.exists(bConf) or os.path.exists(bLocal): - SafeConfigParser.read(self, [bConf, bLocal]) + SafeConfigParserWithIncludes.read(self, [bConf, bLocal]) return True else: logSys.error(bConf + " and " + bLocal + " do not exist") diff --git a/client/configurator.py b/client/configurator.py index f37161a9..f1afee04 100644 --- a/client/configurator.py +++ b/client/configurator.py @@ -16,11 +16,11 @@ # Author: Cyril Jaquier # -# $Revision: 518 $ +# $Revision: 655 $ __author__ = "Cyril Jaquier" -__version__ = "$Revision: 518 $" -__date__ = "$Date: 2007-01-08 22:15:47 +0100 (Mon, 08 Jan 2007) $" +__version__ = "$Revision: 655 $" +__date__ = "$Date: 2008-03-04 01:13:39 +0100 (Tue, 04 Mar 2008) $" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" @@ -40,13 +40,15 @@ class Configurator: self.__fail2ban = Fail2banReader() self.__jails = JailsReader() - @staticmethod + #@staticmethod def setBaseDir(folderName): ConfigReader.setBaseDir(folderName) + setBaseDir = staticmethod(setBaseDir) - @staticmethod + #@staticmethod def getBaseDir(): return ConfigReader.getBaseDir() + getBaseDir = staticmethod(getBaseDir) def readEarly(self): self.__fail2ban.read() @@ -54,13 +56,13 @@ class Configurator: def readAll(self): self.readEarly() self.__jails.read() - + def getEarlyOptions(self): return self.__fail2ban.getEarlyOptions() - - def getAllOptions(self): + + def getOptions(self, jail = None): self.__fail2ban.getOptions() - return self.__jails.getOptions() + return self.__jails.getOptions(jail) def convertToProtocol(self): self.__streams["general"] = self.__fail2ban.convert() diff --git a/client/csocket.py b/client/csocket.py index 3bfa9e81..b485bfc5 100644 --- a/client/csocket.py +++ b/client/csocket.py @@ -16,11 +16,11 @@ # Author: Cyril Jaquier # -# $Revision: 459 $ +# $Revision: 635 $ __author__ = "Cyril Jaquier" -__version__ = "$Revision: 459 $" -__date__ = "$Date: 2006-11-12 22:55:57 +0100 (Sun, 12 Nov 2006) $" +__version__ = "$Revision: 635 $" +__date__ = "$Date: 2007-12-16 22:38:04 +0100 (Sun, 16 Dec 2007) $" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" @@ -32,7 +32,7 @@ class CSocket: END_STRING = "" - def __init__(self, sock = "/tmp/fail2ban.sock"): + def __init__(self, sock = "/var/run/fail2ban/fail2ban.sock"): # Create an INET, STREAMing socket #self.csock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.__csock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) @@ -47,7 +47,7 @@ class CSocket: self.__csock.close() return ret - @staticmethod + #@staticmethod def receive(sock): msg = '' while msg.rfind(CSocket.END_STRING) == -1: @@ -56,3 +56,4 @@ class CSocket: raise RuntimeError, "socket connection broken" msg = msg + chunk return loads(msg) + receive = staticmethod(receive) diff --git a/client/jailreader.py b/client/jailreader.py index 0730b933..af245ae1 100644 --- a/client/jailreader.py +++ b/client/jailreader.py @@ -16,11 +16,11 @@ # Author: Cyril Jaquier # -# $Revision: 509 $ +# $Revision: 659 $ __author__ = "Cyril Jaquier" -__version__ = "$Revision: 509 $" -__date__ = "$Date: 2007-01-04 12:58:58 +0100 (Thu, 04 Jan 2007) $" +__version__ = "$Revision: 659 $" +__date__ = "$Date: 2008-03-05 00:09:30 +0100 (Wed, 05 Mar 2008) $" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" @@ -90,7 +90,7 @@ class JailReader(ConfigReader): self.__actions.append(action) else: raise AttributeError("Unable to read action") - except AttributeError, e: + except Exception, e: logSys.error("Error in action definition " + act) logSys.debug(e) return False @@ -129,7 +129,7 @@ class JailReader(ConfigReader): stream.insert(0, ["add", self.__name, backend]) return stream - @staticmethod + #@staticmethod def splitAction(action): m = JailReader.actionCRE.match(action) d = dict() @@ -165,3 +165,4 @@ class JailReader(ConfigReader): except IndexError: logSys.error("Invalid argument %s in '%s'" % (p, m.group(2))) return [m.group(1), d] + splitAction = staticmethod(splitAction) diff --git a/client/jailsreader.py b/client/jailsreader.py index 9acf33ab..f6e49136 100644 --- a/client/jailsreader.py +++ b/client/jailsreader.py @@ -16,11 +16,11 @@ # Author: Cyril Jaquier # -# $Revision: 518 $ +# $Revision: 655 $ __author__ = "Cyril Jaquier" -__version__ = "$Revision: 518 $" -__date__ = "$Date: 2007-01-08 22:15:47 +0100 (Mon, 08 Jan 2007) $" +__version__ = "$Revision: 655 $" +__date__ = "$Date: 2008-03-04 01:13:39 +0100 (Tue, 04 Mar 2008) $" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" @@ -40,12 +40,13 @@ class JailsReader(ConfigReader): def read(self): ConfigReader.read(self, "jail") - def getOptions(self): + def getOptions(self, section = None): opts = [] self.__opts = ConfigReader.getOptions(self, "Definition", opts) - for sec in self.sections(): - jail = JailReader(sec) + if section: + # Get the options of a specific jail. + jail = JailReader(section) jail.read() ret = jail.getOptions() if ret: @@ -53,8 +54,21 @@ class JailsReader(ConfigReader): # We only add enabled jails self.__jails.append(jail) else: - logSys.error("Errors in jail '" + sec + "'. Skipping...") + logSys.error("Errors in jail '%s'. Skipping..." % section) return False + else: + # Get the options of all jails. + for sec in self.sections(): + jail = JailReader(sec) + jail.read() + ret = jail.getOptions() + if ret: + if jail.isEnabled(): + # We only add enabled jails + self.__jails.append(jail) + else: + logSys.error("Errors in jail '" + sec + "'. Skipping...") + return False return True def convert(self): diff --git a/common/protocol.py b/common/protocol.py index 9a2dd25d..2b19964b 100644 --- a/common/protocol.py +++ b/common/protocol.py @@ -16,11 +16,11 @@ # Author: Cyril Jaquier # -# $Revision: 529 $ +# $Revision: 662 $ __author__ = "Cyril Jaquier" -__version__ = "$Revision: 529 $" -__date__ = "$Date: 2007-01-29 21:27:51 +0100 (Mon, 29 Jan 2007) $" +__version__ = "$Revision: 662 $" +__date__ = "$Date: 2008-03-05 00:41:58 +0100 (Wed, 05 Mar 2008) $" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" @@ -33,6 +33,7 @@ protocol = [ ['', "BASIC", ""], ["start", "starts the server and the jails"], ["reload", "reloads the configuration"], +["reload ", "reloads the jail "], ["stop", "stops all jails and terminate the server"], ["status", "gets the current status of the server"], ["ping", "tests if the server is alive"], @@ -51,9 +52,7 @@ protocol = [ ["set addignoreip ", "adds to the ignore list of "], ["set delignoreip ", "removes from the ignore list of "], ["set addlogpath ", "adds to the monitoring list of "], -["set dellogpath ", "removes to the monitoring list of "], -["set timeregex ", "sets the regular expression to match the date format for . This will disable the autodetection feature."], -["set timepattern ", "sets the pattern to match the date format for . This will disable the autodetection feature."], +["set dellogpath ", "removes to the monitoring list of "], ["set addfailregex ", "adds the regular expression which must match failures for "], ["set delfailregex ", "removes the regular expression at for failregex"], ["set addignoreregex ", "adds the regular expression which should match pattern to exclude for "], diff --git a/common/version.py b/common/version.py index 8b2a3d84..3dd7f9bf 100644 --- a/common/version.py +++ b/common/version.py @@ -16,12 +16,12 @@ # Author: Cyril Jaquier # -# $Revision: 614 $ +# $Revision: 673 $ __author__ = "Cyril Jaquier" -__version__ = "$Revision: 614 $" -__date__ = "$Date: 2007-08-14 23:39:15 +0200 (Tue, 14 Aug 2007) $" +__version__ = "$Revision: 673 $" +__date__ = "$Date: 2008-03-06 00:19:45 +0100 (Thu, 06 Mar 2008) $" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" -version = "0.8.1" +version = "0.8.2" diff --git a/config/action.d/hostsdeny.conf b/config/action.d/hostsdeny.conf index 72601dc3..9db90d64 100644 --- a/config/action.d/hostsdeny.conf +++ b/config/action.d/hostsdeny.conf @@ -2,7 +2,7 @@ # # Author: Cyril Jaquier # -# $Revision: 554 $ +# $Revision: 660 $ # [Definition] @@ -13,7 +13,7 @@ # actionstart = -# Option: actionend +# Option: actionstop # Notes.: command executed once at the end of Fail2Ban # Values: CMD # @@ -34,7 +34,7 @@ actioncheck = # Values: CMD # actionban = IP= && - echo "ALL: $IP" >> + printf %%b "ALL: $IP\n" >> # Option: actionunban # Notes.: command executed when unbanning an IP. Take care that the diff --git a/config/action.d/ipfw.conf b/config/action.d/ipfw.conf index 64fa6597..a81bb3f4 100644 --- a/config/action.d/ipfw.conf +++ b/config/action.d/ipfw.conf @@ -3,7 +3,7 @@ # Author: Nick Munger # Modified by: Cyril Jaquier # -# $Revision: 510 $ +# $Revision: 658 $ # [Definition] @@ -15,7 +15,7 @@ actionstart = -# Option: actionend +# Option: actionstop # Notes.: command executed once at the end of Fail2Ban # Values: CMD # @@ -37,7 +37,7 @@ actioncheck = #