Merge remote-tracking branch 'remotes/gh-upstream/0.10' into 0.10-full

pull/1460/head
sebres 2016-10-15 19:19:58 +02:00
commit cbfecea112
8 changed files with 103 additions and 57 deletions

View File

@ -310,7 +310,7 @@ class Fail2banClient(Fail2banCmdLine, Thread):
# stop options - jail name or --all
break
if self.__ping(timeout=-1):
if len(cmd) == 1:
if len(cmd) == 1 or cmd[1] == '--all':
jail = '--all'
ret, stream = self.readConfig()
else:

View File

@ -184,13 +184,19 @@ class Fail2banServer(Fail2banCmdLine):
logSys.log(5, ' server phase %s', phase)
if not phase.get('start', False):
raise ServerExecutionException('Async configuration of server failed')
# event for server ready flag:
def _server_ready():
phase['start-ready'] = True
logSys.log(5, ' server phase %s', phase)
# notify waiting thread if server really ready
self._conf['onstart'] = _server_ready
# Start server, daemonize it, etc.
pid = os.getpid()
server = Fail2banServer.startServerDirect(self._conf, background)
# notify waiting thread server ready resp. done (background execution, error case, etc):
if not async:
phase['start-ready'] = True
logSys.log(5, ' server phase %s', phase)
_server_ready()
# If forked - just exit other processes
if pid != os.getpid(): # pragma: no cover
os._exit(0)

View File

@ -147,6 +147,7 @@ class AsyncServer(asyncore.dispatcher):
self.__sock = "/var/run/fail2ban/fail2ban.sock"
self.__init = False
self.__active = False
self.onstart = None
##
# Returns False as we only read the socket first.
@ -196,6 +197,9 @@ class AsyncServer(asyncore.dispatcher):
self.listen(1)
# Sets the init flag.
self.__init = self.__loop = self.__active = True
# Execute on start event (server ready):
if self.onstart:
self.onstart()
# Event loop as long as active:
loop(lambda: self.__loop, use_poll=use_poll)
self.__active = False

View File

