ENH: Log unhandled exceptions to Fail2Ban log

pull/701/head
Steven Hiscocks 2014-06-09 22:27:51 +01:00
parent e8131475cd
commit f7da091437
5 changed files with 37 additions and 2 deletions

View File

@ -40,6 +40,7 @@ ver. 0.9.1 (2014/xx/xx) - better, faster, stronger
* Match non "Bye Bye" disconnect messages for sshd locked account regex * Match non "Bye Bye" disconnect messages for sshd locked account regex
* Realign fail2ban log output with white space to improve readability. Does * Realign fail2ban log output with white space to improve readability. Does
not affect SYSLOG output. not affect SYSLOG output.
* Log unhandled exceptions
ver. 0.9.0 (2014/03/14) - beta ver. 0.9.0 (2014/03/14) - beta
---------- ----------

View File

@ -112,3 +112,10 @@ def getF2BLogger(name):
"""Get logging.Logger instance with Fail2Ban logger name convention """Get logging.Logger instance with Fail2Ban logger name convention
""" """
return logging.getLogger("fail2ban.%s" % name.rpartition(".")[-1]) return logging.getLogger("fail2ban.%s" % name.rpartition(".")[-1])
def fail2ban_excepthook(exctype, value, traceback):
"""Except hook used to log unhandled exceptions to Fail2Ban log
"""
logging.getLogger("fail2ban").critical(
"Unhandled exception in Fail2Ban:", exc_info=True)
return sys.__excepthook__(exctype, value, traceback)

View File

@ -24,9 +24,12 @@ __author__ = "Cyril Jaquier"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
import sys
from threading import Thread from threading import Thread
from abc import abstractproperty, abstractmethod from abc import abstractproperty, abstractmethod
from ..helpers import fail2ban_excepthook
class JailThread(Thread): class JailThread(Thread):
"""Abstract class for threading elements in Fail2Ban. """Abstract class for threading elements in Fail2Ban.
@ -53,6 +56,16 @@ class JailThread(Thread):
## The time the thread sleeps in the loop. ## The time the thread sleeps in the loop.
self.sleeptime = 1 self.sleeptime = 1
# excepthook workaround for threads, derived from:
# http://bugs.python.org/issue1230540#msg91244
run = self.run
def run_with_except_hook(*args, **kwargs):
try:
run(*args, **kwargs)
except:
fail2ban_excepthook(*sys.exc_info())
self.run = run_with_except_hook
@abstractproperty @abstractproperty
def status(self): # pragma: no cover - abstract def status(self): # pragma: no cover - abstract
"""Abstract - Should provide status information. """Abstract - Should provide status information.

View File

@ -32,7 +32,7 @@ from .filter import FileFilter, JournalFilter
from .transmitter import Transmitter from .transmitter import Transmitter
from .asyncserver import AsyncServer, AsyncServerException from .asyncserver import AsyncServer, AsyncServerException
from .. import version from .. import version
from ..helpers import getF2BLogger from ..helpers import getF2BLogger, fail2ban_excepthook
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = getF2BLogger(__name__) logSys = getF2BLogger(__name__)
@ -70,6 +70,9 @@ class Server:
signal.signal(signal.SIGTERM, self.__sigTERMhandler) signal.signal(signal.SIGTERM, self.__sigTERMhandler)
signal.signal(signal.SIGINT, self.__sigTERMhandler) signal.signal(signal.SIGINT, self.__sigTERMhandler)
# Ensure unhandled exceptions are logged
sys.excepthook = fail2ban_excepthook
# First set the mask to only allow access to owner # First set the mask to only allow access to owner
os.umask(0077) os.umask(0077)
if self.__daemon: # pragma: no cover if self.__daemon: # pragma: no cover

View File

@ -35,6 +35,8 @@ import logging
from ..server.failregex import Regex, FailRegex, RegexException from ..server.failregex import Regex, FailRegex, RegexException
from ..server.server import Server from ..server.server import Server
from ..server.jail import Jail from ..server.jail import Jail
from ..server.jailthread import JailThread
from .utils import LogCaptureTestCase
from ..helpers import getF2BLogger from ..helpers import getF2BLogger
try: try:
@ -797,10 +799,19 @@ class RegexTests(unittest.TestCase):
self.assertTrue(fr.hasMatched()) self.assertTrue(fr.hasMatched())
self.assertRaises(RegexException, fr.getHost) self.assertRaises(RegexException, fr.getHost)
class LoggingTests(unittest.TestCase): class _BadThread(JailThread):
def run(self):
int("cat")
class LoggingTests(LogCaptureTestCase):
def testGetF2BLogger(self): def testGetF2BLogger(self):
testLogSys = getF2BLogger("fail2ban.some.string.with.name") testLogSys = getF2BLogger("fail2ban.some.string.with.name")
self.assertEqual(testLogSys.parent.name, "fail2ban") self.assertEqual(testLogSys.parent.name, "fail2ban")
self.assertEqual(testLogSys.name, "fail2ban.name") self.assertEqual(testLogSys.name, "fail2ban.name")
def testFail2BanExceptHook(self):
badThread = _BadThread()
badThread.start()
badThread.join()
self.assertTrue(self._is_logged("Unhandled exception"))