closes gh-969: introduces new section `[Thread]` and option `stacksize` to configure default stack-size of the threads running in fail2ban. Example:

```ini
[Thread]
stacksize = 32
```
pull/2356/head
sebres 2019-02-24 16:45:14 +01:00
parent af18993ba2
commit 3c70fe298a
7 changed files with 60 additions and 7 deletions

View File

@ -67,3 +67,11 @@ dbfile = /var/lib/fail2ban/fail2ban.sqlite3
# Notes.: Sets age at which bans should be purged from the database # Notes.: Sets age at which bans should be purged from the database
# Values: [ SECONDS ] Default: 86400 (24hours) # Values: [ SECONDS ] Default: 86400 (24hours)
dbpurgeage = 1d dbpurgeage = 1d
[Thread]
# Options: stacksize
# Notes.: Specifies the stack size (in KiB) to be used for subsequently created threads,
# and must be 0 or a positive integer value of at least 32.
# Values: [ SIZE ] Default: 0 (use platform or configured default)
#stacksize = 0

View File

@ -239,8 +239,10 @@ class ConfigReaderUnshared(SafeConfigParserWithIncludes):
try: try:
if opttype == "bool": if opttype == "bool":
v = self.getboolean(sec, optname) v = self.getboolean(sec, optname)
if v is None: continue
elif opttype == "int": elif opttype == "int":
v = self.getint(sec, optname) v = self.getint(sec, optname)
if v is None: continue
else: else:
v = self.get(sec, optname, vars=pOptions) v = self.get(sec, optname, vars=pOptions)
values[optname] = v values[optname] = v

View File

@ -24,7 +24,7 @@ __author__ = "Cyril Jaquier"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
from .configreader import ConfigReader from .configreader import ConfigReader, NoSectionError
from ..helpers import getLogger, str2LogLevel from ..helpers import getLogger, str2LogLevel
# Gets the instance of the logger. # Gets the instance of the logger.
@ -60,12 +60,19 @@ class Fail2banReader(ConfigReader):
self.__opts.update(updateMainOpt) self.__opts.update(updateMainOpt)
# check given log-level: # check given log-level:
str2LogLevel(self.__opts.get('loglevel', 0)) str2LogLevel(self.__opts.get('loglevel', 0))
# thread options:
opts = [["int", "stacksize", ],
]
if ConfigReader.has_section(self, "Thread"):
thopt = ConfigReader.getOptions(self, "Thread", opts)
if thopt:
self.__opts['thread'] = thopt
def convert(self): def convert(self):
# Ensure logtarget/level set first so any db errors are captured # Ensure logtarget/level set first so any db errors are captured
# Also dbfile should be set before all other database options. # Also dbfile should be set before all other database options.
# So adding order indices into items, to be stripped after sorting, upon return # So adding order indices into items, to be stripped after sorting, upon return
order = {"syslogsocket":0, "loglevel":1, "logtarget":2, order = {"thread":0, "syslogsocket":11, "loglevel":12, "logtarget":13,
"dbfile":50, "dbpurgeage":51} "dbfile":50, "dbpurgeage":51}
stream = list() stream = list()
for opt in self.__opts: for opt in self.__opts:

View File

@ -709,6 +709,16 @@ class Server:
logSys.info("flush performed on %s" % self.__logTarget) logSys.info("flush performed on %s" % self.__logTarget)
return "flushed" return "flushed"
def setThreadOptions(self, value):
for o, v in value.iteritems():
if o == 'stacksize':
threading.stack_size(int(v)*1024)
else: # pragma: no cover
raise KeyError("unknown option %r" % o)
def getThreadOptions(self):
return {'stacksize': threading.stack_size() / 1024}
def setDatabase(self, filename): def setDatabase(self, filename):
# if not changed - nothing to do # if not changed - nothing to do
if self.__db and self.__db.filename == filename: if self.__db and self.__db.filename == filename:

View File

