diff --git a/fail2ban/client/configreader.py b/fail2ban/client/configreader.py index c6dd1b60..f333cdc1 100644 --- a/fail2ban/client/configreader.py +++ b/fail2ban/client/configreader.py @@ -208,7 +208,7 @@ class ConfigReaderUnshared(SafeConfigParserWithIncludes): # 1 -> the name of the option # 2 -> the default value for the option - def getOptions(self, sec, options, pOptions=None): + def getOptions(self, sec, options, pOptions=None, shouldExist=False): values = dict() for option in options: try: @@ -222,6 +222,8 @@ class ConfigReaderUnshared(SafeConfigParserWithIncludes): continue values[option[1]] = v except NoSectionError, e: + if shouldExist: + raise # No "Definition" section or wrong basedir logSys.error(e) values[option[1]] = option[2] diff --git a/fail2ban/client/fail2banclient.py b/fail2ban/client/fail2banclient.py index 23d31dc5..a8e0a331 100644 --- a/fail2ban/client/fail2banclient.py +++ b/fail2ban/client/fail2banclient.py @@ -64,7 +64,7 @@ class Fail2banClient(Fail2banCmdLine, Thread): output("and bans the corresponding IP addresses using firewall rules.") output("") - def __sigTERMhandler(self, signum, frame): + def __sigTERMhandler(self, signum, frame): # pragma: no cover # Print a new line because we probably come from wait output("") logSys.warning("Caught signal %d. Exiting" % signum) @@ -141,7 +141,7 @@ class Fail2banClient(Fail2banCmdLine, Thread): logSys.error("Failed to access socket path: %s." " Is fail2ban running?", self._conf["socket"]) - except Exception as e: + except Exception as e: # pragma: no cover logSys.error("Exception while checking socket access: %s", self._conf["socket"]) logSys.error(e) @@ -165,7 +165,7 @@ class Fail2banClient(Fail2banCmdLine, Thread): "There is no directory %s to contain the socket file %s." % (socket_dir, self._conf["socket"])) return None - if not os.access(socket_dir, os.W_OK | os.X_OK): + if not os.access(socket_dir, os.W_OK | os.X_OK): # pragma: no cover logSys.error( "Directory %s exists but not accessible for writing" % (socket_dir,)) @@ -204,9 +204,9 @@ class Fail2banClient(Fail2banCmdLine, Thread): # Start server direct here in main thread (not fork): self._server = Fail2banServer.startServerDirect(self._conf, False) - except ExitException: + except ExitException: # pragma: no cover pass - except Exception as e: + except Exception as e: # pragma: no cover output("") logSys.error("Exception while starting server " + ("background" if background else "foreground")) if self._conf["verbose"] > 1: @@ -259,7 +259,7 @@ class Fail2banClient(Fail2banCmdLine, Thread): if self._conf.get("interactive", False): output(' ## stop ... ') self.__processCommand(['stop']) - if not self.__waitOnServer(False): + if not self.__waitOnServer(False): # pragma: no cover logSys.error("Could not stop server") return False # in interactive mode reset config, to make full-reload if there something changed: @@ -298,12 +298,12 @@ class Fail2banClient(Fail2banCmdLine, Thread): def __processStartStreamAfterWait(self, *args): try: # Wait for the server to start - if not self.__waitOnServer(): + if not self.__waitOnServer(): # pragma: no cover logSys.error("Could not find server, waiting failed") return False # Configure the server self.__processCmd(*args) - except ServerExecutionException as e: + except ServerExecutionException as e: # pragma: no cover if self._conf["verbose"] > 1: logSys.exception(e) logSys.error("Could not start server. Maybe an old " @@ -385,12 +385,12 @@ class Fail2banClient(Fail2banCmdLine, Thread): elif not cmd == "": try: self.__processCommand(shlex.split(cmd)) - except Exception, e: + except Exception, e: # pragma: no cover if self._conf["verbose"] > 1: logSys.exception(e) else: logSys.error(e) - except (EOFError, KeyboardInterrupt): + except (EOFError, KeyboardInterrupt): # pragma: no cover output("") raise # Single command mode diff --git a/fail2ban/client/fail2banserver.py b/fail2ban/client/fail2banserver.py index a6c8a7c5..a511e017 100644 --- a/fail2ban/client/fail2banserver.py +++ b/fail2ban/client/fail2banserver.py @@ -56,7 +56,7 @@ class Fail2banServer(Fail2banCmdLine): server.start(conf["socket"], conf["pidfile"], conf["force"], conf=conf) - except Exception as e: + except Exception as e: # pragma: no cover try: if server: server.quit() @@ -77,7 +77,7 @@ class Fail2banServer(Fail2banCmdLine): # Forks the current process, don't fork if async specified (ex: test cases) pid = 0 frk = not conf["async"] and PRODUCTION - if frk: + if frk: # pragma: no cover pid = os.fork() logSys.debug("-- async starting of server in %s, fork: %s - %s", os.getpid(), frk, pid) if pid == 0: @@ -108,22 +108,20 @@ class Fail2banServer(Fail2banCmdLine): exe = sys.executable args[0:0] = [exe] logSys.debug("Starting %r with args %r", exe, args) - if frk: - return os.execv(exe, args) + if frk: # pragma: no cover + os.execv(exe, args) else: # use P_WAIT instead of P_NOWAIT (to prevent defunct-zomby process), it startet as daemon, so parent exit fast after fork): ret = os.spawnv(os.P_WAIT, exe, args) - if ret != 0: + if ret != 0: # pragma: no cover raise OSError(ret, "Unknown error by executing server %r with %r" % (args[1], exe)) - return 0 except OSError as e: # pragma: no cover if not frk: #not PRODUCTION: raise # Use the PATH env. logSys.warning("Initial start attempt failed (%s). Starting %r with the same args", e, SERVER) - if frk: - return os.execvp(SERVER, args) - return pid + if frk: # pragma: no cover + os.execvp(SERVER, args) @staticmethod def getServerPath(): @@ -189,7 +187,7 @@ class Fail2banServer(Fail2banCmdLine): pid = os.getpid() server = Fail2banServer.startServerDirect(self._conf, background) # If forked - just exit other processes - if pid != os.getpid(): + if pid != os.getpid(): # pragma: no cover os._exit(0) if cli: cli._server = server @@ -198,7 +196,7 @@ class Fail2banServer(Fail2banCmdLine): if not async and cli: Utils.wait_for(lambda: phase.get('done', None) is not None, self._conf["timeout"]) if not phase.get('done', False): - if server: + if server: # pragma: no cover server.quit() exit(-1) logSys.debug('Starting server done') @@ -206,7 +204,9 @@ class Fail2banServer(Fail2banCmdLine): except Exception, e: if self._conf["verbose"] > 1: logSys.exception(e) - if server: + else: + logSys.error(e) + if server: # pragma: no cover server.quit() exit(-1) diff --git a/fail2ban/client/jailreader.py b/fail2ban/client/jailreader.py index 46f910e3..be53b3f3 100644 --- a/fail2ban/client/jailreader.py +++ b/fail2ban/client/jailreader.py @@ -109,7 +109,7 @@ class JailReader(ConfigReader): ["string", "action", ""]] # Read first options only needed for merge defaults ('known/...' from filter): - self.__opts = ConfigReader.getOptions(self, self.__name, opts1st) + self.__opts = ConfigReader.getOptions(self, self.__name, opts1st, shouldExist=True) if not self.__opts: return False diff --git a/fail2ban/tests/fail2banclienttestcase.py b/fail2ban/tests/fail2banclienttestcase.py index aa507fe6..82ffe9b3 100644 --- a/fail2ban/tests/fail2banclienttestcase.py +++ b/fail2ban/tests/fail2banclienttestcase.py @@ -347,6 +347,21 @@ class Fail2banClientTest(Fail2banClientServerBase): self.assertLogged("Server ready") self.assertLogged("Exit with code 0") self.pruneLog() + # test reload missing jail (interactive): + INTERACT += [ + "reload ~~unknown~jail~fail~~", + "exit" + ] + self.assertRaises(ExitException, _exec_client, + (CLIENT,) + startparams + ("-i",)) + self.assertLogged("Failed during configuration: No section: '~~unknown~jail~fail~~'") + self.pruneLog() + # test reload missing jail (direct): + self.assertRaises(FailExitException, _exec_client, + (CLIENT,) + startparams + ("reload", "~~unknown~jail~fail~~")) + self.assertLogged("Failed during configuration: No section: '~~unknown~jail~fail~~'") + self.assertLogged("Exit with code -1") + self.pruneLog() finally: self.pruneLog() # stop: @@ -425,6 +440,12 @@ class Fail2banClientTest(Fail2banClientServerBase): self.assertLogged("There is no directory " + tmp+"/miss" + " to contain the socket file") self.pruneLog() + ## not running + self.assertRaises(FailExitException, _exec_client, + (CLIENT, "-c", tmp+"/config", "-s", tmp+"/f2b.sock", "reload",)) + self.assertLogged("Could not find server") + self.pruneLog() + ## already exists: open(tmp+"/f2b.sock", 'a').close() self.assertRaises(FailExitException, _exec_client,