- Initial commit of the new prefix remover.

git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@688 a942ae1a-1317-0410-a47c-b1dcaea8d605
0.x
Cyril Jaquier 2008-05-05 21:22:27 +00:00
parent 543741f6e8
commit 1b3828421e
16 changed files with 509 additions and 541 deletions

View File

@ -25,16 +25,18 @@ server/server.py
server/actions.py
server/faildata.py
server/failmanager.py
server/datedetector.py
server/jailthread.py
server/transmitter.py
server/template.py
server/hosttemplate.py
server/action.py
server/ticket.py
server/jail.py
server/jails.py
server/__init__.py
server/banmanager.py
server/datetemplate.py
server/timetemplate.py
server/prefixtemplate.py
server/mytime.py
server/failregex.py
testcases/banmanagertestcase.py

View File

@ -222,7 +222,12 @@ class Fail2banRegex:
try:
self.__filter.addFailRegex(regex.getFailRegex())
try:
ret = self.__filter.processLine(line)
try:
# Decode line to UTF-8
l = line.decode('utf-8')
except UnicodeDecodeError:
l = line
ret = self.__filter.findFailure(l)
if not len(ret) == 0:
if found == True:
ret[0].append(True)
@ -320,12 +325,6 @@ class Fail2banRegex:
print
print "Date template hits:"
for template in self.__filter.dateDetector.getTemplates():
print `template.getHits()` + " hit(s): " + template.getName()
print
print "Success, the total number of match is " + str(total)
print
print "However, look at the above section 'Running tests' which could contain important"
@ -333,7 +332,7 @@ class Fail2banRegex:
return True
if __name__ == "__main__":
def main():
fail2banRegex = Fail2banRegex()
# Reads the command line options.
try:
@ -388,3 +387,9 @@ if __name__ == "__main__":
sys.exit(0)
else:
sys.exit(-1)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print "Interrupted by the user"

View File

@ -34,7 +34,6 @@ from testcases import clientreadertestcase
from testcases import failmanagertestcase
from testcases import filtertestcase
from testcases import servertestcase
from testcases import datedetectortestcase
from testcases import actiontestcase
from server.mytime import MyTime
@ -67,8 +66,6 @@ tests.addTest(unittest.makeSuite(failmanagertestcase.AddFailure))
tests.addTest(unittest.makeSuite(banmanagertestcase.AddFailure))
# ClientReader
tests.addTest(unittest.makeSuite(clientreadertestcase.JailReaderTest))
# DateDetector
tests.addTest(unittest.makeSuite(datedetectortestcase.DateDetectorTest))
# Tests runner
testRunner = unittest.TextTestRunner()

View File