@ -97,32 +97,40 @@ class FilterPoll(FileFilter):
def run(self):
while self.active:
if logSys.getEffectiveLevel() <= 6:
logSys.log(6, "Woke up idle=%s with %d files monitored",
self.idle, self.getLogCount())
if self.idle:
if not Utils.wait_for(lambda: not self.active or not self.idle,
self.sleeptime * 10, self.sleeptime
):
self.ticks += 1
continue
# Get file modification
modlst = []
Utils.wait_for(lambda: not self.active or self.getModified(modlst),
self.sleeptime)
for filename in modlst:
self.getFailures(filename)
self.__modified = True
try:
if logSys.getEffectiveLevel() <= 6:
logSys.log(6, "Woke up idle=%s with %d files monitored",
self.idle, self.getLogCount())
if self.idle:
if not Utils.wait_for(lambda: not self.active or not self.idle,
self.sleeptime * 10, self.sleeptime
):
self.ticks += 1
continue
# Get file modification
modlst = []
Utils.wait_for(lambda: not self.active or self.getModified(modlst),
self.sleeptime)
for filename in modlst:
self.getFailures(filename)
self.__modified = True
self.ticks += 1
if self.__modified:
try:
while True:
ticket = self.failManager.toBan()
self.jail.putFailTicket(ticket)
except FailManagerEmpty:
self.failManager.cleanup(MyTime.time())
self.__modified = False
self.ticks += 1
if self.__modified:
try:
while True:
ticket = self.failManager.toBan()
self.jail.putFailTicket(ticket)
except FailManagerEmpty:
self.failManager.cleanup(MyTime.time())
self.__modified = False
except Exception as e: # pragma: no cover
if not self.active: # if not active - error by stop...
break
logSys.error("Caught unhandled exception in main cycle: %r", e,
exc_info=logSys.getEffectiveLevel()<=logging.DEBUG)
# incr common error counter:
self.commonError()
logSys.debug("[%s] filter terminated", self.jailName)
return True
@ -151,9 +159,9 @@ class FilterPoll(FileFilter):
return True
except Exception as e:
# stil alive (may be deleted because multi-threaded):
if not self.getLog(filename):
if not self.getLog(filename) or self.__prevStats.get(filename) is None:
logSys.warning("Log %r seems to be down: %s", filename, e)
return
return False
# log error:
if self.__file404Cnt[filename] < 2:
logSys.error("Unable to get stat on %s because of: %s",

View File

@ -82,12 +82,12 @@ class Server:
}
self.__prev_signals = {}
def __sigTERMhandler(self, signum, frame):
logSys.debug("Caught signal %d. Exiting" % signum)
def __sigTERMhandler(self, signum, frame): # pragma: no cover - indirect tested
logSys.debug("Caught signal %d. Exiting", signum)
self.quit()
def __sigUSR1handler(self, signum, fname):
logSys.debug("Caught signal %d. Flushing logs" % signum)
def __sigUSR1handler(self, signum, fname): # pragma: no cover - indirect tested
logSys.debug("Caught signal %d. Flushing logs", signum)
self.flushLogs()
def _rebindSignal(self, s, new):
@ -138,12 +138,12 @@ class Server:
# Creates a PID file.
try:
logSys.debug("Creating PID file %s" % pidfile)
logSys.debug("Creating PID file %s", pidfile)
pidFile = open(pidfile, 'w')
pidFile.write("%s\n" % os.getpid())
pidFile.close()
except IOError as e:
logSys.error("Unable to create PID file: %s" % e)
except (OSError, IOError) as e: # pragma: no cover
logSys.error("Unable to create PID file: %s", e)
# Create observers and start it:
if observer:
@ -155,15 +155,16 @@ class Server:
logSys.debug("Starting communication")
try:
self.__asyncServer = AsyncServer(self.__transm)
self.__asyncServer.onstart = conf.get('onstart')
self.__asyncServer.start(sock, force)
except AsyncServerException as e:
logSys.error("Could not start server: %s", e)
# Removes the PID file.
try:
logSys.debug("Remove PID file %s" % pidfile)
logSys.debug("Remove PID file %s", pidfile)
os.remove(pidfile)
except OSError as e:
logSys.error("Unable to remove PID file: %s" % e)
except (OSError, IOError) as e: # pragma: no cover
logSys.error("Unable to remove PID file: %s", e)
# Stop observer and exit
if Observers.Main is not None:
Observers.Main.stop()
@ -260,7 +261,7 @@ class Server:
def reloadJails(self, name, opts, begin):
if begin:
# begin reload:
if self.__reload_state and (name == '--all' or self.__reload_state.get(name)):
if self.__reload_state and (name == '--all' or self.__reload_state.get(name)): # pragma: no cover
raise ValueError('Reload already in progress')
logSys.info("Reload " + (("jail %s" % name) if name != '--all' else "all jails"))
with self.__lock:
@ -391,11 +392,8 @@ class Server:
def addFailRegex(self, name, value, multiple=False):
flt = self.__jails[name].filter
if multiple:
for value in value:
logSys.debug(" failregex: %r", value)
flt.addFailRegex(value)
else:
if not multiple: value = (value,)
for value in value:
logSys.debug(" failregex: %r", value)
flt.addFailRegex(value)
@ -407,11 +405,8 @@ class Server:
def addIgnoreRegex(self, name, value, multiple=False):
flt = self.__jails[name].filter
if multiple:
for value in value:
logSys.debug(" ignoreregex: %r", value)
flt.addIgnoreRegex(value)
else:
if not multiple: value = (value,)
for value in value:
logSys.debug(" ignoreregex: %r", value)
flt.addIgnoreRegex(value)
@ -615,6 +610,7 @@ class Server:
if logger.getEffectiveLevel() <= logging.DEBUG: # pragma: no cover
if self.__verbose is None:
self.__verbose = logging.DEBUG - logger.getEffectiveLevel() + 1
if self.__verbose is not None and self.__verbose > 2: # pragma: no cover
fmt = getVerbosityFormat(self.__verbose-1)
# tell the handler to use this format
hdlr.setFormatter(logging.Formatter(fmt))
@ -686,7 +682,7 @@ class Server:
if Fail2BanDb is not None:
self.__db = Fail2BanDb(filename)
self.__db.delAllJails()
else:
else: # pragma: no cover
logSys.error(
"Unable to import fail2ban database module as sqlite "
"is not available.")

View File

