Merge branch '0.11'

pull/2642/head
sebres 2020-02-07 13:57:00 +01:00
commit 65ce64ba6d
6 changed files with 71 additions and 24 deletions

View File

@ -35,7 +35,8 @@ logSys = getLogger("fail2ban")
def output(s): # pragma: no cover def output(s): # pragma: no cover
print(s) print(s)
CONFIG_PARAMS = ("socket", "pidfile", "logtarget", "loglevel", "syslogsocket",) # Config parameters required to start fail2ban which can be also set via command line (overwrite fail2ban.conf),
CONFIG_PARAMS = ("socket", "pidfile", "logtarget", "loglevel", "syslogsocket")
# Used to signal - we are in test cases (ex: prevents change logging params, log capturing, etc) # Used to signal - we are in test cases (ex: prevents change logging params, log capturing, etc)
PRODUCTION = True PRODUCTION = True
@ -94,9 +95,10 @@ class Fail2banCmdLine():
output("and bans the corresponding IP addresses using firewall rules.") output("and bans the corresponding IP addresses using firewall rules.")
output("") output("")
output("Options:") output("Options:")
output(" -c <DIR> configuration directory") output(" -c, --conf <DIR> configuration directory")
output(" -s <FILE> socket path") output(" -s, --socket <FILE> socket path")
output(" -p <FILE> pidfile path") output(" -p, --pidfile <FILE> pidfile path")
output(" --pname <NAME> name of the process (main thread) to identify instance (default fail2ban-server)")
output(" --loglevel <LEVEL> logging level") output(" --loglevel <LEVEL> logging level")
output(" --logtarget <TARGET> logging target, use file-name or stdout, stderr, syslog or sysout.") output(" --logtarget <TARGET> logging target, use file-name or stdout, stderr, syslog or sysout.")
output(" --syslogsocket auto|<FILE>") output(" --syslogsocket auto|<FILE>")
@ -129,17 +131,15 @@ class Fail2banCmdLine():
""" """
for opt in optList: for opt in optList:
o = opt[0] o = opt[0]
if o == "-c": if o in ("-c", "--conf"):
self._conf["conf"] = opt[1] self._conf["conf"] = opt[1]
elif o == "-s": elif o in ("-s", "--socket"):
self._conf["socket"] = opt[1] self._conf["socket"] = opt[1]
elif o == "-p": elif o in ("-p", "--pidfile"):
self._conf["pidfile"] = opt[1] self._conf["pidfile"] = opt[1]
elif o.startswith("--log") or o.startswith("--sys"): elif o in ("-d", "--dp", "--dump-pretty"):
self._conf[ o[2:] ] = opt[1]
elif o in ["-d", "--dp", "--dump-pretty"]:
self._conf["dump"] = True if o == "-d" else 2 self._conf["dump"] = True if o == "-d" else 2
elif o == "-t" or o == "--test": elif o in ("-t", "--test"):
self.cleanConfOnly = True self.cleanConfOnly = True
self._conf["test"] = True self._conf["test"] = True
elif o == "-v": elif o == "-v":
@ -163,12 +163,14 @@ class Fail2banCmdLine():
from ..server.mytime import MyTime from ..server.mytime import MyTime
output(MyTime.str2seconds(opt[1])) output(MyTime.str2seconds(opt[1]))
return True return True
elif o in ["-h", "--help"]: elif o in ("-h", "--help"):
self.dispUsage() self.dispUsage()
return True return True
elif o in ["-V", "--version"]: elif o in ("-V", "--version"):
self.dispVersion(o == "-V") self.dispVersion(o == "-V")
return True return True
elif o.startswith("--"): # other long named params (see also resetConf)
self._conf[ o[2:] ] = opt[1]
return None return None
def initCmdLine(self, argv): def initCmdLine(self, argv):
@ -185,6 +187,7 @@ class Fail2banCmdLine():
try: try:
cmdOpts = 'hc:s:p:xfbdtviqV' cmdOpts = 'hc:s:p:xfbdtviqV'
cmdLongOpts = ['loglevel=', 'logtarget=', 'syslogsocket=', 'test', 'async', cmdLongOpts = ['loglevel=', 'logtarget=', 'syslogsocket=', 'test', 'async',
'conf=', 'pidfile=', 'pname=', 'socket=',
'timeout=', 'str2sec=', 'help', 'version', 'dp', '--dump-pretty'] 'timeout=', 'str2sec=', 'help', 'version', 'dp', '--dump-pretty']
optList, self._args = getopt.getopt(self._argv[1:], cmdOpts, cmdLongOpts) optList, self._args = getopt.getopt(self._argv[1:], cmdOpts, cmdLongOpts)
except getopt.GetoptError: except getopt.GetoptError:
@ -227,7 +230,8 @@ class Fail2banCmdLine():
if not conf: if not conf:
self.configurator.readEarly() self.configurator.readEarly()
conf = self.configurator.getEarlyOptions() conf = self.configurator.getEarlyOptions()
self._conf[o] = conf[o] if o in conf:
self._conf[o] = conf[o]
logSys.info("Using socket file %s", self._conf["socket"]) logSys.info("Using socket file %s", self._conf["socket"])