@ -1,152 +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$
__author__ = "Cyril Jaquier"
__version__ = "$Revision$"
__date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import time, logging
from datetemplate import DateStrptime
from datetemplate import DateTai64n
from datetemplate import DateEpoch
from threading import Lock
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.filter.datedetector")
class DateDetector:
def __init__(self):
self.__lock = Lock()
self.__templates = list()
def addDefaultTemplate(self):
self.__lock.acquire()
try:
# standard
template = DateStrptime()
template.setName("Month Day Hour:Minute:Second")
template.setRegex("^\S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}")
template.setPattern("%b %d %H:%M:%S")
self.__templates.append(template)
# asctime
template = DateStrptime()
template.setName("Weekday Month Day Hour:Minute:Second Year")
template.setRegex("\S{3} \S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2} \d{4}")
template.setPattern("%a %b %d %H:%M:%S %Y")
self.__templates.append(template)
# asctime without year
template = DateStrptime()
template.setName("Weekday Month Day Hour:Minute:Second")
template.setRegex("\S{3} \S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}")
template.setPattern("%a %b %d %H:%M:%S")
self.__templates.append(template)
# simple date
template = DateStrptime()
template.setName("Year/Month/Day Hour:Minute:Second")
template.setRegex("\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}")
template.setPattern("%Y/%m/%d %H:%M:%S")
self.__templates.append(template)
# simple date too (from x11vnc)
template = DateStrptime()
template.setName("Day/Month/Year Hour:Minute:Second")
template.setRegex("\d{2}/\d{2}/\d{4} \d{2}:\d{2}:\d{2}")
template.setPattern("%d/%m/%Y %H:%M:%S")
self.__templates.append(template)
# Apache format [31/Oct/2006:09:22:55 -0000]
template = DateStrptime()
template.setName("Day/Month/Year:Hour:Minute:Second")
template.setRegex("\d{2}/\S{3}/\d{4}:\d{2}:\d{2}:\d{2}")
template.setPattern("%d/%b/%Y:%H:%M:%S")
self.__templates.append(template)
# Exim 2006-12-21 06:43:20
template = DateStrptime()
template.setName("Year-Month-Day Hour:Minute:Second")
template.setRegex("\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}")
template.setPattern("%Y-%m-%d %H:%M:%S")
self.__templates.append(template)
# named 26-Jul-2007 15:20:52.252
template = DateStrptime()
template.setName("Day-Month-Year Hour:Minute:Second[.Millisecond]")
template.setRegex("\d{2}-\S{3}-\d{4} \d{2}:\d{2}:\d{2}")
template.setPattern("%d-%b-%Y %H:%M:%S")
self.__templates.append(template)
# TAI64N
template = DateTai64n()
template.setName("TAI64N")
self.__templates.append(template)
# Epoch
template = DateEpoch()
template.setName("Epoch")
self.__templates.append(template)
finally:
self.__lock.release()
def getTemplates(self):
return self.__templates
def matchTime(self, line):
self.__lock.acquire()
try:
for template in self.__templates:
match = template.matchDate(line)
if not match == None:
return match
return None
finally:
self.__lock.release()
def getTime(self, line):
self.__lock.acquire()
try:
for template in self.__templates:
try:
date = template.getDate(line)
if date == None:
continue
return date
except ValueError:
pass
return None
finally:
self.__lock.release()
def getUnixTime(self, line):
date = self.getTime(line)
if date == None:
return None
else:
return time.mktime(date)
##
# Sort the template lists using the hits score. This method is not called
# in this object and thus should be called from time to time.
def sortTemplate(self):
self.__lock.acquire()
try:
logSys.debug("Sorting the template list")
self.__templates.sort(lambda x, y: cmp(x.getHits(), y.getHits()))
self.__templates.reverse()
finally:
self.__lock.release()

View File

@ -1,166 +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$
__author__ = "Cyril Jaquier"
__version__ = "$Revision$"
__date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import re, time
from mytime import MyTime
class DateTemplate:
def __init__(self):
self.__name = ""
self.__regex = ""
self.__cRegex = None
self.__hits = 0
def setName(self, name):
self.__name = name
def getName(self):
return self.__name
def setRegex(self, regex):
self.__regex = regex.strip()
self.__cRegex = re.compile(regex)
def getRegex(self):
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):
self.__pattern = pattern.strip()
def getPattern(self):
return self.__pattern
#@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
convertLocale = staticmethod(convertLocale)
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
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

