- Added support for several "failregex" and "ignoreregex". This should simplify the configuration files.

- Configuration files are backward-compatible but need to be updated in order to take advantage of this feature.

git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@503 a942ae1a-1317-0410-a47c-b1dcaea8d605
0.x
Cyril Jaquier 2006-12-23 16:31:00 +00:00
parent 34a48157dc
commit 0f31cc0feb
15 changed files with 349 additions and 118 deletions

View File

@ -15,7 +15,8 @@ ver. 0.7.6 (200?/??/??) - ???
- Fixed removal of host in hosts.deny. Thanks to René Berber - Fixed removal of host in hosts.deny. Thanks to René Berber
- Added new date format (2006-12-21 06:43:20) and Exim4 - Added new date format (2006-12-21 06:43:20) and Exim4
filter. Thanks to mEDI filter. Thanks to mEDI
- Improved regular expression checking a bit - Several "failregex" and "ignoreregex" are now accepted.
Creation of rules should be easier now.
ver. 0.7.5 (2006/12/07) - beta ver. 0.7.5 (2006/12/07) - beta
---------- ----------

View File

@ -39,6 +39,8 @@ server/dateepoch.py
server/banmanager.py server/banmanager.py
server/datetemplate.py server/datetemplate.py
server/mytime.py server/mytime.py
server/regex.py
server/failregex.py
testcases/banmanagertestcase.py testcases/banmanagertestcase.py
testcases/failmanagertestcase.py testcases/failmanagertestcase.py
testcases/clientreadertestcase.py testcases/clientreadertestcase.py

View File