View File

@ -709,6 +709,7 @@ class Fail2banRegex(object):
def exec_command_line(*args): def exec_command_line(*args):
logging.exitOnIOError = True
parser = get_opt_parser() parser = get_opt_parser()
(opts, args) = parser.parse_args(*args) (opts, args) = parser.parse_args(*args)
errors = [] errors = []

View File

@ -208,6 +208,25 @@ class FormatterWithTraceBack(logging.Formatter):
return logging.Formatter.format(self, record) return logging.Formatter.format(self, record)
logging.exitOnIOError = False
def __stopOnIOError(logSys=None, logHndlr=None): # pragma: no cover
if logSys and len(logSys.handlers):
logSys.removeHandler(logSys.handlers[0])
if logHndlr:
logHndlr.close = lambda: None
logging.StreamHandler.flush = lambda self: None
#sys.excepthook = lambda *args: None
if logging.exitOnIOError:
try:
sys.stderr.close()
except:
pass
sys.exit(0)
try:
BrokenPipeError
except NameError: # pragma: 3.x no cover
BrokenPipeError = IOError
__origLog = logging.Logger._log __origLog = logging.Logger._log
def __safeLog(self, level, msg, args, **kwargs): def __safeLog(self, level, msg, args, **kwargs):
"""Safe log inject to avoid possible errors by unsafe log-handlers, """Safe log inject to avoid possible errors by unsafe log-handlers,
@ -223,6 +242,10 @@ def __safeLog(self, level, msg, args, **kwargs):
try: try:
# if isEnabledFor(level) already called... # if isEnabledFor(level) already called...
__origLog(self, level, msg, args, **kwargs) __origLog(self, level, msg, args, **kwargs)
except (BrokenPipeError, IOError) as e: # pragma: no cover
if e.errno == 32: # closed / broken pipe
__stopOnIOError(self)
raise
except Exception as e: # pragma: no cover - unreachable if log-handler safe in this python-version except Exception as e: # pragma: no cover - unreachable if log-handler safe in this python-version
try: try:
for args in ( for args in (
@ -237,6 +260,18 @@ def __safeLog(self, level, msg, args, **kwargs):
pass pass
logging.Logger._log = __safeLog logging.Logger._log = __safeLog
__origLogFlush = logging.StreamHandler.flush
def __safeLogFlush(self):
"""Safe flush inject stopping endless logging on closed streams (redirected pipe).
"""
try:
__origLogFlush(self)
except (BrokenPipeError, IOError) as e: # pragma: no cover
if e.errno == 32: # closed / broken pipe
__stopOnIOError(None, self)
raise
logging.StreamHandler.flush = __safeLogFlush
def getLogger(name): def getLogger(name):
"""Get logging.Logger instance with Fail2Ban logger name convention """Get logging.Logger instance with Fail2Ban logger name convention
""" """

View File

@ -81,8 +81,6 @@ class Server:
'Linux': '/dev/log', 'Linux': '/dev/log',
} }
self.__prev_signals = {} self.__prev_signals = {}
# replace real thread name with short process name (for top/ps/pstree or diagnostic):
prctl_set_th_name('f2b/server')
def __sigTERMhandler(self, signum, frame): # pragma: no cover - indirect tested def __sigTERMhandler(self, signum, frame): # pragma: no cover - indirect tested
logSys.debug("Caught signal %d. Exiting", signum) logSys.debug("Caught signal %d. Exiting", signum)
@ -113,6 +111,9 @@ class Server:
logSys.error(err) logSys.error(err)
raise ServerInitializationError(err) raise ServerInitializationError(err)
# We are daemon. # We are daemon.
# replace main thread (and process) name to identify server (for top/ps/pstree or diagnostic):
prctl_set_th_name(conf.get("pname", "fail2ban-server"))
# Set all logging parameters (or use default if not specified): # Set all logging parameters (or use default if not specified):
self.__verbose = conf.get("verbose", None) self.__verbose = conf.get("verbose", None)

