mirror of https://github.com/fail2ban/fail2ban
Initial changes and test for multi-line filtering
parent
51a3be2d79
commit
aec709f4c1
|
@ -63,6 +63,7 @@ class JailReader(ConfigReader):
|
||||||
["string", "logpath", "/var/log/messages"],
|
["string", "logpath", "/var/log/messages"],
|
||||||
["string", "backend", "auto"],
|
["string", "backend", "auto"],
|
||||||
["int", "maxretry", 3],
|
["int", "maxretry", 3],
|
||||||
|
["int", "maxlines", 1],
|
||||||
["int", "findtime", 600],
|
["int", "findtime", 600],
|
||||||
["int", "bantime", 600],
|
["int", "bantime", 600],
|
||||||
["string", "usedns", "warn"],
|
["string", "usedns", "warn"],
|
||||||
|
@ -114,6 +115,8 @@ class JailReader(ConfigReader):
|
||||||
backend = self.__opts[opt]
|
backend = self.__opts[opt]
|
||||||
elif opt == "maxretry":
|
elif opt == "maxretry":
|
||||||
stream.append(["set", self.__name, "maxretry", self.__opts[opt]])
|
stream.append(["set", self.__name, "maxretry", self.__opts[opt]])
|
||||||
|
elif opt == "maxlines":
|
||||||
|
stream.append(["set", self.__name, "maxlines", self.__opts[opt]])
|
||||||
elif opt == "ignoreip":
|
elif opt == "ignoreip":
|
||||||
for ip in self.__opts[opt].split():
|
for ip in self.__opts[opt].split():
|
||||||
# Do not send a command if the rule is empty.
|
# Do not send a command if the rule is empty.
|
||||||
|
|
|
@ -66,6 +66,7 @@ protocol = [
|
||||||
["set <JAIL> banip <IP>", "manually Ban <IP> for <JAIL>"],
|
["set <JAIL> banip <IP>", "manually Ban <IP> for <JAIL>"],
|
||||||
["set <JAIL> unbanip <IP>", "manually Unban <IP> in <JAIL>"],
|
["set <JAIL> unbanip <IP>", "manually Unban <IP> in <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>"],
|
||||||
|
["set <JAIL> maxlines <LINES>", "sets the number of <LINES> to buffer for regex search for <JAIL>"],
|
||||||
["set <JAIL> addaction <ACT>", "adds a new action named <NAME> for <JAIL>"],
|
["set <JAIL> addaction <ACT>", "adds a new action named <NAME> for <JAIL>"],
|
||||||
["set <JAIL> delaction <ACT>", "removes the action <NAME> from <JAIL>"],
|
["set <JAIL> delaction <ACT>", "removes the action <NAME> from <JAIL>"],
|
||||||
["set <JAIL> setcinfo <ACT> <KEY> <VALUE>", "sets <VALUE> for <KEY> of the action <NAME> for <JAIL>"],
|
["set <JAIL> setcinfo <ACT> <KEY> <VALUE>", "sets <VALUE> for <KEY> of the action <NAME> for <JAIL>"],
|
||||||
|
@ -84,6 +85,7 @@ protocol = [
|
||||||
["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> usedns", "gets the usedns setting for <JAIL>"],
|
["get <JAIL> usedns", "gets the usedns setting for <JAIL>"],
|
||||||
["get <JAIL> maxretry", "gets the number of failures allowed for <JAIL>"],
|
["get <JAIL> maxretry", "gets the number of failures allowed for <JAIL>"],
|
||||||
|
["get <JAIL> maxlines", "gets the number of lines to buffer for <JAIL>"],
|
||||||
["get <JAIL> addaction", "gets the last action which has been added for <JAIL>"],
|
["get <JAIL> addaction", "gets the last action which has been added for <JAIL>"],
|
||||||
["get <JAIL> actionstart <ACT>", "gets the start command for the action <ACT> for <JAIL>"],
|
["get <JAIL> actionstart <ACT>", "gets the start command for the action <ACT> for <JAIL>"],
|
||||||
["get <JAIL> actionstop <ACT>", "gets the stop command for the action <ACT> for <JAIL>"],
|
["get <JAIL> actionstop <ACT>", "gets the stop command for the action <ACT> for <JAIL>"],
|
||||||
|
|
|
@ -145,6 +145,11 @@ sets the number of failures
|
||||||
<RETRY> before banning the host
|
<RETRY> before banning the host
|
||||||
for <JAIL>
|
for <JAIL>
|
||||||
.TP
|
.TP
|
||||||
|
\fBset <JAIL> maxlines <LINES>\fR
|
||||||
|
sets the number of <LINES> to
|
||||||
|
buffer for regex search for
|
||||||
|
<JAIL>
|
||||||
|
.TP
|
||||||
\fBset <JAIL> addaction <ACT>\fR
|
\fBset <JAIL> addaction <ACT>\fR
|
||||||
adds a new action named <NAME> for
|
adds a new action named <NAME> for
|
||||||
<JAIL>
|
<JAIL>
|
||||||
|
@ -222,6 +227,10 @@ gets the time a host is banned for
|
||||||
gets the number of failures
|
gets the number of failures
|
||||||
allowed for <JAIL>
|
allowed for <JAIL>
|
||||||
.TP
|
.TP
|
||||||
|
\fBget <JAIL> maxlines\fR
|
||||||
|
gets the number lines to
|
||||||
|
buffer for <JAIL>
|
||||||
|
.TP
|
||||||
\fBget <JAIL> addaction\fR
|
\fBget <JAIL> addaction\fR
|
||||||
gets the last action which has
|
gets the last action which has
|
||||||
been added for <JAIL>
|
been added for <JAIL>
|
||||||
|
|
|
@ -51,7 +51,7 @@ class Regex:
|
||||||
if regex.lstrip() == '':
|
if regex.lstrip() == '':
|
||||||
raise RegexException("Cannot add empty regex")
|
raise RegexException("Cannot add empty regex")
|
||||||
try:
|
try:
|
||||||
self._regexObj = re.compile(regex)
|
self._regexObj = re.compile(regex, re.MULTILINE)
|
||||||
self._regex = regex
|
self._regex = regex
|
||||||
except sre_constants.error:
|
except sre_constants.error:
|
||||||
raise RegexException("Unable to compile regular expression '%s'" %
|
raise RegexException("Unable to compile regular expression '%s'" %
|
||||||
|
|
|
@ -36,6 +36,7 @@ from mytime import MyTime
|
||||||
from failregex import FailRegex, Regex, RegexException
|
from failregex import FailRegex, Regex, RegexException
|
||||||
|
|
||||||
import logging, re, os, fcntl, time
|
import logging, re, os, fcntl, time
|
||||||
|
from collections import deque
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger("fail2ban.filter")
|
logSys = logging.getLogger("fail2ban.filter")
|
||||||
|
@ -71,6 +72,10 @@ class Filter(JailThread):
|
||||||
self.__findTime = 6000
|
self.__findTime = 6000
|
||||||
## The ignore IP list.
|
## The ignore IP list.
|
||||||
self.__ignoreIpList = []
|
self.__ignoreIpList = []
|
||||||
|
## Size of line buffer
|
||||||
|
self.__line_buffer_size = 1
|
||||||
|
## Line buffer
|
||||||
|
self.__line_buffer = deque()
|
||||||
|
|
||||||
self.dateDetector = DateDetector()
|
self.dateDetector = DateDetector()
|
||||||
self.dateDetector.addDefaultTemplate()
|
self.dateDetector.addDefaultTemplate()
|
||||||
|
@ -204,6 +209,25 @@ class Filter(JailThread):
|
||||||
def getMaxRetry(self):
|
def getMaxRetry(self):
|
||||||
return self.failManager.getMaxRetry()
|
return self.failManager.getMaxRetry()
|
||||||
|
|
||||||
|
##
|
||||||
|
# Set the maximum line buffer size.
|
||||||
|
#
|
||||||
|
# @param value the line buffer size
|
||||||
|
|
||||||
|
def setMaxLines(self, value):
|
||||||
|
if value < 1:
|
||||||
|
value = 1
|
||||||
|
self.__line_buffer_size = value
|
||||||
|
logSys.info("Set maxLines = %s" % value)
|
||||||
|
|
||||||
|
##
|
||||||
|
# Get the maximum line buffer size.
|
||||||
|
#
|
||||||
|
# @return the line buffer size
|
||||||
|
|
||||||
|
def getMaxLines(self):
|
||||||
|
return self.__line_buffer_size
|
||||||
|
|
||||||
##
|
##
|
||||||
# Main loop.
|
# Main loop.
|
||||||
#
|
#
|
||||||
|
@ -305,7 +329,10 @@ class Filter(JailThread):
|
||||||
else:
|
else:
|
||||||
timeLine = l
|
timeLine = l
|
||||||
logLine = l
|
logLine = l
|
||||||
return self.findFailure(timeLine, logLine)
|
self.__line_buffer.append(logLine)
|
||||||
|
while len(self.__line_buffer) > self.__line_buffer_size:
|
||||||
|
self.__line_buffer.popleft()
|
||||||
|
return self.findFailure(timeLine, "".join(self.__line_buffer))
|
||||||
|
|
||||||
def processLineAndAdd(self, line):
|
def processLineAndAdd(self, line):
|
||||||
"""Processes the line for failures and populates failManager
|
"""Processes the line for failures and populates failManager
|
||||||
|
@ -365,6 +392,7 @@ class Filter(JailThread):
|
||||||
"in order to get support for this format."
|
"in order to get support for this format."
|
||||||
% (logLine, timeLine))
|
% (logLine, timeLine))
|
||||||
else:
|
else:
|
||||||
|
self.__line_buffer.clear()
|
||||||
try:
|
try:
|
||||||
host = failRegex.getHost()
|
host = failRegex.getHost()
|
||||||
ipMatch = DNSUtils.textToIp(host, self.__useDns)
|
ipMatch = DNSUtils.textToIp(host, self.__useDns)
|
||||||
|
|
|
@ -216,6 +216,12 @@ class Server:
|
||||||
def getMaxRetry(self, name):
|
def getMaxRetry(self, name):
|
||||||
return self.__jails.getFilter(name).getMaxRetry()
|
return self.__jails.getFilter(name).getMaxRetry()
|
||||||
|
|
||||||
|
def setMaxLines(self, name, value):
|
||||||
|
self.__jails.getFilter(name).setMaxLines(value)
|
||||||
|
|
||||||
|
def getMaxLines(self, name):
|
||||||
|
return self.__jails.getFilter(name).getMaxLines()
|
||||||
|
|
||||||
# Action
|
# Action
|
||||||
def addAction(self, name, value):
|
def addAction(self, name, value):
|
||||||
self.__jails.getAction(name).addAction(value)
|
self.__jails.getAction(name).addAction(value)
|
||||||
|
|
|
@ -167,6 +167,10 @@ class Transmitter:
|
||||||
value = command[2]
|
value = command[2]
|
||||||
self.__server.setMaxRetry(name, int(value))
|
self.__server.setMaxRetry(name, int(value))
|
||||||
return self.__server.getMaxRetry(name)
|
return self.__server.getMaxRetry(name)
|
||||||
|
elif command[1] == "maxlines":
|
||||||
|
value = command[2]
|
||||||
|
self.__server.setMaxLines(name, int(value))
|
||||||
|
return self.__server.getMaxLines(name)
|
||||||
# command
|
# command
|
||||||
elif command[1] == "bantime":
|
elif command[1] == "bantime":
|
||||||
value = command[2]
|
value = command[2]
|
||||||
|
@ -245,6 +249,8 @@ class Transmitter:
|
||||||
return self.__server.getFindTime(name)
|
return self.__server.getFindTime(name)
|
||||||
elif command[1] == "maxretry":
|
elif command[1] == "maxretry":
|
||||||
return self.__server.getMaxRetry(name)
|
return self.__server.getMaxRetry(name)
|
||||||
|
elif command[1] == "maxlines":
|
||||||
|
return self.__server.getMaxLines(name)
|
||||||
# Action
|
# Action
|
||||||
elif command[1] == "bantime":
|
elif command[1] == "bantime":
|
||||||
return self.__server.getBanTime(name)
|
return self.__server.getBanTime(name)
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
Aug 14 11:59:58 [sshd] Invalid user toto...
|
||||||
|
Aug 14 11:59:58 [sshd] from 212.41.96.185
|
||||||
|
Aug 14 11:59:58 [sshd] Invalid user toto...
|
||||||
|
Aug 14 11:59:58 [sshd] from 212.41.96.185
|
||||||
|
Aug 14 11:59:58 [sshd] Invalid user fuck...
|
||||||
|
Aug 14 11:59:58 [sshd] from 212.41.96.185
|
||||||
|
Aug 14 11:59:58 [sshd] Invalid user toto...
|
||||||
|
Aug 14 11:59:58 [sshd] from 212.41.96.185
|
||||||
|
Aug 14 11:59:58 [sshd] Invalid user fuck...
|
||||||
|
Aug 14 11:59:58 [sshd] from 212.41.96.185
|
||||||
|
Aug 14 11:59:58 [sshd] Invalid user fuck...
|
||||||
|
Aug 14 11:59:58 [sshd] from 212.41.96.185
|
|
@ -499,6 +499,7 @@ class GetFailures(unittest.TestCase):
|
||||||
FILENAME_03 = "testcases/files/testcase03.log"
|
FILENAME_03 = "testcases/files/testcase03.log"
|
||||||
FILENAME_04 = "testcases/files/testcase04.log"
|
FILENAME_04 = "testcases/files/testcase04.log"
|
||||||
FILENAME_USEDNS = "testcases/files/testcase-usedns.log"
|
FILENAME_USEDNS = "testcases/files/testcase-usedns.log"
|
||||||
|
FILENAME_MULTILINE = "testcases/files/testcase-multiline.log"
|
||||||
|
|
||||||
# so that they could be reused by other tests
|
# so that they could be reused by other tests
|
||||||
FAILURES_01 = ('193.168.0.128', 3, 1124013599.0,
|
FAILURES_01 = ('193.168.0.128', 3, 1124013599.0,
|
||||||
|
@ -604,6 +605,20 @@ class GetFailures(unittest.TestCase):
|
||||||
|
|
||||||
self.assertRaises(FailManagerEmpty, self.filter.failManager.toBan)
|
self.assertRaises(FailManagerEmpty, self.filter.failManager.toBan)
|
||||||
|
|
||||||
|
def testGetFailuresMultiLine(self):
|
||||||
|
output = ("212.41.96.185", 3, 1124013598.0)
|
||||||
|
self.filter.addLogPath(GetFailures.FILENAME_MULTILINE)
|
||||||
|
self.filter.addFailRegex("Invalid user .+\n.+ from <HOST>$")
|
||||||
|
self.filter.addIgnoreRegex("user fuck")
|
||||||
|
|
||||||
|
self.filter.setMaxLines(2)
|
||||||
|
|
||||||
|
self.filter.getFailures(GetFailures.FILENAME_MULTILINE)
|
||||||
|
|
||||||
|
_assert_correct_last_attempt(self, self.filter, output)
|
||||||
|
|
||||||
|
self.assertRaises(FailManagerEmpty, self.filter.failManager.toBan)
|
||||||
|
|
||||||
class DNSUtilsTests(unittest.TestCase):
|
class DNSUtilsTests(unittest.TestCase):
|
||||||
|
|
||||||
def testUseDns(self):
|
def testUseDns(self):
|
||||||
|
|
Loading…
Reference in New Issue