Merge branch 'debian' into build

* debian: (56 commits)
  Imported upstream version 0.8.2
  debian/watch: switched to git-import-orig
  - Tag for 0.8.2
  - Updated for 0.8.2
  - Updated e-mail
  - Changed homepage and e-mail
  - Updated copyright.
  - readline is now optional in fail2ban-client (not needed in fail2ban-server).
  - Added svn:keywords
  - Fixed Debian bug #461426
  - Fixed Debian bug #462060
  - Fixed Debian bug #468477
  - Fixed Debian bug #456567
  - Added revision.
  - Added "reload <JAIL>"
  - Replaced "reject" with "drop" in shorwall action. Fix #1854875
  - Replaced "echo" with "printf" in actions. Fix #1839673
  - Catch Exception instead of AttributeError.
  - Absorbed some Debian patches. Thanks to Yaroslav Halchenko.
  - Updated.
  ...

Conflicts:

	config/fail2ban.conf
	config/filter.d/proftpd.conf
	config/filter.d/sshd.conf
	man/fail2ban-client.1
	man/fail2ban-server.1
	server/datestrptime.py
	server/server.py
debian-releases/squeeze
Yaroslav Halchenko 2008-03-05 22:30:10 -05:00
commit 9ab6db30c7
74 changed files with 1358 additions and 983 deletions

View File

@ -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 <jail>" 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 ver. 0.8.1 (2007/08/14) - stable
---------- ----------
- Fixed vulnerability in sshd.conf. Thanks to Daniel B. Cid - Fixed vulnerability in sshd.conf. Thanks to Daniel B. Cid

View File

@ -1,10 +1,10 @@
Metadata-Version: 1.0 Metadata-Version: 1.0
Name: fail2ban Name: fail2ban
Version: 0.8.1 Version: 0.8.2
Summary: Ban IPs that make too many password failure 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: Cyril Jaquier
Author-email: lostcontrol@users.sourceforge.net Author-email: cyril.jaquier@fail2ban.org
License: GPL License: GPL
Description: Description:
Fail2Ban scans log files like /var/log/pwdfail or Fail2Ban scans log files like /var/log/pwdfail or

13
README
View File