View File

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4.
.TH FAIL2BAN-CLIENT "1" "January 2020" "fail2ban-client v1.0.1.dev1" "User Commands" .TH FAIL2BAN-CLIENT "1" "February 2020" "fail2ban-client v1.0.1.dev1" "User Commands"
.SH NAME .SH NAME
fail2ban-client \- configure and control the server fail2ban-client \- configure and control the server
.SH SYNOPSIS .SH SYNOPSIS
@ -10,15 +10,18 @@ Fail2Ban v1.0.1.dev1 reads log file that contains password failure report
and bans the corresponding IP addresses using firewall rules. and bans the corresponding IP addresses using firewall rules.
.SH OPTIONS .SH OPTIONS
.TP .TP
\fB\-c\fR <DIR> \fB\-c\fR, \fB\-\-conf\fR <DIR>
configuration directory configuration directory
.TP .TP
\fB\-s\fR <FILE> \fB\-s\fR, \fB\-\-socket\fR <FILE>
socket path socket path
.TP .TP
\fB\-p\fR <FILE> \fB\-p\fR, \fB\-\-pidfile\fR <FILE>
pidfile path pidfile path
.TP .TP
\fB\-\-pname\fR <NAME>
name of the process (main thread) to identify instance (default fail2ban\-server)
.TP
\fB\-\-loglevel\fR <LEVEL> \fB\-\-loglevel\fR <LEVEL>
logging level logging level
.TP .TP

View File

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4.
.TH FAIL2BAN-SERVER "1" "January 2020" "fail2ban-server v1.0.1.dev1" "User Commands" .TH FAIL2BAN-SERVER "1" "February 2020" "fail2ban-server v1.0.1.dev1" "User Commands"
.SH NAME .SH NAME
fail2ban-server \- start the server fail2ban-server \- start the server
.SH SYNOPSIS .SH SYNOPSIS
@ -10,15 +10,18 @@ Fail2Ban v1.0.1.dev1 reads log file that contains password failure report
and bans the corresponding IP addresses using firewall rules. and bans the corresponding IP addresses using firewall rules.
.SH OPTIONS .SH OPTIONS
.TP .TP
\fB\-c\fR <DIR> \fB\-c\fR, \fB\-\-conf\fR <DIR>
configuration directory configuration directory
.TP .TP
\fB\-s\fR <FILE> \fB\-s\fR, \fB\-\-socket\fR <FILE>
socket path socket path
.TP .TP
\fB\-p\fR <FILE> \fB\-p\fR, \fB\-\-pidfile\fR <FILE>
pidfile path pidfile path
.TP .TP
\fB\-\-pname\fR <NAME>
name of the process (main thread) to identify instance (default fail2ban\-server)
.TP
\fB\-\-loglevel\fR <LEVEL> \fB\-\-loglevel\fR <LEVEL>
logging level logging level
.TP .TP