@ -24,66 +24,119 @@ __date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import re, sre_constants
import re, sre_constants, logging
##
# Regular expression class.
#
# This class represents a regular expression with its compiled version.
from template import Template
from timetemplate import TimeTemplates
from prefixtemplate import PrefixTemplates
from hosttemplate import HostTemplates
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.filter.failregex")
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")
self.__originalRegex = regex
self.__convertedRegex = None
self.__compiledRegex = None
self.__templates = dict()
self.__hostRegex = None
self.__dateRegex = None
self.__prefixRegex = None
def process(self):
regex = self.__originalRegex
for item in self.__templates.values():
regex = regex.replace(item.getTag(), item.getRegex(), 1)
try:
self._regexObj = re.compile(regex)
self._regex = regex
self.__compiledRegex = re.compile(regex)
self.__convertedRegex = regex
except sre_constants.error:
raise RegexException("Unable to compile regular expression '%s'" %
regex)
def register(self, template):
self.__templates[template.getName()] = template
##
# Gets the regular expression.
#
# The effective regular expression used is returned.
# @return the regular expression
def getTemplate(self, tag):
return self.__templates[tag]
def match(self, line):
return self.__compiledRegex.match(line)
def getRegex(self):
return self._regex
def getOriginalRegex(self):
return self.__originalRegex
##
# 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 getConvertedRegex(self):
return self.__convertedRegex
class FailRegex:
def search(self, value):
self._matchCache = self._regexObj.search(value)
HOST_TEMPLATES = HostTemplates()
PREFIX_TEMPLATES = PrefixTemplates()
TIME_TEMPLATES = TimeTemplates()
##
# Checks if the previous call to search() matched.
#
# @return True if a match was found, False otherwise
def __init__(self, regex):
self.__regex = Regex(regex)
self.__match = None
self.__found = False
def __autoDetection(self, line):
for host in self.HOST_TEMPLATES.getTemplates():
self.__regex.register(host)
for date in self.TIME_TEMPLATES.getTemplates():
self.__regex.register(date)
for prefix in self.PREFIX_TEMPLATES.getTemplates():
self.__regex.register(prefix)
self.__regex.process()
match = self.__regex.match(line)
if match:
self.__found = True
#logSys.debug("Auto-detection succeeded")
#logSys.debug("failregex is %s" %
# self.__regex.getConvertedRegex())
return match
return None
def search(self, line):
if self.__found:
self.__match = self.__regex.match(line)
else:
self.__match = self.__autoDetection(line)
def hasMatched(self):
if self._matchCache:
if self.__match:
return True
else:
return False
def getOriginalRegex(self):
return self.__regex.getOriginalRegex()
def getHost(self):
template = self.__regex.getTemplate(Template.TEMPLATE_HOST)
host = self.__match.group(template.getName())
if host == None:
# Gets a few information.
s = self.__match.string
r = self.__match.re
raise RegexException("No 'host' found in '%s' using '%s'" % (s, r))
return host
def getTime(self):
template = self.__regex.getTemplate(Template.TEMPLATE_TIME)
time = self.__match.group(template.getName())
if time == None:
# Gets a few information.
s = self.__match.string
r = self.__match.re
raise RegexException("No 'time' found in '%s' using '%s'" % (s, r))
try:
return template.getTime(time)
except Exception:
return None
##
@ -91,40 +144,3 @@ class Regex:
class RegexException(Exception):
pass
##
# 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, regex):
# 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:
# 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

View File

