reader bug fix: prevent to silent "load" of not existing jail;

coverage of test cases increased;
pull/1321/head
sebres 2016-02-15 20:41:20 +01:00
parent 4ec70d7851
commit 0fef5022f0
5 changed files with 47 additions and 24 deletions

View File

@ -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]

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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,