diff --git a/config/fail2ban.conf b/config/fail2ban.conf index e01de77e..fb79da59 100644 --- a/config/fail2ban.conf +++ b/config/fail2ban.conf @@ -49,9 +49,10 @@ pidfile = /var/run/fail2ban/fail2ban.pid # Options: dbfile # Notes.: Set the file for the fail2ban persistent data to be stored. -# A value of :memory: means database is only stored in memory, and -# data is lost once fail2ban is stops. -# Values: [ FILE ] :memory: Default: /var/lib/fail2ban/fail2ban.sqlite3 +# A value of ":memory:" means database is only stored in memory +# and data is lost once fail2ban is stops. +# A value of "None" disables the database. +# Values: [ None :memory: FILE ] Default: /var/lib/fail2ban/fail2ban.sqlite3 dbfile = /var/lib/fail2ban/fail2ban.sqlite3 # Options: dbpurgeage diff --git a/fail2ban/client/beautifier.py b/fail2ban/client/beautifier.py index 033ce816..815a35d4 100644 --- a/fail2ban/client/beautifier.py +++ b/fail2ban/client/beautifier.py @@ -103,11 +103,17 @@ class Beautifier: else: msg = msg + `response` elif inC[1] == "dbfile": - msg = "Current database file is:\n" - msg = msg + "`- " + response + if response is None: + msg = "Database currently disabled" + else: + msg = "Current database file is:\n" + msg = msg + "`- " + response elif inC[1] == "dbpurgeage": - msg = "Current database purge age is:\n" - msg = msg + "`- %iseconds" % response + if response is None: + msg = "Database currently disabled" + else: + msg = "Current database purge age is:\n" + msg = msg + "`- %iseconds" % response elif inC[2] in ("logpath", "addlogpath", "dellogpath"): if len(response) == 0: msg = "No file is currently monitored" diff --git a/fail2ban/protocol.py b/fail2ban/protocol.py index dd3cf0c0..0435a415 100644 --- a/fail2ban/protocol.py +++ b/fail2ban/protocol.py @@ -44,7 +44,7 @@ protocol = [ ["set logtarget ", "sets logging target to . Can be STDOUT, STDERR, SYSLOG or a file"], ["get logtarget", "gets logging target"], ['', "DATABASE", ""], -["set dbfile ", "set the location of fail2ban persistent datastore"], +["set dbfile ", "set the location of fail2ban persistent datastore. Set to \"None\" to disable"], ["get dbfile", "get the location of fail2ban persistent datastore"], ["set dbpurgeage ", "sets the max age in that history of bans will be kept"], ["get dbpurgeage", "gets the max age in seconds that history of bans will be kept"], diff --git a/fail2ban/server/filter.py b/fail2ban/server/filter.py index c8371dc6..47294a87 100644 --- a/fail2ban/server/filter.py +++ b/fail2ban/server/filter.py @@ -527,9 +527,11 @@ class FileFilter(Filter): logSys.error(path + " already exists") else: container = FileContainer(path, self.getLogEncoding(), tail) - lastpos = self.jail.getDatabase().addLog(self.jail, container) - if lastpos and not tail: - container.setPos(lastpos) + db = self.jail.getDatabase() + if db is not None: + lastpos = db.addLog(self.jail, container) + if lastpos and not tail: + container.setPos(lastpos) self.__logPath.append(container) logSys.info("Added logfile = %s" % path) self._addLogPath(path) # backend specific @@ -549,7 +551,9 @@ class FileFilter(Filter): for log in self.__logPath: if log.getFileName() == path: self.__logPath.remove(log) - self.jail.getDatabase().updateLog(self.jail, log) + db = self.jail.getDatabase() + if db is not None: + db.updateLog(self.jail, log) logSys.info("Removed logfile = %s" % path) self._delLogPath(path) return @@ -648,7 +652,9 @@ class FileFilter(Filter): break self.processLineAndAdd(line) container.close() - self.jail.getDatabase().updateLog(self.jail, container) + db = self.jail.getDatabase() + if db is not None: + db.updateLog(self.jail, container) return True def status(self): diff --git a/fail2ban/server/jail.py b/fail2ban/server/jail.py index 3895dfd6..a24810ed 100644 --- a/fail2ban/server/jail.py +++ b/fail2ban/server/jail.py @@ -37,7 +37,7 @@ class Jail: # list had .index until 2.6 _BACKENDS = ['pyinotify', 'gamin', 'polling', 'systemd'] - def __init__(self, db, name, backend = "auto"): + def __init__(self, name, backend = "auto", db=None): self.__db = db self.setName(name) self.__queue = Queue.Queue() @@ -130,7 +130,8 @@ class Jail: def putFailTicket(self, ticket): self.__queue.put(ticket) - self.__db.addBan(self, ticket) + if self.__db is not None: + self.__db.addBan(self, ticket) def getFailTicket(self): try: @@ -142,8 +143,9 @@ class Jail: self.__filter.start() self.__action.start() # Restore any previous valid bans from the database - for ticket in self.__db.getBans(self, self.__action.getBanTime()): - self.__queue.put(ticket) + if self.__db is not None: + for ticket in self.__db.getBans(self, self.__action.getBanTime()): + self.__queue.put(ticket) logSys.info("Jail '%s' started" % self.__name) def stop(self): diff --git a/fail2ban/server/jails.py b/fail2ban/server/jails.py index 5fe9baef..5811dbee 100644 --- a/fail2ban/server/jails.py +++ b/fail2ban/server/jails.py @@ -50,13 +50,13 @@ class Jails: # @param name The name of the jail # @param backend The backend to use - def add(self, db, name, backend): + def add(self, name, backend, db=None): try: self.__lock.acquire() if self.__jails.has_key(name): raise DuplicateJailException(name) else: - self.__jails[name] = Jail(db, name, backend) + self.__jails[name] = Jail(name, backend, db=None) finally: self.__lock.release() diff --git a/fail2ban/server/server.py b/fail2ban/server/server.py index 446a0989..572d40ea 100644 --- a/fail2ban/server/server.py +++ b/fail2ban/server/server.py @@ -43,6 +43,7 @@ class Server: self.__loggingLock = Lock() self.__lock = RLock() self.__jails = Jails() + self.__db = None self.__daemon = daemon self.__transm = Transmitter(self) self.__asyncServer = AsyncServer(self.__transm) @@ -51,9 +52,6 @@ class Server: # Set logging level self.setLogLevel(3) self.setLogTarget("STDOUT") - - # Create database, initially in memory - self.setDatabase(":memory:") def __sigTERMhandler(self, signum, frame): logSys.debug("Caught signal %d. Exiting" % signum) @@ -121,12 +119,14 @@ class Server: def addJail(self, name, backend): - self.__jails.add(self.__db, name, backend) - self.__db.addJail(self.__jails.get(name)) + self.__jails.add(name, backend, self.__db) + if self.__db is not None: + self.__db.addJail(self.__jails.get(name)) def delJail(self, name): self.__jails.remove(name) - self.__db.delJailName(name) + if self.__db is not None: + self.__db.delJailName(name) def startJail(self, name): try: @@ -468,8 +468,11 @@ class Server: def setDatabase(self, filename): if self.__jails.size() == 0: - self.__db = Fail2BanDb(filename) - self.__db.delAllJails() + if filename.lower() == "none": + self.__db = None + else: + self.__db = Fail2BanDb(filename) + self.__db.delAllJails() else: raise RuntimeError( "Cannot change database when there are jails present") diff --git a/fail2ban/server/transmitter.py b/fail2ban/server/transmitter.py index b3712796..3e4be6bf 100644 --- a/fail2ban/server/transmitter.py +++ b/fail2ban/server/transmitter.py @@ -116,10 +116,18 @@ class Transmitter: #Database elif name == "dbfile": self.__server.setDatabase(command[1]) - return self.__server.getDatabase().getFilename() + db = self.__server.getDatabase() + if db is None: + return None + else: + return db.getFilename() elif name == "dbpurgeage": - self.__server.getDatabase().setPurgeAge(command[1]) - return self.__server.getDatabase().getPurgeAge() + db = self.__server.getDatabase() + if db is None: + return None + else: + db.setPurgeAge(command[1]) + return db.getPurgeAge() # Jail elif command[1] == "idle": if command[2] == "on": @@ -266,9 +274,17 @@ class Transmitter: return self.__server.getLogTarget() #Database elif name == "dbfile": - return self.__server.getDatabase().getFilename() + db = self.__server.getDatabase() + if db is None: + return None + else: + return db.getFilename() elif name == "dbpurgeage": - return self.__server.getDatabase().getPurgeAge() + db = self.__server.getDatabase() + if db is None: + return None + else: + return db.getPurgeAge() # Filter elif command[1] == "logpath": return self.__server.getLogPath(name) diff --git a/fail2ban/tests/dummyjail.py b/fail2ban/tests/dummyjail.py index ef291d71..52e47898 100644 --- a/fail2ban/tests/dummyjail.py +++ b/fail2ban/tests/dummyjail.py @@ -24,16 +24,12 @@ __license__ = "GPL" from threading import Lock -from fail2ban.server.database import Fail2BanDb - class DummyJail(object): """A simple 'jail' to suck in all the tickets generated by Filter's """ def __init__(self): self.lock = Lock() self.queue = [] - self.__db = Fail2BanDb(":memory:") - self.__db.addJail(self) def __len__(self): try: @@ -69,5 +65,5 @@ class DummyJail(object): return "DummyJail #%s with %d tickets" % (id(self), len(self)) def getDatabase(self): - return self.__db + return None diff --git a/fail2ban/tests/filtertestcase.py b/fail2ban/tests/filtertestcase.py index c41d7ee4..16df110e 100644 --- a/fail2ban/tests/filtertestcase.py +++ b/fail2ban/tests/filtertestcase.py @@ -40,7 +40,6 @@ from fail2ban.server.filter import FileFilter, DNSUtils from fail2ban.server.failmanager import FailManager from fail2ban.server.failmanager import FailManagerEmpty from fail2ban.server.mytime import MyTime -from fail2ban.server.database import Fail2BanDb from fail2ban.tests.utils import setUpMyTime, tearDownMyTime TEST_FILES_DIR = os.path.join(os.path.dirname(__file__), "files") @@ -565,7 +564,7 @@ def get_monitor_failures_testcase(Filter_): # tail written before, so let's not copy anything yet #_copy_lines_between_files(GetFailures.FILENAME_01, self.name, n=100) # we should detect the failures - self.assert_correct_last_attempt(GetFailures.FAILURES_01, count=3) # was needed if we write twice above + self.assert_correct_last_attempt(GetFailures.FAILURES_01, count=6) # was needed if we write twice above # now copy and get even more _copy_lines_between_files(GetFailures.FILENAME_01, self.file, n=100) @@ -920,5 +919,5 @@ class JailTests(unittest.TestCase): def testSetBackend_gh83(self): # smoke test # Must not fail to initiate - jail = Jail(Fail2BanDb(":memory:"), 'test', backend='polling') + jail = Jail('test', backend='polling') diff --git a/fail2ban/tests/servertestcase.py b/fail2ban/tests/servertestcase.py index bd8a27d7..4c73ae3f 100644 --- a/fail2ban/tests/servertestcase.py +++ b/fail2ban/tests/servertestcase.py @@ -28,7 +28,6 @@ import unittest, socket, time, tempfile, os, locale, sys from fail2ban.server.server import Server from fail2ban.server.jail import Jail -from fail2ban.server.database import Fail2BanDb from fail2ban.exceptions import UnknownJailException try: from fail2ban.server import filtersystemd @@ -175,9 +174,23 @@ class Transmitter(TransmitterBase): self.setGetTestNOK("dbfile", tmpFilename) self.server.delJail(self.jailName) self.setGetTest("dbfile", tmpFilename) - self.setGetTest("dbpurgeage", 600) + self.setGetTest("dbpurgeage", "600", 600) self.setGetTestNOK("dbpurgeage", "LIZARD") + # Disable database + self.assertEqual(self.transm.proceed( + ["set", "dbfile", "None"]), + (0, None)) + self.assertEqual(self.transm.proceed( + ["get", "dbfile"]), + (0, None)) + self.assertEqual(self.transm.proceed( + ["set", "dbpurgeage", "500"]), + (0, None)) + self.assertEqual(self.transm.proceed( + ["get", "dbpurgeage"]), + (0, None)) + def testAddJail(self): jail2 = "TestJail2" jail3 = "TestJail3" @@ -651,5 +664,5 @@ class JailTests(unittest.TestCase): def testLongName(self): # Just a smoke test for now longname = "veryveryverylongname" - jail = Jail(Fail2BanDb(":memory:"), longname) + jail = Jail(longname) self.assertEqual(jail.getName(), longname)