@ -15,4 +15,8 @@ before = common.conf
_daemon = test-demo
failregex = ^%(__prefix_line)sF2B: failure from <HOST>$
ignoreregex =
^%(__prefix_line)sF2B: error from <HOST>$
# just to test multiple ignoreregex:
ignoreregex = ^%(__prefix_line)sF2B: error from 192.0.2.251$
^%(__prefix_line)sF2B: error from 192.0.2.252$

View File

@ -735,7 +735,7 @@ class Fail2banServerTest(Fail2banClientServerBase):
if unittest.F2B.log_level <= logging.DEBUG: # pragma: no cover
_out_file(fn)
def _write_jail_cfg(enabled=(1, 2), actions=()):
def _write_jail_cfg(enabled=(1, 2), actions=(), backend="polling"):
_write_file(pjoin(cfg, "jail.conf"), "w",
"[INCLUDES]", "",
"[DEFAULT]", "",
@ -744,7 +744,7 @@ class Fail2banServerTest(Fail2banClientServerBase):
"findtime = 10m",
"failregex = ^\s*failure (401|403) from <HOST>",
"",
"[test-jail1]", "backend = polling", "filter =",
"[test-jail1]", "backend = " + backend, "filter =",
"action = ",
" test-action1[name='%(__name__)s']" if 1 in actions else "",
" test-action2[name='%(__name__)s']" if 2 in actions else "",
@ -755,7 +755,7 @@ class Fail2banServerTest(Fail2banClientServerBase):
" ^\s*error (401|403) from <HOST>" if 2 in enabled else "",
"enabled = true" if 1 in enabled else "",
"",
"[test-jail2]", "backend = polling", "filter =",
"[test-jail2]", "backend = " + backend, "filter =",
"action =",
"logpath = " + test2log,
"enabled = true" if 2 in enabled else "",
@ -999,6 +999,20 @@ class Fail2banServerTest(Fail2banClientServerBase):
"[test-jail1] Ban 192.0.2.4", all=True
)
# backend-switch (restart instead of reload):
self.pruneLog("[test-phase 8a]")
_write_jail_cfg(enabled=[1], backend="xxx-unknown-backend-zzz")
self.execFailed(startparams, "reload")
self.assertLogged("Reload finished.", all=True, wait=MID_WAITTIME)
self.assertLogged(
"Restart jail 'test-jail1' (reason: 'polling' != ",
"Unknown backend ", all=True)
self.pruneLog("[test-phase 8b]")
_write_jail_cfg(enabled=[1])
self.execSuccess(startparams, "reload")
self.assertLogged("Reload finished.", all=True, wait=MID_WAITTIME)
# several small cases (cover several parts):
self.pruneLog("[test-phase end-1]")
# wrong jail (not-started):
@ -1012,4 +1026,11 @@ class Fail2banServerTest(Fail2banClientServerBase):
self.assertNotLogged(
"Creating new jail 'test-jail2'",
"Jail 'test-jail2' started", all=True)
self.pruneLog()
# restart all jails (without restart server):
self.pruneLog("[test-phase end-2]")
self.execSuccess(startparams,
"--async", "reload", "--restart", "--all")
self.assertLogged(
"Jail 'test-jail1' stopped",
"Jail 'test-jail1' started", all=True)

View File

@ -45,3 +45,10 @@ Jun 22 20:37:04 server test-demo[402]: writeToStorage plist={
# -- wrong time direct in journal-line (used last known date):
# failJSON: { "time": "2005-06-22T20:37:04", "match": true , "host": "192.0.2.2" }
0000-12-30 00:00:00 server test-demo[47831]: F2B: failure from 192.0.2.2
# failJSON: { "time": "2005-06-21T16:56:02", "match": true , "host": "192.0.2.250" }
[Jun 21 16:56:02] machine test-demo(pam_unix)[13709] F2B: error from 192.0.2.250
# failJSON: { "match": false, "desc": "test 1st ignoreregex" }
[Jun 21 16:56:03] machine test-demo(pam_unix)[13709] F2B: error from 192.0.2.251
# failJSON: { "match": false, "desc": "test 2nd ignoreregex" }
[Jun 21 16:56:04] machine test-demo(pam_unix)[13709] F2B: error from 192.0.2.252