mirror of https://github.com/fail2ban/fail2ban
210 lines
6.2 KiB
Python
210 lines
6.2 KiB
Python
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
|
|
# vi: set ft=python sts=4 ts=4 sw=4 noet :
|
|
|
|
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
__author__ = "Cyril Jaquier and Fail2Ban Contributors"
|
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
|
__license__ = "GPL"
|
|
|
|
from threading import Lock
|
|
|
|
from .datetemplate import DatePatternRegex, DateTai64n, DateEpoch
|
|
from ..helpers import getLogger
|
|
|
|
# Gets the instance of the logger.
|
|
logSys = getLogger(__name__)
|
|
|
|
class DateDetector(object):
|
|
"""Manages one or more date templates to find a date within a log line.
|
|
|
|
Attributes
|
|
----------
|
|
templates
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.__lock = Lock()
|
|
self.__templates = list()
|
|
self.__known_names = set()
|
|
|
|
def _appendTemplate(self, template):
|
|
name = template.name
|
|
if name in self.__known_names:
|
|
raise ValueError(
|
|
"There is already a template with name %s" % name)
|
|
self.__known_names.add(name)
|
|
self.__templates.append(template)
|
|
|
|
def appendTemplate(self, template):
|
|
"""Add a date template to manage and use in search of dates.
|
|
|
|
Parameters
|
|
----------
|
|
template : DateTemplate or str
|
|
Can be either a `DateTemplate` instance, or a string which will
|
|
be used as the pattern for the `DatePatternRegex` template. The
|
|
template will then be added to the detector.
|
|
|
|
Raises
|
|
------
|
|
ValueError
|
|
If a template already exists with the same name.
|
|
"""
|
|
if isinstance(template, str):
|
|
template = DatePatternRegex(template)
|
|
self._appendTemplate(template)
|
|
|
|
def addDefaultTemplate(self):
|
|
"""Add Fail2Ban's default set of date templates.
|
|
"""
|
|
self.__lock.acquire()
|
|
try:
|
|
# asctime with optional day, subsecond and/or year:
|
|
# Sun Jan 23 21:59:59.011 2005
|
|
self.appendTemplate("(?:%a )?%b %d %H:%M:%S(?:\.%f)?(?: %Y)?")
|
|
# simple date, optional subsecond (proftpd):
|
|
# 2005-01-23 21:59:59
|
|
# simple date: 2005/01/23 21:59:59
|
|
# custom for syslog-ng 2006.12.21 06:43:20
|
|
self.appendTemplate("%Y(?P<_sep>[-/.])%m(?P=_sep)%d %H:%M:%S(?:,%f)?")
|
|
# simple date too (from x11vnc): 23/01/2005 21:59:59
|
|
# and with optional year given by 2 digits: 23/01/05 21:59:59
|
|
# (See http://bugs.debian.org/537610)
|
|
# 17-07-2008 17:23:25
|
|
self.appendTemplate("%d(?P<_sep>[-/])%m(?P=_sep)(?:%Y|%y) %H:%M:%S")
|
|
# Apache format optional time zone:
|
|
# [31/Oct/2006:09:22:55 -0000]
|
|
# 26-Jul-2007 15:20:52
|
|
self.appendTemplate("%d(?P<_sep>[-/])%b(?P=_sep)%Y[ :]?%H:%M:%S(?:\.%f)?(?: %z)?")
|
|
# CPanel 05/20/2008:01:57:39
|
|
self.appendTemplate("%m/%d/%Y:%H:%M:%S")
|
|
# named 26-Jul-2007 15:20:52.252
|
|
# roundcube 26-Jul-2007 15:20:52 +0200
|
|
# 01-27-2012 16:22:44.252
|
|
# subseconds explicit to avoid possible %m<->%d confusion
|
|
# with previous
|
|
self.appendTemplate("%m-%d-%Y %H:%M:%S\.%f")
|
|
# TAI64N
|
|
template = DateTai64n()
|
|
template.name = "TAI64N"
|
|
self.appendTemplate(template)
|
|
# Epoch
|
|
template = DateEpoch()
|
|
template.name = "Epoch"
|
|
self.appendTemplate(template)
|
|
# ISO 8601
|
|
self.appendTemplate("%Y-%m-%d[T ]%H:%M:%S(?:\.%f)?(?:%z)?")
|
|
# Only time information in the log
|
|
self.appendTemplate("^%H:%M:%S")
|
|
# <09/16/08@05:03:30>
|
|
self.appendTemplate("^<%m/%d/%y@%H:%M:%S>")
|
|
# MySQL: 130322 11:46:11
|
|
self.appendTemplate("^%y%m%d ?%H:%M:%S")
|
|
# Apache Tomcat
|
|
self.appendTemplate("%b %d, %Y %I:%M:%S %p")
|
|
# ASSP: Apr-27-13 02:33:06
|
|
self.appendTemplate("^%b-%d-%y %H:%M:%S")
|
|
finally:
|
|
self.__lock.release()
|
|
|
|
@property
|
|
def templates(self):
|
|
"""List of template instances managed by the detector.
|
|
"""
|
|
return self.__templates
|
|
|
|
def matchTime(self, line):
|
|
"""Attempts to find date on a log line using templates.
|
|
|
|
This uses the templates' `matchDate` method in an attempt to find
|
|
a date. It also increments the match hit count for the winning
|
|
template.
|
|
|
|
Parameters
|
|
----------
|
|
line : str
|
|
Line which is searched by the date templates.
|
|
|
|
Returns
|
|
-------
|
|
re.MatchObject
|
|
The regex match returned from the first successfully matched
|
|
template.
|
|
"""
|
|
self.__lock.acquire()
|
|
try:
|
|
for template in self.__templates:
|
|
match = template.matchDate(line)
|
|
if not match is None:
|
|
logSys.debug("Matched time template %s" % template.name)
|
|
template.hits += 1
|
|
return match
|
|
return None
|
|
finally:
|
|
self.__lock.release()
|
|
|
|
def getTime(self, line):
|
|
"""Attempts to return the date on a log line using templates.
|
|
|
|
This uses the templates' `getDate` method in an attempt to find
|
|
a date.
|
|
|
|
Parameters
|
|
----------
|
|
line : str
|
|
Line which is searched by the date templates.
|
|
|
|
Returns
|
|
-------
|
|
float
|
|
The Unix timestamp returned from the first successfully matched
|
|
template.
|
|
"""
|
|
self.__lock.acquire()
|
|
try:
|
|
for template in self.__templates:
|
|
try:
|
|
date = template.getDate(line)
|
|
if date is None:
|
|
continue
|
|
logSys.debug("Got time %f for \"%r\" using template %s" %
|
|
(date[0], date[1].group(), template.name))
|
|
return date
|
|
except ValueError:
|
|
pass
|
|
return None
|
|
finally:
|
|
self.__lock.release()
|
|
|
|
def sortTemplate(self):
|
|
"""Sort the date templates by number of hits
|
|
|
|
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.
|
|
This ensures the most commonly matched templates are checked first,
|
|
improving performance of matchTime and getTime.
|
|
"""
|
|
self.__lock.acquire()
|
|
try:
|
|
logSys.debug("Sorting the template list")
|
|
self.__templates.sort(key=lambda x: x.hits, reverse=True)
|
|
t = self.__templates[0]
|
|
logSys.debug("Winning template: %s with %d hits" % (t.name, t.hits))
|
|
finally:
|
|
self.__lock.release()
|