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