@ -109,7 +109,18 @@ class Beautifier:
msg = "These IP addresses/networks are ignored:\n" msg = "These IP addresses/networks are ignored:\n"
for ip in response[:-1]: for ip in response[:-1]:
msg = msg + "|- " + ip + "\n" msg = msg + "|- " + ip + "\n"
msg = msg + "`- " + response[len(response)-1] msg = msg + "`- " + response[len(response)-1]
elif inC[2] in ("failregex", "addfailregex", "delfailregex",
"ignoreregex", "addignoreregex", "delignoreregex"):
if len(response) == 0:
msg = "No regular expression is defined"
else:
msg = "The following regular expression are defined:\n"
c = 0
for ip in response[:-1]:
msg = msg + "|- [" + str(c) + "]: " + ip + "\n"
c += 1
msg = msg + "`- [" + str(c) + "]: " + response[len(response)-1]
except Exception: except Exception:
logSys.warn("Beautifier error. Please report the error") logSys.warn("Beautifier error. Please report the error")
logSys.error("Beautify " + `response` + " with " + `self.__inputCmd` + logSys.error("Beautify " + `response` + " with " + `self.__inputCmd` +

View File

@ -67,8 +67,10 @@ class FilterReader(ConfigReader):
elif opt == "timepattern": elif opt == "timepattern":
stream.append(["set", self.__name, "timepattern", self.__opts[opt]]) stream.append(["set", self.__name, "timepattern", self.__opts[opt]])
elif opt == "failregex": elif opt == "failregex":
stream.append(["set", self.__name, "failregex", self.__opts[opt]]) for regex in self.__opts[opt].split('\n'):
stream.append(["set", self.__name, "addfailregex", regex])
elif opt == "ignoreregex": elif opt == "ignoreregex":
stream.append(["set", self.__name, "ignoreregex", self.__opts[opt]]) for regex in self.__opts[opt].split('\n'):
stream.append(["set", self.__name, "addignoreregex", regex])
return stream return stream

View File

@ -54,8 +54,10 @@ protocol = [
["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> 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> timepattern <PATTERN>", "sets the pattern <PATTERN> to match the date format for <JAIL>. This will disable the autodetection feature."],
["set <JAIL> failregex <REGEX>", "sets 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> ignoreregex <REGEX>", "sets the regular expression <REGEX> which should match pattern to exclude for <JAIL>"], ["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> delignoreregex <INDEX>", "removes the regular expression at <INDEX> for ignoreregex"],
["set <JAIL> findtime <TIME>", "sets the number of seconds <TIME> for which the filter will look back for <JAIL>"], ["set <JAIL> findtime <TIME>", "sets the number of seconds <TIME> for which the filter will look back for <JAIL>"],
["set <JAIL> bantime <TIME>", "sets the number of seconds <TIME> a host will be banned for <JAIL>"], ["set <JAIL> bantime <TIME>", "sets the number of seconds <TIME> a host will be banned for <JAIL>"],
["set <JAIL> maxretry <RETRY>", "sets the number of failures <RETRY> before banning the host for <JAIL>"], ["set <JAIL> maxretry <RETRY>", "sets the number of failures <RETRY> before banning the host for <JAIL>"],
@ -73,8 +75,8 @@ protocol = [
["get <JAIL> ignoreip", "gets the list of ignored IP addresses for <JAIL>"], ["get <JAIL> ignoreip", "gets the list of ignored IP addresses for <JAIL>"],
["get <JAIL> timeregex", "gets the regular expression used for the time detection for <JAIL>"], ["get <JAIL> timeregex", "gets the regular expression used for the time detection for <JAIL>"],
["get <JAIL> timepattern", "gets the pattern used for the time detection for <JAIL>"], ["get <JAIL> timepattern", "gets the pattern used for the time detection for <JAIL>"],
["get <JAIL> failregex", "gets the regular expression which matches the failures for <JAIL>"], ["get <JAIL> failregex", "gets the list of regular expressions which matches the failures for <JAIL>"],
["get <JAIL> ignoreregex", "gets the regular expression which matches patterns to ignore for <JAIL>"], ["get <JAIL> ignoreregex", "gets the list of regular expressions which matches patterns to ignore for <JAIL>"],
["get <JAIL> findtime", "gets the time for which the filter will look back for failures for <JAIL>"], ["get <JAIL> findtime", "gets the time for which the filter will look back for failures for <JAIL>"],
["get <JAIL> bantime", "gets the time a host is banned for <JAIL>"], ["get <JAIL> bantime", "gets the time a host is banned for <JAIL>"],
["get <JAIL> maxretry", "gets the number of failures allowed for <JAIL>"], ["get <JAIL> maxretry", "gets the number of failures allowed for <JAIL>"],

View File

@ -33,6 +33,7 @@ sys.path.insert(1, "/usr/lib/fail2ban")
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
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.regex") logSys = logging.getLogger("fail2ban.regex")
@ -83,17 +84,17 @@ class Fail2banRegex:
self.dispVersion() self.dispVersion()
sys.exit(0) sys.exit(0)
def setRegex(self, value): def testRegex(self, line, regex):
print
self.__filter.setFailRegex(value)
def testRegex(self, line):
print print
try: try:
logging.getLogger("fail2ban").setLevel(logging.DEBUG) logging.getLogger("fail2ban").setLevel(logging.DEBUG)
self.__filter.addFailRegex(regex)
ret = self.__filter.findFailure(line) ret = self.__filter.findFailure(line)
print print
logging.getLogger("fail2ban").setLevel(logging.CRITICAL) logging.getLogger("fail2ban").setLevel(logging.CRITICAL)
except RegexException, e:
print e
return False
except IndexError: except IndexError:
print "Sorry, but no <host> found in regex" print "Sorry, but no <host> found in regex"
return False return False
@ -156,8 +157,7 @@ if __name__ == "__main__":
regex.dispUsage() regex.dispUsage()
sys.exit(-1) sys.exit(-1)
else: else:
regex.setRegex(sys.argv[2]) ret = regex.testRegex(sys.argv[1], sys.argv[2])
ret = regex.testRegex(sys.argv[1])
if ret: if ret:
sys.exit(0) sys.exit(0)
else: else:

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-CLIENT "1" "December 2006" "fail2ban-client v0.7.4-SVN" "User Commands" .TH FAIL2BAN-CLIENT "1" "December 2006" "fail2ban-client v0.7.5-SVN" "User Commands"
.SH NAME .SH NAME
fail2ban-client \- configure and control the server fail2ban-client \- configure and control the server
.SH SYNOPSIS .SH SYNOPSIS
.B fail2ban-client .B fail2ban-client
[\fIOPTIONS\fR]... \fI<COMMAND>\fR [\fIOPTIONS\fR]... \fI<COMMAND>\fR
.SH DESCRIPTION .SH DESCRIPTION
Fail2Ban v0.7.4\-SVN reads log file that contains password failure report Fail2Ban v0.7.5\-SVN 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
@ -37,6 +37,8 @@ display this help message
\fB\-V\fR, \fB\-\-version\fR \fB\-V\fR, \fB\-\-version\fR
print the version print the version
.SH COMMAND .SH COMMAND
.IP
Basic
.TP .TP
\fBstart\fR \fBstart\fR
starts the server and the jails starts the server and the jails
@ -54,6 +56,8 @@ server
.TP .TP
\fBping\fR \fBping\fR
tests if the server is alive tests if the server is alive
.IP
Logging
.TP .TP
\fBset loglevel <LEVEL>\fR \fBset loglevel <LEVEL>\fR
sets logging level to <LEVEL>. 0 sets logging level to <LEVEL>. 0
@ -69,10 +73,24 @@ file
.TP .TP
\fBget logtarget\fR \fBget logtarget\fR
gets logging target gets logging target
.IP
Jail control
.TP .TP
\fBadd <JAIL> <BACKEND>\fR \fBadd <JAIL> <BACKEND>\fR
creates <JAIL> using <BACKEND> creates <JAIL> using <BACKEND>
.TP .TP
\fBstart <JAIL>\fR
starts the jail <JAIL>
.TP
\fBstop <JAIL>\fR
stops the jail <JAIL>. The jail is
removed
.TP
\fBstatus <JAIL>\fR
gets the current status of <JAIL>
.IP
Jail configuration
.TP
\fBset <JAIL> idle on|off\fR \fBset <JAIL> idle on|off\fR
sets the idle state of <JAIL> sets the idle state of <JAIL>
.TP .TP
@ -104,16 +122,24 @@ match the date format for <JAIL>.
This will disable the This will disable the
autodetection feature. autodetection feature.
.TP .TP
\fBset <JAIL> failregex <REGEX>\fR \fBset <JAIL> addfailregex <REGEX>\fR
sets the regular expression adds the regular expression
<REGEX> which must match failures <REGEX> which must match failures
for <JAIL> for <JAIL>
.TP .TP
\fBset <JAIL> ignoreregex <REGEX>\fR \fBset <JAIL> delfailregex <INDEX>\fR
sets the regular expression removes the regular expression at
<INDEX> for failregex
.TP
\fBset <JAIL> addignoreregex <REGEX>\fR
adds the regular expression
<REGEX> which should match pattern <REGEX> which should match pattern
to exclude for <JAIL> to exclude for <JAIL>
.TP .TP
\fBset <JAIL> delignoreregex <INDEX>\fR
removes the regular expression at
<INDEX> for ignoreregex
.TP
\fBset <JAIL> findtime <TIME>\fR \fBset <JAIL> findtime <TIME>\fR
sets the number of seconds <TIME> sets the number of seconds <TIME>
for which the filter will look for which the filter will look
@ -163,6 +189,8 @@ action <ACT> for <JAIL>
\fBset <JAIL> actionunban <ACT> <CMD>\fR \fBset <JAIL> actionunban <ACT> <CMD>\fR
sets the unban command <CMD> of sets the unban command <CMD> of
the action <ACT> for <JAIL> the action <ACT> for <JAIL>
.IP
Jail information
.TP .TP
\fBget <JAIL> logpath\fR \fBget <JAIL> logpath\fR
gets the list of the monitored gets the list of the monitored
@ -181,13 +209,14 @@ gets the pattern used for the time
detection for <JAIL> detection for <JAIL>
.TP .TP
\fBget <JAIL> failregex\fR \fBget <JAIL> failregex\fR
gets the regular expression which gets the list of regular
matches the failures for <JAIL> expressions which matches the
failures for <JAIL>
.TP .TP
\fBget <JAIL> ignoreregex\fR \fBget <JAIL> ignoreregex\fR
gets the regular expression which gets the list of regular
matches patterns to ignore for expressions which matches patterns
<JAIL> to ignore for <JAIL>
.TP .TP
\fBget <JAIL> findtime\fR \fBget <JAIL> findtime\fR
gets the time for which the filter gets the time for which the filter
@ -225,16 +254,6 @@ action <ACT> for <JAIL>
\fBget <JAIL> actionunban <ACT>\fR \fBget <JAIL> actionunban <ACT>\fR
gets the unban command for the gets the unban command for the
action <ACT> for <JAIL> action <ACT> for <JAIL>
.TP
\fBstart <JAIL>\fR
starts the jail <JAIL>
.TP
\fBstop <JAIL>\fR
stops the jail <JAIL>. The jail is
removed
.TP
\fBstatus <JAIL>\fR
gets the current status of <JAIL>
.SH FILES .SH FILES
\fI/etc/fail2ban/*\fR \fI/etc/fail2ban/*\fR
.SH AUTHOR .SH AUTHOR

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" "December 2006" "fail2ban-regex v0.7.4-SVN" "User Commands" .TH FAIL2BAN-REGEX "1" "December 2006" "fail2ban-regex v0.7.5-SVN" "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
\fI<logline> <failregex>\fR \fI<logline> <failregex>\fR
.SH DESCRIPTION .SH DESCRIPTION
Fail2Ban v0.7.4\-SVN reads log file that contains password failure report Fail2Ban v0.7.5\-SVN 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 and benchmark your regular expressions for the "failregex" This tools can test and benchmark your regular expressions for the "failregex"

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" "December 2006" "fail2ban-server v0.7.4-SVN" "User Commands" .TH FAIL2BAN-SERVER "1" "December 2006" "fail2ban-server v0.7.5-SVN" "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.7.4\-SVN reads log file that contains password failure report Fail2Ban v0.7.5\-SVN 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

62
server/failregex.py Normal file
View File

@ -0,0 +1,62 @@
# 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$
__author__ = "Cyril Jaquier"
__version__ = "$Revision$"
__date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
from regex import Regex, RegexException
##
# Regular expression class.
#
# This class represents a regular expression with its compiled version.
class FailRegex(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, value):
# Replace "<HOST>" with default regular expression for host.
regex = value.replace("<HOST>", "(?:::f{4,6}:)?(?P<host>\S+)")
# Initializes the parent.
Regex.__init__(self, regex)
# Check for group "host"
if "host" not in self._regexObj.groupindex:
raise RegexException("No 'host' group in '%s'" % self._regex)
##
# Returns the matched host.
#
# This corresponds to the pattern matched by the named group "host".
# @return the matched host
def getHost(self):
host = self._matchCache.group("host")
if host == None:
raise RegexException("Unexpected error. Please check your regex")
return host

View File

@ -29,8 +29,10 @@ from failticket 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
import logging, re, sre_constants import logging, re
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.filter") logSys = logging.getLogger("fail2ban.filter")
@ -61,12 +63,10 @@ class Filter(JailThread):
self.__crtFilename = None self.__crtFilename = None
## The log file path. ## The log file path.
self.__logPath = [] self.__logPath = []
## The regular expression matching the failure. ## The regular expression list matching the failures.
self.__failRegex = '' self.__failRegex = list()
self.__failRegexObj = None ## The regular expression list with expressions to ignore.
## The regular expression with expression to ignore. self.__ignoreRegex = list()
self.__ignoreRegex = ''
self.__ignoreRegexObj = None
## The amount of time to look back. ## The amount of time to look back.
self.__findTime = 6000 self.__findTime = 6000
## The ignore IP list. ## The ignore IP list.
@ -158,26 +158,26 @@ class Filter(JailThread):
return self.dateDetector.getDefaultPattern() return self.dateDetector.getDefaultPattern()
## ##
# Set the regular expression which matches the failure. # Add a regular expression which matches the failure.
# #
# The regular expression can also match any other pattern than failures # The regular expression can also match any other pattern than failures
# and thus can be used for many purporse. # and thus can be used for many purporse.
# @param value the regular expression # @param value the regular expression
def setFailRegex(self, value): def addFailRegex(self, value):
try: try:
if value.lstrip() == '': regex = FailRegex(value)
self.__failRegex = value self.__failRegex.append(regex)
self.__failRegexObj = None except RegexException, e:
else: logSys.error(e)
# Replace "<HOST>" with default regular expression for host.
regex = value.replace("<HOST>", "(?:::f{4,6}:)?(?P<host>\S+)")
self.__failRegex = regex def delFailRegex(self, index):
self.__failRegexObj = re.compile(regex) try:
logSys.info("Set failregex = %s" % self.__failRegex) del self.__failRegex[index]
except sre_constants.error: except IndexError:
logSys.error("Unable to compile regular expression " + logSys.error("Cannot remove regular expression. Index %d is not "
self.__failRegex) "valid" % index)
## ##
# Get the regular expression which matches the failure. # Get the regular expression which matches the failure.
@ -185,25 +185,31 @@ class Filter(JailThread):
# @return the regular expression # @return the regular expression
def getFailRegex(self): def getFailRegex(self):
return self.__failRegex failRegex = list()
for regex in self.__failRegex:
failRegex.append(regex.getRegex())
return failRegex
## ##
# Set the regular expression which matches the failure. # Add the regular expression which matches the failure.
# #
# The regular expression can also match any other pattern than failures # The regular expression can also match any other pattern than failures
# and thus can be used for many purporse. # and thus can be used for many purporse.
# @param value the regular expression # @param value the regular expression
def setIgnoreRegex(self, value): def addIgnoreRegex(self, value):
try: try:
if value.lstrip() == '': regex = Regex(value)
self.__ignoreRegexObj = None self.__ignoreRegex.append(regex)
else: except RegexException, e:
self.__ignoreRegexObj = re.compile(value) logSys.error(e)
self.__ignoreRegex = value
logSys.info("Set ignoreregex = %s" % value) def delIgnoreRegex(self, index):
except sre_constants.error: try:
logSys.error("Unable to compile regular expression " + value) del self.__ignoreRegex[index]
except IndexError:
logSys.error("Cannot remove regular expression. Index %d is not "
"valid" % index)
## ##
# Get the regular expression which matches the failure. # Get the regular expression which matches the failure.
@ -211,7 +217,10 @@ class Filter(JailThread):
# @return the regular expression # @return the regular expression
def getIgnoreRegex(self): def getIgnoreRegex(self):
return self.__ignoreRegex ignoreRegex = list()
for regex in self.__ignoreRegex:
ignoreRegex.append(regex.getRegex())
return ignoreRegex
## ##
# Set the time needed to find a failure. # Set the time needed to find a failure.
@ -413,43 +422,35 @@ class Filter(JailThread):
def findFailure(self, line): def findFailure(self, line):
failList = list() failList = list()
# Checks if failregex is defined. # Checks if we must ignore this line.
if self.__failRegexObj == None: for ignoreRegex in self.__ignoreRegex:
logSys.error("No failregex is set") ignoreRegex.search(line)
return failList if ignoreRegex.hasMatched():
# Checks if ignoreregex is defined.
if not self.__ignoreRegexObj == None:
match = self.__ignoreRegexObj.search(line)
if match:
# The ignoreregex matched. Return. # The ignoreregex matched. Return.
logSys.debug("Ignoring this line") logSys.debug("Ignoring this line")
return failList return failList
match = self.__failRegexObj.search(line) # Iterates over all the regular expressions.
if match: for failRegex in self.__failRegex:
# The failregex matched. failRegex.search(line)
date = self.dateDetector.getUnixTime(match.string) if failRegex.hasMatched():
if date == None: # The failregex matched.
logSys.debug("Found a match but no valid date/time found " date = self.dateDetector.getUnixTime(line)
+ "for " + match.string + ". Please contact " if date == None:
+ "the author in order to get support for " logSys.debug("Found a match but no valid date/time found "
+ "this format") + "for " + line + ". Please contact the "
else: + "author in order to get support for this "
try: + "format")
matchGroup = match.group("host") else:
# For strange reasons, match.group can return None with some try:
# regular expressions. However, these expressions can be host = failRegex.getHost()
# compiled successfully. ipMatch = DNSUtils.textToIp(host)
if matchGroup == None:
logSys.error("Unexpected error. Please correct your "
+ "configuration.")
else:
ipMatch = DNSUtils.textToIp(match.group("host"))
if ipMatch: if ipMatch:
for ip in ipMatch: for ip in ipMatch:
failList.append([ip, date]) failList.append([ip, date])
except IndexError: # We matched a regex, it is enough to stop.
logSys.error("There is no 'host' group in the rule. " + break
"Please correct your configuration.") except RegexException, e:
logSys.error(e)
return failList return failList

88
server/regex.py Normal file
View File

@ -0,0 +1,88 @@
# 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$
__author__ = "Cyril Jaquier"
__version__ = "$Revision$"
__date__ = "$Date$"
__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
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

@ -165,14 +165,20 @@ class Server:
def getFindTime(self, name): def getFindTime(self, name):
return self.__jails.getFilter(name).getFindTime() return self.__jails.getFilter(name).getFindTime()
def setFailRegex(self, name, value): def addFailRegex(self, name, value):
self.__jails.getFilter(name).setFailRegex(value) self.__jails.getFilter(name).addFailRegex(value)
def delFailRegex(self, name, index):
self.__jails.getFilter(name).delFailRegex(index)
def getFailRegex(self, name): def getFailRegex(self, name):
return self.__jails.getFilter(name).getFailRegex() return self.__jails.getFilter(name).getFailRegex()
def setIgnoreRegex(self, name, value): def addIgnoreRegex(self, name, value):
self.__jails.getFilter(name).setIgnoreRegex(value) self.__jails.getFilter(name).addIgnoreRegex(value)
def delIgnoreRegex(self, name, index):
self.__jails.getFilter(name).delIgnoreRegex(index)
def getIgnoreRegex(self, name): def getIgnoreRegex(self, name):
return self.__jails.getFilter(name).getIgnoreRegex() return self.__jails.getFilter(name).getIgnoreRegex()

View File

@ -143,13 +143,21 @@ class Transmitter:
value = command[2] value = command[2]
self.__server.setTimePattern(name, value) self.__server.setTimePattern(name, value)
return self.__server.getTimePattern(name) return self.__server.getTimePattern(name)
elif command[1] == "failregex": elif command[1] == "addfailregex":
value = command[2] value = command[2]
self.__server.setFailRegex(name, value) self.__server.addFailRegex(name, value)
return self.__server.getFailRegex(name) return self.__server.getFailRegex(name)
elif command[1] == "ignoreregex": elif command[1] == "delfailregex":
value = int(command[2])
self.__server.delFailRegex(name, value)
return self.__server.getFailRegex(name)
elif command[1] == "addignoreregex":
value = command[2] value = command[2]
self.__server.setIgnoreRegex(name, value) self.__server.addIgnoreRegex(name, value)
return self.__server.getIgnoreRegex(name)
elif command[1] == "delignoreregex":
value = int(command[2])
self.__server.delIgnoreRegex(name, value)
return self.__server.getIgnoreRegex(name) return self.__server.getIgnoreRegex(name)
elif command[1] == "findtime": elif command[1] == "findtime":
value = command[2] value = command[2]

View File

@ -99,7 +99,7 @@ class GetFailures(unittest.TestCase):
output = ('193.168.0.128', 3, 1124013599.0) output = ('193.168.0.128', 3, 1124013599.0)
self.__filter.addLogPath(GetFailures.FILENAME_01) self.__filter.addLogPath(GetFailures.FILENAME_01)
self.__filter.setFailRegex("(?:(?:Authentication failure|Failed [-/\w+]+) for(?: [iI](?:llegal|nvalid) user)?|[Ii](?:llegal|nvalid) user|ROOT LOGIN REFUSED) .*(?: from|FROM) (?:::f{4,6}:)?(?P<host>\S*)") self.__filter.addFailRegex("(?:(?:Authentication failure|Failed [-/\w+]+) for(?: [iI](?:llegal|nvalid) user)?|[Ii](?:llegal|nvalid) user|ROOT LOGIN REFUSED) .*(?: from|FROM) (?:::f{4,6}:)?(?P<host>\S*)")
self.__filter.getFailures(GetFailures.FILENAME_01) self.__filter.getFailures(GetFailures.FILENAME_01)
@ -116,7 +116,7 @@ class GetFailures(unittest.TestCase):
output = ('141.3.81.106', 4, 1124013539.0) output = ('141.3.81.106', 4, 1124013539.0)
self.__filter.addLogPath(GetFailures.FILENAME_02) self.__filter.addLogPath(GetFailures.FILENAME_02)
self.__filter.setFailRegex("Failed .* (?:::f{4,6}:)(?P<host>\S*)") self.__filter.addFailRegex("Failed .* (?:::f{4,6}:)(?P<host>\S*)")
self.__filter.getFailures(GetFailures.FILENAME_02) self.__filter.getFailures(GetFailures.FILENAME_02)
@ -127,13 +127,13 @@ class GetFailures(unittest.TestCase):
ip = ticket.getIP() ip = ticket.getIP()
found = (ip, attempts, date) found = (ip, attempts, date)
self.assertEqual(found, output) self.assertEqual(found, output)
def testGetFailures03(self): def testGetFailures03(self):
output = ('203.162.223.135', 6, 1124013544.0) output = ('203.162.223.135', 6, 1124013544.0)
self.__filter.addLogPath(GetFailures.FILENAME_03) self.__filter.addLogPath(GetFailures.FILENAME_03)
self.__filter.setFailRegex("error,relay=(?:::f{4,6}:)?(?P<host>\S*),.*550 User unknown") self.__filter.addFailRegex("error,relay=(?:::f{4,6}:)?(?P<host>\S*),.*550 User unknown")
self.__filter.getFailures(GetFailures.FILENAME_03) self.__filter.getFailures(GetFailures.FILENAME_03)
@ -151,7 +151,7 @@ class GetFailures(unittest.TestCase):
('212.41.96.185', 4, 1124013598.0)] ('212.41.96.185', 4, 1124013598.0)]
self.__filter.addLogPath(GetFailures.FILENAME_04) self.__filter.addLogPath(GetFailures.FILENAME_04)
self.__filter.setFailRegex("Invalid user .* (?P<host>\S*)") self.__filter.addFailRegex("Invalid user .* (?P<host>\S*)")
self.__filter.getFailures(GetFailures.FILENAME_04) self.__filter.getFailures(GetFailures.FILENAME_04)
@ -165,4 +165,33 @@ class GetFailures(unittest.TestCase):
self.assertEqual(found, output[i]) self.assertEqual(found, output[i])
except FailManagerEmpty: except FailManagerEmpty:
pass pass
def testGetFailuresMultiRegex(self):
output = ('141.3.81.106', 8, 1124013541.0)
self.__filter.addLogPath(GetFailures.FILENAME_02)
self.__filter.addFailRegex("Failed .* from <HOST>")
self.__filter.addFailRegex("Accepted .* from <HOST>")
self.__filter.getFailures(GetFailures.FILENAME_02)
ticket = self.__filter.failManager.toBan()
attempts = ticket.getAttempt()
date = ticket.getTime()
ip = ticket.getIP()
found = (ip, attempts, date)
self.assertEqual(found, output)
def testGetFailuresIgnoreRegex(self):
output = ('141.3.81.106', 8, 1124013541.0)
self.__filter.addLogPath(GetFailures.FILENAME_02)
self.__filter.addFailRegex("Failed .* from <HOST>")
self.__filter.addFailRegex("Accepted .* from <HOST>")
self.__filter.addIgnoreRegex("for roehl")
self.__filter.getFailures(GetFailures.FILENAME_02)
self.assertRaises(FailManagerEmpty, self.__filter.failManager.toBan)