Merge branch '0.11'

pull/2842/head
sebres 2020-08-26 13:49:41 +02:00
commit 8bc7623388
14 changed files with 189 additions and 61 deletions

View File

@ -28,6 +28,8 @@ ver. 1.0.1-dev-1 (20??/??/??) - development nightly edition
* ensure we've unique action name per jail (also if parameter `actname` is not set but name deviates from standard name, gh-2686) * ensure we've unique action name per jail (also if parameter `actname` is not set but name deviates from standard name, gh-2686)
* don't use `%(banaction)s` interpolation because it can be complex value (containing `[...]` and/or quotes), * don't use `%(banaction)s` interpolation because it can be complex value (containing `[...]` and/or quotes),
so would bother the action interpolation so would bother the action interpolation
* fixed type conversion in config readers (take place after all interpolations get ready), that allows to
specify typed parameters variable (as substitutions) as well as to supply it in other sections or as init parameters.
* `action.d/*-ipset*.conf`: several ipset actions fixed (no timeout per default anymore), so no discrepancy * `action.d/*-ipset*.conf`: several ipset actions fixed (no timeout per default anymore), so no discrepancy
between ipset and fail2ban (removal from ipset will be managed by fail2ban only, gh-2703) between ipset and fail2ban (removal from ipset will be managed by fail2ban only, gh-2703)
* `action.d/cloudflare.conf`: fixed `actionunban` (considering new-line chars and optionally real json-parsing * `action.d/cloudflare.conf`: fixed `actionunban` (considering new-line chars and optionally real json-parsing
@ -45,6 +47,7 @@ ver. 1.0.1-dev-1 (20??/??/??) - development nightly edition
### New Features and Enhancements ### New Features and Enhancements
* new filter and jail for GitLab recognizing failed application logins (gh-2689) * new filter and jail for GitLab recognizing failed application logins (gh-2689)
* `filter.d/guacamole.conf` extended with `logging` parameter to follow webapp-logging if it's configured (gh-2631)
* introduced new prefix `{UNB}` for `datepattern` to disable word boundaries in regex; * introduced new prefix `{UNB}` for `datepattern` to disable word boundaries in regex;
* datetemplate: improved anchor detection for capturing groups `(^...)`; * datetemplate: improved anchor detection for capturing groups `(^...)`;
* datepattern: improved handling with wrong recognized timestamps (timezones, no datepattern, etc) * datepattern: improved handling with wrong recognized timestamps (timezones, no datepattern, etc)

View File

@ -5,21 +5,47 @@
[Definition] [Definition]
# Option: failregex logging = catalina
# Notes.: regex to match the password failures messages in the logfile. failregex = <L_<logging>/failregex>
# Values: TEXT maxlines = <L_<logging>/maxlines>
# datepattern = <L_<logging>/datepattern>
[L_catalina]
failregex = ^.*\nWARNING: Authentication attempt from <HOST> for user "[^"]*" failed\.$ failregex = ^.*\nWARNING: Authentication attempt from <HOST> for user "[^"]*" failed\.$
# Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored.
# Values: TEXT
#
ignoreregex =
# "maxlines" is number of log lines to buffer for multi-line regex searches
maxlines = 2 maxlines = 2
datepattern = ^%%b %%d, %%ExY %%I:%%M:%%S %%p datepattern = ^%%b %%d, %%ExY %%I:%%M:%%S %%p
^WARNING:()** ^WARNING:()**
{^LN-BEG} {^LN-BEG}
[L_webapp]
failregex = ^ \[\S+\] WARN \S+ - Authentication attempt from <HOST> for user "<F-USER>[^"]+</F-USER>" failed.
maxlines = 1
datepattern = ^%%H:%%M:%%S.%%f
# DEV Notes:
#
# failregex is based on the default pattern given in Guacamole documentation :
# https://guacamole.apache.org/doc/gug/configuring-guacamole.html#webapp-logging
#
# The following logback.xml Guacamole configuration file can then be used accordingly :
# <configuration>
# <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
# <file>/var/log/guacamole.log</file>
# <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
# <fileNamePattern>/var/log/guacamole.%d.log.gz</fileNamePattern>
# <maxHistory>32</maxHistory>
# </rollingPolicy>
# <encoder>
# <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
# </encoder>
# </appender>
# <root level="info">
# <appender-ref ref="FILE" />
# </root>
# </configuration>

View File

@ -478,6 +478,7 @@ backend = %(syslog_backend)s
port = http,https port = http,https
logpath = /var/log/tomcat*/catalina.out logpath = /var/log/tomcat*/catalina.out
#logpath = /var/log/guacamole.log
[monit] [monit]
#Ban clients brute-forcing the monit gui login #Ban clients brute-forcing the monit gui login

View File

@ -38,18 +38,18 @@ class ActionReader(DefinitionInitConfigReader):
_configOpts = { _configOpts = {
"actionstart": ["string", None], "actionstart": ["string", None],
"actionstart_on_demand": ["string", None], "actionstart_on_demand": ["bool", None],
"actionstop": ["string", None], "actionstop": ["string", None],
"actionflush": ["string", None], "actionflush": ["string", None],
"actionreload": ["string", None], "actionreload": ["string", None],
"actioncheck": ["string", None], "actioncheck": ["string", None],
"actionrepair": ["string", None], "actionrepair": ["string", None],
"actionrepair_on_unban": ["string", None], "actionrepair_on_unban": ["bool", None],
"actionban": ["string", None], "actionban": ["string", None],
"actionprolong": ["string", None], "actionprolong": ["string", None],
"actionreban": ["string", None], "actionreban": ["string", None],
"actionunban": ["string", None], "actionunban": ["string", None],
"norestored": ["string", None], "norestored": ["bool", None],
} }
def __init__(self, file_, jailName, initOpts, **kwargs): def __init__(self, file_, jailName, initOpts, **kwargs):
@ -84,11 +84,6 @@ class ActionReader(DefinitionInitConfigReader):
def convert(self): def convert(self):
opts = self.getCombined( opts = self.getCombined(
ignore=CommandAction._escapedTags | set(('timeout', 'bantime'))) ignore=CommandAction._escapedTags | set(('timeout', 'bantime')))
# type-convert only after combined (otherwise boolean converting prevents substitution):
for o in ('norestored', 'actionstart_on_demand', 'actionrepair_on_unban'):
if opts.get(o):
opts[o] = self._convert_to_boolean(opts[o])
# stream-convert: # stream-convert:
head = ["set", self._jailName] head = ["set", self._jailName]
stream = list() stream = list()

View File

@ -29,7 +29,7 @@ import re
import sys import sys
from ..helpers import getLogger from ..helpers import getLogger
if sys.version_info >= (3,2): if sys.version_info >= (3,): # pragma: 2.x no cover
# SafeConfigParser deprecated from Python 3.2 (renamed to ConfigParser) # SafeConfigParser deprecated from Python 3.2 (renamed to ConfigParser)
from configparser import ConfigParser as SafeConfigParser, BasicInterpolation, \ from configparser import ConfigParser as SafeConfigParser, BasicInterpolation, \
@ -61,7 +61,7 @@ if sys.version_info >= (3,2):
return super(BasicInterpolationWithName, self)._interpolate_some( return super(BasicInterpolationWithName, self)._interpolate_some(
parser, option, accum, rest, section, map, *args, **kwargs) parser, option, accum, rest, section, map, *args, **kwargs)
else: # pragma: no cover else: # pragma: 3.x no cover
from ConfigParser import SafeConfigParser, \ from ConfigParser import SafeConfigParser, \
InterpolationMissingOptionError, NoOptionError, NoSectionError InterpolationMissingOptionError, NoOptionError, NoSectionError
@ -372,7 +372,8 @@ after = 1.conf
s2 = alls.get(n) s2 = alls.get(n)
if isinstance(s2, dict): if isinstance(s2, dict):
# save previous known values, for possible using in local interpolations later: # save previous known values, for possible using in local interpolations later:
self.merge_section('KNOWN/'+n, s2, '') self.merge_section('KNOWN/'+n,
dict(filter(lambda i: i[0] in s, s2.iteritems())), '')
# merge section # merge section
s2.update(s) s2.update(s)
else: else:

View File

@ -34,6 +34,30 @@ from ..helpers import getLogger, _as_bool, _merge_dicts, substituteRecursiveTags
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = getLogger(__name__) logSys = getLogger(__name__)
CONVERTER = {
"bool": _as_bool,
"int": int,
}
def _OptionsTemplateGen(options):
"""Iterator over the options template with default options.
Each options entry is composed of an array or tuple with:
[[type, name, ?default?], ...]
Or it is a dict:
{name: [type, default], ...}
"""
if isinstance(options, (list,tuple)):
for optname in options:
if len(optname) > 2:
opttype, optname, optvalue = optname
else:
(opttype, optname), optvalue = optname, None
yield opttype, optname, optvalue
else:
for optname in options:
opttype, optvalue = options[optname]
yield opttype, optname, optvalue
class ConfigReader(): class ConfigReader():
"""Generic config reader class. """Generic config reader class.
@ -228,31 +252,22 @@ class ConfigReaderUnshared(SafeConfigParserWithIncludes):
# Or it is a dict: # Or it is a dict:
# {name: [type, default], ...} # {name: [type, default], ...}
def getOptions(self, sec, options, pOptions=None, shouldExist=False): def getOptions(self, sec, options, pOptions=None, shouldExist=False, convert=True):
values = dict() values = dict()
if pOptions is None: if pOptions is None:
pOptions = {} pOptions = {}
# Get only specified options: # Get only specified options:
for optname in options: for opttype, optname, optvalue in _OptionsTemplateGen(options):
if isinstance(options, (list,tuple)):
if len(optname) > 2:
opttype, optname, optvalue = optname
else:
(opttype, optname), optvalue = optname, None
else:
opttype, optvalue = options[optname]
if optname in pOptions: if optname in pOptions:
continue continue
try: try:
if opttype == "bool": v = self.get(sec, optname, vars=pOptions)
v = self.getboolean(sec, optname)
if v is None: continue
elif opttype == "int":
v = self.getint(sec, optname)
if v is None: continue
else:
v = self.get(sec, optname, vars=pOptions)
values[optname] = v values[optname] = v
if convert:
conv = CONVERTER.get(opttype)
if conv:
if v is None: continue
values[optname] = conv(v)
except NoSectionError as e: except NoSectionError as e:
if shouldExist: if shouldExist:
raise raise
@ -324,8 +339,9 @@ class DefinitionInitConfigReader(ConfigReader):
pOpts = dict() pOpts = dict()
if self._initOpts: if self._initOpts:
pOpts = _merge_dicts(pOpts, self._initOpts) pOpts = _merge_dicts(pOpts, self._initOpts)
# type-convert only in combined (otherwise int/bool converting prevents substitution):
self._opts = ConfigReader.getOptions( self._opts = ConfigReader.getOptions(
self, "Definition", self._configOpts, pOpts) self, "Definition", self._configOpts, pOpts, convert=False)
self._pOpts = pOpts self._pOpts = pOpts
if self.has_section("Init"): if self.has_section("Init"):
# get only own options (without options from default): # get only own options (without options from default):
@ -346,10 +362,21 @@ class DefinitionInitConfigReader(ConfigReader):
if opt == '__name__' or opt in self._opts: continue if opt == '__name__' or opt in self._opts: continue
self._opts[opt] = self.get("Definition", opt) self._opts[opt] = self.get("Definition", opt)
def convertOptions(self, opts, configOpts):
"""Convert interpolated combined options to expected type.
"""
for opttype, optname, optvalue in _OptionsTemplateGen(configOpts):
conv = CONVERTER.get(opttype)
if conv:
v = opts.get(optname)
if v is None: continue
try:
opts[optname] = conv(v)
except ValueError:
logSys.warning("Wrong %s value %r for %r. Using default one: %r",
opttype, v, optname, optvalue)
opts[optname] = optvalue
def _convert_to_boolean(self, value):
return _as_bool(value)
def getCombOption(self, optname): def getCombOption(self, optname):
"""Get combined definition option (as string) using pre-set and init """Get combined definition option (as string) using pre-set and init
options as preselection (values with higher precedence as specified in section). options as preselection (values with higher precedence as specified in section).
@ -384,6 +411,8 @@ class DefinitionInitConfigReader(ConfigReader):
ignore=ignore, addrepl=self.getCombOption) ignore=ignore, addrepl=self.getCombOption)
if not opts: if not opts:
raise ValueError('recursive tag definitions unable to be resolved') raise ValueError('recursive tag definitions unable to be resolved')
# convert options after all interpolations:
self.convertOptions(opts, self._configOpts)
return opts return opts
def convert(self): def convert(self):

