diff --git a/MANIFEST b/MANIFEST index 877720af..834f70b0 100644 --- a/MANIFEST +++ b/MANIFEST @@ -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 diff --git a/fail2ban-regex b/fail2ban-regex index 08aaccaa..360db4b1 100755 --- a/fail2ban-regex +++ b/fail2ban-regex @@ -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" diff --git a/fail2ban-testcases b/fail2ban-testcases index 9573d62b..1c29f7b2 100755 --- a/fail2ban-testcases +++ b/fail2ban-testcases @@ -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() diff --git a/server/datedetector.py b/server/datedetector.py deleted file mode 100644 index 564fcd3d..00000000 --- a/server/datedetector.py +++ /dev/null @@ -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() diff --git a/server/datetemplate.py b/server/datetemplate.py deleted file mode 100644 index 59dc4745..00000000 --- a/server/datetemplate.py +++ /dev/null @@ -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 \ No newline at end of file diff --git a/server/failregex.py b/server/failregex.py index 398817fd..9129892a 100644 --- a/server/failregex.py +++ b/server/failregex.py @@ -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 "" with default regular expression for host. - regex = regex.replace("", "(?:::f{4,6}:)?(?P\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 diff --git a/server/filter.py b/server/filter.py index dd9115d5..e9ff090b 100644 --- a/server/filter.py +++ b/server/filter.py @@ -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) diff --git a/server/filtergamin.py b/server/filtergamin.py index 5d124c7b..5907aaab 100644 --- a/server/filtergamin.py +++ b/server/filtergamin.py @@ -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: diff --git a/server/filterpoll.py b/server/filterpoll.py index 47d12a47..93fb995a 100644 --- a/server/filterpoll.py +++ b/server/filterpoll.py @@ -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: diff --git a/server/hosttemplate.py b/server/hosttemplate.py new file mode 100644 index 00000000..56bc1d9d --- /dev/null +++ b/server/hosttemplate.py @@ -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, "") + + +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) diff --git a/server/prefixtemplate.py b/server/prefixtemplate.py new file mode 100644 index 00000000..8568d382 --- /dev/null +++ b/server/prefixtemplate.py @@ -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, "") + + +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) diff --git a/server/server.py b/server/server.py index ecfe45c4..2a7e9b5c 100644 --- a/server/server.py +++ b/server/server.py @@ -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) diff --git a/server/template.py b/server/template.py new file mode 100644 index 00000000..0c961d22 --- /dev/null +++ b/server/template.py @@ -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 diff --git a/server/timetemplate.py b/server/timetemplate.py new file mode 100644 index 00000000..f3cf710f --- /dev/null +++ b/server/timetemplate.py @@ -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, "