@ -27,11 +27,10 @@ __license__ = "GPL"
from failmanager import FailManager
from ticket import FailTicket
from jailthread import JailThread
from datedetector import DateDetector
from mytime import MyTime
from failregex import FailRegex, Regex, RegexException
import logging, re
import logging, re, time
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.filter")
@ -65,9 +64,6 @@ class Filter(JailThread):
self.__findTime = 6000
## The ignore IP list.
self.__ignoreIpList = []
self.dateDetector = DateDetector()
self.dateDetector.addDefaultTemplate()
logSys.info("Created Filter")
@ -101,7 +97,7 @@ class Filter(JailThread):
def getFailRegex(self):
failRegex = list()
for regex in self.__failRegex:
failRegex.append(regex.getRegex())
failRegex.append(regex.getOriginalRegex())
return failRegex
##
@ -232,28 +228,14 @@ class Filter(JailThread):
if a == b:
return True
return False
def processLine(self, line):
def processLineAndAdd(self, line):
try:
# Decode line to UTF-8
l = line.decode('utf-8')
except UnicodeDecodeError:
l = line
timeMatch = self.dateDetector.matchTime(l)
if not timeMatch:
# There is no valid time in this line
return []
# Lets split into time part and log part of the line
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
# at least allow injection. Should be harmless otherwise
logLine = l[:timeMatch.start()] + l[timeMatch.end():]
return self.findFailure(timeLine, logLine)
def processLineAndAdd(self, line):
for element in self.processLine(line):
for element in self.findFailure(l):
ip = element[0]
unixTime = element[1]
if unixTime < MyTime.time() - self.getFindTime():
@ -273,8 +255,8 @@ class Filter(JailThread):
def ignoreLine(self, line):
for ignoreRegex in self.__ignoreRegex:
ignoreRegex.search(line)
if ignoreRegex.hasMatched():
ignoreRegex.process()
if ignoreRegex.match(line):
return True
return False
@ -285,35 +267,33 @@ class Filter(JailThread):
# to find the logging time.
# @return a dict with IP and timestamp.
def findFailure(self, timeLine, logLine):
def findFailure(self, line):
failList = list()
# Checks if we must ignore this line.
if self.ignoreLine(logLine):
if self.ignoreLine(line):
# The ignoreregex matched. Return.
return failList
# Iterates over all the regular expressions.
for failRegex in self.__failRegex:
failRegex.search(logLine)
failRegex.search(line)
if failRegex.hasMatched():
# The failregex matched.
date = self.dateDetector.getUnixTime(timeLine)
if date == None:
logSys.debug("Found a match for '" + logLine +"' but no "
+ "valid date/time found for '"
+ timeLine + "'. Please contact the "
+ "author in order to get support for this "
+ "format")
else:
try:
host = failRegex.getHost()
ipMatch = DNSUtils.textToIp(host)
if ipMatch:
for ip in ipMatch:
failList.append([ip, date])
# We matched a regex, it is enough to stop.
break
except RegexException, e:
logSys.error(e)
try:
host = failRegex.getHost()
date = failRegex.getTime()
if not date:
logSys.warning("Unable to get a valid date: %s" % line)
break
# Use Unix timestamp.
dateUnix = time.mktime(date)
ipMatch = DNSUtils.textToIp(host)
if ipMatch:
for ip in ipMatch:
failList.append([ip, dateUnix])
# We matched a regex, it is enough to stop.
break
except RegexException, e:
logSys.error(e)
return failList
@ -325,7 +305,7 @@ class Filter(JailThread):
# @return a list with tuple
def status(self):
ret = [("Currently failed", self.failManager.size()),
ret = [("Currently failed", self.failManager.size()),
("Total failed", self.failManager.getFailTotal())]
return ret
@ -342,7 +322,7 @@ class FileFilter(Filter):
#
# @param path log file path
def addLogPath(self, path, tail = False):
def addLogPath(self, path, tail = True):
container = FileContainer(path, tail)
self.__logPath.append(container)

View File

@ -113,7 +113,6 @@ class FilterGamin(FileFilter):
self.jail.putFailTicket(ticket)
except FailManagerEmpty:
self.failManager.cleanup(MyTime.time())
self.dateDetector.sortTemplate()
self.__modified = False
time.sleep(self.getSleepTime())
else:

View File

@ -108,7 +108,6 @@ class FilterPoll(FileFilter):
self.jail.putFailTicket(ticket)
except FailManagerEmpty:
self.failManager.cleanup(MyTime.time())
self.dateDetector.sortTemplate()
self.__modified = False
time.sleep(self.getSleepTime())
else:

43
server/hosttemplate.py Normal file
View File

@ -0,0 +1,43 @@
# -*- 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: 645 $
__author__ = "Cyril Jaquier"
__version__ = "$Revision: 645 $"
__date__ = "$Date: 2008-01-16 23:55:04 +0100 (Wed, 16 Jan 2008) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
from template import Template, Templates
class HostTemplate(Template):
def __init__(self):
Template.__init__(self, Template.TEMPLATE_HOST, "<HOST>")
class HostTemplates(Templates):
def __init__(self):
Templates.__init__(self)
# ::ffff:141.3.81.106
template = HostTemplate()
template.setRegex("(?:::f{4,6}:)?(?P<%s>\S+)" % template.getName())
self.templates.append(template)

51
server/prefixtemplate.py Normal file
View File

@ -0,0 +1,51 @@
# -*- 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: 645 $
__author__ = "Cyril Jaquier"
__version__ = "$Revision: 645 $"
__date__ = "$Date: 2008-01-16 23:55:04 +0100 (Wed, 16 Jan 2008) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
from template import Template, Templates
class PrefixTemplate(Template):
def __init__(self):
Template.__init__(self, Template.TEMPLATE_PREFIX, "<PREFIX>")
class PrefixTemplates(Templates):
def __init__(self):
Templates.__init__(self)
# i60p295 sshd[12365]:
template = PrefixTemplate()
template.setRegex("\S+ \S+\[\d+\]:")
self.templates.append(template)
# [sshd] error: PAM:
template = PrefixTemplate()
template.setRegex("\[\S+\] \S+: \S+:")
self.templates.append(template)
# HOSTNAME courieresmtpd
template = PrefixTemplate()
template.setRegex("\S+ \S+:")
self.templates.append(template)

View File

@ -159,7 +159,8 @@ class Server:
return self.__jails.getFilter(name).getIgnoreIP()
def addLogPath(self, name, fileName):
self.__jails.getFilter(name).addLogPath(fileName)
# Enable tail mode.
self.__jails.getFilter(name).addLogPath(fileName, True)
def delLogPath(self, name, fileName):
self.__jails.getFilter(name).delLogPath(fileName)

65
server/template.py Normal file
View File

@ -0,0 +1,65 @@
# 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: 642 $
__author__ = "Cyril Jaquier"
__version__ = "$Revision: 642 $"
__date__ = "$Date: 2008-01-05 23:33:44 +0100 (Sat, 05 Jan 2008) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
class Template:
TEMPLATE_HOST = "host"
TEMPLATE_TIME = "time"
TEMPLATE_PREFIX = "prefix"
def __init__(self, name, tag):
self.__name = name
self.__tag = tag
self.__regex = ""
self.__description = ""
def getName(self):
return self.__name
def getTag(self):
return self.__tag
def setDescription(self, description):
self.__description = description
def getDescription(self):
return self.__description
def setRegex(self, regex):
self.__regex = regex
def getRegex(self):
return self.__regex
class Templates:
def __init__(self):
self.templates = list()
def getTemplates(self):
return self.templates

195
server/timetemplate.py Normal file
View File

@ -0,0 +1,195 @@
# -*- 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: 645 $
__author__ = "Cyril Jaquier"
__version__ = "$Revision: 645 $"
__date__ = "$Date: 2008-01-16 23:55:04 +0100 (Wed, 16 Jan 2008) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import time
from template import Template, Templates
from mytime import MyTime
class TimeTemplate(Template):
def __init__(self):
Template.__init__(self, Template.TEMPLATE_TIME, "<TIME>")
def setRegex(self, regex):
Template.setRegex(self, "(?P<%s>%s)" % (self.getName(), regex))
def getTime(self, line):
raise Exception("getTime() is abstract")
class TimeEpoch(TimeTemplate):
def __init__(self):
TimeTemplate.__init__(self)
# We already know the format for TAI64N
self.setRegex("\d{10}(\.\d{6})?")
def getTime(self, line):
# extract part of format which represents seconds since epoch
return list(time.localtime(float(line)))
##
# 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 TimeStrptime(TimeTemplate):
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):
TimeTemplate.__init__(self)
self.__pattern = ""
def setPattern(self, pattern):
self.__pattern = pattern.strip()
def getPattern(self):
return self.__pattern
#@staticmethod
def convertLocale(date):
for t in TimeStrptime.TABLE:
for m in TimeStrptime.TABLE[t]:
if date.find(m) >= 0:
return date.replace(m, t)
return date
convertLocale = staticmethod(convertLocale)
def getTime(self, line):
try:
# Try first with 'C' locale
date = list(time.strptime(line, self.getPattern()))
except ValueError:
# Try to convert date string to 'C' locale
conv = self.convertLocale(line)
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 TimeTai64n(TimeTemplate):
def __init__(self):
TimeTemplate.__init__(self)
# We already know the format for TAI64N
self.setRegex("@[0-9a-f]{24}")
def getTime(self, line):
# extract part of format which represents seconds since epoch
seconds_since_epoch = line[2:17]
return list(time.gmtime(int(seconds_since_epoch, 16)))
class TimeTemplates(Templates):
def __init__(self):
Templates.__init__(self)
# standard
template = TimeStrptime()
template.setDescription("Month Day Hour:Minute:Second")
template.setRegex("\S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}")
template.setPattern("%b %d %H:%M:%S")
self.templates.append(template)
# asctime
template = TimeStrptime()
template.setDescription("Weekday Month Day Hour:Minute:Second Year")
template.setRegex("\S{3} \S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2} \d{4}")
template.setPattern("%a %b %d %H:%M:%S %Y")
self.templates.append(template)
# asctime without year
template = TimeStrptime()
template.setDescription("Weekday Month Day Hour:Minute:Second")
template.setRegex("\S{3} \S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}")
template.setPattern("%a %b %d %H:%M:%S")
self.templates.append(template)
# simple date
template = TimeStrptime()
template.setDescription("Year/Month/Day Hour:Minute:Second")
template.setRegex("\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}")
template.setPattern("%Y/%m/%d %H:%M:%S")
self.templates.append(template)
# simple date too (from x11vnc)
template = TimeStrptime()
template.setDescription("Day/Month/Year Hour:Minute:Second")
template.setRegex("\d{2}/\d{2}/\d{4} \d{2}:\d{2}:\d{2}")
template.setPattern("%d/%m/%Y %H:%M:%S")
self.templates.append(template)
# Apache format [31/Oct/2006:09:22:55 -0000]
template = TimeStrptime()
template.setDescription("Day/Month/Year:Hour:Minute:Second")
template.setRegex("\d{2}/\S{3}/\d{4}:\d{2}:\d{2}:\d{2}")
template.setPattern("%d/%b/%Y:%H:%M:%S")
self.templates.append(template)
# Exim 2006-12-21 06:43:20
template = TimeStrptime()
template.setDescription("Year-Month-Day Hour:Minute:Second")
template.setRegex("\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}")
template.setPattern("%Y-%m-%d %H:%M:%S")
self.templates.append(template)
# named 26-Jul-2007 15:20:52.252
template = TimeStrptime()
template.setDescription("Day-Month-Year Hour:Minute:Second[.Millisecond]")
template.setRegex("\d{2}-\S{3}-\d{4} \d{2}:\d{2}:\d{2}")
template.setPattern("%d-%b-%Y %H:%M:%S")
self.templates.append(template)
# TAI64N
template = TimeTai64n()
template.setDescription("TAI64N")
self.templates.append(template)
# Epoch
template = TimeEpoch()
template.setDescription("Epoch")
self.templates.append(template)

