Created
May 2, 2013 22:34
-
-
Save kwirk/5506003 to your computer and use it in GitHub Desktop.
fail2ban/fail2ban date detector ideas
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/fail2ban/server/datedetector.py b/fail2ban/server/datedetector.py | |
index a29c975..734131d 100644 | |
--- a/fail2ban/server/datedetector.py | |
+++ b/fail2ban/server/datedetector.py | |
@@ -29,7 +29,7 @@ __license__ = "GPL" | |
import time, logging | |
-from datetemplate import DateStrptime, DateTai64n, DateEpoch, DateISO8601 | |
+from datetemplate import DatePatternRegex, DateTai64n, DateEpoch, DateISO8601 | |
from threading import Lock | |
# Gets the instance of the logger. | |
@@ -42,7 +42,7 @@ class DateDetector: | |
self.__templates = list() | |
self.__known_names = set() | |
- def _appendTemplate(self, template): | |
+ def appendTemplate(self, template): | |
name = template.getName() | |
if name in self.__known_names: | |
raise ValueError("There is already a template with name %s" % name) | |
@@ -53,120 +53,86 @@ class DateDetector: | |
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 = DatePatternRegex() | |
template.setPattern("%b %d %H:%M:%S") | |
- self._appendTemplate(template) | |
+ self.appendTemplate(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 = DatePatternRegex() | |
template.setPattern("%a %b %d %H:%M:%S %Y") | |
- self._appendTemplate(template) | |
+ self.appendTemplate(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 = DatePatternRegex() | |
template.setPattern("%a %b %d %H:%M:%S") | |
- self._appendTemplate(template) | |
+ self.appendTemplate(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 = DatePatternRegex() | |
template.setPattern("%Y/%m/%d %H:%M:%S") | |
- self._appendTemplate(template) | |
+ self.appendTemplate(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 = DatePatternRegex() | |
template.setPattern("%d/%m/%Y %H:%M:%S") | |
- self._appendTemplate(template) | |
+ self.appendTemplate(template) | |
# previous one but with year given by 2 digits | |
# (See http://bugs.debian.org/537610) | |
- template = DateStrptime() | |
- template.setName("Day/Month/Year2 Hour:Minute:Second") | |
- template.setRegex("\d{2}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}") | |
+ template = DatePatternRegex() | |
template.setPattern("%d/%m/%y %H:%M:%S") | |
- self._appendTemplate(template) | |
+ self.appendTemplate(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 = DatePatternRegex() | |
template.setPattern("%d/%b/%Y:%H:%M:%S") | |
- self._appendTemplate(template) | |
+ self.appendTemplate(template) | |
# CPanel 05/20/2008:01:57:39 | |
- template = DateStrptime() | |
- template.setName("Month/Day/Year:Hour:Minute:Second") | |
- template.setRegex("\d{2}/\d{2}/\d{4}:\d{2}:\d{2}:\d{2}") | |
+ template = DatePatternRegex() | |
template.setPattern("%m/%d/%Y:%H:%M:%S") | |
- self._appendTemplate(template) | |
+ self.appendTemplate(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 = DatePatternRegex() | |
template.setPattern("%Y-%m-%d %H:%M:%S") | |
- self._appendTemplate(template) | |
+ self.appendTemplate(template) | |
# custom for syslog-ng 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 = DatePatternRegex() | |
template.setPattern("%Y.%m.%d %H:%M:%S") | |
- self._appendTemplate(template) | |
+ self.appendTemplate(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 = DatePatternRegex() | |
template.setPattern("%d-%b-%Y %H:%M:%S") | |
- self._appendTemplate(template) | |
+ self.appendTemplate(template) | |
# 17-07-2008 17:23:25 | |
- 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 = DatePatternRegex() | |
template.setPattern("%d-%m-%Y %H:%M:%S") | |
- self._appendTemplate(template) | |
+ self.appendTemplate(template) | |
# 01-27-2012 16:22:44.252 | |
- template = DateStrptime() | |
- template.setName("Month-Day-Year Hour:Minute:Second[.Millisecond]") | |
- template.setRegex("\d{2}-\d{2}-\d{4} \d{2}:\d{2}:\d{2}") | |
+ template = DatePatternRegex() | |
template.setPattern("%m-%d-%Y %H:%M:%S") | |
- self._appendTemplate(template) | |
+ self.appendTemplate(template) | |
# TAI64N | |
template = DateTai64n() | |
template.setName("TAI64N") | |
- self._appendTemplate(template) | |
+ self.appendTemplate(template) | |
# Epoch | |
template = DateEpoch() | |
template.setName("Epoch") | |
- self._appendTemplate(template) | |
+ self.appendTemplate(template) | |
# ISO 8601 | |
template = DateISO8601() | |
template.setName("ISO 8601") | |
- self._appendTemplate(template) | |
+ self.appendTemplate(template) | |
# Only time information in the log | |
- template = DateStrptime() | |
- template.setName("Hour:Minute:Second") | |
- template.setRegex("^\d{2}:\d{2}:\d{2}") | |
- template.setPattern("%H:%M:%S") | |
- self._appendTemplate(template) | |
+ template = DatePatternRegex() | |
+ template.setPattern("%H:%M:%S", anchor=True) | |
+ self.appendTemplate(template) | |
# <09/16/08@05:03:30> | |
- template = DateStrptime() | |
- template.setName("<Month/Day/Year@Hour:Minute:Second>") | |
- template.setRegex("^<\d{2}/\d{2}/\d{2}@\d{2}:\d{2}:\d{2}>") | |
- template.setPattern("<%m/%d/%y@%H:%M:%S>") | |
- self._appendTemplate(template) | |
+ template = DatePatternRegex() | |
+ template.setPattern("<%m/%d/%y@%H:%M:%S>", anchor=True) | |
+ self.appendTemplate(template) | |
# MySQL: 130322 11:46:11 | |
- template = DateStrptime() | |
- template.setName("MonthDayYear Hour:Minute:Second") | |
- template.setRegex("^\d{2}\d{2}\d{2} +\d{1,2}:\d{2}:\d{2}") | |
- template.setPattern("%y%m%d %H:%M:%S") | |
- self._appendTemplate(template) | |
+ template = DatePatternRegex() | |
+ template.setPattern("%y%m%d %H:%M:%S", anchor=True) | |
+ self.appendTemplate(template) | |
# Apache Tomcat | |
- template = DateStrptime() | |
- template.setName("MONTH Day, Year 12hour:Minute:Second AM/PM") | |
- template.setRegex("\S{3}\s{1,2}\d{1,2}, \d{4} \d{1,2}:\d{2}:\d{2} [AP]M") | |
+ template = DatePatternRegex() | |
template.setPattern("%b %d, %Y %I:%M:%S %p") | |
- self._appendTemplate(template) | |
+ self.appendTemplate(template) | |
finally: | |
self.__lock.release() | |
diff --git a/fail2ban/server/datetemplate.py b/fail2ban/server/datetemplate.py | |
index f77f00c..f378c38 100644 | |
--- a/fail2ban/server/datetemplate.py | |
+++ b/fail2ban/server/datetemplate.py | |
@@ -180,6 +180,48 @@ class DateStrptime(DateTemplate): | |
date[2] = MyTime.gmtime()[2] | |
return date | |
+class DatePatternRegex(DateStrptime): | |
+ _patternRE = r"\%([aAbBdHIjmMpSUwWyY])" | |
+ _patternName = { | |
+ 'a': "DAY", 'A': "DAYNAME", 'b': "MON", 'B': "MONTH", 'd': "Day", | |
+ 'H': "24hour", 'I': "12hour", 'j': "Yearday", 'm': "Month", | |
+ 'M': "Minute", 'p': "AMPM", 'S': "Second", 'U': "Yearweek", | |
+ 'w': "Weekday", 'W': "Yearweek", "y": 'Year2', "Y": "Year"} | |
+ _patternRegex = { | |
+ 'a': r"\S{3}", 'A': r"\S+", 'b': r"\S{3}", 'B': r"\S+", | |
+ 'd': r"(?:3[0-1]|[1-2]\d|[ 0]?\d)", 'H': r"(?:2[0-4]|1\d|[ 0]?\d)", | |
+ 'I': r"(?:1[0-2]|0?\d)", | |
+ 'j': r"(?:3[0-6][0-6]|[1-2]\d\d|0?\d\d|0{0,2}\d)", | |
+ 'm': r"(?:1[0-2]|[ 0]?[1-9])", 'M': r"[0-5]\d", 'p': r"[AP]M", | |
+ 'S': r"(?:6[01]|[0-5]\d)", 'U': r"(?:5[0-3]|[1-4]\d|[ 0]?\d)", | |
+ 'w': r"[0-6]", 'W': r"(?:5[0-3]|[ 0]?\d)", 'y': r"\d{2}", | |
+ 'Y': r"\d{4}"} | |
+ | |
+ def __init__(self): | |
+ DateTemplate.__init__(self) | |
+ self.__pattern = "" | |
+ | |
+ def setPattern(self, pattern, anchor=False, wordBegin=True): | |
+ self.__pattern = pattern.strip() | |
+ | |
+ name = re.sub(self._patternRE, r'%(\1)s', pattern) % self._patternName | |
+ DateStrptime.setName(self, name) | |
+ | |
+ pattern = re.escape(pattern) | |
+ regex = re.sub( | |
+ r"\\" + self._patternRE, r'%(\1)s', pattern) % self._patternRegex | |
+ if anchor: | |
+ regex = r"^" + regex | |
+ DateStrptime.setRegex(self, regex, wordBegin) | |
+ | |
+ def getPattern(self): | |
+ return self.__pattern | |
+ | |
+ def setRegex(self, line): | |
+ raise NotImplementedError("Regex derived from pattern") | |
+ | |
+ def setName(self, line): | |
+ raise NotImplementedError("Name derived from pattern") | |
class DateTai64n(DateTemplate): | |
diff --git a/fail2ban/server/filter.py b/fail2ban/server/filter.py | |
index d4108dc..8a799c7 100644 | |
--- a/fail2ban/server/filter.py | |
+++ b/fail2ban/server/filter.py | |
@@ -32,6 +32,7 @@ from failmanager import FailManager | |
from ticket import FailTicket | |
from jailthread import JailThread | |
from datedetector import DateDetector | |
+from datetemplate import DatePatternRegex | |
from mytime import MyTime | |
from failregex import FailRegex, Regex, RegexException | |
@@ -196,6 +197,42 @@ class Filter(JailThread): | |
return self.__findTime | |
## | |
+ # Set the date detector pattern | |
+ # | |
+ # @param pattern the date template pattern | |
+ | |
+ def setDatePattern(self, pattern): | |
+ template = DatePatternRegex() | |
+ if pattern[0] == "^": # Special extra to enable anchor | |
+ template.setPattern(pattern[1:], anchor=True) | |
+ else: | |
+ template.setPattern(pattern) | |
+ dateDetector = DateDetector() | |
+ dateDetector.appendTemplate(template) | |
+ self.dateDetector = dateDetector | |
+ logSys.info("Date format set to `%r`: `%s`" % | |
+ (pattern, template.getName())) | |
+ logSys.debug("Date format regex: %s" % (template.getRegex())) | |
+ | |
+ ## | |
+ # Get the date detector pattern | |
+ # | |
+ # @return pattern of the date template pattern | |
+ | |
+ def getDatePattern(self): | |
+ templates = self.dateDetector.getTemplates() | |
+ if len(templates) > 1: | |
+ return "Default Detectors" | |
+ elif len(templates) == 1: | |
+ pattern = templates[0].getPattern() | |
+ if templates[0].getRegex()[0] == "^": | |
+ return "^" + pattern | |
+ else: | |
+ return pattern | |
+ else: | |
+ raise RuntimeError("Oh dear...") | |
+ | |
+ ## | |
# Set the maximum retry value. | |
# | |
# @param value the retry value | |
diff --git a/fail2ban/server/server.py b/fail2ban/server/server.py | |
index 092867b..c4a9674 100644 | |
--- a/fail2ban/server/server.py | |
+++ b/fail2ban/server/server.py | |
@@ -193,6 +193,12 @@ class Server: | |
def getFindTime(self, name): | |
return self.__jails.getFilter(name).getFindTime() | |
+ def setDatePattern(self, name, pattern): | |
+ self.__jails.getFilter(name).setDatePattern(pattern) | |
+ | |
+ def getDatePattern(self, name): | |
+ return self.__jails.getFilter(name).getDatePattern() | |
+ | |
def addFailRegex(self, name, value): | |
self.__jails.getFilter(name).addFailRegex(value) | |
diff --git a/fail2ban/server/transmitter.py b/fail2ban/server/transmitter.py | |
index b293217..6ed4e18 100644 | |
--- a/fail2ban/server/transmitter.py | |
+++ b/fail2ban/server/transmitter.py | |
@@ -171,6 +171,10 @@ class Transmitter: | |
value = command[2] | |
self.__server.setFindTime(name, int(value)) | |
return self.__server.getFindTime(name) | |
+ elif command[1] == "datepattern": | |
+ value = command[2] | |
+ self.__server.setDatePattern(name, value) | |
+ return self.__server.getDatePattern(name) | |
elif command[1] == "maxretry": | |
value = command[2] | |
self.__server.setMaxRetry(name, int(value)) | |
@@ -258,6 +262,8 @@ class Transmitter: | |
return self.__server.getUseDns(name) | |
elif command[1] == "findtime": | |
return self.__server.getFindTime(name) | |
+ elif command[1] == "datepattern": | |
+ return self.__server.getDatePattern(name) | |
elif command[1] == "maxretry": | |
return self.__server.getMaxRetry(name) | |
elif command[1] == "maxlines": | |
diff --git a/fail2ban/tests/datedetectortestcase.py b/fail2ban/tests/datedetectortestcase.py | |
index 534abdb..ffd4925 100644 | |
--- a/fail2ban/tests/datedetectortestcase.py | |
+++ b/fail2ban/tests/datedetectortestcase.py | |
@@ -106,7 +106,7 @@ class DateDetectorTest(unittest.TestCase): | |
self.assertEqual(old_name, n.getName()) # "Sort must be stable" | |
def testAllUniqueTemplateNames(self): | |
- self.assertRaises(ValueError, self.__datedetector._appendTemplate, | |
+ self.assertRaises(ValueError, self.__datedetector.appendTemplate, | |
self.__datedetector.getTemplates()[0]) | |
def testFullYearMatch_gh130(self): |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment