mirror of https://github.com/fail2ban/fail2ban
move extractOptions from JailReader to helpers (common usage server- / client-side);
parent
ff987b60cd
commit
de97dedba0
|
@ -46,12 +46,12 @@ except ImportError:
|
||||||
FilterSystemd = None
|
FilterSystemd = None
|
||||||
|
|
||||||
from ..version import version
|
from ..version import version
|
||||||
from .jailreader import JailReader
|
|
||||||
from .filterreader import FilterReader
|
from .filterreader import FilterReader
|
||||||
from ..server.filter import Filter, FileContainer
|
from ..server.filter import Filter, FileContainer
|
||||||
from ..server.failregex import Regex, RegexException
|
from ..server.failregex import Regex, RegexException
|
||||||
|
|
||||||
from ..helpers import str2LogLevel, getVerbosityFormat, FormatterWithTraceBack, getLogger, PREFER_ENC
|
from ..helpers import str2LogLevel, getVerbosityFormat, FormatterWithTraceBack, getLogger, \
|
||||||
|
extractOptions, PREFER_ENC
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger("fail2ban")
|
logSys = getLogger("fail2ban")
|
||||||
|
|
||||||
|
@ -287,7 +287,7 @@ class Fail2banRegex(object):
|
||||||
fltFile = None
|
fltFile = None
|
||||||
fltOpt = {}
|
fltOpt = {}
|
||||||
if regextype == 'fail':
|
if regextype == 'fail':
|
||||||
fltName, fltOpt = JailReader.extractOptions(value)
|
fltName, fltOpt = extractOptions(value)
|
||||||
if fltName is not None:
|
if fltName is not None:
|
||||||
if "." in fltName[~5:]:
|
if "." in fltName[~5:]:
|
||||||
tryNames = (fltName,)
|
tryNames = (fltName,)
|
||||||
|
@ -606,7 +606,7 @@ class Fail2banRegex(object):
|
||||||
return False
|
return False
|
||||||
output( "Use systemd journal" )
|
output( "Use systemd journal" )
|
||||||
output( "Use encoding : %s" % self._encoding )
|
output( "Use encoding : %s" % self._encoding )
|
||||||
backend, beArgs = JailReader.extractOptions(cmd_log)
|
backend, beArgs = extractOptions(cmd_log)
|
||||||
flt = FilterSystemd(None, **beArgs)
|
flt = FilterSystemd(None, **beArgs)
|
||||||
flt.setLogEncoding(self._encoding)
|
flt.setLogEncoding(self._encoding)
|
||||||
myjournal = flt.getJournalReader()
|
myjournal = flt.getJournalReader()
|
||||||
|
|
|
@ -33,8 +33,7 @@ from .configreader import ConfigReaderUnshared, ConfigReader
|
||||||
from .filterreader import FilterReader
|
from .filterreader import FilterReader
|
||||||
from .actionreader import ActionReader
|
from .actionreader import ActionReader
|
||||||
from ..version import version
|
from ..version import version
|
||||||
from ..helpers import getLogger
|
from ..helpers import getLogger, extractOptions, splitwords
|
||||||
from ..helpers import splitwords
|
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
@ -42,15 +41,6 @@ logSys = getLogger(__name__)
|
||||||
|
|
||||||
class JailReader(ConfigReader):
|
class JailReader(ConfigReader):
|
||||||
|
|
||||||
# regex, to extract list of options:
|
|
||||||
optionCRE = re.compile(r"^([^\[]+)(?:\[(.*)\])?\s*$", re.DOTALL)
|
|
||||||
# regex, to iterate over single option in option list, syntax:
|
|
||||||
# `action = act[p1="...", p2='...', p3=...]`, where the p3=... not contains `,` or ']'
|
|
||||||
# since v0.10 separator extended with `]\s*[` for support of multiple option groups, syntax
|
|
||||||
# `action = act[p1=...][p2=...]`
|
|
||||||
optionExtractRE = re.compile(
|
|
||||||
r'([\w\-_\.]+)=(?:"([^"]*)"|\'([^\']*)\'|([^,\]]*))(?:,|\]\s*\[|$)', re.DOTALL)
|
|
||||||
|
|
||||||
def __init__(self, name, force_enable=False, **kwargs):
|
def __init__(self, name, force_enable=False, **kwargs):
|
||||||
ConfigReader.__init__(self, **kwargs)
|
ConfigReader.__init__(self, **kwargs)
|
||||||
self.__name = name
|
self.__name = name
|
||||||
|
@ -134,7 +124,7 @@ class JailReader(ConfigReader):
|
||||||
# Read filter
|
# Read filter
|
||||||
flt = self.__opts["filter"]
|
flt = self.__opts["filter"]
|
||||||
if flt:
|
if flt:
|
||||||
filterName, filterOpt = JailReader.extractOptions(flt)
|
filterName, filterOpt = extractOptions(flt)
|
||||||
if not filterName:
|
if not filterName:
|
||||||
raise JailDefError("Invalid filter definition %r" % flt)
|
raise JailDefError("Invalid filter definition %r" % flt)
|
||||||
self.__filter = FilterReader(
|
self.__filter = FilterReader(
|
||||||
|
@ -164,7 +154,7 @@ class JailReader(ConfigReader):
|
||||||
try:
|
try:
|
||||||
if not act: # skip empty actions
|
if not act: # skip empty actions
|
||||||
continue
|
continue
|
||||||
actName, actOpt = JailReader.extractOptions(act)
|
actName, actOpt = extractOptions(act)
|
||||||
if not actName:
|
if not actName:
|
||||||
raise JailDefError("Invalid action definition %r" % act)
|
raise JailDefError("Invalid action definition %r" % act)
|
||||||
if actName.endswith(".py"):
|
if actName.endswith(".py"):
|
||||||
|
@ -268,22 +258,5 @@ class JailReader(ConfigReader):
|
||||||
stream.insert(0, ["add", self.__name, backend])
|
stream.insert(0, ["add", self.__name, backend])
|
||||||
return stream
|
return stream
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def extractOptions(option):
|
|
||||||
match = JailReader.optionCRE.match(option)
|
|
||||||
if not match:
|
|
||||||
# TODO proper error handling
|
|
||||||
return None, None
|
|
||||||
option_name, optstr = match.groups()
|
|
||||||
option_opts = dict()
|
|
||||||
if optstr:
|
|
||||||
for optmatch in JailReader.optionExtractRE.finditer(optstr):
|
|
||||||
opt = optmatch.group(1)
|
|
||||||
value = [
|
|
||||||
val for val in optmatch.group(2,3,4) if val is not None][0]
|
|
||||||
option_opts[opt.strip()] = value.strip()
|
|
||||||
return option_name, option_opts
|
|
||||||
|
|
||||||
|
|
||||||
class JailDefError(Exception):
|
class JailDefError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -237,6 +237,34 @@ else:
|
||||||
return uni_decode(x, enc, 'replace')
|
return uni_decode(x, enc, 'replace')
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Following function used for parse options from parameter (e.g. `name[p1=0, p2="..."][p3='...']`).
|
||||||
|
#
|
||||||
|
|
||||||
|
# regex, to extract list of options:
|
||||||
|
OPTION_CRE = re.compile(r"^([^\[]+)(?:\[(.*)\])?\s*$", re.DOTALL)
|
||||||
|
# regex, to iterate over single option in option list, syntax:
|
||||||
|
# `action = act[p1="...", p2='...', p3=...]`, where the p3=... not contains `,` or ']'
|
||||||
|
# since v0.10 separator extended with `]\s*[` for support of multiple option groups, syntax
|
||||||
|
# `action = act[p1=...][p2=...]`
|
||||||
|
OPTION_EXTRACT_CRE = re.compile(
|
||||||
|
r'([\w\-_\.]+)=(?:"([^"]*)"|\'([^\']*)\'|([^,\]]*))(?:,|\]\s*\[|$)', re.DOTALL)
|
||||||
|
|
||||||
|
def extractOptions(option):
|
||||||
|
match = OPTION_CRE.match(option)
|
||||||
|
if not match:
|
||||||
|
# TODO proper error handling
|
||||||
|
return None, None
|
||||||
|
option_name, optstr = match.groups()
|
||||||
|
option_opts = dict()
|
||||||
|
if optstr:
|
||||||
|
for optmatch in OPTION_EXTRACT_CRE.finditer(optstr):
|
||||||
|
opt = optmatch.group(1)
|
||||||
|
value = [
|
||||||
|
val for val in optmatch.group(2,3,4) if val is not None][0]
|
||||||
|
option_opts[opt.strip()] = value.strip()
|
||||||
|
return option_name, option_opts
|
||||||
|
|
||||||
#
|
#
|
||||||
# Following facilities used for safe recursive interpolation of
|
# Following facilities used for safe recursive interpolation of
|
||||||
# tags (<tag>) in tagged options.
|
# tags (<tag>) in tagged options.
|
||||||
|
|
|
@ -27,8 +27,7 @@ import logging
|
||||||
import Queue
|
import Queue
|
||||||
|
|
||||||
from .actions import Actions
|
from .actions import Actions
|
||||||
from ..client.jailreader import JailReader
|
from ..helpers import getLogger, extractOptions, MyTime
|
||||||
from ..helpers import getLogger, MyTime
|
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
@ -85,7 +84,7 @@ class Jail(object):
|
||||||
return "%s(%r)" % (self.__class__.__name__, self.name)
|
return "%s(%r)" % (self.__class__.__name__, self.name)
|
||||||
|
|
||||||
def _setBackend(self, backend):
|
def _setBackend(self, backend):
|
||||||
backend, beArgs = JailReader.extractOptions(backend)
|
backend, beArgs = extractOptions(backend)
|
||||||
backend = backend.lower() # to assure consistent matching
|
backend = backend.lower() # to assure consistent matching
|
||||||
|
|
||||||
backends = self._BACKENDS
|
backends = self._BACKENDS
|
||||||
|
|
|
@ -30,7 +30,7 @@ import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
from ..client.configreader import ConfigReader, ConfigReaderUnshared, NoSectionError
|
from ..client.configreader import ConfigReader, ConfigReaderUnshared, NoSectionError
|
||||||
from ..client import configparserinc
|
from ..client import configparserinc
|
||||||
from ..client.jailreader import JailReader
|
from ..client.jailreader import JailReader, extractOptions
|
||||||
from ..client.filterreader import FilterReader
|
from ..client.filterreader import FilterReader
|
||||||
from ..client.jailsreader import JailsReader
|
from ..client.jailsreader import JailsReader
|
||||||
from ..client.actionreader import ActionReader, CommandAction
|
from ..client.actionreader import ActionReader, CommandAction
|
||||||
|
@ -260,25 +260,25 @@ class JailReaderTest(LogCaptureTestCase):
|
||||||
# Simple example
|
# Simple example
|
||||||
option = "mail-whois[name=SSH]"
|
option = "mail-whois[name=SSH]"
|
||||||
expected = ('mail-whois', {'name': 'SSH'})
|
expected = ('mail-whois', {'name': 'SSH'})
|
||||||
result = JailReader.extractOptions(option)
|
result = extractOptions(option)
|
||||||
self.assertEqual(expected, result)
|
self.assertEqual(expected, result)
|
||||||
|
|
||||||
self.assertEqual(('mail.who_is', {}), JailReader.extractOptions("mail.who_is"))
|
self.assertEqual(('mail.who_is', {}), extractOptions("mail.who_is"))
|
||||||
self.assertEqual(('mail.who_is', {'a':'cat', 'b':'dog'}), JailReader.extractOptions("mail.who_is[a=cat,b=dog]"))
|
self.assertEqual(('mail.who_is', {'a':'cat', 'b':'dog'}), extractOptions("mail.who_is[a=cat,b=dog]"))
|
||||||
self.assertEqual(('mail--ho_is', {}), JailReader.extractOptions("mail--ho_is"))
|
self.assertEqual(('mail--ho_is', {}), extractOptions("mail--ho_is"))
|
||||||
|
|
||||||
self.assertEqual(('mail--ho_is', {}), JailReader.extractOptions("mail--ho_is['s']"))
|
self.assertEqual(('mail--ho_is', {}), extractOptions("mail--ho_is['s']"))
|
||||||
#self.printLog()
|
#self.printLog()
|
||||||
#self.assertLogged("Invalid argument ['s'] in ''s''")
|
#self.assertLogged("Invalid argument ['s'] in ''s''")
|
||||||
|
|
||||||
self.assertEqual(('mail', {'a': ','}), JailReader.extractOptions("mail[a=',']"))
|
self.assertEqual(('mail', {'a': ','}), extractOptions("mail[a=',']"))
|
||||||
|
|
||||||
#self.assertRaises(ValueError, JailReader.extractOptions ,'mail-how[')
|
#self.assertRaises(ValueError, extractOptions ,'mail-how[')
|
||||||
|
|
||||||
# Empty option
|
# Empty option
|
||||||
option = "abc[]"
|
option = "abc[]"
|
||||||
expected = ('abc', {})
|
expected = ('abc', {})
|
||||||
result = JailReader.extractOptions(option)
|
result = extractOptions(option)
|
||||||
self.assertEqual(expected, result)
|
self.assertEqual(expected, result)
|
||||||
|
|
||||||
# More complex examples
|
# More complex examples
|
||||||
|
@ -296,11 +296,11 @@ class JailReaderTest(LogCaptureTestCase):
|
||||||
'opt10': "",
|
'opt10': "",
|
||||||
'opt11': "",
|
'opt11': "",
|
||||||
})
|
})
|
||||||
result = JailReader.extractOptions(option)
|
result = extractOptions(option)
|
||||||
self.assertEqual(expected, result)
|
self.assertEqual(expected, result)
|
||||||
|
|
||||||
# And multiple groups (`][` instead of `,`)
|
# And multiple groups (`][` instead of `,`)
|
||||||
result = JailReader.extractOptions(option.replace(',', ']['))
|
result = extractOptions(option.replace(',', ']['))
|
||||||
expected2 = (expected[0],
|
expected2 = (expected[0],
|
||||||
dict((k, v.replace(',', '][')) for k, v in expected[1].iteritems())
|
dict((k, v.replace(',', '][')) for k, v in expected[1].iteritems())
|
||||||
)
|
)
|
||||||
|
@ -439,7 +439,7 @@ class FilterReaderTest(unittest.TestCase):
|
||||||
|
|
||||||
def testFilterReaderSubstitionKnown(self):
|
def testFilterReaderSubstitionKnown(self):
|
||||||
output = [['set', 'jailname', 'addfailregex', 'to=test,sweet@example.com,test2,sweet@example.com fromip=<IP>']]
|
output = [['set', 'jailname', 'addfailregex', 'to=test,sweet@example.com,test2,sweet@example.com fromip=<IP>']]
|
||||||
filterName, filterOpt = JailReader.extractOptions(
|
filterName, filterOpt = extractOptions(
|
||||||
'substition[honeypot="<sweet>,<known/honeypot>", sweet="test,<known/honeypot>,test2"]')
|
'substition[honeypot="<sweet>,<known/honeypot>", sweet="test,<known/honeypot>,test2"]')
|
||||||
filterReader = FilterReader('substition', "jailname", filterOpt,
|
filterReader = FilterReader('substition', "jailname", filterOpt,
|
||||||
share_config=TEST_FILES_DIR_SHARE_CFG, basedir=TEST_FILES_DIR)
|
share_config=TEST_FILES_DIR_SHARE_CFG, basedir=TEST_FILES_DIR)
|
||||||
|
@ -650,7 +650,7 @@ class JailsReaderTest(LogCaptureTestCase):
|
||||||
if jail == 'INCLUDES':
|
if jail == 'INCLUDES':
|
||||||
continue
|
continue
|
||||||
filterName = jails.get(jail, 'filter')
|
filterName = jails.get(jail, 'filter')
|
||||||
filterName, filterOpt = JailReader.extractOptions(filterName)
|
filterName, filterOpt = extractOptions(filterName)
|
||||||
allFilters.add(filterName)
|
allFilters.add(filterName)
|
||||||
self.assertTrue(len(filterName))
|
self.assertTrue(len(filterName))
|
||||||
# moreover we must have a file for it
|
# moreover we must have a file for it
|
||||||
|
@ -669,7 +669,7 @@ class JailsReaderTest(LogCaptureTestCase):
|
||||||
# somewhat duplicating here what is done in JailsReader if
|
# somewhat duplicating here what is done in JailsReader if
|
||||||
# the jail is enabled
|
# the jail is enabled
|
||||||
for act in actions.split('\n'):
|
for act in actions.split('\n'):
|
||||||
actName, actOpt = JailReader.extractOptions(act)
|
actName, actOpt = extractOptions(act)
|
||||||
self.assertTrue(len(actName))
|
self.assertTrue(len(actName))
|
||||||
self.assertTrue(isinstance(actOpt, dict))
|
self.assertTrue(isinstance(actOpt, dict))
|
||||||
if actName == 'iptables-multiport':
|
if actName == 'iptables-multiport':
|
||||||
|
@ -696,7 +696,7 @@ class JailsReaderTest(LogCaptureTestCase):
|
||||||
if not (a.endswith('common.conf') or a.endswith('-aggressive.conf')))
|
if not (a.endswith('common.conf') or a.endswith('-aggressive.conf')))
|
||||||
# get filters of all jails (filter names without options inside filter[...])
|
# get filters of all jails (filter names without options inside filter[...])
|
||||||
filters_jail = set(
|
filters_jail = set(
|
||||||
JailReader.extractOptions(jail.options['filter'])[0] for jail in jails.jails
|
extractOptions(jail.options['filter'])[0] for jail in jails.jails
|
||||||
)
|
)
|
||||||
self.maxDiff = None
|
self.maxDiff = None
|
||||||
self.assertTrue(filters.issubset(filters_jail),
|
self.assertTrue(filters.issubset(filters_jail),
|
||||||
|
|
|
@ -42,7 +42,7 @@ from ..server.ticket import BanTicket
|
||||||
from ..server.utils import Utils
|
from ..server.utils import Utils
|
||||||
from .dummyjail import DummyJail
|
from .dummyjail import DummyJail
|
||||||
from .utils import LogCaptureTestCase
|
from .utils import LogCaptureTestCase
|
||||||
from ..helpers import getLogger, PREFER_ENC
|
from ..helpers import getLogger, extractOptions, PREFER_ENC
|
||||||
from .. import version
|
from .. import version
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -1034,7 +1034,7 @@ class LoggingTests(LogCaptureTestCase):
|
||||||
os.remove(f)
|
os.remove(f)
|
||||||
|
|
||||||
|
|
||||||
from clientreadertestcase import ActionReader, JailReader, JailsReader, CONFIG_DIR, STOCK
|
from clientreadertestcase import ActionReader, JailsReader, CONFIG_DIR, STOCK
|
||||||
|
|
||||||
class ServerConfigReaderTests(LogCaptureTestCase):
|
class ServerConfigReaderTests(LogCaptureTestCase):
|
||||||
|
|
||||||
|
@ -1145,7 +1145,7 @@ class ServerConfigReaderTests(LogCaptureTestCase):
|
||||||
|
|
||||||
def getDefaultJailStream(self, jail, act):
|
def getDefaultJailStream(self, jail, act):
|
||||||
act = act.replace('%(__name__)s', jail)
|
act = act.replace('%(__name__)s', jail)
|
||||||
actName, actOpt = JailReader.extractOptions(act)
|
actName, actOpt = extractOptions(act)
|
||||||
stream = [
|
stream = [
|
||||||
['add', jail, 'polling'],
|
['add', jail, 'polling'],
|
||||||
# ['set', jail, 'addfailregex', 'DUMMY-REGEX <HOST>'],
|
# ['set', jail, 'addfailregex', 'DUMMY-REGEX <HOST>'],
|
||||||
|
|
Loading…
Reference in New Issue