mirror of https://github.com/fail2ban/fail2ban
Merge remote-tracking branch 'remotes/gh-upstream/0.10' into 0.10-full
commit
cbfecea112
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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.")
|
||||
|
|
|
@ -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$
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue