diff --git a/ChangeLog b/ChangeLog index 395d59d7..85d2cbad 100644 --- a/ChangeLog +++ b/ChangeLog @@ -22,6 +22,8 @@ ver. 0.8.12 (2013/12/XX) - things-can-only-get-better - smtps not a IANA standard and has been removed from Arch. Replaced with 465. Thanks Stefan. Closes gh-447 - mysqld-syslog-iptables rule was too long. Part of gh-447. + - add 'flushlogs' command to allow logrotation without clobbering logtarget + settings. Closes gh-458, Debian bug #697333, Redhat bug #891798. - Enhancements: - long names on jails documented based on iptables limit of 30 less diff --git a/client/beautifier.py b/client/beautifier.py index 8e690656..bc9e89b1 100644 --- a/client/beautifier.py +++ b/client/beautifier.py @@ -63,6 +63,8 @@ class Beautifier: msg = "Jail stopped" elif inC[0] == "add": msg = "Added jail " + response + elif inC[0] == "flushlogs": + msg = "logs: " + response elif inC[0:1] == ['status']: if len(inC) > 1: # Create IP list diff --git a/common/protocol.py b/common/protocol.py index 9309ce7f..278ccd53 100644 --- a/common/protocol.py +++ b/common/protocol.py @@ -43,6 +43,7 @@ protocol = [ ["get loglevel", "gets the logging level"], ["set logtarget ", "sets logging target to . Can be STDOUT, STDERR, SYSLOG or a file"], ["get logtarget", "gets logging target"], +["flushlogs", "flushes the logtarget if a file and reopens it. For log rotation."], ['', "JAIL CONTROL", ""], ["add ", "creates using "], ["start ", "starts the jail "], diff --git a/files/fail2ban-logrotate b/files/fail2ban-logrotate index 67c6364a..a09870af 100644 --- a/files/fail2ban-logrotate +++ b/files/fail2ban-logrotate @@ -13,6 +13,6 @@ missingok compress postrotate - /usr/bin/fail2ban-client set logtarget /var/log/fail2ban.log 1>/dev/null || true + /usr/bin/fail2ban-client flushlogs 1>/dev/null || true endscript } diff --git a/server/server.py b/server/server.py index 6ac93a54..1358391b 100644 --- a/server/server.py +++ b/server/server.py @@ -361,7 +361,7 @@ class Server: # Target should be a file try: open(target, "a").close() - hdlr = logging.FileHandler(target) + hdlr = logging.handlers.RotatingFileHandler(target) except IOError: logSys.error("Unable to log to " + target) logSys.info("Logging to previous target " + self.__logTarget) @@ -401,6 +401,17 @@ class Server: finally: self.__loggingLock.release() + def flushLogs(self): + if self.__logTarget not in ['STDERR', 'STDOUT', 'SYSLOG']: + for handler in logging.getLogger("fail2ban").handlers: + handler.doRollover() + return "rolled over" + else: + for handler in logging.getLogger("fail2ban").handlers: + handler.flush() + return "flushed" + + def __createDaemon(self): # pragma: no cover """ Detach a process from the controlling terminal and run it in the background as a daemon. diff --git a/server/transmitter.py b/server/transmitter.py index deaf9adf..cf65dada 100644 --- a/server/transmitter.py +++ b/server/transmitter.py @@ -92,6 +92,8 @@ class Transmitter: value = command[1] time.sleep(int(value)) return None + elif command[0] == "flushlogs": + return self.__server.flushLogs() elif command[0] == "set": return self.__commandSet(command[1:]) elif command[0] == "get": diff --git a/testcases/servertestcase.py b/testcases/servertestcase.py index c93326a5..e6137d2f 100644 --- a/testcases/servertestcase.py +++ b/testcases/servertestcase.py @@ -25,7 +25,7 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" import unittest, socket, time, tempfile, os, sys -from server.server import Server +from server.server import Server, logSys from server.jail import Jail from common.exceptions import UnknownJailException @@ -521,6 +521,35 @@ class TransmitterLogging(TransmitterBase): self.setGetTest("loglevel", "0", 0) self.setGetTestNOK("loglevel", "Bird") + def testFlushLogs(self): + self.assertEqual(self.transm.proceed(["flushlogs"]), (0, "rolled over")) + try: + f, fn = tempfile.mkstemp("fail2ban.log") + os.close(f) + self.server.setLogLevel(2) + self.assertEqual(self.transm.proceed(["set", "logtarget", fn]), (0, fn)) + logSys.warn("Before file moved") + try: + f2, fn2 = tempfile.mkstemp("fail2ban.log") + os.close(f2) + os.rename(fn, fn2) + logSys.warn("After file moved") + self.assertEqual(self.transm.proceed(["flushlogs"]), (0, "rolled over")) + logSys.warn("After flushlogs") + with open(fn2,'r') as f: + self.assertTrue(f.next().endswith("Before file moved\n")) + self.assertTrue(f.next().endswith("After file moved\n")) + self.assertRaises(StopIteration, f.next) + with open(fn,'r') as f: + self.assertTrue(f.next().endswith("After flushlogs\n")) + self.assertRaises(StopIteration, f.next) + finally: + os.remove(fn2) + finally: + os.remove(fn) + self.assertEqual(self.transm.proceed(["set", "logtarget", "STDERR"]), (0, "STDERR")) + self.assertEqual(self.transm.proceed(["flushlogs"]), (0, "flushed")) + class JailTests(unittest.TestCase):