View File

@ -1,67 +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$
__author__ = "Cyril Jaquier"
__version__ = "$Revision$"
__date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import unittest
from server.datedetector import DateDetector
from server.datetemplate import DateTemplate
class DateDetectorTest(unittest.TestCase):
def setUp(self):
"""Call before every test case."""
self.__datedetector = DateDetector()
self.__datedetector.addDefaultTemplate()
def tearDown(self):
"""Call after every test case."""
def testGetEpochTime(self):
log = "1138049999 [sshd] error: PAM: Authentication failure"
date = [2006, 1, 23, 21, 59, 59, 0, 23, 0]
dateUnix = 1138049999.0
self.assertEqual(self.__datedetector.getTime(log), date)
self.assertEqual(self.__datedetector.getUnixTime(log), dateUnix)
def testGetTime(self):
log = "Jan 23 21:59:59 [sshd] error: PAM: Authentication failure"
date = [2005, 1, 23, 21, 59, 59, 1, 23, -1]
dateUnix = 1106513999.0
self.assertEqual(self.__datedetector.getTime(log), date)
self.assertEqual(self.__datedetector.getUnixTime(log), dateUnix)
# def testDefaultTempate(self):
# 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")
#
# log = "Jan 23 21:59:59 [sshd] error: PAM: Authentication failure"
# date = [2005, 1, 23, 21, 59, 59, 1, 23, -1]
# dateUnix = 1106513999.0
#
# self.assertEqual(self.__datedetector.getTime(log), date)
# self.assertEqual(self.__datedetector.getUnixTime(log), dateUnix)

