mirror of https://github.com/fail2ban/fail2ban
ENH: Log unhandled exceptions to Fail2Ban log
parent
e8131475cd
commit
f7da091437
|
@ -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
|
||||||
----------
|
----------
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"))
|
||||||
|
|
Loading…
Reference in New Issue