View File

@ -398,8 +398,8 @@ def splitWithOptions(option):
# tags (<tag>) in tagged options. # tags (<tag>) in tagged options.
# #
# max tag replacement count: # max tag replacement count (considering tag X in tag Y repeat):
MAX_TAG_REPLACE_COUNT = 10 MAX_TAG_REPLACE_COUNT = 25
# compiled RE for tag name (replacement name) # compiled RE for tag name (replacement name)
TAG_CRE = re.compile(r'<([^ <>]+)>') TAG_CRE = re.compile(r'<([^ <>]+)>')
@ -433,6 +433,7 @@ def substituteRecursiveTags(inptags, conditional='',
done = set() done = set()
noRecRepl = hasattr(tags, "getRawItem") noRecRepl = hasattr(tags, "getRawItem")
# repeat substitution while embedded-recursive (repFlag is True) # repeat substitution while embedded-recursive (repFlag is True)
repCounts = {}
while True: while True:
repFlag = False repFlag = False
# substitute each value: # substitute each value:
@ -444,7 +445,7 @@ def substituteRecursiveTags(inptags, conditional='',
value = orgval = uni_string(tags[tag]) value = orgval = uni_string(tags[tag])
# search and replace all tags within value, that can be interpolated using other tags: # search and replace all tags within value, that can be interpolated using other tags:
m = tre_search(value) m = tre_search(value)
refCounts = {} rplc = repCounts.get(tag, {})
#logSys.log(5, 'TAG: %s, value: %s' % (tag, value)) #logSys.log(5, 'TAG: %s, value: %s' % (tag, value))
while m: while m:
# found replacement tag: # found replacement tag:
@ -454,13 +455,13 @@ def substituteRecursiveTags(inptags, conditional='',
m = tre_search(value, m.end()) m = tre_search(value, m.end())
continue continue
#logSys.log(5, 'found: %s' % rtag) #logSys.log(5, 'found: %s' % rtag)
if rtag == tag or refCounts.get(rtag, 1) > MAX_TAG_REPLACE_COUNT: if rtag == tag or rplc.get(rtag, 1) > MAX_TAG_REPLACE_COUNT:
# recursive definitions are bad # recursive definitions are bad
#logSys.log(5, 'recursion fail tag: %s value: %s' % (tag, value) ) #logSys.log(5, 'recursion fail tag: %s value: %s' % (tag, value) )
raise ValueError( raise ValueError(
"properties contain self referencing definitions " "properties contain self referencing definitions "
"and cannot be resolved, fail tag: %s, found: %s in %s, value: %s" % "and cannot be resolved, fail tag: %s, found: %s in %s, value: %s" %
(tag, rtag, refCounts, value)) (tag, rtag, rplc, value))
repl = None repl = None
if conditional: if conditional:
repl = tags.get(rtag + '?' + conditional) repl = tags.get(rtag + '?' + conditional)
@ -480,7 +481,7 @@ def substituteRecursiveTags(inptags, conditional='',
value = value.replace('<%s>' % rtag, repl) value = value.replace('<%s>' % rtag, repl)
#logSys.log(5, 'value now: %s' % value) #logSys.log(5, 'value now: %s' % value)
# increment reference count: # increment reference count:
refCounts[rtag] = refCounts.get(rtag, 0) + 1 rplc[rtag] = rplc.get(rtag, 0) + 1
# the next match for replace: # the next match for replace:
m = tre_search(value, m.start()) m = tre_search(value, m.start())
#logSys.log(5, 'TAG: %s, newvalue: %s' % (tag, value)) #logSys.log(5, 'TAG: %s, newvalue: %s' % (tag, value))
@ -488,6 +489,7 @@ def substituteRecursiveTags(inptags, conditional='',
if orgval != value: if orgval != value:
# check still contains any tag - should be repeated (possible embedded-recursive substitution): # check still contains any tag - should be repeated (possible embedded-recursive substitution):
if tre_search(value): if tre_search(value):
repCounts[tag] = rplc
repFlag = True repFlag = True
# copy return tags dict to prevent modifying of inptags: # copy return tags dict to prevent modifying of inptags:
if id(tags) == id(inptags): if id(tags) == id(inptags):

View File

@ -121,8 +121,11 @@ class MyTime:
@return ISO-capable string representation of given unixTime @return ISO-capable string representation of given unixTime
""" """
return datetime.datetime.fromtimestamp( # consider end of 9999th year (in GMT+23 to avoid year overflow in other TZ)
unixTime).replace(microsecond=0).strftime(format) dt = datetime.datetime.fromtimestamp(
unixTime).replace(microsecond=0
) if unixTime < 253402214400 else datetime.datetime(9999, 12, 31, 23, 59, 59)
return dt.strftime(format)
## precreate/precompile primitives used in str2seconds: ## precreate/precompile primitives used in str2seconds:

View File

@ -252,7 +252,7 @@ class CommandActionTest(LogCaptureTestCase):
delattr(self.__action, 'ac') delattr(self.__action, 'ac')
# produce self-referencing query except: # produce self-referencing query except:
self.assertRaisesRegexp(ValueError, r"possible self referencing definitions in query", self.assertRaisesRegexp(ValueError, r"possible self referencing definitions in query",
lambda: self.__action.replaceTag("<x<x<x<x<x<x<x<x<x<x<x<x<x<x<x<x<x<x<x<x<x>>>>>>>>>>>>>>>>>>>>>", lambda: self.__action.replaceTag("<x"*30+">"*30,
self.__action._properties, conditional="family=inet6") self.__action._properties, conditional="family=inet6")
) )

View File

@ -154,6 +154,21 @@ class AddFailure(unittest.TestCase):
finally: finally:
self.__banManager.setBanTime(btime) self.__banManager.setBanTime(btime)
def testBanList(self):
tickets = [
BanTicket('192.0.2.1', 1167605999.0),
BanTicket('192.0.2.2', 1167605999.0),
]
tickets[1].setBanTime(-1)
for t in tickets:
self.__banManager.addBanTicket(t)
self.assertSortedEqual(self.__banManager.getBanList(ordered=True, withTime=True),
[
'192.0.2.1 \t2006-12-31 23:59:59 + 600 = 2007-01-01 00:09:59',
'192.0.2.2 \t2006-12-31 23:59:59 + -1 = 9999-12-31 23:59:59'
]
)
class StatusExtendedCymruInfo(unittest.TestCase): class StatusExtendedCymruInfo(unittest.TestCase):
def setUp(self): def setUp(self):

View File

@ -87,6 +87,21 @@ option = %s
self.assertTrue(self.c.read(f)) # we got some now self.assertTrue(self.c.read(f)) # we got some now
return self.c.getOptions('section', [("int", 'option')])['option'] return self.c.getOptions('section', [("int", 'option')])['option']
def testConvert(self):
self.c.add_section("Definition")
self.c.set("Definition", "a", "1")
self.c.set("Definition", "b", "1")
self.c.set("Definition", "c", "test")
opts = self.c.getOptions("Definition",
(('int', 'a', 0), ('bool', 'b', 0), ('int', 'c', 0)))
self.assertSortedEqual(opts, {'a': 1, 'b': True, 'c': 0})
opts = self.c.getOptions("Definition",
(('int', 'a'), ('bool', 'b'), ('int', 'c')))
self.assertSortedEqual(opts, {'a': 1, 'b': True, 'c': None})
opts = self.c.getOptions("Definition",
{'a': ('int', 0), 'b': ('bool', 0), 'c': ('int', 0)})
self.assertSortedEqual(opts, {'a': 1, 'b': True, 'c': 0})
def testInaccessibleFile(self): def testInaccessibleFile(self):
f = os.path.join(self.d, "d.conf") # inaccessible file f = os.path.join(self.d, "d.conf") # inaccessible file
self._write('d.conf', 0) self._write('d.conf', 0)
@ -483,14 +498,12 @@ class JailReaderTest(LogCaptureTestCase):
self.assertRaises(NoSectionError, c.getOptions, 'test', {}) self.assertRaises(NoSectionError, c.getOptions, 'test', {})
class FilterReaderTest(unittest.TestCase): class FilterReaderTest(LogCaptureTestCase):
def __init__(self, *args, **kwargs):
super(FilterReaderTest, self).__init__(*args, **kwargs)
self.__share_cfg = {}
def testConvert(self): def testConvert(self):
output = [['multi-set', 'testcase01', 'addfailregex', [ output = [
['set', 'testcase01', 'maxlines', 1],
['multi-set', 'testcase01', 'addfailregex', [
"^\\s*(?:\\S+ )?(?:kernel: \\[\\d+\\.\\d+\\] )?(?:@vserver_\\S+ )" "^\\s*(?:\\S+ )?(?:kernel: \\[\\d+\\.\\d+\\] )?(?:@vserver_\\S+ )"
"?(?:(?:\\[\\d+\\])?:\\s+[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?|" "?(?:(?:\\[\\d+\\])?:\\s+[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?|"
"[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?(?:\\[\\d+\\])?:)?\\s*(?:" "[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?(?:\\[\\d+\\])?:)?\\s*(?:"
@ -512,7 +525,6 @@ class FilterReaderTest(unittest.TestCase):
['set', 'testcase01', 'addjournalmatch', ['set', 'testcase01', 'addjournalmatch',
"FIELD= with spaces ", "+", "AFIELD= with + char and spaces"], "FIELD= with spaces ", "+", "AFIELD= with + char and spaces"],
['set', 'testcase01', 'datepattern', "%Y %m %d %H:%M:%S"], ['set', 'testcase01', 'datepattern', "%Y %m %d %H:%M:%S"],
['set', 'testcase01', 'maxlines', 1], # Last for overide test
] ]
filterReader = FilterReader("testcase01", "testcase01", {}) filterReader = FilterReader("testcase01", "testcase01", {})
filterReader.setBaseDir(TEST_FILES_DIR) filterReader.setBaseDir(TEST_FILES_DIR)
@ -529,9 +541,18 @@ class FilterReaderTest(unittest.TestCase):
filterReader.read() filterReader.read()
#filterReader.getOptions(["failregex", "ignoreregex"]) #filterReader.getOptions(["failregex", "ignoreregex"])
filterReader.getOptions(None) filterReader.getOptions(None)
output[-1][-1] = "5" output[0][-1] = 5; # maxlines = 5
self.assertSortedEqual(filterReader.convert(), output) self.assertSortedEqual(filterReader.convert(), output)
def testConvertOptions(self):
filterReader = FilterReader("testcase01", "testcase01", {'maxlines': '<test>', 'test': 'X'},
share_config=TEST_FILES_DIR_SHARE_CFG, basedir=TEST_FILES_DIR)
filterReader.read()
filterReader.getOptions(None)
opts = filterReader.getCombined();
self.assertNotEqual(opts['maxlines'], 'X'); # wrong int value 'X' for 'maxlines'
self.assertLogged("Wrong int value 'X' for 'maxlines'. Using default one:")
def testFilterReaderSubstitionDefault(self): def testFilterReaderSubstitionDefault(self):
output = [['set', 'jailname', 'addfailregex', 'to=sweet@example.com fromip=<IP>']] output = [['set', 'jailname', 'addfailregex', 'to=sweet@example.com fromip=<IP>']]
filterReader = FilterReader('substition', "jailname", {}, filterReader = FilterReader('substition', "jailname", {},
@ -541,6 +562,17 @@ class FilterReaderTest(unittest.TestCase):
c = filterReader.convert() c = filterReader.convert()
self.assertSortedEqual(c, output) self.assertSortedEqual(c, output)
def testFilterReaderSubstKnown(self):
# testcase02.conf + testcase02.local, test covering that known/option is not overridden
# with unmodified (not available) value of option from .local config file, so wouldn't
# cause self-recursion if option already has a reference to known/option in .conf file.
filterReader = FilterReader('testcase02', "jailname", {},
share_config=TEST_FILES_DIR_SHARE_CFG, basedir=TEST_FILES_DIR)
filterReader.read()
filterReader.getOptions(None)
opts = filterReader.getCombined()
self.assertTrue('sshd' in opts['failregex'])
def testFilterReaderSubstitionSet(self): def testFilterReaderSubstitionSet(self):
output = [['set', 'jailname', 'addfailregex', 'to=sour@example.com fromip=<IP>']] output = [['set', 'jailname', 'addfailregex', 'to=sour@example.com fromip=<IP>']]
filterReader = FilterReader('substition', "jailname", {'honeypot': 'sour@example.com'}, filterReader = FilterReader('substition', "jailname", {'honeypot': 'sour@example.com'},

View File

@ -0,0 +1,12 @@
[INCLUDES]
# Read common prefixes. If any customizations available -- read them from
# common.local
before = testcase-common.conf
[Definition]
_daemon = sshd
__prefix_line = %(known/__prefix_line)s(?:\w{14,20}: )?
failregex = %(__prefix_line)s test

View File

@ -0,0 +1,4 @@
[Definition]
# no options here, coverage for testFilterReaderSubstKnown:
# avoid to overwrite known/option with unmodified (not available) value of option from .local config file

View File

@ -10,3 +10,8 @@ WARNING: Authentication attempt from 192.0.2.0 for user "null" failed.
apr 16, 2013 8:32:28 AM org.slf4j.impl.JCLLoggerAdapter warn apr 16, 2013 8:32:28 AM org.slf4j.impl.JCLLoggerAdapter warn
# failJSON: { "time": "2013-04-16T08:32:28", "match": true , "host": "192.0.2.0" } # failJSON: { "time": "2013-04-16T08:32:28", "match": true , "host": "192.0.2.0" }
WARNING: Authentication attempt from 192.0.2.0 for user "pippo" failed. WARNING: Authentication attempt from 192.0.2.0 for user "pippo" failed.
# filterOptions: {"logging": "webapp"}
# failJSON: { "time": "2005-08-13T12:57:32", "match": true , "host": "182.23.72.36" }
12:57:32.907 [http-nio-8080-exec-10] WARN o.a.g.r.auth.AuthenticationService - Authentication attempt from 182.23.72.36 for user "guacadmin" failed.