@ -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 Fail2Ban scans log files like /var/log/pwdfail and bans IP
@ -21,15 +21,15 @@ Installation:
------------- -------------
Required: Required:
>=python-2.4 (http://www.python.org) >=python-2.3 (http://www.python.org)
Optional: Optional:
>=gamin-0.0.21 (http://www.gnome.org/~veillard/gamin) >=gamin-0.0.21 (http://www.gnome.org/~veillard/gamin)
To install, just do: To install, just do:
> tar xvfj fail2ban-0.8.1.tar.bz2 > tar xvfj fail2ban-0.8.2.tar.bz2
> cd fail2ban-0.8.1 > cd fail2ban-0.8.2
> python setup.py install > python setup.py install
This will install Fail2Ban into /usr/share/fail2ban. The 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 Website: http://www.fail2ban.org
Cyril Jaquier: <lostcontrol@users.sourceforge.net> Cyril Jaquier: <cyril.jaquier@fail2ban.org>
Thanks: Thanks:
------- -------
@ -75,7 +75,8 @@ Nick Munger, Christoph Haas, Justin Shore, Joël Bertrand,
René Berber, mEDI, Axel Thimm, Eric Gerbier, Christian Rauch, René Berber, mEDI, Axel Thimm, Eric Gerbier, Christian Rauch,
Michael C. Haller, Jonathan Underwood, Hanno 'Rince' Wagner, Michael C. Haller, Jonathan Underwood, Hanno 'Rince' Wagner,
Daniel B. Cid, David Nutter, Raphaël Marichez, Guillaume 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: License:
-------- --------

9
TODO
View File

@ -4,7 +4,7 @@
|_| \__,_|_|_/___|_.__/\__,_|_||_| |_| \__,_|_|_/___|_.__/\__,_|_||_|
============================================================= =============================================================
ToDo $Revision: 557 $ ToDo $Revision: 653 $
============================================================= =============================================================
Legend: Legend:
@ -15,9 +15,6 @@ Legend:
- Removed relative imports - 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 - Cleanup fail2ban-client and fail2ban-server. Move code to
server/ and client/ server/ and client/
@ -45,12 +42,8 @@ Legend:
- Add gettext support (I18N) - Add gettext support (I18N)
- Fix the cPickle issue with Python 2.5
- Multiline log reading - Multiline log reading
- Improve communication. (asyncore, asynchat??)
- Improve execution of action. Why does subprocess.call - Improve execution of action. Why does subprocess.call
deadlock with multi-jails? deadlock with multi-jails?

View File

@ -16,11 +16,11 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 547 $ # $Revision: 644 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 547 $" __version__ = "$Revision: 644 $"
__date__ = "$Date: 2007-02-12 00:21:56 +0100 (Mon, 12 Feb 2007) $" __date__ = "$Date: 2008-01-15 00:12:21 +0100 (Tue, 15 Jan 2008) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
@ -72,9 +72,14 @@ class Beautifier:
ipList = "" ipList = ""
for ip in response[1][1][2][1]: for ip in response[1][1][2][1]:
ipList += ip + " " ipList += ip + " "
# Creates file list.
fileList = ""
for f in response[0][1][2][1]:
fileList += f + " "
# Display information # Display information
msg = "Status for the jail: " + inC[1] + "\n" msg = "Status for the jail: " + inC[1] + "\n"
msg = msg + "|- " + response[0][0] + "\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][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[0][1][1][0] + ":\t" + `response[0][1][1][1]` + "\n"
msg = msg + "`- " + response[1][0] + "\n" msg = msg + "`- " + response[1][0] + "\n"

108
client/configparserinc.py Normal file
View File

@ -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)

View File

@ -15,38 +15,40 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# Author: Cyril Jaquier # Author: Cyril Jaquier
# # Modified by: Yaroslav Halchenko (SafeConfigParserWithIncludes)
# $Revision: 458 $ # $Revision: 656 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 458 $" __version__ = "$Revision: 656 $"
__date__ = "$Date: 2006-11-12 15:52:36 +0100 (Sun, 12 Nov 2006) $" __date__ = "$Date: 2008-03-04 01:17:56 +0100 (Tue, 04 Mar 2008) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
import logging, os import logging, os
from ConfigParser import SafeConfigParser from configparserinc import SafeConfigParserWithIncludes
from ConfigParser import NoOptionError, NoSectionError from ConfigParser import NoOptionError, NoSectionError
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.client.config") logSys = logging.getLogger("fail2ban.client.config")
class ConfigReader(SafeConfigParser): class ConfigReader(SafeConfigParserWithIncludes):
BASE_DIRECTORY = "/etc/fail2ban/" BASE_DIRECTORY = "/etc/fail2ban/"
def __init__(self): def __init__(self):
SafeConfigParser.__init__(self) SafeConfigParserWithIncludes.__init__(self)
self.__opts = None self.__opts = None
@staticmethod #@staticmethod
def setBaseDir(folderName): def setBaseDir(folderName):
path = folderName.rstrip('/') path = folderName.rstrip('/')
ConfigReader.BASE_DIRECTORY = path + '/' ConfigReader.BASE_DIRECTORY = path + '/'
setBaseDir = staticmethod(setBaseDir)
@staticmethod #@staticmethod
def getBaseDir(): def getBaseDir():
return ConfigReader.BASE_DIRECTORY return ConfigReader.BASE_DIRECTORY
getBaseDir = staticmethod(getBaseDir)
def read(self, filename): def read(self, filename):
basename = ConfigReader.BASE_DIRECTORY + filename basename = ConfigReader.BASE_DIRECTORY + filename
@ -54,7 +56,7 @@ class ConfigReader(SafeConfigParser):
bConf = basename + ".conf" bConf = basename + ".conf"
bLocal = basename + ".local" bLocal = basename + ".local"
if os.path.exists(bConf) or os.path.exists(bLocal): if os.path.exists(bConf) or os.path.exists(bLocal):
SafeConfigParser.read(self, [bConf, bLocal]) SafeConfigParserWithIncludes.read(self, [bConf, bLocal])
return True return True
else: else:
logSys.error(bConf + " and " + bLocal + " do not exist") logSys.error(bConf + " and " + bLocal + " do not exist")

View File

@ -16,11 +16,11 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 518 $ # $Revision: 655 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 518 $" __version__ = "$Revision: 655 $"
__date__ = "$Date: 2007-01-08 22:15:47 +0100 (Mon, 08 Jan 2007) $" __date__ = "$Date: 2008-03-04 01:13:39 +0100 (Tue, 04 Mar 2008) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
@ -40,13 +40,15 @@ class Configurator:
self.__fail2ban = Fail2banReader() self.__fail2ban = Fail2banReader()
self.__jails = JailsReader() self.__jails = JailsReader()
@staticmethod #@staticmethod
def setBaseDir(folderName): def setBaseDir(folderName):
ConfigReader.setBaseDir(folderName) ConfigReader.setBaseDir(folderName)
setBaseDir = staticmethod(setBaseDir)
@staticmethod #@staticmethod
def getBaseDir(): def getBaseDir():
return ConfigReader.getBaseDir() return ConfigReader.getBaseDir()
getBaseDir = staticmethod(getBaseDir)
def readEarly(self): def readEarly(self):
self.__fail2ban.read() self.__fail2ban.read()
@ -54,13 +56,13 @@ class Configurator:
def readAll(self): def readAll(self):
self.readEarly() self.readEarly()
self.__jails.read() self.__jails.read()
def getEarlyOptions(self): def getEarlyOptions(self):
return self.__fail2ban.getEarlyOptions() return self.__fail2ban.getEarlyOptions()
def getAllOptions(self): def getOptions(self, jail = None):
self.__fail2ban.getOptions() self.__fail2ban.getOptions()
return self.__jails.getOptions() return self.__jails.getOptions(jail)
def convertToProtocol(self): def convertToProtocol(self):
self.__streams["general"] = self.__fail2ban.convert() self.__streams["general"] = self.__fail2ban.convert()

View File

@ -16,11 +16,11 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 459 $ # $Revision: 635 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 459 $" __version__ = "$Revision: 635 $"
__date__ = "$Date: 2006-11-12 22:55:57 +0100 (Sun, 12 Nov 2006) $" __date__ = "$Date: 2007-12-16 22:38:04 +0100 (Sun, 16 Dec 2007) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
@ -32,7 +32,7 @@ class CSocket:
END_STRING = "<F2B_END_COMMAND>" END_STRING = "<F2B_END_COMMAND>"
def __init__(self, sock = "/tmp/fail2ban.sock"): def __init__(self, sock = "/var/run/fail2ban/fail2ban.sock"):
# Create an INET, STREAMing socket # Create an INET, STREAMing socket
#self.csock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #self.csock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.__csock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) self.__csock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
@ -47,7 +47,7 @@ class CSocket:
self.__csock.close() self.__csock.close()
return ret return ret
@staticmethod #@staticmethod
def receive(sock): def receive(sock):
msg = '' msg = ''
while msg.rfind(CSocket.END_STRING) == -1: while msg.rfind(CSocket.END_STRING) == -1:
@ -56,3 +56,4 @@ class CSocket:
raise RuntimeError, "socket connection broken" raise RuntimeError, "socket connection broken"
msg = msg + chunk msg = msg + chunk
return loads(msg) return loads(msg)
receive = staticmethod(receive)

View File

@ -16,11 +16,11 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 509 $ # $Revision: 659 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 509 $" __version__ = "$Revision: 659 $"
__date__ = "$Date: 2007-01-04 12:58:58 +0100 (Thu, 04 Jan 2007) $" __date__ = "$Date: 2008-03-05 00:09:30 +0100 (Wed, 05 Mar 2008) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
@ -90,7 +90,7 @@ class JailReader(ConfigReader):
self.__actions.append(action) self.__actions.append(action)
else: else:
raise AttributeError("Unable to read action") raise AttributeError("Unable to read action")
except AttributeError, e: except Exception, e:
logSys.error("Error in action definition " + act) logSys.error("Error in action definition " + act)
logSys.debug(e) logSys.debug(e)
return False return False
@ -129,7 +129,7 @@ class JailReader(ConfigReader):
stream.insert(0, ["add", self.__name, backend]) stream.insert(0, ["add", self.__name, backend])
return stream return stream
@staticmethod #@staticmethod
def splitAction(action): def splitAction(action):
m = JailReader.actionCRE.match(action) m = JailReader.actionCRE.match(action)
d = dict() d = dict()
@ -165,3 +165,4 @@ class JailReader(ConfigReader):
except IndexError: except IndexError:
logSys.error("Invalid argument %s in '%s'" % (p, m.group(2))) logSys.error("Invalid argument %s in '%s'" % (p, m.group(2)))
return [m.group(1), d] return [m.group(1), d]
splitAction = staticmethod(splitAction)

View File

@ -16,11 +16,11 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 518 $ # $Revision: 655 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 518 $" __version__ = "$Revision: 655 $"
__date__ = "$Date: 2007-01-08 22:15:47 +0100 (Mon, 08 Jan 2007) $" __date__ = "$Date: 2008-03-04 01:13:39 +0100 (Tue, 04 Mar 2008) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
@ -40,12 +40,13 @@ class JailsReader(ConfigReader):
def read(self): def read(self):
ConfigReader.read(self, "jail") ConfigReader.read(self, "jail")
def getOptions(self): def getOptions(self, section = None):
opts = [] opts = []
self.__opts = ConfigReader.getOptions(self, "Definition", opts) self.__opts = ConfigReader.getOptions(self, "Definition", opts)
for sec in self.sections(): if section:
jail = JailReader(sec) # Get the options of a specific jail.
jail = JailReader(section)
jail.read() jail.read()
ret = jail.getOptions() ret = jail.getOptions()
if ret: if ret:
@ -53,8 +54,21 @@ class JailsReader(ConfigReader):
# We only add enabled jails # We only add enabled jails
self.__jails.append(jail) self.__jails.append(jail)
else: else:
logSys.error("Errors in jail '" + sec + "'. Skipping...") logSys.error("Errors in jail '%s'. Skipping..." % section)
return False 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 return True
def convert(self): def convert(self):

View File

@ -16,11 +16,11 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 529 $ # $Revision: 662 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 529 $" __version__ = "$Revision: 662 $"
__date__ = "$Date: 2007-01-29 21:27:51 +0100 (Mon, 29 Jan 2007) $" __date__ = "$Date: 2008-03-05 00:41:58 +0100 (Wed, 05 Mar 2008) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
@ -33,6 +33,7 @@ protocol = [
['', "BASIC", ""], ['', "BASIC", ""],
["start", "starts the server and the jails"], ["start", "starts the server and the jails"],
["reload", "reloads the configuration"], ["reload", "reloads the configuration"],
["reload <JAIL>", "reloads the jail <JAIL>"],
["stop", "stops all jails and terminate the server"], ["stop", "stops all jails and terminate the server"],
["status", "gets the current status of the server"], ["status", "gets the current status of the server"],
["ping", "tests if the server is alive"], ["ping", "tests if the server is alive"],
@ -51,9 +52,7 @@ protocol = [
["set <JAIL> addignoreip <IP>", "adds <IP> to the ignore list of <JAIL>"], ["set <JAIL> addignoreip <IP>", "adds <IP> to the ignore list of <JAIL>"],
["set <JAIL> delignoreip <IP>", "removes <IP> from the ignore list of <JAIL>"], ["set <JAIL> delignoreip <IP>", "removes <IP> from the ignore list of <JAIL>"],
["set <JAIL> addlogpath <FILE>", "adds <FILE> to the monitoring list of <JAIL>"], ["set <JAIL> addlogpath <FILE>", "adds <FILE> to the monitoring list of <JAIL>"],
["set <JAIL> dellogpath <FILE>", "removes <FILE> to the monitoring list of <JAIL>"], ["set <JAIL> dellogpath <FILE>", "removes <FILE> to the monitoring list of <JAIL>"],
["set <JAIL> timeregex <REGEX>", "sets the regular expression <REGEX> to match the date format for <JAIL>. This will disable the autodetection feature."],
["set <JAIL> timepattern <PATTERN>", "sets the pattern <PATTERN> to match the date format for <JAIL>. This will disable the autodetection feature."],
["set <JAIL> addfailregex <REGEX>", "adds the regular expression <REGEX> which must match failures for <JAIL>"], ["set <JAIL> addfailregex <REGEX>", "adds the regular expression <REGEX> which must match failures for <JAIL>"],
["set <JAIL> delfailregex <INDEX>", "removes the regular expression at <INDEX> for failregex"], ["set <JAIL> delfailregex <INDEX>", "removes the regular expression at <INDEX> for failregex"],
["set <JAIL> addignoreregex <REGEX>", "adds the regular expression <REGEX> which should match pattern to exclude for <JAIL>"], ["set <JAIL> addignoreregex <REGEX>", "adds the regular expression <REGEX> which should match pattern to exclude for <JAIL>"],

View File

@ -16,12 +16,12 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 614 $ # $Revision: 673 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 614 $" __version__ = "$Revision: 673 $"
__date__ = "$Date: 2007-08-14 23:39:15 +0200 (Tue, 14 Aug 2007) $" __date__ = "$Date: 2008-03-06 00:19:45 +0100 (Thu, 06 Mar 2008) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
version = "0.8.1" version = "0.8.2"

View File

@ -2,7 +2,7 @@
# #
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 554 $ # $Revision: 660 $
# #
[Definition] [Definition]
@ -13,7 +13,7 @@
# #
actionstart = actionstart =
# Option: actionend # Option: actionstop
# Notes.: command executed once at the end of Fail2Ban # Notes.: command executed once at the end of Fail2Ban
# Values: CMD # Values: CMD
# #
@ -34,7 +34,7 @@ actioncheck =
# Values: CMD # Values: CMD
# #
actionban = IP=<ip> && actionban = IP=<ip> &&
echo "ALL: $IP" >> <file> printf %%b "ALL: $IP\n" >> <file>
# Option: actionunban # Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the # Notes.: command executed when unbanning an IP. Take care that the

View File

@ -3,7 +3,7 @@
# Author: Nick Munger # Author: Nick Munger
# Modified by: Cyril Jaquier # Modified by: Cyril Jaquier
# #
# $Revision: 510 $ # $Revision: 658 $
# #
[Definition] [Definition]
@ -15,7 +15,7 @@
actionstart = actionstart =
# Option: actionend # Option: actionstop
# Notes.: command executed once at the end of Fail2Ban # Notes.: command executed once at the end of Fail2Ban
# Values: CMD # Values: CMD
# #
@ -37,7 +37,7 @@ actioncheck =
# <time> unix timestamp of the ban time # <time> unix timestamp of the ban time
# Values: CMD # Values: CMD
# #
actionban = ipaction add deny tcp from <ip> to <localhost> <port> actionban = ipfw add deny tcp from <ip> to <localhost> <port>
# Option: actionunban # Option: actionunban
@ -48,7 +48,7 @@ actionban = ipaction add deny tcp from <ip> to <localhost> <port>
# <time> unix timestamp of the ban time # <time> unix timestamp of the ban time
# Values: CMD # Values: CMD
# #
actionunban = ipaction delete `ipfw list | grep -i <ip> | awk '{print $1;}'` actionunban = ipfw delete `ipfw list | grep -i <ip> | awk '{print $1;}'`
[Init] [Init]

View File

@ -4,7 +4,7 @@
# Modified: Yaroslav O. Halchenko <debian@onerussian.com> # Modified: Yaroslav O. Halchenko <debian@onerussian.com>
# made active on all ports from original iptables.conf # made active on all ports from original iptables.conf
# #
# $Revision: 606 $ # $Revision: 658 $
# #
[Definition] [Definition]
@ -17,7 +17,7 @@ actionstart = iptables -N fail2ban-<name>
iptables -A fail2ban-<name> -j RETURN iptables -A fail2ban-<name> -j RETURN
iptables -I INPUT -p <protocol> -j fail2ban-<name> iptables -I INPUT -p <protocol> -j fail2ban-<name>
# Option: actionend # Option: actionstop
# Notes.: command executed once at the end of Fail2Ban # Notes.: command executed once at the end of Fail2Ban
# Values: CMD # Values: CMD
# #

View File

@ -0,0 +1,78 @@
# Fail2Ban configuration file
#
# Author: Guido Bozzetto
# Modified: Cyril Jaquier
#
# make "fail2ban-<name>" chain to match drop IP
# make "fail2ban-<name>-log" chain to log and drop
# insert a jump to fail2ban-<name> from -I INPUT if proto/port match
#
# $Revision: 668 $
#
[Definition]
# Option: actionstart
# Notes.: command executed once at the start of Fail2Ban.
# Values: CMD
#
actionstart = iptables -N fail2ban-<name>
iptables -A fail2ban-<name> -j RETURN
iptables -I INPUT 1 -p <protocol> -m multiport --dports <port> -j fail2ban-<name>
iptables -N fail2ban-<name>-log
iptables -I fail2ban-<name>-log -j LOG --log-prefix "$(expr fail2ban-<name> : '\(.\{1,23\}\)'):DROP " --log-level warning -m limit --limit 6/m --limit-burst 2
iptables -A fail2ban-<name>-log -j DROP
# Option: actionstop
# Notes.: command executed once at the end of Fail2Ban
# Values: CMD
#
actionstop = iptables -D INPUT -p <protocol> -m multiport --dports <port> -j fail2ban-<name>
iptables -F fail2ban-<name>
iptables -F fail2ban-<name>-log
iptables -X fail2ban-<name>
iptables -X fail2ban-<name>-log
# Option: actioncheck
# Notes.: command executed once before each actionban command
# Values: CMD
#
actioncheck = iptables -n -L fail2ban-<name>-log >/dev/null
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Values: CMD
#
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j fail2ban-<name>-log
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Values: CMD
#
actionunban = iptables -D fail2ban-<name> -s <ip> -j fail2ban-<name>-log
[Init]
# Defaut name of the chain
#
name = default
# Option: port
# Notes.: specifies port to monitor
# Values: [ NUM | STRING ] Default:
#
port = ssh
# Option: protocol
# Notes.: internally used by config reader for interpolations.
# Values: [ tcp | udp | icmp | all ] Default: tcp
#
protocol = tcp

View File

@ -2,7 +2,7 @@
# #
# Author: Cyril Jaquier # Author: Cyril Jaquier
# Modified by Yaroslav Halchenko for multiport banning # Modified by Yaroslav Halchenko for multiport banning
# $Revision: 520 $ # $Revision: 658 $
# #
[Definition] [Definition]
@ -15,7 +15,7 @@ actionstart = iptables -N fail2ban-<name>
iptables -A fail2ban-<name> -j RETURN iptables -A fail2ban-<name> -j RETURN
iptables -I INPUT -p <protocol> -m multiport --dports <port> -j fail2ban-<name> iptables -I INPUT -p <protocol> -m multiport --dports <port> -j fail2ban-<name>
# Option: actionend # Option: actionstop
# Notes.: command executed once at the end of Fail2Ban # Notes.: command executed once at the end of Fail2Ban
# Values: CMD # Values: CMD
# #

View File

@ -4,7 +4,7 @@
# Copied from iptables.conf and modified by Yaroslav Halchenko # Copied from iptables.conf and modified by Yaroslav Halchenko
# to fullfill the needs of bugreporter dbts#350746. # to fullfill the needs of bugreporter dbts#350746.
# #
# $Revision: 520 $ # $Revision: 658 $
# #
[Definition] [Definition]
@ -17,7 +17,7 @@ actionstart = iptables -N fail2ban-<name>
iptables -A fail2ban-<name> -j RETURN iptables -A fail2ban-<name> -j RETURN
iptables -I INPUT -m state --state NEW -p <protocol> --dport <port> -j fail2ban-<name> iptables -I INPUT -m state --state NEW -p <protocol> --dport <port> -j fail2ban-<name>
# Option: actionend # Option: actionstop
# Notes.: command executed once at the end of Fail2Ban # Notes.: command executed once at the end of Fail2Ban
# Values: CMD # Values: CMD
# #

View File

@ -2,7 +2,7 @@
# #
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 494 $ # $Revision: 658 $
# #
[Definition] [Definition]
@ -15,7 +15,7 @@ actionstart = iptables -N fail2ban-<name>
iptables -A fail2ban-<name> -j RETURN iptables -A fail2ban-<name> -j RETURN
iptables -I INPUT -p <protocol> --dport <port> -j fail2ban-<name> iptables -I INPUT -p <protocol> --dport <port> -j fail2ban-<name>
# Option: actionend # Option: actionstop
# Notes.: command executed once at the end of Fail2Ban # Notes.: command executed once at the end of Fail2Ban
# Values: CMD # Values: CMD
# #

View File

@ -2,7 +2,7 @@
# #
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 510 $ # $Revision: 668 $
# #
[Definition] [Definition]
@ -11,25 +11,25 @@
# Notes.: command executed once at the start of Fail2Ban. # Notes.: command executed once at the start of Fail2Ban.
# Values: CMD # Values: CMD
# #
actionstart = echo -en "Hi,\n actionstart = printf %%b "Hi,\n
The jail <name> has been started successfully.\n The jail <name> has been started successfully.\n
Output will be buffered until <lines> lines are available.\n Output will be buffered until <lines> lines are available.\n
Regards,\n Regards,\n
Fail2Ban"|mail -s "[Fail2Ban] <name>: started" <dest> Fail2Ban"|mail -s "[Fail2Ban] <name>: started" <dest>
# Option: actionend # Option: actionstop
# Notes.: command executed once at the end of Fail2Ban # Notes.: command executed once at the end of Fail2Ban
# Values: CMD # Values: CMD
# #
actionstop = if [ -f <tmpfile> ]; then actionstop = if [ -f <tmpfile> ]; then
echo -en "Hi,\n printf %%b "Hi,\n
These hosts have been banned by Fail2Ban.\n These hosts have been banned by Fail2Ban.\n
`cat <tmpfile>` `cat <tmpfile>`
Regards,\n Regards,\n
Fail2Ban"|mail -s "[Fail2Ban] <name>: Summary" <dest> Fail2Ban"|mail -s "[Fail2Ban] <name>: Summary" <dest>
rm <tmpfile> rm <tmpfile>
fi fi
echo -en "Hi,\n printf %%b "Hi,\n
The jail <name> has been stopped.\n The jail <name> has been stopped.\n
Regards,\n Regards,\n
Fail2Ban"|mail -s "[Fail2Ban] <name>: stopped" <dest> Fail2Ban"|mail -s "[Fail2Ban] <name>: stopped" <dest>
@ -48,10 +48,10 @@ actioncheck =
# <time> unix timestamp of the ban time # <time> unix timestamp of the ban time
# Values: CMD # Values: CMD
# #
actionban = echo `date`": <ip> (<failures> failures)" >> <tmpfile> actionban = printf %%b "`date`: <ip> (<failures> failures)\n" >> <tmpfile>
LINE=$( wc -l <tmpfile> | awk '{ print $1 }' ) LINE=$( wc -l <tmpfile> | awk '{ print $1 }' )
if [ $LINE -eq <lines> ]; then if [ $LINE -eq <lines> ]; then
echo -en "Hi,\n printf %%b "Hi,\n
These hosts have been banned by Fail2Ban.\n These hosts have been banned by Fail2Ban.\n
`cat <tmpfile>` `cat <tmpfile>`
\nRegards,\n \nRegards,\n

View File

@ -2,7 +2,7 @@
# #
# Author: Cyril Jaquier # Author: Cyril Jaquier
# Modified-By: Yaroslav Halchenko to include grepping on IP over log files # Modified-By: Yaroslav Halchenko to include grepping on IP over log files
# $Revision: 595 $ # $Revision: 660 $
# #
[Definition] [Definition]
@ -11,7 +11,7 @@
# Notes.: command executed once at the start of Fail2Ban. # Notes.: command executed once at the start of Fail2Ban.
# Values: CMD # Values: CMD
# #
actionstart = echo -en "Hi,\n actionstart = printf %%b "Hi,\n
The jail <name> has been started successfully.\n The jail <name> has been started successfully.\n
Regards,\n Regards,\n
Fail2Ban"|mail -s "[Fail2Ban] <name>: started" <dest> Fail2Ban"|mail -s "[Fail2Ban] <name>: started" <dest>
@ -20,7 +20,7 @@ actionstart = echo -en "Hi,\n
# Notes.: command executed once at the end of Fail2Ban # Notes.: command executed once at the end of Fail2Ban
# Values: CMD # Values: CMD
# #
actionstop = echo -en "Hi,\n actionstop = printf %%b "Hi,\n
The jail <name> has been stopped.\n The jail <name> has been stopped.\n
Regards,\n Regards,\n
Fail2Ban"|mail -s "[Fail2Ban] <name>: stopped" <dest> Fail2Ban"|mail -s "[Fail2Ban] <name>: stopped" <dest>
@ -40,7 +40,7 @@ actioncheck =
# <bantime> unix timestamp of the ban time # <bantime> unix timestamp of the ban time
# Values: CMD # Values: CMD
# #
actionban = echo -en "Hi,\n actionban = printf %%b "Hi,\n
The IP <ip> has just been banned by Fail2Ban after The IP <ip> has just been banned by Fail2Ban after
<failures> attempts against <name>.\n\n <failures> attempts against <name>.\n\n
Here are more information about <ip>:\n Here are more information about <ip>:\n

View File

@ -2,7 +2,7 @@
# #
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 595 $ # $Revision: 660 $
# #
[Definition] [Definition]
@ -11,16 +11,16 @@
# Notes.: command executed once at the start of Fail2Ban. # Notes.: command executed once at the start of Fail2Ban.
# Values: CMD # Values: CMD
# #
actionstart = echo -en "Hi,\n actionstart = printf %%b "Hi,\n
The jail <name> has been started successfully.\n The jail <name> has been started successfully.\n
Regards,\n Regards,\n
Fail2Ban"|mail -s "[Fail2Ban] <name>: started" <dest> Fail2Ban"|mail -s "[Fail2Ban] <name>: started" <dest>
# Option: actionend # Option: actionstop
# Notes.: command executed once at the end of Fail2Ban # Notes.: command executed once at the end of Fail2Ban
# Values: CMD # Values: CMD
# #
actionstop = echo -en "Hi,\n actionstop = printf %%b "Hi,\n
The jail <name> has been stopped.\n The jail <name> has been stopped.\n
Regards,\n Regards,\n
Fail2Ban"|mail -s "[Fail2Ban] <name>: stopped" <dest> Fail2Ban"|mail -s "[Fail2Ban] <name>: stopped" <dest>
@ -39,7 +39,7 @@ actioncheck =
# <time> unix timestamp of the ban time # <time> unix timestamp of the ban time
# Values: CMD # Values: CMD
# #
actionban = echo -en "Hi,\n actionban = printf %%b "Hi,\n
The IP <ip> has just been banned by Fail2Ban after The IP <ip> has just been banned by Fail2Ban after
<failures> attempts against <name>.\n\n <failures> attempts against <name>.\n\n
Here are more information about <ip>:\n Here are more information about <ip>:\n

View File

@ -2,7 +2,7 @@
# #
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 595 $ # $Revision: 660 $
# #
[Definition] [Definition]
@ -11,16 +11,16 @@
# Notes.: command executed once at the start of Fail2Ban. # Notes.: command executed once at the start of Fail2Ban.
# Values: CMD # Values: CMD
# #
actionstart = echo -en "Hi,\n actionstart = printf %%b "Hi,\n
The jail <name> has been started successfully.\n The jail <name> has been started successfully.\n
Regards,\n Regards,\n
Fail2Ban"|mail -s "[Fail2Ban] <name>: started" <dest> Fail2Ban"|mail -s "[Fail2Ban] <name>: started" <dest>
# Option: actionend # Option: actionstop
# Notes.: command executed once at the end of Fail2Ban # Notes.: command executed once at the end of Fail2Ban
# Values: CMD # Values: CMD
# #
actionstop = echo -en "Hi,\n actionstop = printf %%b "Hi,\n
The jail <name> has been stopped.\n The jail <name> has been stopped.\n
Regards,\n Regards,\n
Fail2Ban"|mail -s "[Fail2Ban] <name>: stopped" <dest> Fail2Ban"|mail -s "[Fail2Ban] <name>: stopped" <dest>
@ -39,7 +39,7 @@ actioncheck =
# <time> unix timestamp of the ban time # <time> unix timestamp of the ban time
# Values: CMD # Values: CMD
# #
actionban = echo -en "Hi,\n actionban = printf %%b "Hi,\n
The IP <ip> has just been banned by Fail2Ban after The IP <ip> has just been banned by Fail2Ban after
<failures> attempts against <name>.\n <failures> attempts against <name>.\n
Regards,\n Regards,\n

View File

@ -2,7 +2,7 @@
# #
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 604 $ # $Revision: 660 $
# #
[Definition] [Definition]
@ -11,7 +11,7 @@
# Notes.: command executed once at the start of Fail2Ban. # Notes.: command executed once at the start of Fail2Ban.
# Values: CMD # Values: CMD
# #
actionstart = echo -en "Subject: [Fail2Ban] <name>: started actionstart = printf %%b "Subject: [Fail2Ban] <name>: started
From: Fail2Ban <<sender>> From: Fail2Ban <<sender>>
To: <dest>\n To: <dest>\n
Hi,\n Hi,\n
@ -20,12 +20,12 @@ actionstart = echo -en "Subject: [Fail2Ban] <name>: started
Regards,\n Regards,\n
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest> Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
# Option: actionend # Option: actionstop
# Notes.: command executed once at the end of Fail2Ban # Notes.: command executed once at the end of Fail2Ban
# Values: CMD # Values: CMD
# #
actionstop = if [ -f <tmpfile> ]; then actionstop = if [ -f <tmpfile> ]; then
echo -en "Subject: [Fail2Ban] <name>: summary printf %%b "Subject: [Fail2Ban] <name>: summary
From: Fail2Ban <<sender>> From: Fail2Ban <<sender>>
To: <dest>\n To: <dest>\n
Hi,\n Hi,\n
@ -35,7 +35,7 @@ actionstop = if [ -f <tmpfile> ]; then
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest> Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
rm <tmpfile> rm <tmpfile>
fi fi
echo -en "Subject: [Fail2Ban] <name>: stopped printf %%b "Subject: [Fail2Ban] <name>: stopped
From: Fail2Ban <<sender>> From: Fail2Ban <<sender>>
To: <dest>\n To: <dest>\n
Hi,\n Hi,\n
@ -57,10 +57,10 @@ actioncheck =
# <time> unix timestamp of the ban time # <time> unix timestamp of the ban time
# Values: CMD # Values: CMD
# #
actionban = echo `date`": <ip> (<failures> failures)" >> <tmpfile> actionban = printf %%b "`date`: <ip> (<failures> failures)\n" >> <tmpfile>
LINE=$( wc -l <tmpfile> | awk '{ print $1 }' ) LINE=$( wc -l <tmpfile> | awk '{ print $1 }' )
if [ $LINE -eq <lines> ]; then if [ $LINE -eq <lines> ]; then
echo -en "Subject: [Fail2Ban] <name>: summary printf %%b "Subject: [Fail2Ban] <name>: summary
From: Fail2Ban <<sender>> From: Fail2Ban <<sender>>
To: <dest>\n To: <dest>\n
Hi,\n Hi,\n

View File

@ -2,7 +2,7 @@
# #
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 595 $ # $Revision: 660 $
# #
[Definition] [Definition]
@ -11,7 +11,7 @@
# Notes.: command executed once at the start of Fail2Ban. # Notes.: command executed once at the start of Fail2Ban.
# Values: CMD # Values: CMD
# #
actionstart = echo -en "Subject: [Fail2Ban] <name>: started actionstart = printf %%b "Subject: [Fail2Ban] <name>: started
From: Fail2Ban <<sender>> From: Fail2Ban <<sender>>
To: <dest>\n To: <dest>\n
Hi,\n Hi,\n
@ -19,11 +19,11 @@ actionstart = echo -en "Subject: [Fail2Ban] <name>: started
Regards,\n Regards,\n
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest> Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
# Option: actionend # Option: actionstop
# Notes.: command executed once at the end of Fail2Ban # Notes.: command executed once at the end of Fail2Ban
# Values: CMD # Values: CMD
# #
actionstop = echo -en "Subject: [Fail2Ban] <name>: stopped actionstop = printf %%b "Subject: [Fail2Ban] <name>: stopped
From: Fail2Ban <<sender>> From: Fail2Ban <<sender>>
To: <dest>\n To: <dest>\n
Hi,\n Hi,\n
@ -45,7 +45,7 @@ actioncheck =
# <time> unix timestamp of the ban time # <time> unix timestamp of the ban time
# Values: CMD # Values: CMD
# #
actionban = echo -en "Subject: [Fail2Ban] <name>: banned <ip> actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip>
From: Fail2Ban <<sender>> From: Fail2Ban <<sender>>
To: <dest>\n To: <dest>\n
Hi,\n Hi,\n

View File

@ -2,7 +2,7 @@
# #
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 595 $ # $Revision: 660 $
# #
[Definition] [Definition]
@ -11,7 +11,7 @@
# Notes.: command executed once at the start of Fail2Ban. # Notes.: command executed once at the start of Fail2Ban.
# Values: CMD # Values: CMD
# #
actionstart = echo -en "Subject: [Fail2Ban] <name>: started actionstart = printf %%b "Subject: [Fail2Ban] <name>: started
From: Fail2Ban <<sender>> From: Fail2Ban <<sender>>
To: <dest>\n To: <dest>\n
Hi,\n Hi,\n
@ -19,11 +19,11 @@ actionstart = echo -en "Subject: [Fail2Ban] <name>: started
Regards,\n Regards,\n
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest> Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
# Option: actionend # Option: actionstop
# Notes.: command executed once at the end of Fail2Ban # Notes.: command executed once at the end of Fail2Ban
# Values: CMD # Values: CMD
# #
actionstop = echo -en "Subject: [Fail2Ban] <name>: stopped actionstop = printf %%b "Subject: [Fail2Ban] <name>: stopped
From: Fail2Ban <<sender>> From: Fail2Ban <<sender>>
To: <dest>\n To: <dest>\n
Hi,\n Hi,\n
@ -45,7 +45,7 @@ actioncheck =
# <time> unix timestamp of the ban time # <time> unix timestamp of the ban time
# Values: CMD # Values: CMD
# #
actionban = echo -en "Subject: [Fail2Ban] <name>: banned <ip> actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip>
From: Fail2Ban <<sender>> From: Fail2Ban <<sender>>
To: <dest>\n To: <dest>\n
Hi,\n Hi,\n

View File

@ -2,7 +2,7 @@
# #
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 595 $ # $Revision: 660 $
# #
[Definition] [Definition]
@ -11,7 +11,7 @@
# Notes.: command executed once at the start of Fail2Ban. # Notes.: command executed once at the start of Fail2Ban.
# Values: CMD # Values: CMD
# #
actionstart = echo -en "Subject: [Fail2Ban] <name>: started actionstart = printf %%b "Subject: [Fail2Ban] <name>: started
From: Fail2Ban <<sender>> From: Fail2Ban <<sender>>
To: <dest>\n To: <dest>\n
Hi,\n Hi,\n
@ -19,11 +19,11 @@ actionstart = echo -en "Subject: [Fail2Ban] <name>: started
Regards,\n Regards,\n
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest> Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
# Option: actionend # Option: actionstop
# Notes.: command executed once at the end of Fail2Ban # Notes.: command executed once at the end of Fail2Ban
# Values: CMD # Values: CMD
# #
actionstop = echo -en "Subject: [Fail2Ban] <name>: stopped actionstop = printf %%b "Subject: [Fail2Ban] <name>: stopped
From: Fail2Ban <<sender>> From: Fail2Ban <<sender>>
To: <dest>\n To: <dest>\n
Hi,\n Hi,\n
@ -45,7 +45,7 @@ actioncheck =
# <time> unix timestamp of the ban time # <time> unix timestamp of the ban time
# Values: CMD # Values: CMD
# #
actionban = echo -en "Subject: [Fail2Ban] <name>: banned <ip> actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip>
From: Fail2Ban <<sender>> From: Fail2Ban <<sender>>
To: <dest>\n To: <dest>\n
Hi,\n Hi,\n

View File

@ -2,8 +2,16 @@
# #
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 510 $ # $Revision: 661 $
# #
# The default Shorewall configuration is with "BLACKLISTNEWONLY=Yes" (see
# file /etc/shorewall/shorewall.conf). This means that when Fail2ban adds a
# new shorewall rule to ban an IP address, that rule will affect only new
# connections. So if the attempter goes on trying using the same connection
# he could even log in. In order to get the same behavior of the iptable
# action (so that the ban is immediate) the /etc/shorewall/shorewall.conf
# file should me modified with "BLACKLISTNEWONLY=No".
#
[Definition] [Definition]
@ -13,7 +21,7 @@
# #
actionstart = actionstart =
# Option: actionend # Option: actionstop
# Notes.: command executed once at the end of Fail2Ban # Notes.: command executed once at the end of Fail2Ban
# Values: CMD # Values: CMD
# #
@ -33,7 +41,7 @@ actioncheck =
# <time> unix timestamp of the ban time # <time> unix timestamp of the ban time
# Values: CMD # Values: CMD
# #
actionban = shorewall reject <ip> actionban = shorewall drop <ip>
# Option: actionunban # Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the # Notes.: command executed when unbanning an IP. Take care that the

View File

@ -2,7 +2,7 @@
# #
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 494 $ # $Revision: 629 $
# #
[Definition] [Definition]
@ -28,7 +28,7 @@ logtarget = /var/log/fail2ban.log
# Notes.: Set the socket file. This is used to communicate with the daemon. Do # Notes.: Set the socket file. This is used to communicate with the daemon. Do
# not remove this file when Fail2ban runs. It will not be possible to # not remove this file when Fail2ban runs. It will not be possible to
# communicate with the server afterwards. # communicate with the server afterwards.
# Values: FILE Default: /var/run/fail2ban.sock # Values: FILE Default: /var/run/fail2ban/fail2ban.sock
# #
socket = /var/run/fail2ban.sock socket = /var/run/fail2ban/fail2ban.sock

View File

@ -5,10 +5,12 @@
# #
# Author: Yaroslav Halchenko # Author: Yaroslav Halchenko
# #
# $Revision: 668 $
#
[Definition] [Definition]
badbotscustom = EmailCollector|WebEMailExtrac|TrackBack/1\.02 badbotscustom = EmailCollector|WebEMailExtrac|TrackBack/1\.02|sogou music spider
badbots = atSpider/1\.0|autoemailspider|China Local Browse 2\.6|ContentSmartz|DataCha0s/2\.0|DataCha0s/2\.0|DBrowse 1\.4b|DBrowse 1\.4d|Demo Bot DOT 16b|Demo Bot Z 16b|DSurf15a 01|DSurf15a 71|DSurf15a 81|DSurf15a VA|EBrowse 1\.4b|Educate Search VxB|EmailSiphon|EmailWolf 1\.00|ESurf15a 15|ExtractorPro|Franklin Locator 1\.8|FSurf15a 01|Full Web Bot 0416B|Full Web Bot 0516B|Full Web Bot 2816B|Industry Program 1\.0\.x|ISC Systems iRc Search 2\.1|IUPUI Research Bot v 1\.9a|LARBIN-EXPERIMENTAL \(efp@gmx\.net\)|LetsCrawl\.com/1\.0 +http\://letscrawl\.com/|Lincoln State Web Browser|LWP\:\:Simple/5\.803|Mac Finder 1\.0\.xx|MFC Foundation Class Library 4\.0|Microsoft URL Control - 6\.00\.8xxx|Missauga Locate 1\.0\.0|Missigua Locator 1\.9|Missouri College Browse|Mizzu Labs 2\.2|Mo College 1\.9|Mozilla/2\.0 \(compatible; NEWT ActiveX; Win32\)|Mozilla/3\.0 \(compatible; Indy Library\)|Mozilla/4\.0 \(compatible; Advanced Email Extractor v2\.xx\)|Mozilla/4\.0 \(compatible; Iplexx Spider/1\.0 http\://www\.iplexx\.at\)|Mozilla/4\.0 \(compatible; MSIE 5\.0; Windows NT; DigExt; DTS Agent|Mozilla/4\.0 efp@gmx\.net|Mozilla/5\.0 \(Version\: xxxx Type\:xx\)|MVAClient|NASA Search 1\.0|Nsauditor/1\.x|PBrowse 1\.4b|PEval 1\.4b|Poirot|Port Huron Labs|Production Bot 0116B|Production Bot 2016B|Production Bot DOT 3016B|Program Shareware 1\.0\.2|PSurf15a 11|PSurf15a 51|PSurf15a VA|psycheclone|RSurf15a 41|RSurf15a 51|RSurf15a 81|searchbot admin@google\.com|sogou spider|sohu agent|SSurf15a 11 |TSurf15a 11|Under the Rainbow 2\.2|User-Agent\: Mozilla/4\.0 \(compatible; MSIE 6\.0; Windows NT 5\.1\)|WebVulnCrawl\.blogspot\.com/1\.0 libwww-perl/5\.803|Wells Search II|WEP Search 00 badbots = atSpider/1\.0|autoemailspider|China Local Browse 2\.6|ContentSmartz|DataCha0s/2\.0|DataCha0s/2\.0|DBrowse 1\.4b|DBrowse 1\.4d|Demo Bot DOT 16b|Demo Bot Z 16b|DSurf15a 01|DSurf15a 71|DSurf15a 81|DSurf15a VA|EBrowse 1\.4b|Educate Search VxB|EmailSiphon|EmailWolf 1\.00|ESurf15a 15|ExtractorPro|Franklin Locator 1\.8|FSurf15a 01|Full Web Bot 0416B|Full Web Bot 0516B|Full Web Bot 2816B|Industry Program 1\.0\.x|ISC Systems iRc Search 2\.1|IUPUI Research Bot v 1\.9a|LARBIN-EXPERIMENTAL \(efp@gmx\.net\)|LetsCrawl\.com/1\.0 +http\://letscrawl\.com/|Lincoln State Web Browser|LWP\:\:Simple/5\.803|Mac Finder 1\.0\.xx|MFC Foundation Class Library 4\.0|Microsoft URL Control - 6\.00\.8xxx|Missauga Locate 1\.0\.0|Missigua Locator 1\.9|Missouri College Browse|Mizzu Labs 2\.2|Mo College 1\.9|Mozilla/2\.0 \(compatible; NEWT ActiveX; Win32\)|Mozilla/3\.0 \(compatible; Indy Library\)|Mozilla/4\.0 \(compatible; Advanced Email Extractor v2\.xx\)|Mozilla/4\.0 \(compatible; Iplexx Spider/1\.0 http\://www\.iplexx\.at\)|Mozilla/4\.0 \(compatible; MSIE 5\.0; Windows NT; DigExt; DTS Agent|Mozilla/4\.0 efp@gmx\.net|Mozilla/5\.0 \(Version\: xxxx Type\:xx\)|MVAClient|NASA Search 1\.0|Nsauditor/1\.x|PBrowse 1\.4b|PEval 1\.4b|Poirot|Port Huron Labs|Production Bot 0116B|Production Bot 2016B|Production Bot DOT 3016B|Program Shareware 1\.0\.2|PSurf15a 11|PSurf15a 51|PSurf15a VA|psycheclone|RSurf15a 41|RSurf15a 51|RSurf15a 81|searchbot admin@google\.com|sogou spider|sohu agent|SSurf15a 11 |TSurf15a 11|Under the Rainbow 2\.2|User-Agent\: Mozilla/4\.0 \(compatible; MSIE 6\.0; Windows NT 5\.1\)|WebVulnCrawl\.blogspot\.com/1\.0 libwww-perl/5\.803|Wells Search II|WEP Search 00
# Option: failregex # Option: failregex

View File

@ -2,7 +2,7 @@
# #
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 510 $ # $Revision: 658 $
# #
[Definition] [Definition]

View File

@ -0,0 +1,20 @@
# Fail2Ban configuration file
#
# Author: Tim Connors
#
# $Revision: 668 $
#
[Definition]
# Option: failregex
# Notes.: Regexp to catch Apache overflow attempts.
# Values: TEXT
#
failregex = [[]client <HOST>[]] (Invalid method in request|request failed: URI too long|erroneous characters after protocol string)
# Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored.
# Values: TEXT
#
ignoreregex =

View File

@ -0,0 +1,41 @@
# Generic configuration items (to be used as interpolations) in other
# filters or actions configurations
#
# Author: Yaroslav Halchenko
#
# $Revision: $
#
[INCLUDES]
# Load customizations if any available
after = common.local
[DEFAULT]
# Daemon definition is to be specialized (if needed) in .conf file
_daemon = \S*
#
# Shortcuts for easier comprehension of the failregex
#
# PID.
# EXAMPLES: [123]
__pid_re = (?:\[\d+\])
# Daemon name (with optional source_file:line or whatever)
# EXAMPLES: pam_rhosts_auth, [sshd], pop(pam_unix)
__daemon_re = [\[\(]?%(_daemon)s(?:\(\S+\))?[\]\)]?:?
# Combinations of daemon name and PID
# EXAMPLES: sshd[31607], pop(pam_unix)[4920]
__daemon_combs_re = (?:%(__pid_re)s?:\s+%(__daemon_re)s|%(__daemon_re)s%(__pid_re)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*

View File

@ -4,7 +4,7 @@
# #
# Author: Yaroslav Halchenko # Author: Yaroslav Halchenko
# #
# $Revision: 608 $ # $Revision: 616 $
# #
[Definition] [Definition]

View File

@ -2,7 +2,7 @@
# #
# Author: Yaroslav Halchenko # Author: Yaroslav Halchenko
# #
# $Revision: 603 $ # $Revision: 665 $
# #
[Definition] [Definition]
@ -14,10 +14,10 @@
# (?:::f{4,6}:)?(?P<host>\S+) # (?:::f{4,6}:)?(?P<host>\S+)
# Values: TEXT # Values: TEXT
# #
failregex = \(\S+\[<HOST>\]\): USER \S+: no such user found from \S+ \[[0-9.]+\] to \S+:\S+$ failregex = \(\S+\[<HOST>\]\)[: -]+ USER \S+: no such user found from \S+ \[[0-9.]+\] to \S+:\S+$
\(\S+\[<HOST>\]\): USER \S+ \(Login failed\): Incorrect password\.$ \(\S+\[<HOST>\]\)[: -]+ USER \S+ \(Login failed\): Incorrect password\.$
\(\S+\[<HOST>\]\): SECURITY VIOLATION: \S+ login attempted\.$ \(\S+\[<HOST>\]\)[: -]+ SECURITY VIOLATION: \S+ login attempted\.$
\(\S+\[<HOST>\]\): Maximum login attempts \(\d+\) exceeded$ \(\S+\[<HOST>\]\)[: -]+ Maximum login attempts \(\d+\) exceeded$
# Option: ignoreregex # Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored. # Notes.: regex to ignore. If this regex matches, the line is ignored.

View File

@ -2,11 +2,20 @@
# #
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 613 $ # $Revision: 663 $
# #
[INCLUDES]
# Read common prefixes. If any customizations available -- read them from
# common.local
before = common.conf
[Definition] [Definition]
_daemon = sshd
# Option: failregex # Option: failregex
# Notes.: regex to match the password failures messages in the logfile. The # Notes.: regex to match the password failures messages in the logfile. The
# host must be matched by a group named "host". The tag "<HOST>" can # host must be matched by a group named "host". The tag "<HOST>" can
@ -14,13 +23,15 @@
# (?:::f{4,6}:)?(?P<host>\S+) # (?:::f{4,6}:)?(?P<host>\S+)
# Values: TEXT # Values: TEXT
# #
failregex = (?:error: PAM: )?Authentication failure for .* from <HOST>\s*$ failregex = ^%(__prefix_line)s(?:error: PAM: )?Authentication failure for .* from <HOST>\s*$
Failed [-/\w]+ for .* from <HOST>(?: port \d*)?(?: ssh\d*)?\s*$ ^%(__prefix_line)sFailed [-/\w]+ for .* from <HOST>(?: port \d*)?(?: ssh\d*)?$
ROOT LOGIN REFUSED.* FROM <HOST>\s*$ ^%(__prefix_line)sROOT LOGIN REFUSED.* FROM <HOST>\s*$
[iI](?:llegal|nvalid) user .* from <HOST>\s*$ ^%(__prefix_line)s[iI](?:llegal|nvalid) user .* from <HOST>\s*$
User .+ from <HOST> not allowed because not listed in AllowUsers\s*$ ^%(__prefix_line)sUser .+ from <HOST> not allowed because not listed in AllowUsers$
User .+ from <HOST> not allowed because none of user's groups are listed in AllowGroups\s*$ ^%(__prefix_line)sUser .+ from <HOST> not allowed because none of user's groups are listed in AllowGroups\s*$
sshd(?:\[\d+\])?: refused connect from \S+ \(<HOST>\)\s*$ ^%(__prefix_line)sauthentication failure; logname=\S* uid=\S* euid=\S* tty=\S* ruser=\S* rhost=<HOST>(?:\s+user=.*)?\s*$
^%(__prefix_line)srefused connect from \S+ \(<HOST>\)\s*$
^%(__prefix_line)sAddress <HOST> .* POSSIBLE BREAK-IN ATTEMPT\s*$
# Option: ignoreregex # Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored. # Notes.: regex to ignore. If this regex matches, the line is ignored.

View File

@ -2,7 +2,7 @@
# #
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 610 $ # $Revision: 658 $
# #
[Definition] [Definition]

View File

@ -0,0 +1,30 @@
# Fail2Ban configuration file
#
# Author: Guido Bozzetto
#
# $Revision: 668 $
#
[Definition]
# Option: failregex
# Notes.: regex to match the password failures messages in the logfile. The
# host must be matched by a group named "host". The tag "<HOST>" can
# be used for standard IP/hostname matching and is only an alias for
# (?:::f{4,6}:)?(?P<host>\S+)
# Values: TEXT
#
# Cfr.: /var/log/(daemon\.|sys)log
# libwrap => tcp wrappers: hosts.(allow|deny)
# address => xinetd: deny_from|only_from
# load => xinetd: max_load (temporary problem)
#
failregex = xinetd(?:\[\d{1,5}\])?: FAIL: \S+ address from=<HOST>$
xinetd(?:\[\d{1,5}\])?: FAIL: \S+ libwrap from=<HOST>$
# Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored.
# Values: TEXT
#
ignoreregex =

View File

@ -2,7 +2,7 @@
# #
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 611 $ # $Revision: 617 $
# #
# The DEFAULT allows a global definition of the options. They can be override # The DEFAULT allows a global definition of the options. They can be override
@ -89,8 +89,8 @@ logpath = /var/log/sshd.log
enabled = false enabled = false
filter = apache-auth filter = apache-auth
action = hostsdeny action = hostsdeny
logpath = /var/log/apache*/*access.log logpath = /var/log/apache*/*error.log
/home/www/myhomepage/access.log /home/www/myhomepage/error.log
maxretry = 6 maxretry = 6
# The hosts.deny path can be defined with the "file" argument if it is # The hosts.deny path can be defined with the "file" argument if it is

2
debian/watch vendored
View File

@ -3,4 +3,4 @@
# Site Directory Pattern Version Script # Site Directory Pattern Version Script
version=3 version=3
http://sf.net/fail2ban/ fail2ban-(.*)\.tar\.bz2 debian svn-upgrade http://sf.net/fail2ban/ fail2ban-(.*)\.tar\.bz2 debian git-import-orig

View File

@ -17,16 +17,16 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 528 $ # $Revision: 672 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 528 $" __version__ = "$Revision: 672 $"
__date__ = "$Date: 2007-01-29 21:27:01 +0100 (Mon, 29 Jan 2007) $" __date__ = "$Date: 2008-03-06 00:18:06 +0100 (Thu, 06 Mar 2008) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
import sys, string, os, pickle, re, logging, signal import sys, string, os, pickle, re, logging, signal
import getopt, time, readline, shlex, socket import getopt, time, shlex, socket
# Inserts our own modules path first in the list # Inserts our own modules path first in the list
# fix for bug #343821 # fix for bug #343821
@ -48,7 +48,8 @@ logSys = logging.getLogger("fail2ban.client")
class Fail2banClient: class Fail2banClient:
prompt = "fail2ban> " SERVER = "fail2ban-server"
PROMPT = "fail2ban> "
def __init__(self): def __init__(self):
self.__argv = None self.__argv = None
@ -65,11 +66,11 @@ class Fail2banClient:
def dispVersion(self): def dispVersion(self):
print "Fail2Ban v" + version print "Fail2Ban v" + version
print print
print "Copyright (c) 2004-2006 Cyril Jaquier" print "Copyright (c) 2004-2008 Cyril Jaquier"
print "Copyright of modifications held by their respective authors." print "Copyright of modifications held by their respective authors."
print "Licensed under the GNU General Public License v2 (GPL)." print "Licensed under the GNU General Public License v2 (GPL)."
print print
print "Written by Cyril Jaquier <lostcontrol@users.sourceforge.net>." print "Written by Cyril Jaquier <cyril.jaquier@fail2ban.org>."
print "Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>." print "Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>."
def dispUsage(self): def dispUsage(self):
@ -97,7 +98,7 @@ class Fail2banClient:
printFormatted() printFormatted()
print print
print "Report bugs to <lostcontrol@users.sourceforge.net>" print "Report bugs to <cyril.jaquier@fail2ban.org>"
def dispInteractive(self): def dispInteractive(self):
print "Fail2Ban v" + version + " reads log file that contains password failure report" print "Fail2Ban v" + version + " reads log file that contains password failure report"
@ -208,6 +209,19 @@ class Fail2banClient:
else: else:
logSys.error("Could not find server") logSys.error("Could not find server")
return False return False
elif len(cmd) == 2 and cmd[0] == "reload":
if self.__ping():
jail = cmd[1]
ret = self.__readJailConfig(jail)
# Do not continue if configuration is not 100% valid
if not ret:
return False
self.__processCmd([['stop', jail]], False)
# Configure the server
return self.__processCmd(self.__stream, False)
else:
logSys.error("Could not find server")
return False
else: else:
return self.__processCmd([cmd]) return self.__processCmd([cmd])
@ -222,7 +236,7 @@ class Fail2banClient:
pid = os.fork() pid = os.fork()
if pid == 0: if pid == 0:
args = list() args = list()
args.append("fail2ban-server") args.append(self.SERVER)
# Start in background mode. # Start in background mode.
args.append("-b") args.append("-b")
# Set the socket path. # Set the socket path.
@ -232,14 +246,15 @@ class Fail2banClient:
if force: if force:
args.append("-x") args.append("-x")
try: try:
# Use the PATH env # Use the current directory.
os.execvp("fail2ban-server", args) exe = os.path.abspath(os.path.join(sys.path[0], self.SERVER))
os.execv(exe, args)
except OSError: except OSError:
try: try:
# Use the current directory # Use the PATH env.
os.execv("fail2ban-server", args) os.execvp(self.SERVER, args)
except OSError: except OSError:
print "Could not find fail2ban-server" print "Could not find %s" % self.SERVER
os.exit(-1) os.exit(-1)
@ -325,6 +340,11 @@ class Fail2banClient:
# Interactive mode # Interactive mode
if self.__conf["interactive"]: if self.__conf["interactive"]:
try:
import readline
except ImportError:
logSys.error("Readline not available")
return False
try: try:
ret = True ret = True
if len(args) > 0: if len(args) > 0:
@ -333,7 +353,7 @@ class Fail2banClient:
readline.parse_and_bind("tab: complete") readline.parse_and_bind("tab: complete")
self.dispInteractive() self.dispInteractive()
while True: while True:
cmd = raw_input(self.prompt) cmd = raw_input(self.PROMPT)
if cmd == "exit" or cmd == "quit": if cmd == "exit" or cmd == "quit":
# Exit # Exit
return True return True
@ -352,16 +372,24 @@ class Fail2banClient:
def __readConfig(self): def __readConfig(self):
# Read the configuration # Read the configuration
self.__configurator.readAll() self.__configurator.readAll()
ret = self.__configurator.getAllOptions() ret = self.__configurator.getOptions()
self.__configurator.convertToProtocol() self.__configurator.convertToProtocol()
self.__stream = self.__configurator.getConfigStream() self.__stream = self.__configurator.getConfigStream()
return ret return ret
@staticmethod def __readJailConfig(self, jail):
self.__configurator.readAll()
ret = self.__configurator.getOptions(jail)
self.__configurator.convertToProtocol()
self.__stream = self.__configurator.getConfigStream()
return ret
#@staticmethod
def dumpConfig(cmd): def dumpConfig(cmd):
for c in cmd: for c in cmd:
print c print c
return True return True
dumpConfig = staticmethod(dumpConfig)
class ServerExecutionException(Exception): class ServerExecutionException(Exception):

View File

@ -17,11 +17,11 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 596 $ # $Revision: 672 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 596 $" __version__ = "$Revision: 672 $"
__date__ = "$Date: 2007-07-10 21:54:01 +0200 (Tue, 10 Jul 2007) $" __date__ = "$Date: 2008-03-06 00:18:06 +0100 (Thu, 06 Mar 2008) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
@ -31,11 +31,11 @@ import getopt, sys, time, logging, os
# fix for bug #343821 # fix for bug #343821
sys.path.insert(1, "/usr/share/fail2ban") sys.path.insert(1, "/usr/share/fail2ban")
from ConfigParser import SafeConfigParser from client.configparserinc import SafeConfigParserWithIncludes
from ConfigParser import NoOptionError, NoSectionError, MissingSectionHeaderError from ConfigParser import NoOptionError, NoSectionError, MissingSectionHeaderError
from common.version import version from common.version import version
from server.filter import Filter from server.filter import Filter
from server.regex import RegexException from server.failregex import RegexException
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.regex") logSys = logging.getLogger("fail2ban.regex")
@ -65,7 +65,9 @@ class RegexStat:
class Fail2banRegex: class Fail2banRegex:
test = None test = None
CONFIG_DEFAULTS = {'configpath' : "/etc/fail2ban/"}
def __init__(self): def __init__(self):
self.__filter = Filter(None) self.__filter = Filter(None)
self.__ignoreregex = list() self.__ignoreregex = list()
@ -80,18 +82,19 @@ class Fail2banRegex:
logging.getLogger("fail2ban").addHandler(self.__hdlr) logging.getLogger("fail2ban").addHandler(self.__hdlr)
logging.getLogger("fail2ban").setLevel(logging.ERROR) logging.getLogger("fail2ban").setLevel(logging.ERROR)
@staticmethod #@staticmethod
def dispVersion(): def dispVersion():
print "Fail2Ban v" + version print "Fail2Ban v" + version
print print
print "Copyright (c) 2004-2006 Cyril Jaquier" print "Copyright (c) 2004-2008 Cyril Jaquier"
print "Copyright of modifications held by their respective authors." print "Copyright of modifications held by their respective authors."
print "Licensed under the GNU General Public License v2 (GPL)." print "Licensed under the GNU General Public License v2 (GPL)."
print print
print "Written by Cyril Jaquier <lostcontrol@users.sourceforge.net>." print "Written by Cyril Jaquier <cyril.jaquier@fail2ban.org>."
print "Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>." print "Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>."
dispVersion = staticmethod(dispVersion)
@staticmethod #@staticmethod
def dispUsage(): def dispUsage():
print "Usage: "+sys.argv[0]+" [OPTIONS] <LOG> <REGEX> [IGNOREREGEX]" print "Usage: "+sys.argv[0]+" [OPTIONS] <LOG> <REGEX> [IGNOREREGEX]"
print print
@ -116,7 +119,8 @@ class Fail2banRegex:
print " string a string representing an 'ignoreregex'" print " string a string representing an 'ignoreregex'"
print " filename path to a filter file (filter.d/sshd.conf)" print " filename path to a filter file (filter.d/sshd.conf)"
print print
print "Report bugs to <lostcontrol@users.sourceforge.net>" print "Report bugs to <cyril.jaquier@fail2ban.org>"
dispUsage = staticmethod(dispUsage)
def getCmdLineOptions(self, optList): def getCmdLineOptions(self, optList):
""" Gets the command line options """ Gets the command line options
@ -129,13 +133,14 @@ class Fail2banRegex:
self.dispVersion() self.dispVersion()
sys.exit(0) sys.exit(0)
@staticmethod #@staticmethod
def logIsFile(value): def logIsFile(value):
return os.path.isfile(value) return os.path.isfile(value)
logIsFile = staticmethod(logIsFile)
def readIgnoreRegex(self, value): def readIgnoreRegex(self, value):
if os.path.isfile(value): if os.path.isfile(value):
reader = SafeConfigParser() reader = SafeConfigParserWithIncludes(defaults=self.CONFIG_DEFAULTS)
try: try:
reader.read(value) reader.read(value)
print "Use ignoreregex file : " + value print "Use ignoreregex file : " + value
@ -164,7 +169,7 @@ class Fail2banRegex:
def readRegex(self, value): def readRegex(self, value):
if os.path.isfile(value): if os.path.isfile(value):
reader = SafeConfigParser() reader = SafeConfigParserWithIncludes(defaults=self.CONFIG_DEFAULTS)
try: try:
reader.read(value) reader.read(value)
print "Use regex file : " + value print "Use regex file : " + value
@ -217,7 +222,7 @@ class Fail2banRegex:
try: try:
self.__filter.addFailRegex(regex.getFailRegex()) self.__filter.addFailRegex(regex.getFailRegex())
try: try:
ret = self.__filter.findFailure(line) ret = self.__filter.processLine(line)
if not len(ret) == 0: if not len(ret) == 0:
if found == True: if found == True:
ret[0].append(True) ret[0].append(True)

View File

@ -17,15 +17,15 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 522 $ # $Revision: 672 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 522 $" __version__ = "$Revision: 672 $"
__date__ = "$Date: 2007-01-21 23:19:57 +0100 (Sun, 21 Jan 2007) $" __date__ = "$Date: 2008-03-06 00:18:06 +0100 (Thu, 06 Mar 2008) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
import getopt, sys import getopt, sys, logging
# Inserts our own modules path first in the list # Inserts our own modules path first in the list
# fix for bug #343821 # fix for bug #343821
@ -34,6 +34,9 @@ sys.path.insert(1, "/usr/share/fail2ban")
from common.version import version from common.version import version
from server.server import Server from server.server import Server
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban")
## ##
# \mainpage Fail2Ban # \mainpage Fail2Ban
# #
@ -50,16 +53,16 @@ class Fail2banServer:
self.__conf = dict() self.__conf = dict()
self.__conf["background"] = True self.__conf["background"] = True
self.__conf["force"] = False self.__conf["force"] = False
self.__conf["socket"] = "/tmp/fail2ban.sock" self.__conf["socket"] = "/var/run/fail2ban/fail2ban.sock"
def dispVersion(self): def dispVersion(self):
print "Fail2Ban v" + version print "Fail2Ban v" + version
print print
print "Copyright (c) 2004-2006 Cyril Jaquier" print "Copyright (c) 2004-2008 Cyril Jaquier"
print "Copyright of modifications held by their respective authors." print "Copyright of modifications held by their respective authors."
print "Licensed under the GNU General Public License v2 (GPL)." print "Licensed under the GNU General Public License v2 (GPL)."
print print
print "Written by Cyril Jaquier <lostcontrol@users.sourceforge.net>." print "Written by Cyril Jaquier <cyril.jaquier@fail2ban.org>."
print "Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>." print "Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>."
def dispUsage(self): def dispUsage(self):
@ -82,7 +85,7 @@ class Fail2banServer:
print " -h, --help display this help message" print " -h, --help display this help message"
print " -V, --version print the version" print " -V, --version print the version"
print print
print "Report bugs to <lostcontrol@users.sourceforge.net>" print "Report bugs to <cyril.jaquier@fail2ban.org>"
def __getCmdLineOptions(self, optList): def __getCmdLineOptions(self, optList):
""" Gets the command line options """ Gets the command line options
@ -123,7 +126,7 @@ class Fail2banServer:
self.__server.start(self.__conf["socket"], self.__conf["force"]) self.__server.start(self.__conf["socket"], self.__conf["force"])
return True return True
except Exception, e: except Exception, e:
print e logSys.exception(e)
self.__server.quit() self.__server.quit()
return False return False

View File

@ -4,7 +4,7 @@
|_| \__,_|_|_/___|_.__/\__,_|_||_| |_| \__,_|_|_/___|_.__/\__,_|_||_|
============================================================= =============================================================
Fail2Ban (version 0.7.7) 2007/??/?? Fail2Ban (version 0.8.2) 2008/03/06
============================================================= =============================================================
Cacti is a graphing solution using RRDTool. It is possible to Cacti is a graphing solution using RRDTool. It is possible to
@ -13,7 +13,7 @@ use Cacti to display statistics about Fail2ban.
Installation: Installation:
------------- -------------
1/ Install Fail2ban version 0.7 or higher and ensure that it 1/ Install Fail2ban version 0.8 or higher and ensure that it
works properly. works properly.
2/ The user running poller.php must have read and write 2/ The user running poller.php must have read and write
access to the socket used by Fail2ban. access to the socket used by Fail2ban.
@ -30,7 +30,7 @@ appreciate this program, you can contact me at:
Website: http://www.fail2ban.org Website: http://www.fail2ban.org
Cyril Jaquier: <lostcontrol@users.sourceforge.net> Cyril Jaquier: <cyril.jaquier@fail2ban.org>
License: License:
-------- --------

19
files/macosx-initd Normal file
View File

@ -0,0 +1,19 @@
/Library/LaunchDaemonsm/org.fail2ban.plist
===================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Disabled</key>
<false/>
<key>Label</key>
<string>fail2ban</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/fail2ban-client</string>
<string>start</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>

View File

@ -1,11 +1,12 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.36. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.36.
.TH FAIL2BAN-CLIENT "1" "August 2007" "fail2ban-client v0.8.1" "User Commands" .TH FAIL2BAN-CLIENT "1" "March 2008" "fail2ban-client v0.8.2" "User Commands"
.SH NAME .SH NAME
fail2ban-client \- configure and control the server fail2ban-client \- configure and control the server
.SH SYNOPSIS
.B fail2ban-client
[\fIOPTIONS\fR] \fI<COMMAND>\fR
.SH DESCRIPTION .SH DESCRIPTION
[?1034hUsage: ../fail2ban\-client [OPTIONS] <COMMAND> Fail2Ban v0.8.2 reads log file that contains password failure report
.PP
Fail2Ban v0.8.1 reads log file that contains password failure report
and bans the corresponding IP addresses using firewall rules. and bans the corresponding IP addresses using firewall rules.
.SH OPTIONS .SH OPTIONS
.TP .TP
@ -45,6 +46,9 @@ starts the server and the jails
\fBreload\fR \fBreload\fR
reloads the configuration reloads the configuration
.TP .TP
\fBreload <JAIL>\fR
reloads the jail <JAIL>
.TP
\fBstop\fR \fBstop\fR
stops all jails and terminate the stops all jails and terminate the
server server
@ -109,18 +113,6 @@ of <JAIL>
removes <FILE> to the monitoring removes <FILE> to the monitoring
list of <JAIL> list of <JAIL>
.TP .TP
\fBset <JAIL> timeregex <REGEX>\fR
sets the regular expression
<REGEX> to match the date format
for <JAIL>. This will disable the
autodetection feature.
.TP
\fBset <JAIL> timepattern <PATTERN>\fR
sets the pattern <PATTERN> to
match the date format for <JAIL>.
This will disable the
autodetection feature.
.TP
\fBset <JAIL> addfailregex <REGEX>\fR \fBset <JAIL> addfailregex <REGEX>\fR
adds the regular expression adds the regular expression
<REGEX> which must match failures <REGEX> which must match failures
@ -256,13 +248,13 @@ action <ACT> for <JAIL>
.SH FILES .SH FILES
\fI/etc/fail2ban/*\fR \fI/etc/fail2ban/*\fR
.SH AUTHOR .SH AUTHOR
Written by Cyril Jaquier <lostcontrol@users.sourceforge.net>. Written by Cyril Jaquier <cyril.jaquier@fail2ban.org>.
Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>. Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>.
.SH "REPORTING BUGS" .SH "REPORTING BUGS"
Please report bugs via Debian bug tracking system Please report bugs via Debian bug tracking system
http://www.debian.org/Bugs/. http://www.debian.org/Bugs/.
.SH COPYRIGHT .SH COPYRIGHT
Copyright \(co 2004-2006 Cyril Jaquier Copyright \(co 2004-2008 Cyril Jaquier
.br .br
Copyright of modifications held by their respective authors. Copyright of modifications held by their respective authors.
Licensed under the GNU General Public License v2 (GPL). Licensed under the GNU General Public License v2 (GPL).

View File

@ -1,12 +1,12 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.36. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.36.
.TH FAIL2BAN-REGEX "1" "August 2007" "fail2ban-regex v0.8.1" "User Commands" .TH FAIL2BAN-REGEX "1" "March 2008" "fail2ban-regex v0.8.2" "User Commands"
.SH NAME .SH NAME
fail2ban-regex \- test Fail2ban "failregex" option fail2ban-regex \- test Fail2ban "failregex" option
.SH SYNOPSIS .SH SYNOPSIS
.B fail2ban-regex .B fail2ban-regex
[\fIOPTIONS\fR] \fI<LOG> <REGEX> \fR[\fIIGNOREREGEX\fR] [\fIOPTIONS\fR] \fI<LOG> <REGEX> \fR[\fIIGNOREREGEX\fR]
.SH DESCRIPTION .SH DESCRIPTION
Fail2Ban v0.8.1 reads log file that contains password failure report Fail2Ban v0.8.2 reads log file that contains password failure report
and bans the corresponding IP addresses using firewall rules. and bans the corresponding IP addresses using firewall rules.
.PP .PP
This tools can test regular expressions for "fail2ban". This tools can test regular expressions for "fail2ban".
@ -39,12 +39,12 @@ a string representing an 'ignoreregex'
\fBfilename\fR \fBfilename\fR
path to a filter file (filter.d/sshd.conf) path to a filter file (filter.d/sshd.conf)
.SH AUTHOR .SH AUTHOR
Written by Cyril Jaquier <lostcontrol@users.sourceforge.net>. Written by Cyril Jaquier <cyril.jaquier@fail2ban.org>.
Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>. Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>.
.SH "REPORTING BUGS" .SH "REPORTING BUGS"
Report bugs to <lostcontrol@users.sourceforge.net> Report bugs to <cyril.jaquier@fail2ban.org>
.SH COPYRIGHT .SH COPYRIGHT
Copyright \(co 2004-2006 Cyril Jaquier Copyright \(co 2004-2008 Cyril Jaquier
.br .br
Copyright of modifications held by their respective authors. Copyright of modifications held by their respective authors.
Licensed under the GNU General Public License v2 (GPL). Licensed under the GNU General Public License v2 (GPL).

View File

@ -1,12 +1,12 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.36. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.36.
.TH FAIL2BAN-SERVER "1" "August 2007" "fail2ban-server v0.8.1" "User Commands" .TH FAIL2BAN-SERVER "1" "March 2008" "fail2ban-server v0.8.2" "User Commands"
.SH NAME .SH NAME
fail2ban-server \- start the server fail2ban-server \- start the server
.SH SYNOPSIS .SH SYNOPSIS
.B fail2ban-server .B fail2ban-server
[\fIOPTIONS\fR] [\fIOPTIONS\fR]
.SH DESCRIPTION .SH DESCRIPTION
Fail2Ban v0.8.1 reads log file that contains password failure report Fail2Ban v0.8.2 reads log file that contains password failure report
and bans the corresponding IP addresses using firewall rules. and bans the corresponding IP addresses using firewall rules.
.PP .PP
Only use this command for debugging purpose. Start the server with Only use this command for debugging purpose. Start the server with
@ -32,13 +32,13 @@ display this help message
\fB\-V\fR, \fB\-\-version\fR \fB\-V\fR, \fB\-\-version\fR
print the version print the version
.SH AUTHOR .SH AUTHOR
Written by Cyril Jaquier <lostcontrol@users.sourceforge.net>. Written by Cyril Jaquier <cyril.jaquier@fail2ban.org>.
Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>. Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>.
.SH "REPORTING BUGS" .SH "REPORTING BUGS"
Please report bugs via Debian bug tracking system Please report bugs via Debian bug tracking system
http://www.debian.org/Bugs/. http://www.debian.org/Bugs/.
.SH COPYRIGHT .SH COPYRIGHT
Copyright \(co 2004-2006 Cyril Jaquier Copyright \(co 2004-2008 Cyril Jaquier
.br .br
Copyright of modifications held by their respective authors. Copyright of modifications held by their respective authors.
Licensed under the GNU General Public License v2 (GPL). Licensed under the GNU General Public License v2 (GPL).

View File

@ -16,11 +16,11 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 556 $ # $Revision: 635 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 556 $" __version__ = "$Revision: 635 $"
__date__ = "$Date: 2007-03-07 21:54:32 +0100 (Wed, 07 Mar 2007) $" __date__ = "$Date: 2007-12-16 22:38:04 +0100 (Sun, 16 Dec 2007) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
@ -231,7 +231,7 @@ class Action:
# @param aInfo the properties # @param aInfo the properties
# @return a string # @return a string
@staticmethod #@staticmethod
def replaceTag(query, aInfo): def replaceTag(query, aInfo):
""" Replace tags in query """ Replace tags in query
""" """
@ -241,6 +241,7 @@ class Action:
# New line # New line
string = string.replace("<br>", '\n') string = string.replace("<br>", '\n')
return string return string
replaceTag = staticmethod(replaceTag)
## ##
# Executes a command with preliminary checks and substitutions. # Executes a command with preliminary checks and substitutions.
@ -297,7 +298,7 @@ class Action:
# @param realCmd the command to execute # @param realCmd the command to execute
# @return True if the command succeeded # @return True if the command succeeded
@staticmethod #@staticmethod
def executeCmd(realCmd): def executeCmd(realCmd):
logSys.debug(realCmd) logSys.debug(realCmd)
try: try:
@ -312,3 +313,5 @@ class Action:
except OSError, e: except OSError, e:
logSys.error("%s failed with %s" % (realCmd, e)) logSys.error("%s failed with %s" % (realCmd, e))
return False return False
executeCmd = staticmethod(executeCmd)

155
server/asyncserver.py Normal file
View File

@ -0,0 +1,155 @@
# 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: Cyril Jaquier
#
# $Revision: 567 $
__author__ = "Cyril Jaquier"
__version__ = "$Revision: 567 $"
__date__ = "$Date: 2007-03-26 23:17:31 +0200 (Mon, 26 Mar 2007) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
from pickle import dumps, loads, HIGHEST_PROTOCOL
import asyncore, asynchat, socket, os, logging
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.server")
##
# Request handler class.
#
# This class extends asynchat in order to provide a request handler for
# incoming query.
class RequestHandler(asynchat.async_chat):
END_STRING = "<F2B_END_COMMAND>"
def __init__(self, conn, transmitter):
asynchat.async_chat.__init__(self, conn)
self.__transmitter = transmitter
self.__buffer = []
# Sets the terminator.
self.set_terminator(RequestHandler.END_STRING)
def collect_incoming_data(self, data):
#logSys.debug("Received raw data: " + str(data))
self.__buffer.append(data)
##
# Handles a new request.
#
# This method is called once we have a complete request.
def found_terminator(self):
# Joins the buffer items.
message = loads("".join(self.__buffer))
# Gives the message to the transmitter.
message = self.__transmitter.proceed(message)
# Serializes the response.
message = dumps(message, HIGHEST_PROTOCOL)
# Sends the response to the client.
self.send(message + RequestHandler.END_STRING)
# Closes the channel.
self.close_when_done()
def handle_error(self):
logSys.error("Unexpected communication error")
self.close()
##
# Asynchronous server class.
#
# This class extends asyncore and dispatches connection requests to
# RequestHandler.
class AsyncServer(asyncore.dispatcher):
def __init__(self, transmitter):
asyncore.dispatcher.__init__(self)
self.__transmitter = transmitter
self.__sock = "/var/run/fail2ban/fail2ban.sock"
self.__init = False
##
# Returns False as we only read the socket first.
def writable(self):
return False
def handle_accept(self):
try:
conn, addr = self.accept()
except socket.error:
logSys.warning("Socket error")
return
except TypeError:
logSys.warning("Type error")
return
# Creates an instance of the handler class to handle the
# request/response on the incoming connection.
RequestHandler(conn, self.__transmitter)
##
# Starts the communication server.
#
# @param sock: socket file.
# @param force: remove the socket file if exists.
def start(self, sock, force):
self.__sock = sock
# Remove socket
if os.path.exists(sock):
logSys.error("Fail2ban seems to be already running")
if force:
logSys.warn("Forcing execution of the server")
os.remove(sock)
else:
raise AsyncServerException("Server already running")
# Creates the socket.
self.create_socket(socket.AF_UNIX, socket.SOCK_STREAM)
self.set_reuse_addr()
try:
self.bind(sock)
except Exception:
raise AsyncServerException("Unable to bind socket %s" % self.__sock)
self.listen(1)
# Sets the init flag.
self.__init = True
# TODO Add try..catch
asyncore.loop()
##
# Stops the communication server.
def stop(self):
if self.__init:
# Only closes the socket if it was initialized first.
self.close()
# Remove socket
if os.path.exists(self.__sock):
logSys.debug("Removed socket file " + self.__sock)
os.remove(self.__sock)
logSys.debug("Socket shutdown")
##
# AsyncServerException is used to wrap communication exceptions.
class AsyncServerException(Exception):
pass

View File

@ -16,15 +16,15 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 553 $ # $Revision: 638 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 553 $" __version__ = "$Revision: 638 $"
__date__ = "$Date: 2007-02-26 00:53:22 +0100 (Mon, 26 Feb 2007) $" __date__ = "$Date: 2007-12-17 21:00:36 +0100 (Mon, 17 Dec 2007) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
from banticket import BanTicket from ticket import BanTicket
from threading import Lock from threading import Lock
from mytime import MyTime from mytime import MyTime
import logging import logging
@ -125,7 +125,7 @@ class BanManager:
# @param ticket the FailTicket # @param ticket the FailTicket
# @return a BanTicket # @return a BanTicket
@staticmethod #@staticmethod
def createBanTicket(ticket): def createBanTicket(ticket):
ip = ticket.getIP() ip = ticket.getIP()
#lastTime = ticket.getTime() #lastTime = ticket.getTime()
@ -133,6 +133,7 @@ class BanManager:
banTicket = BanTicket(ip, lastTime) banTicket = BanTicket(ip, lastTime)
banTicket.setAttempt(ticket.getAttempt()) banTicket.setAttempt(ticket.getAttempt())
return banTicket return banTicket
createBanTicket = staticmethod(createBanTicket)
## ##
# Add a ban ticket. # Add a ban ticket.

View File

@ -1,50 +0,0 @@
# 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: Cyril Jaquier
#
# $Revision: 382 $
__author__ = "Cyril Jaquier"
__version__ = "$Revision: 382 $"
__date__ = "$Date: 2006-09-25 19:03:48 +0200 (Mon, 25 Sep 2006) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import logging
from ticket import Ticket
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban")
##
# Ban 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)

View File

@ -16,19 +16,19 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 607 $ # $Revision: 645 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 607 $" __version__ = "$Revision: 645 $"
__date__ = "$Date: 2007-08-09 00:16:22 +0200 (Thu, 09 Aug 2007) $" __date__ = "$Date: 2008-01-16 23:55:04 +0100 (Wed, 16 Jan 2008) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
import time, logging import time, logging
from datestrptime import DateStrptime from datetemplate import DateStrptime
from datetai64n import DateTai64n from datetemplate import DateTai64n
from dateepoch import DateEpoch from datetemplate import DateEpoch
from threading import Lock from threading import Lock
# Gets the instance of the logger. # Gets the instance of the logger.
@ -39,11 +39,10 @@ class DateDetector:
def __init__(self): def __init__(self):
self.__lock = Lock() self.__lock = Lock()
self.__templates = list() self.__templates = list()
self.__defTemplate = DateStrptime()
def addDefaultTemplate(self): def addDefaultTemplate(self):
self.__lock.acquire()
try: try:
self.__lock.acquire()
# standard # standard
template = DateStrptime() template = DateStrptime()
template.setName("Month Day Hour:Minute:Second") template.setName("Month Day Hour:Minute:Second")
@ -100,54 +99,31 @@ class DateDetector:
def getTemplates(self): def getTemplates(self):
return self.__templates return self.__templates
def setDefaultRegex(self, value):
self.__defTemplate.setRegex(value)
def getDefaultRegex(self):
return self.__defTemplate.getRegex()
def setDefaultPattern(self, value):
self.__defTemplate.setPattern(value)
def getDefaultPattern(self):
return self.__defTemplate.getPattern()
def matchTime(self, line): def matchTime(self, line):
if self.__defTemplate.isValid(): self.__lock.acquire()
return self.__defTemplate.matchDate(line) try:
else: for template in self.__templates:
try: match = template.matchDate(line)
self.__lock.acquire() if not match == None:
for template in self.__templates: return match
match = template.matchDate(line) return None
if not match == None: finally:
return match self.__lock.release()
return None
finally:
self.__lock.release()
def getTime(self, line): def getTime(self, line):
if self.__defTemplate.isValid(): self.__lock.acquire()
try: try:
date = self.__defTemplate.getDate(line) for template in self.__templates:
return date try:
except ValueError: date = template.getDate(line)
return None if date == None:
else: continue
try: return date
self.__lock.acquire() except ValueError:
for template in self.__templates: pass
try: return None
date = template.getDate(line) finally:
if date == None: self.__lock.release()
continue
template.incHits()
return date
except ValueError:
pass
return None
finally:
self.__lock.release()
def getUnixTime(self, line): def getUnixTime(self, line):
date = self.getTime(line) date = self.getTime(line)
@ -161,11 +137,10 @@ class DateDetector:
# in this object and thus should be called from time to time. # in this object and thus should be called from time to time.
def sortTemplate(self): def sortTemplate(self):
self.__lock.acquire()
try: try:
self.__lock.acquire()
logSys.debug("Sorting the template list") logSys.debug("Sorting the template list")
self.__templates.sort(cmp = lambda x, y: self.__templates.sort(lambda x, y: cmp(x.getHits(), y.getHits()))
cmp(x.getHits(), y.getHits()), self.__templates.reverse()
reverse = True)
finally: finally:
self.__lock.release() self.__lock.release()

View File

@ -1,44 +0,0 @@
# 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: Cyril Jaquier
#
# $Revision: 504 $
__author__ = "Cyril Jaquier"
__version__ = "$Revision: 504 $"
__date__ = "$Date: 2006-12-23 17:37:17 +0100 (Sat, 23 Dec 2006) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import time
from datetemplate import DateTemplate
class DateEpoch(DateTemplate):
def __init__(self):
DateTemplate.__init__(self)
# We already know the format for TAI64N
self.setRegex("^\d{10}(\.\d{6})?")
def getDate(self, line):
date = None
dateMatch = self.matchDate(line)
if dateMatch:
# extract part of format which represents seconds since epoch
date = list(time.gmtime(float(dateMatch.group())))
return date

View File

@ -1,91 +0,0 @@
# -*- coding: utf8 -*-
# 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: Cyril Jaquier
#
# $Revision: 504 $
__author__ = "Cyril Jaquier"
__version__ = "$Revision: 504 $"
__date__ = "$Date: 2006-12-23 17:37:17 +0100 (Sat, 23 Dec 2006) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
from mytime import MyTime
import time
from datetemplate import DateTemplate
##
# Use strptime() to parse a date. Our current locale is the 'C'
# one because we do not set the locale explicitly. This is POSIX
# 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"]
def __init__(self):
DateTemplate.__init__(self)
@staticmethod
def convertLocale(date):
for t in DateStrptime.TABLE:
for m in DateStrptime.TABLE[t]:
if date.find(m) >= 0:
return date.replace(m, t)
return date
def getDate(self, line):
date = None
dateMatch = self.matchDate(line)
if dateMatch:
try:
# Try first with 'C' locale
date = list(time.strptime(dateMatch.group(), self.getPattern()))
except ValueError:
# Try to convert date string to 'C' locale
conv = self.convertLocale(dateMatch.group())
try:
date = list(time.strptime(conv, self.getPattern()))
except ValueError:
# Try to add the current year to the pattern. Should fix
# the "Feb 29" issue.
conv += " %s" % MyTime.gmtime()[0]
pattern = "%s %%Y" % self.getPattern()
date = list(time.strptime(conv, pattern))
if date[0] < 2000:
# There is probably no year field in the logs
date[0] = MyTime.gmtime()[0]
# Bug fix for #1241756
# 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():
date[0] -= 1
return date

View File

@ -1,46 +0,0 @@
# 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: Cyril Jaquier
#
# $Revision: 504 $
__author__ = "Cyril Jaquier"
__version__ = "$Revision: 504 $"
__date__ = "$Date: 2006-12-23 17:37:17 +0100 (Sat, 23 Dec 2006) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import time
from datetemplate import DateTemplate
class DateTai64n(DateTemplate):
def __init__(self):
DateTemplate.__init__(self)
# We already know the format for TAI64N
self.setRegex("@[0-9a-f]{24}")
def getDate(self, line):
date = None
dateMatch = self.matchDate(line)
if dateMatch:
# extract part of format which represents seconds since epoch
value = dateMatch.group()
seconds_since_epoch = value[2:17]
date = list(time.gmtime(int(seconds_since_epoch, 16)))
return date

View File

@ -1,3 +1,4 @@
# -*- coding: utf8 -*-
# This file is part of Fail2Ban. # This file is part of Fail2Ban.
# #
# Fail2Ban is free software; you can redistribute it and/or modify # Fail2Ban is free software; you can redistribute it and/or modify
@ -16,15 +17,17 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 504 $ # $Revision: 652 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 504 $" __version__ = "$Revision: 652 $"
__date__ = "$Date: 2006-12-23 17:37:17 +0100 (Sat, 23 Dec 2006) $" __date__ = "$Date: 2008-02-29 00:01:30 +0100 (Fri, 29 Feb 2008) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
import re import re, time
from mytime import MyTime
class DateTemplate: class DateTemplate:
@ -32,7 +35,6 @@ class DateTemplate:
self.__name = "" self.__name = ""
self.__regex = "" self.__regex = ""
self.__cRegex = None self.__cRegex = None
self.__pattern = ""
self.__hits = 0 self.__hits = 0
def setName(self, name): def setName(self, name):
@ -48,24 +50,117 @@ class DateTemplate:
def getRegex(self): def getRegex(self):
return self.__regex return self.__regex
def getHits(self):
return self.__hits
def matchDate(self, line):
dateMatch = self.__cRegex.search(line)
if not dateMatch == None:
self.__hits += 1
return dateMatch
def getDate(self, line):
raise Exception("matchDate() is abstract")
class DateEpoch(DateTemplate):
def __init__(self):
DateTemplate.__init__(self)
# We already know the format for TAI64N
self.setRegex("^\d{10}(\.\d{6})?")
def getDate(self, line):
date = None
dateMatch = self.matchDate(line)
if dateMatch:
# extract part of format which represents seconds since epoch
date = list(time.localtime(float(dateMatch.group())))
return date
##
# Use strptime() to parse a date. Our current locale is the 'C'
# one because we do not set the locale explicitly. This is POSIX
# 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"]
def __init__(self):
DateTemplate.__init__(self)
self.__pattern = ""
def setPattern(self, pattern): def setPattern(self, pattern):
self.__pattern = pattern.strip() self.__pattern = pattern.strip()
def getPattern(self): def getPattern(self):
return self.__pattern return self.__pattern
def isValid(self): #@staticmethod
return self.__regex != "" and self.__pattern != "" def convertLocale(date):
for t in DateStrptime.TABLE:
def incHits(self): for m in DateStrptime.TABLE[t]:
self.__hits = self.__hits + 1 if date.find(m) >= 0:
return date.replace(m, t)
def getHits(self): return date
return self.__hits convertLocale = staticmethod(convertLocale)
def matchDate(self, line):
dateMatch = self.__cRegex.search(line)
return dateMatch
def getDate(self, line): def getDate(self, line):
raise Exception("matchDate() is abstract") date = None
dateMatch = self.matchDate(line)
if dateMatch:
try:
# Try first with 'C' locale
date = list(time.strptime(dateMatch.group(), self.getPattern()))
except ValueError:
# Try to convert date string to 'C' locale
conv = self.convertLocale(dateMatch.group())
try:
date = list(time.strptime(conv, self.getPattern()))
except ValueError:
# Try to add the current year to the pattern. Should fix
# the "Feb 29" issue.
conv += " %s" % MyTime.gmtime()[0]
pattern = "%s %%Y" % self.getPattern()
date = list(time.strptime(conv, pattern))
if date[0] < 2000:
# There is probably no year field in the logs
date[0] = MyTime.gmtime()[0]
# Bug fix for #1241756
# 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():
date[0] -= 1
return date
class DateTai64n(DateTemplate):
def __init__(self):
DateTemplate.__init__(self)
# We already know the format for TAI64N
self.setRegex("@[0-9a-f]{24}")
def getDate(self, line):
date = None
dateMatch = self.matchDate(line)
if dateMatch:
# extract part of format which represents seconds since epoch
value = dateMatch.group()
seconds_since_epoch = value[2:17]
date = list(time.gmtime(int(seconds_since_epoch, 16)))
return date

View File

@ -16,16 +16,16 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 553 $ # $Revision: 638 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 553 $" __version__ = "$Revision: 638 $"
__date__ = "$Date: 2007-02-26 00:53:22 +0100 (Mon, 26 Feb 2007) $" __date__ = "$Date: 2007-12-17 21:00:36 +0100 (Mon, 17 Dec 2007) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
from faildata import FailData from faildata import FailData
from failticket import FailTicket from ticket import FailTicket
from threading import Lock from threading import Lock
import logging import logging

View File

@ -16,15 +16,82 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 589 $ # $Revision: 642 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 589 $" __version__ = "$Revision: 642 $"
__date__ = "$Date: 2007-06-25 23:43:25 +0200 (Mon, 25 Jun 2007) $" __date__ = "$Date: 2008-01-05 23:33:44 +0100 (Sat, 05 Jan 2008) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
from regex import Regex, RegexException import re, sre_constants
##
# Regular expression class.
#
# This class represents a regular expression with its compiled version.
class Regex:
##
# Constructor.
#
# Creates a new object. This method can throw RegexException in order to
# avoid construction of invalid object.
# @param value the regular expression
def __init__(self, regex):
self._matchCache = None
# Perform shortcuts expansions.
# Replace "<HOST>" with default regular expression for host.
regex = regex.replace("<HOST>", "(?:::f{4,6}:)?(?P<host>\S+)")
if regex.lstrip() == '':
raise RegexException("Cannot add empty regex")
try:
self._regexObj = re.compile(regex)
self._regex = regex
except sre_constants.error:
raise RegexException("Unable to compile regular expression '%s'" %
regex)
##
# Gets the regular expression.
#
# The effective regular expression used is returned.
# @return the regular expression
def getRegex(self):
return self._regex
##
# Searches the regular expression.
#
# Sets an internal cache (match object) in order to avoid searching for
# the pattern again. This method must be called before calling any other
# method of this object.
# @param value the line
def search(self, value):
self._matchCache = self._regexObj.search(value)
##
# Checks if the previous call to search() matched.
#
# @return True if a match was found, False otherwise
def hasMatched(self):
if self._matchCache:
return True
else:
return False
##
# Exception dedicated to the class Regex.
class RegexException(Exception):
pass
## ##
# Regular expression class. # Regular expression class.
@ -56,5 +123,8 @@ class FailRegex(Regex):
def getHost(self): def getHost(self):
host = self._matchCache.group("host") host = self._matchCache.group("host")
if host == None: if host == None:
raise RegexException("Unexpected error. Please check your regex") # Gets a few information.
s = self._matchCache.string
r = self._matchCache.re
raise RegexException("No 'host' found in '%s' using '%s'" % (s, r))
return host return host

View File

@ -1,37 +0,0 @@
# 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: Cyril Jaquier
#
# $Revision: 382 $
__author__ = "Cyril Jaquier"
__version__ = "$Revision: 382 $"
__date__ = "$Date: 2006-09-25 19:03:48 +0200 (Mon, 25 Sep 2006) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import logging
from ticket import Ticket
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban")
class FailTicket(Ticket):
def __init__(self, ip, time):
Ticket.__init__(self, ip, time)

View File

@ -16,21 +16,20 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 605 $ # $Revision: 656 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 605 $" __version__ = "$Revision: 656 $"
__date__ = "$Date: 2007-08-08 00:11:34 +0200 (Wed, 08 Aug 2007) $" __date__ = "$Date: 2008-03-04 01:17:56 +0100 (Tue, 04 Mar 2008) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
from failmanager import FailManager from failmanager import FailManager
from failticket import FailTicket from ticket import FailTicket
from jailthread import JailThread from jailthread import JailThread
from datedetector import DateDetector from datedetector import DateDetector
from mytime import MyTime from mytime import MyTime
from regex import Regex, RegexException from failregex import FailRegex, Regex, RegexException
from failregex import FailRegex
import logging, re import logging, re
@ -58,11 +57,6 @@ class Filter(JailThread):
self.jail = jail self.jail = jail
## The failures manager. ## The failures manager.
self.failManager = FailManager() self.failManager = FailManager()
## The log file handler.
self.__crtHandler = None
self.__crtFilename = None
## The log file path.
self.__logPath = []
## The regular expression list matching the failures. ## The regular expression list matching the failures.
self.__failRegex = list() self.__failRegex = list()
## The regular expression list with expressions to ignore. ## The regular expression list with expressions to ignore.
@ -71,92 +65,12 @@ class Filter(JailThread):
self.__findTime = 6000 self.__findTime = 6000
## The ignore IP list. ## The ignore IP list.
self.__ignoreIpList = [] self.__ignoreIpList = []
## The last position of the file.
self.__lastPos = dict()
## The last date in tht log file.
self.__lastDate = dict()
self.dateDetector = DateDetector() self.dateDetector = DateDetector()
self.dateDetector.addDefaultTemplate() self.dateDetector.addDefaultTemplate()
logSys.info("Created Filter") logSys.info("Created Filter")
##
# Add a log file path
#
# @param path log file path
def addLogPath(self, path):
self.getLogPath().append(path)
# Initialize default values
self.__lastDate[path] = 0
self.__lastPos[path] = 0
##
# Delete a log path
#
# @param path the log file to delete
def delLogPath(self, path):
self.getLogPath().remove(path)
del self.__lastDate[path]
del self.__lastPos[path]
##
# Get the log file path
#
# @return log file path
def getLogPath(self):
return self.__logPath
##
# Check whether path is already monitored.
#
# @param path The path
# @return True if the path is already monitored else False
def containsLogPath(self, path):
try:
self.getLogPath().index(path)
return True
except ValueError:
return False
##
# Set the regular expression which matches the time.
#
# @param value the regular expression
def setTimeRegex(self, value):
self.dateDetector.setDefaultRegex(value)
logSys.info("Set default regex = %s" % value)
##
# Get the regular expression which matches the time.
#
# @return the regular expression
def getTimeRegex(self):
return self.dateDetector.getDefaultRegex()
##
# Set the time pattern.
#
# @param value the time pattern
def setTimePattern(self, value):
self.dateDetector.setDefaultPattern(value)
logSys.info("Set default pattern = %s" % value)
##
# Get the time pattern.
#
# @return the time pattern
def getTimePattern(self):
return self.dateDetector.getDefaultPattern()
## ##
# Add a regular expression which matches the failure. # Add a regular expression which matches the failure.
# #
@ -299,7 +213,7 @@ class Filter(JailThread):
for i in self.__ignoreIpList: for i in self.__ignoreIpList:
# An empty string is always false # An empty string is always false
if i == "": if i == "":
return False continue
s = i.split('/', 1) s = i.split('/', 1)
# IP address without CIDR mask # IP address without CIDR mask
if len(s) == 1: if len(s) == 1:
@ -314,104 +228,41 @@ class Filter(JailThread):
if ip in ips: if ip in ips:
return True return True
else: else:
return False continue
if a == b: if a == b:
return True return True
return False return False
##
# Open the log file. def processLine(self, line):
def __openLogFile(self, filename):
""" Opens the log file specified on init.
"""
try: try:
self.__crtFilename = filename # Decode line to UTF-8
self.__crtHandler = open(filename) l = line.decode('utf-8')
logSys.debug("Opened " + filename) except UnicodeDecodeError:
return True l = line
except OSError: timeMatch = self.dateDetector.matchTime(l)
logSys.error("Unable to open " + filename) if not timeMatch:
except IOError: # There is no valid time in this line
logSys.error("Unable to read " + filename + return []
". Please check permissions") # Lets split into time part and log part of the line
return False timeLine = timeMatch.group()
# Lets leave the beginning in as well, so if there is no
## # anchore at the beginning of the time regexp, we don't
# Close the log file. # at least allow injection. Should be harmless otherwise
logLine = l[:timeMatch.start()] + l[timeMatch.end():]
def __closeLogFile(self): return self.findFailure(timeLine, logLine)
self.__crtFilename = None
self.__crtHandler.close()
## def processLineAndAdd(self, line):
# Set the file position. for element in self.processLine(line):
# ip = element[0]
# Sets the file position. We must take care of log file rotation unixTime = element[1]
# and reset the position to 0 in that case. Use the log message if unixTime < MyTime.time() - self.getFindTime():
# timestamp in order to detect this.
def __setFilePos(self):
line = self.__crtHandler.readline()
lastDate = self.__lastDate[self.__crtFilename]
lineDate = self.dateDetector.getUnixTime(line)
if lastDate < lineDate:
logSys.debug("Date " + `lastDate` + " is smaller than " + `lineDate`)
logSys.debug("Log rotation detected for " + self.__crtFilename)
self.__lastPos[self.__crtFilename] = 0
lastPos = self.__lastPos[self.__crtFilename]
logSys.debug("Setting file position to " + `lastPos` + " for " +
self.__crtFilename)
self.__crtHandler.seek(lastPos)
##
# Get the file position.
def __getFilePos(self):
return self.__crtHandler.tell()
##
# Gets all the failure in the log file.
#
# Gets all the failure in the log file which are newer than
# MyTime.time()-self.findTime. When a failure is detected, a FailTicket
# is created and is added to the FailManager.
def getFailures(self, filename):
# Try to open log file.
if not self.__openLogFile(filename):
logSys.error("Unable to get failures in " + filename)
return False
self.__setFilePos()
lastLine = None
for line in self.__crtHandler:
if not self._isActive():
# The jail has been stopped
break break
try: if self.inIgnoreIPList(ip):
# Decode line to UTF-8 logSys.debug("Ignore %s" % ip)
line = line.decode('utf-8')
except UnicodeDecodeError:
pass
if not self.dateDetector.matchTime(line):
# There is no valid time in this line
continue continue
lastLine = line logSys.debug("Found %s" % ip)
for element in self.findFailure(line): self.failManager.addFailure(FailTicket(ip, unixTime))
ip = element[0]
unixTime = element[1]
if unixTime < MyTime.time()-self.__findTime:
break
if self.inIgnoreIPList(ip):
logSys.debug("Ignore "+ip)
continue
logSys.debug("Found "+ip)
self.failManager.addFailure(FailTicket(ip, unixTime))
self.__lastPos[filename] = self.__getFilePos()
if lastLine:
self.__lastDate[filename] = self.dateDetector.getUnixTime(lastLine)
self.__closeLogFile()
return True
## ##
# Returns true if the line should be ignored. # Returns true if the line should be ignored.
@ -428,27 +279,28 @@ class Filter(JailThread):
return False return False
## ##
# Finds the failure in a line. # Finds the failure in a line given split into time and log parts.
# #
# Uses the failregex pattern to find it and timeregex in order # Uses the failregex pattern to find it and timeregex in order
# to find the logging time. # to find the logging time.
# @return a dict with IP and timestamp. # @return a dict with IP and timestamp.
def findFailure(self, line): def findFailure(self, timeLine, logLine):
failList = list() failList = list()
# Checks if we must ignore this line. # Checks if we must ignore this line.
if self.ignoreLine(line): if self.ignoreLine(logLine):
# The ignoreregex matched. Return. # The ignoreregex matched. Return.
return failList return failList
# Iterates over all the regular expressions. # Iterates over all the regular expressions.
for failRegex in self.__failRegex: for failRegex in self.__failRegex:
failRegex.search(line) failRegex.search(logLine)
if failRegex.hasMatched(): if failRegex.hasMatched():
# The failregex matched. # The failregex matched.
date = self.dateDetector.getUnixTime(line) date = self.dateDetector.getUnixTime(timeLine)
if date == None: if date == None:
logSys.debug("Found a match but no valid date/time found " logSys.debug("Found a match for '" + logLine +"' but no "
+ "for " + line + ". Please contact the " + "valid date/time found for '"
+ timeLine + "'. Please contact the "
+ "author in order to get support for this " + "author in order to get support for this "
+ "format") + "format")
else: else:
@ -478,6 +330,157 @@ class Filter(JailThread):
return ret return ret
class FileFilter(Filter):
def __init__(self, jail):
Filter.__init__(self, jail)
## The log file path.
self.__logPath = []
##
# Add a log file path
#
# @param path log file path
def addLogPath(self, path, tail = False):
container = FileContainer(path, tail)
self.__logPath.append(container)
##
# Delete a log path
#
# @param path the log file to delete
def delLogPath(self, path):
for log in self.__logPath:
if log.getFileName() == path:
self.__logPath.remove(log)
return
##
# Get the log file path
#
# @return log file path
def getLogPath(self):
return self.__logPath
##
# Check whether path is already monitored.
#
# @param path The path
# @return True if the path is already monitored else False
def containsLogPath(self, path):
for log in self.__logPath:
if log.getFileName() == path:
return True
return False
def getFileContainer(self, path):
for log in self.__logPath:
if log.getFileName() == path:
return log
return None
##
# Gets all the failure in the log file.
#
# Gets all the failure in the log file which are newer than
# MyTime.time()-self.findTime. When a failure is detected, a FailTicket
# is created and is added to the FailManager.
def getFailures(self, filename):
container = self.getFileContainer(filename)
if container == None:
logSys.error("Unable to get failures in " + filename)
return False
# Try to open log file.
try:
container.open()
except Exception, e:
logSys.error("Unable to open %s" % filename)
logSys.exception(e)
return False
line = container.readline()
while not line == "":
if not self._isActive():
# The jail has been stopped
break
self.processLineAndAdd(line)
# Read a new line.
line = container.readline()
container.close()
return True
def status(self):
ret = Filter.status(self)
path = [m.getFileName() for m in self.getLogPath()]
ret.append(("File list", path))
return ret
##
# FileContainer class.
#
# This class manages a file handler and takes care of log rotation detection.
# In order to detect log rotation, the hash (MD5) of the first line of the file
# is computed and compared to the previous hash of this line.
import md5
class FileContainer:
def __init__(self, filename, tail = False):
self.__filename = filename
self.__tail = tail
self.__handler = None
# Try to open the file. Raises an exception if an error occured.
handler = open(filename)
try:
firstLine = handler.readline()
# Computes the MD5 of the first line.
self.__hash = md5.new(firstLine).digest()
# Start at the beginning of file if tail mode is off.
if tail:
handler.seek(0, 2)
self.__pos = handler.tell()
else:
self.__pos = 0
finally:
handler.close()
def getFileName(self):
return self.__filename
def open(self):
self.__handler = open(self.__filename)
firstLine = self.__handler.readline()
# Computes the MD5 of the first line.
myHash = md5.new(firstLine).digest()
# Compare hash.
if not self.__hash == myHash:
logSys.info("Log rotation detected for %s" % self.__filename)
self.__hash = myHash
self.__pos = 0
# Sets the file pointer to the last position.
self.__handler.seek(self.__pos)
def readline(self):
if self.__handler == None:
return ""
return self.__handler.readline()
def close(self):
if not self.__handler == None:
# Saves the last position.
self.__pos = self.__handler.tell()
# Closes the file.
self.__handler.close()
self.__handler = None
## ##
# Utils class for DNS and IP handling. # Utils class for DNS and IP handling.
# #
@ -488,10 +491,9 @@ import socket, struct
class DNSUtils: class DNSUtils:
DNS_CRE = re.compile("(?:(?:\w|-)+\.){2,}\w+")
IP_CRE = re.compile("(?:\d{1,3}\.){3}\d{1,3}") IP_CRE = re.compile("(?:\d{1,3}\.){3}\d{1,3}")
@staticmethod #@staticmethod
def dnsToIp(dns): def dnsToIp(dns):
""" Convert a DNS into an IP address using the Python socket module. """ Convert a DNS into an IP address using the Python socket module.
Thanks to Kevin Drapel. Thanks to Kevin Drapel.
@ -502,8 +504,9 @@ class DNSUtils:
logSys.warn("Unable to find a corresponding IP address for %s" logSys.warn("Unable to find a corresponding IP address for %s"
% dns) % dns)
return list() return list()
dnsToIp = staticmethod(dnsToIp)
@staticmethod #@staticmethod
def searchIP(text): def searchIP(text):
""" Search if an IP address if directly available and return """ Search if an IP address if directly available and return
it. it.
@ -513,8 +516,9 @@ class DNSUtils:
return match return match
else: else:
return None return None
searchIP = staticmethod(searchIP)
@staticmethod #@staticmethod
def isValidIP(string): def isValidIP(string):
""" Return true if str is a valid IP """ Return true if str is a valid IP
""" """
@ -524,8 +528,9 @@ class DNSUtils:
return True return True
except socket.error: except socket.error:
return False return False
isValidIP = staticmethod(isValidIP)
@staticmethod #@staticmethod
def textToIp(text): def textToIp(text):
""" Return the IP of DNS found in a given text. """ Return the IP of DNS found in a given text.
""" """
@ -542,8 +547,9 @@ class DNSUtils:
for e in ip: for e in ip:
ipList.append(e) ipList.append(e)
return ipList return ipList
textToIp = staticmethod(textToIp)
@staticmethod #@staticmethod
def cidr(i, n): def cidr(i, n):
""" Convert an IP address string with a CIDR mask into a 32-bit """ Convert an IP address string with a CIDR mask into a 32-bit
integer. integer.
@ -551,15 +557,18 @@ class DNSUtils:
# 32-bit IPv4 address mask # 32-bit IPv4 address mask
MASK = 0xFFFFFFFFL MASK = 0xFFFFFFFFL
return ~(MASK >> n) & MASK & DNSUtils.addr2bin(i) return ~(MASK >> n) & MASK & DNSUtils.addr2bin(i)
cidr = staticmethod(cidr)
@staticmethod #@staticmethod
def addr2bin(string): def addr2bin(string):
""" Convert a string IPv4 address into an unsigned integer. """ Convert a string IPv4 address into an unsigned integer.
""" """
return struct.unpack("!L", socket.inet_aton(string))[0] return struct.unpack("!L", socket.inet_aton(string))[0]
addr2bin = staticmethod(addr2bin)
@staticmethod #@staticmethod
def bin2addr(addr): def bin2addr(addr):
""" Convert a numeric IPv4 address into string n.n.n.n form. """ Convert a numeric IPv4 address into string n.n.n.n form.
""" """
return socket.inet_ntoa(struct.pack("!L", addr)) return socket.inet_ntoa(struct.pack("!L", addr))
bin2addr = staticmethod(bin2addr)

View File

@ -16,16 +16,16 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 567 $ # $Revision: 649 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 567 $" __version__ = "$Revision: 649 $"
__date__ = "$Date: 2007-03-26 23:17:31 +0200 (Mon, 26 Mar 2007) $" __date__ = "$Date: 2008-02-02 18:04:11 +0100 (Sat, 02 Feb 2008) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
from failmanager import FailManagerEmpty from failmanager import FailManagerEmpty
from filter import Filter from filter import FileFilter
from mytime import MyTime from mytime import MyTime
import time, logging, gamin import time, logging, gamin
@ -40,7 +40,7 @@ logSys = logging.getLogger("fail2ban.filter")
# that matches a given regular expression. This class is instanciated by # that matches a given regular expression. This class is instanciated by
# a Jail object. # a Jail object.
class FilterGamin(Filter): class FilterGamin(FileFilter):
## ##
# Constructor. # Constructor.
@ -49,7 +49,7 @@ class FilterGamin(Filter):
# @param jail the jail object # @param jail the jail object
def __init__(self, jail): def __init__(self, jail):
Filter.__init__(self, jail) FileFilter.__init__(self, jail)
self.__modified = False self.__modified = False
# Gamin monitor # Gamin monitor
self.monitor = gamin.WatchMonitor() self.monitor = gamin.WatchMonitor()
@ -69,12 +69,12 @@ class FilterGamin(Filter):
# #
# @param path log file path # @param path log file path
def addLogPath(self, path): def addLogPath(self, path, tail = False):
if self.containsLogPath(path): if self.containsLogPath(path):
logSys.error(path + " already exists") logSys.error(path + " already exists")
else: else:
self.monitor.watch_file(path, self.callback) self.monitor.watch_file(path, self.callback)
Filter.addLogPath(self, path) FileFilter.addLogPath(self, path, tail)
logSys.info("Added logfile = %s" % path) logSys.info("Added logfile = %s" % path)
## ##
@ -87,7 +87,7 @@ class FilterGamin(Filter):
logSys.error(path + " is not monitored") logSys.error(path + " is not monitored")
else: else:
self.monitor.stop_watch(path) self.monitor.stop_watch(path)
Filter.delLogPath(self, path) FileFilter.delLogPath(self, path)
logSys.info("Removed logfile = %s" % path) logSys.info("Removed logfile = %s" % path)
## ##
@ -126,6 +126,6 @@ class FilterGamin(Filter):
# Desallocates the resources used by Gamin. # Desallocates the resources used by Gamin.
def __cleanup(self): def __cleanup(self):
for path in Filter.getLogPath(self): for path in self.getLogPath():
self.monitor.stop_watch(path) self.monitor.stop_watch(path.getFileName())
del self.monitor del self.monitor

View File

@ -16,16 +16,16 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 567 $ # $Revision: 649 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 567 $" __version__ = "$Revision: 649 $"
__date__ = "$Date: 2007-03-26 23:17:31 +0200 (Mon, 26 Mar 2007) $" __date__ = "$Date: 2008-02-02 18:04:11 +0100 (Sat, 02 Feb 2008) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
from failmanager import FailManagerEmpty from failmanager import FailManagerEmpty
from filter import Filter from filter import FileFilter
from mytime import MyTime from mytime import MyTime
import time, logging, os import time, logging, os
@ -40,7 +40,7 @@ logSys = logging.getLogger("fail2ban.filter")
# that matches a given regular expression. This class is instanciated by # that matches a given regular expression. This class is instanciated by
# a Jail object. # a Jail object.
class FilterPoll(Filter): class FilterPoll(FileFilter):
## ##
# Constructor. # Constructor.
@ -49,7 +49,7 @@ class FilterPoll(Filter):
# @param jail the jail object # @param jail the jail object
def __init__(self, jail): def __init__(self, jail):
Filter.__init__(self, jail) FileFilter.__init__(self, jail)
self.__modified = False self.__modified = False
## The time of the last modification of the file. ## The time of the last modification of the file.
self.__lastModTime = dict() self.__lastModTime = dict()
@ -61,13 +61,13 @@ class FilterPoll(Filter):
# #
# @param path log file path # @param path log file path
def addLogPath(self, path): def addLogPath(self, path, tail = False):
if self.containsLogPath(path): if self.containsLogPath(path):
logSys.error(path + " already exists") logSys.error(path + " already exists")
else: else:
self.__lastModTime[path] = 0 self.__lastModTime[path] = 0
self.__file404Cnt[path] = 0 self.__file404Cnt[path] = 0
Filter.addLogPath(self, path) FileFilter.addLogPath(self, path, tail)
logSys.info("Added logfile = %s" % path) logSys.info("Added logfile = %s" % path)
## ##
@ -81,7 +81,7 @@ class FilterPoll(Filter):
else: else:
del self.__lastModTime[path] del self.__lastModTime[path]
del self.__file404Cnt[path] del self.__file404Cnt[path]
Filter.delLogPath(self, path) FileFilter.delLogPath(self, path)
logSys.info("Removed logfile = %s" % path) logSys.info("Removed logfile = %s" % path)
## ##
@ -96,9 +96,9 @@ class FilterPoll(Filter):
while self._isActive(): while self._isActive():
if not self.getIdle(): if not self.getIdle():
# Get file modification # Get file modification
for f in self.getLogPath(): for container in self.getLogPath():
if self.isModified(f): if self.isModified(container.getFileName()):
self.getFailures(f) self.getFailures(container.getFileName())
self.__modified = True self.__modified = True
if self.__modified: if self.__modified:

View File

@ -16,11 +16,11 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 556 $ # $Revision: 635 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 556 $" __version__ = "$Revision: 635 $"
__date__ = "$Date: 2007-03-07 21:54:32 +0100 (Wed, 07 Mar 2007) $" __date__ = "$Date: 2007-12-16 22:38:04 +0100 (Sun, 16 Dec 2007) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
@ -46,31 +46,34 @@ class MyTime:
# #
# @param t the time to set or None # @param t the time to set or None
@staticmethod #@staticmethod
def setTime(t): def setTime(t):
MyTime.myTime = t MyTime.myTime = t
setTime = staticmethod(setTime)
## ##
# Equivalent to time.time() # Equivalent to time.time()
# #
# @return time.time() if setTime was called with None # @return time.time() if setTime was called with None
@staticmethod #@staticmethod
def time(): def time():
if MyTime.myTime == None: if MyTime.myTime == None:
return time.time() return time.time()
else: else:
return MyTime.myTime return MyTime.myTime
time = staticmethod(time)
## ##
# Equivalent to time.gmtime() # Equivalent to time.gmtime()
# #
# @return time.gmtime() if setTime was called with None # @return time.gmtime() if setTime was called with None
@staticmethod #@staticmethod
def gmtime(): def gmtime():
if MyTime.myTime == None: if MyTime.myTime == None:
return time.gmtime() return time.gmtime()
else: else:
return time.gmtime(MyTime.myTime) return time.gmtime(MyTime.myTime)
gmtime = staticmethod(gmtime)

View File

@ -1,93 +0,0 @@
# 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: Cyril Jaquier
#
# $Revision: 589 $
__author__ = "Cyril Jaquier"
__version__ = "$Revision: 589 $"
__date__ = "$Date: 2007-06-25 23:43:25 +0200 (Mon, 25 Jun 2007) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import re, sre_constants
##
# Regular expression class.
#
# This class represents a regular expression with its compiled version.
class Regex:
##
# Constructor.
#
# Creates a new object. This method can throw RegexException in order to
# avoid construction of invalid object.
# @param value the regular expression
def __init__(self, regex):
self._matchCache = None
# Perform shortcuts expansions.
# Replace "<HOST>" with default regular expression for host.
regex = regex.replace("<HOST>", "(?:::f{4,6}:)?(?P<host>\S+)")
if regex.lstrip() == '':
raise RegexException("Cannot add empty regex")
try:
self._regexObj = re.compile(regex)
self._regex = regex
except sre_constants.error:
raise RegexException("Unable to compile regular expression '%s'" %
regex)
##
# Gets the regular expression.
#
# The effective regular expression used is returned.
# @return the regular expression
def getRegex(self):
return self._regex
##
# Searches the regular expression.
#
# Sets an internal cache (match object) in order to avoid searching for
# the pattern again. This method must be called before calling any other
# method of this object.
# @param value the line
def search(self, value):
self._matchCache = self._regexObj.search(value)
##
# Checks if the previous call to search() matched.
#
# @return True if a match was found, False otherwise
def hasMatched(self):
if self._matchCache:
return True
else:
return False
##
# Exception dedicated to the class Regex.
class RegexException(Exception):
pass

View File

@ -16,25 +16,28 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 567 $ # $Revision: 647 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 567 $" __version__ = "$Revision: 647 $"
__date__ = "$Date: 2007-03-26 23:17:31 +0200 (Mon, 26 Mar 2007) $" __date__ = "$Date: 2008-01-20 17:30:35 +0100 (Sun, 20 Jan 2008) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
from threading import Lock, RLock from threading import Lock, RLock
from jails import Jails from jails import Jails
from transmitter import Transmitter from transmitter import Transmitter
from communication.asyncserver import AsyncServer from asyncserver import AsyncServer
from communication.asyncserver import AsyncServerException from asyncserver import AsyncServerException
from common import version
import logging, logging.handlers, sys, os, signal import logging, logging.handlers, sys, os, signal
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.server") logSys = logging.getLogger("fail2ban.server")
class Server: class Server:
PID_FILE = "/var/run/fail2ban/fail2ban.pid"
def __init__(self, daemon = False): def __init__(self, daemon = False):
self.__loggingLock = Lock() self.__loggingLock = Lock()
@ -54,7 +57,16 @@ class Server:
self.quit() self.quit()
def start(self, sock, force = False): def start(self, sock, force = False):
logSys.info("Starting Fail2ban") logSys.info("Starting Fail2ban v" + version.version)
# Creates a PID file.
try:
logSys.debug("Creating PID file %s" % Server.PID_FILE)
pidFile = open(Server.PID_FILE, 'w')
pidFile.write("%s\n" % os.getpid())
pidFile.close()
except IOError, e:
logSys.error("Unable to create PID file: %s" % e)
# Install signal handlers # Install signal handlers
signal.signal(signal.SIGTERM, self.__sigTERMhandler) signal.signal(signal.SIGTERM, self.__sigTERMhandler)
@ -73,14 +85,26 @@ class Server:
logSys.debug("Starting communication") logSys.debug("Starting communication")
try: try:
self.__asyncServer.start(sock, force) self.__asyncServer.start(sock, force)
except AsyncServerException: except AsyncServerException, e:
logSys.error("Could not start server") logSys.error("Could not start server: %s", e)
# Removes the PID file.
try:
logSys.debug("Remove PID file %s" % Server.PID_FILE)
os.remove(Server.PID_FILE)
except OSError, e:
logSys.error("Unable to remove PID file: %s" % e)
logSys.info("Exiting Fail2ban") logSys.info("Exiting Fail2ban")
def quit(self): def quit(self):
self.stopAllJail() self.stopAllJail()
# Stop communication # Stop communication
self.__asyncServer.stop() self.__asyncServer.stop()
# Shutdowns the logging.
try:
self.__loggingLock.acquire()
logging.shutdown()
finally:
self.__loggingLock.release()
def addJail(self, name, backend): def addJail(self, name, backend):
self.__jails.add(name, backend) self.__jails.add(name, backend)
@ -142,18 +166,6 @@ class Server:
def getLogPath(self, name): def getLogPath(self, name):
return self.__jails.getFilter(name).getLogPath() return self.__jails.getFilter(name).getLogPath()
def setTimeRegex(self, name, value):
self.__jails.getFilter(name).setTimeRegex(value)
def getTimeRegex(self, name):
return self.__jails.getFilter(name).getTimeRegex()
def setTimePattern(self, name, value):
self.__jails.getFilter(name).setTimePattern(value)
def getTimePattern(self, name):
return self.__jails.getFilter(name).getTimePattern()
def setFindTime(self, name, value): def setFindTime(self, name, value):
self.__jails.getFilter(name).setFindTime(value) self.__jails.getFilter(name).setFindTime(value)
@ -310,7 +322,11 @@ class Server:
def setLogTarget(self, target): def setLogTarget(self, target):
try: try:
self.__loggingLock.acquire() self.__loggingLock.acquire()
# set a format which is simpler for console use
formatter = logging.Formatter("%(asctime)s %(name)-16s: %(levelname)-6s %(message)s")
if target == "SYSLOG": if target == "SYSLOG":
# Syslog daemons already add date to the message.
formatter = logging.Formatter("%(name)-16s: %(levelname)-6s %(message)s")
facility = logging.handlers.SysLogHandler.LOG_DAEMON facility = logging.handlers.SysLogHandler.LOG_DAEMON
hdlr = logging.handlers.SysLogHandler("/dev/log", hdlr = logging.handlers.SysLogHandler("/dev/log",
facility = facility) facility = facility)
@ -331,10 +347,8 @@ class Server:
# Removes previous handlers # Removes previous handlers
for handler in logging.getLogger("fail2ban").handlers: for handler in logging.getLogger("fail2ban").handlers:
# Closes the handler. # Closes the handler.
handler.close()
logging.getLogger("fail2ban").removeHandler(handler) logging.getLogger("fail2ban").removeHandler(handler)
# set a format which is simpler for console use handler.close()
formatter = logging.Formatter("%(asctime)s %(name)-16s: %(levelname)-6s %(message)s")
# tell the handler to use this format # tell the handler to use this format
hdlr.setFormatter(formatter) hdlr.setFormatter(formatter)
logging.getLogger("fail2ban").addHandler(hdlr) logging.getLogger("fail2ban").addHandler(hdlr)

View File

@ -16,11 +16,11 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 382 $ # $Revision: 638 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 382 $" __version__ = "$Revision: 638 $"
__date__ = "$Date: 2006-09-25 19:03:48 +0200 (Mon, 25 Sep 2006) $" __date__ = "$Date: 2007-12-17 21:00:36 +0100 (Mon, 17 Dec 2007) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
@ -53,4 +53,28 @@ class Ticket:
def getAttempt(self): def getAttempt(self):
return self.__attempt return self.__attempt
class FailTicket(Ticket):
def __init__(self, ip, time):
Ticket.__init__(self, ip, time)
##
# Ban 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)

View File

@ -16,11 +16,11 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 503 $ # $Revision: 639 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 503 $" __version__ = "$Revision: 639 $"
__date__ = "$Date: 2006-12-23 17:31:00 +0100 (Sat, 23 Dec 2006) $" __date__ = "$Date: 2007-12-17 21:04:29 +0100 (Mon, 17 Dec 2007) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
@ -135,14 +135,6 @@ class Transmitter:
value = command[2] value = command[2]
self.__server.delLogPath(name, value) self.__server.delLogPath(name, value)
return self.__server.getLogPath(name) return self.__server.getLogPath(name)
elif command[1] == "timeregex":
value = command[2]
self.__server.setTimeRegex(name, value)
return self.__server.getTimeRegex(name)
elif command[1] == "timepattern":
value = command[2]
self.__server.setTimePattern(name, value)
return self.__server.getTimePattern(name)
elif command[1] == "addfailregex": elif command[1] == "addfailregex":
value = command[2] value = command[2]
self.__server.addFailRegex(name, value) self.__server.addFailRegex(name, value)
@ -229,10 +221,6 @@ class Transmitter:
return self.__server.getLogPath(name) return self.__server.getLogPath(name)
elif command[1] == "ignoreip": elif command[1] == "ignoreip":
return self.__server.getIgnoreIP(name) return self.__server.getIgnoreIP(name)
elif command[1] == "timeregex":
return self.__server.getTimeRegex(name)
elif command[1] == "timepattern":
return self.__server.getTimePattern(name)
elif command[1] == "failregex": elif command[1] == "failregex":
return self.__server.getFailRegex(name) return self.__server.getFailRegex(name)
elif command[1] == "ignoreregex": elif command[1] == "ignoreregex":

View File

@ -18,11 +18,11 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 522 $ # $Revision: 671 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 522 $" __version__ = "$Revision: 671 $"
__date__ = "$Date: 2007-01-21 23:19:57 +0100 (Sun, 21 Jan 2007) $" __date__ = "$Date: 2008-03-06 00:12:41 +0100 (Thu, 06 Mar 2008) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
@ -45,8 +45,8 @@ setup(
description = "Ban IPs that make too many password failure", description = "Ban IPs that make too many password failure",
long_description = longdesc, long_description = longdesc,
author = "Cyril Jaquier", author = "Cyril Jaquier",
author_email = "lostcontrol@users.sourceforge.net", author_email = "cyril.jaquier@fail2ban.org",
url = "http://fail2ban.sourceforge.net", url = "http://www.fail2ban.org",
license = "GPL", license = "GPL",
platforms = "Posix", platforms = "Posix",
scripts = [ scripts = [

View File

@ -16,17 +16,17 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 382 $ # $Revision: 638 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 382 $" __version__ = "$Revision: 638 $"
__date__ = "$Date: 2006-09-25 19:03:48 +0200 (Mon, 25 Sep 2006) $" __date__ = "$Date: 2007-12-17 21:00:36 +0100 (Mon, 17 Dec 2007) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
import unittest, socket, time, pickle import unittest
from server.banmanager import BanManager from server.banmanager import BanManager
from server.banticket import BanTicket from server.ticket import BanTicket
class AddFailure(unittest.TestCase): class AddFailure(unittest.TestCase):

View File

@ -16,11 +16,11 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 504 $ # $Revision: 650 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 504 $" __version__ = "$Revision: 650 $"
__date__ = "$Date: 2006-12-23 17:37:17 +0100 (Sat, 23 Dec 2006) $" __date__ = "$Date: 2008-02-02 21:07:06 +0100 (Sat, 02 Feb 2008) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
@ -40,8 +40,8 @@ class DateDetectorTest(unittest.TestCase):
def testGetEpochTime(self): def testGetEpochTime(self):
log = "1138049999 [sshd] error: PAM: Authentication failure" log = "1138049999 [sshd] error: PAM: Authentication failure"
date = [2006, 1, 23, 20, 59, 59, 0, 23, 0] date = [2006, 1, 23, 21, 59, 59, 0, 23, 0]
dateUnix = 1138046399.0 dateUnix = 1138049999.0
self.assertEqual(self.__datedetector.getTime(log), date) self.assertEqual(self.__datedetector.getTime(log), date)
self.assertEqual(self.__datedetector.getUnixTime(log), dateUnix) self.assertEqual(self.__datedetector.getUnixTime(log), dateUnix)
@ -54,14 +54,14 @@ class DateDetectorTest(unittest.TestCase):
self.assertEqual(self.__datedetector.getTime(log), date) self.assertEqual(self.__datedetector.getTime(log), date)
self.assertEqual(self.__datedetector.getUnixTime(log), dateUnix) self.assertEqual(self.__datedetector.getUnixTime(log), dateUnix)
def testDefaultTempate(self): # def testDefaultTempate(self):
self.__datedetector.setDefaultRegex("^\S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}") # self.__datedetector.setDefaultRegex("^\S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}")
self.__datedetector.setDefaultPattern("%b %d %H:%M:%S") # self.__datedetector.setDefaultPattern("%b %d %H:%M:%S")
#
log = "Jan 23 21:59:59 [sshd] error: PAM: Authentication failure" # log = "Jan 23 21:59:59 [sshd] error: PAM: Authentication failure"
date = [2005, 1, 23, 21, 59, 59, 1, 23, -1] # date = [2005, 1, 23, 21, 59, 59, 1, 23, -1]
dateUnix = 1106513999.0 # dateUnix = 1106513999.0
#
self.assertEqual(self.__datedetector.getTime(log), date) # self.assertEqual(self.__datedetector.getTime(log), date)
self.assertEqual(self.__datedetector.getUnixTime(log), dateUnix) # self.assertEqual(self.__datedetector.getUnixTime(log), dateUnix)

View File

@ -16,18 +16,17 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 382 $ # $Revision: 638 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 382 $" __version__ = "$Revision: 638 $"
__date__ = "$Date: 2006-09-25 19:03:48 +0200 (Mon, 25 Sep 2006) $" __date__ = "$Date: 2007-12-17 21:00:36 +0100 (Mon, 17 Dec 2007) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
import unittest, socket, time, pickle import unittest, socket, time, pickle
from server.failmanager import FailManager from server.failmanager import FailManager, FailManagerEmpty
from server.failmanager import FailManagerEmpty from server.ticket import FailTicket
from server.failticket import FailTicket
class AddFailure(unittest.TestCase): class AddFailure(unittest.TestCase):

View File

@ -16,17 +16,17 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 503 $ # $Revision: 641 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 503 $" __version__ = "$Revision: 641 $"
__date__ = "$Date: 2006-12-23 17:31:00 +0100 (Sat, 23 Dec 2006) $" __date__ = "$Date: 2007-12-26 12:46:22 +0100 (Wed, 26 Dec 2007) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
import unittest import unittest
from server.filterpoll import FilterPoll from server.filterpoll import FilterPoll
from server.filter import Filter from server.filter import FileFilter
from server.failmanager import FailManager from server.failmanager import FailManager
from server.failmanager import FailManagerEmpty from server.failmanager import FailManagerEmpty
@ -34,7 +34,7 @@ class IgnoreIP(unittest.TestCase):
def setUp(self): def setUp(self):
"""Call before every test case.""" """Call before every test case."""
self.__filter = Filter(None) self.__filter = FileFilter(None)
def tearDown(self): def tearDown(self):
"""Call after every test case.""" """Call after every test case."""
@ -86,7 +86,7 @@ class GetFailures(unittest.TestCase):
def setUp(self): def setUp(self):
"""Call before every test case.""" """Call before every test case."""
self.__filter = Filter(None) self.__filter = FileFilter(None)
self.__filter.setActive(True) self.__filter.setActive(True)
# TODO Test this # TODO Test this
#self.__filter.setTimeRegex("\S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}") #self.__filter.setTimeRegex("\S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}")