@ -170,6 +170,10 @@ class Transmitter:
return self.__server.getSyslogSocket() return self.__server.getSyslogSocket()
else: else:
raise Exception("Failed to change syslog socket") raise Exception("Failed to change syslog socket")
#Thread
elif name == "thread":
value = command[1]
return self.__server.setThreadOptions(value)
#Database #Database
elif name == "dbfile": elif name == "dbfile":
self.__server.setDatabase(command[1]) self.__server.setDatabase(command[1])
@ -384,6 +388,9 @@ class Transmitter:
return self.__server.getLogTarget() return self.__server.getLogTarget()
elif name == "syslogsocket": elif name == "syslogsocket":
return self.__server.getSyslogSocket() return self.__server.getSyslogSocket()
#Thread
elif name == "thread":
return self.__server.getThreadOptions()
#Database #Database
elif name == "dbfile": elif name == "dbfile":
db = self.__server.getDatabase() db = self.__server.getDatabase()

View File

@ -478,6 +478,12 @@ class Fail2banClientTest(Fail2banClientServerBase):
def testClientStartBackgroundInside(self, tmp): def testClientStartBackgroundInside(self, tmp):
# use once the stock configuration (to test starting also) # use once the stock configuration (to test starting also)
startparams = _start_params(tmp, True) startparams = _start_params(tmp, True)
# test additional options:
_write_file(pjoin(tmp, "config", "fail2ban.conf"), "a",
"[Thread]",
"stacksize = 32"
"",
)
# start: # start:
self.execCmd(SUCCESS, ("-b",) + startparams, "start") self.execCmd(SUCCESS, ("-b",) + startparams, "start")
# wait for server (socket and ready): # wait for server (socket and ready):
@ -485,10 +491,16 @@ class Fail2banClientTest(Fail2banClientServerBase):
self.assertLogged("Server ready") self.assertLogged("Server ready")
self.assertLogged("Exit with code 0") self.assertLogged("Exit with code 0")
try: try:
# check thread options were set:
self.pruneLog()
self.execCmd(SUCCESS, startparams, "get", "thread")
self.assertLogged("{'stacksize': 32}")
# several:
self.pruneLog()
self.execCmd(SUCCESS, startparams, "echo", "TEST-ECHO") self.execCmd(SUCCESS, startparams, "echo", "TEST-ECHO")
self.execCmd(FAILED, startparams, "~~unknown~cmd~failed~~") self.execCmd(FAILED, startparams, "~~unknown~cmd~failed~~")
self.pruneLog()
# start again (should fail): # start again (should fail):
self.pruneLog()
self.execCmd(FAILED, ("-b",) + startparams, "start") self.execCmd(FAILED, ("-b",) + startparams, "start")
self.assertLogged("Server already running") self.assertLogged("Server already running")
finally: finally:

View File

@ -127,9 +127,7 @@ Comments: use '#' for comment lines and '; ' (space is important) for inline com
.SH "FAIL2BAN CONFIGURATION FILE(S) (\fIfail2ban.conf\fB)" .SH "FAIL2BAN CONFIGURATION FILE(S) (\fIfail2ban.conf\fB)"
These files have one section, [Definition]. The items that can be set in section [Definition] are:
The items that can be set are:
.TP .TP
.B loglevel .B loglevel
verbosity level of log output: CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG, TRACEDEBUG, HEAVYDEBUG or corresponding numeric value (50-5). Default: ERROR (equal 40) verbosity level of log output: CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG, TRACEDEBUG, HEAVYDEBUG or corresponding numeric value (50-5). Default: ERROR (equal 40)
@ -163,6 +161,15 @@ Database purge age in seconds. Default: 86400 (24hours)
.br .br
This sets the age at which bans should be purged from the database. This sets the age at which bans should be purged from the database.
.RE
The config parameters of section [Thread] are:
.TP
.B stacksize
Stack size of each thread in fail2ban. Default: 0 (platform or configured default)
.br
This specifies the stack size (in KiB) to be used for subsequently created threads, and must be 0 or a positive integer value of at least 32.
.SH "JAIL CONFIGURATION FILE(S) (\fIjail.conf\fB)" .SH "JAIL CONFIGURATION FILE(S) (\fIjail.conf\fB)"
The following options are applicable to any jail. They appear in a section specifying the jail name or in the \fI[DEFAULT]\fR section which defines default values to be used if not specified in the individual section. The following options are applicable to any jail. They appear in a section specifying the jail name or in the \fI[DEFAULT]\fR section which defines default values to be used if not specified in the individual section.
.TP .TP