View File

@ -98,8 +98,8 @@ class GetFailures(unittest.TestCase):
def testGetFailures01(self):
output = ('193.168.0.128', 3, 1124013599.0)
self.__filter.addLogPath(GetFailures.FILENAME_01)
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.addLogPath(GetFailures.FILENAME_01, False)
self.__filter.addFailRegex("<TIME> <PREFIX> (?:(?:Authentication failure|Failed [-/\w+]+) for(?: [iI](?:llegal|nvalid) user)?|[Ii](?:llegal|nvalid) user|ROOT LOGIN REFUSED) .*(?: from|FROM) <HOST>")
self.__filter.getFailures(GetFailures.FILENAME_01)
@ -115,8 +115,8 @@ class GetFailures(unittest.TestCase):
def testGetFailures02(self):
output = ('141.3.81.106', 4, 1124013539.0)
self.__filter.addLogPath(GetFailures.FILENAME_02)
self.__filter.addFailRegex("Failed .* (?:::f{4,6}:)(?P<host>\S*)")
self.__filter.addLogPath(GetFailures.FILENAME_02, False)
self.__filter.addFailRegex("<TIME> <PREFIX> Failed .* (?:::f{4,6}:)(?P<host>\S*)")
self.__filter.getFailures(GetFailures.FILENAME_02)
@ -132,8 +132,8 @@ class GetFailures(unittest.TestCase):
def testGetFailures03(self):
output = ('203.162.223.135', 6, 1124013544.0)
self.__filter.addLogPath(GetFailures.FILENAME_03)
self.__filter.addFailRegex("error,relay=(?:::f{4,6}:)?(?P<host>\S*),.*550 User unknown")
self.__filter.addLogPath(GetFailures.FILENAME_03, False)
self.__filter.addFailRegex("<TIME> <PREFIX> error,relay=(?:::f{4,6}:)?(?P<host>\S*),.*550 User unknown")
self.__filter.getFailures(GetFailures.FILENAME_03)
@ -150,8 +150,8 @@ class GetFailures(unittest.TestCase):
output = [('212.41.96.186', 4, 1124013600.0),
('212.41.96.185', 4, 1124013598.0)]
self.__filter.addLogPath(GetFailures.FILENAME_04)
self.__filter.addFailRegex("Invalid user .* (?P<host>\S*)")
self.__filter.addLogPath(GetFailures.FILENAME_04, False)
self.__filter.addFailRegex("<TIME> <PREFIX> Invalid user .* (?P<host>\S*)")
self.__filter.getFailures(GetFailures.FILENAME_04)
@ -169,9 +169,9 @@ class GetFailures(unittest.TestCase):
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.addLogPath(GetFailures.FILENAME_02, False)
self.__filter.addFailRegex("<TIME> <PREFIX> Failed .* from <HOST>")
self.__filter.addFailRegex("<TIME> <PREFIX> Accepted .* from <HOST>")
self.__filter.getFailures(GetFailures.FILENAME_02)
@ -187,10 +187,10 @@ class GetFailures(unittest.TestCase):
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.addLogPath(GetFailures.FILENAME_02, False)
self.__filter.addFailRegex("<TIME> <PREFIX> Failed .* from <HOST>")
self.__filter.addFailRegex("<TIME> <PREFIX> Accepted .* from <HOST>")
self.__filter.addIgnoreRegex(".* for roehl")
self.__filter.getFailures(GetFailures.FILENAME_02)