mirror of https://github.com/fail2ban/fail2ban
- 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-b1dcaea8d6050.x
parent
34a48157dc
commit
0f31cc0feb
|
@ -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
|
||||||
----------
|
----------
|
||||||
|
|
2
MANIFEST
2
MANIFEST
|
@ -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
|
||||||
|
|
|
@ -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` +
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>"],
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
129
server/filter.py
129
server/filter.py
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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()
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue