From 9c7bd8080762d63610b8134f84163e45547616ba Mon Sep 17 00:00:00 2001 From: sebres Date: Mon, 3 Feb 2020 20:09:13 +0100 Subject: [PATCH] fail2ban-regex: stop endless logging on closed streams (redirected pipes like `... | head -n 100`), exit if stdout channel is closed --- fail2ban/client/fail2banregex.py | 1 + fail2ban/helpers.py | 35 ++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/fail2ban/client/fail2banregex.py b/fail2ban/client/fail2banregex.py index 334c031f..513b765d 100644 --- a/fail2ban/client/fail2banregex.py +++ b/fail2ban/client/fail2banregex.py @@ -709,6 +709,7 @@ class Fail2banRegex(object): def exec_command_line(*args): + logging.exitOnIOError = True parser = get_opt_parser() (opts, args) = parser.parse_args(*args) errors = [] diff --git a/fail2ban/helpers.py b/fail2ban/helpers.py index 241543c1..6f2bcdd7 100644 --- a/fail2ban/helpers.py +++ b/fail2ban/helpers.py @@ -208,6 +208,25 @@ class FormatterWithTraceBack(logging.Formatter): 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 def __safeLog(self, level, msg, args, **kwargs): """Safe log inject to avoid possible errors by unsafe log-handlers, @@ -223,6 +242,10 @@ def __safeLog(self, level, msg, args, **kwargs): try: # if isEnabledFor(level) already called... __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 try: for args in ( @@ -237,6 +260,18 @@ def __safeLog(self, level, msg, args, **kwargs): pass 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): """Get logging.Logger instance with Fail2Ban logger name convention """