ENH: Database now optional, by setting dbfile to "None"

pull/480/head
Steven Hiscocks 2013-12-10 21:16:36 +00:00
parent 174f9a243a
commit e18af48e34
11 changed files with 85 additions and 43 deletions

View File

@ -49,9 +49,10 @@ pidfile = /var/run/fail2ban/fail2ban.pid
# Options: dbfile # Options: dbfile
# Notes.: Set the file for the fail2ban persistent data to be stored. # Notes.: Set the file for the fail2ban persistent data to be stored.
# A value of :memory: means database is only stored in memory, and # A value of ":memory:" means database is only stored in memory
# data is lost once fail2ban is stops. # and data is lost once fail2ban is stops.
# Values: [ FILE ] :memory: Default: /var/lib/fail2ban/fail2ban.sqlite3 # A value of "None" disables the database.
# Values: [ None :memory: FILE ] Default: /var/lib/fail2ban/fail2ban.sqlite3
dbfile = /var/lib/fail2ban/fail2ban.sqlite3 dbfile = /var/lib/fail2ban/fail2ban.sqlite3
# Options: dbpurgeage # Options: dbpurgeage

View File

@ -103,11 +103,17 @@ class Beautifier:
else: else:
msg = msg + `response` msg = msg + `response`
elif inC[1] == "dbfile": elif inC[1] == "dbfile":
msg = "Current database file is:\n" if response is None:
msg = msg + "`- " + response msg = "Database currently disabled"
else:
msg = "Current database file is:\n"
msg = msg + "`- " + response
elif inC[1] == "dbpurgeage": elif inC[1] == "dbpurgeage":
msg = "Current database purge age is:\n" if response is None:
msg = msg + "`- %iseconds" % response msg = "Database currently disabled"
else:
msg = "Current database purge age is:\n"
msg = msg + "`- %iseconds" % response
elif inC[2] in ("logpath", "addlogpath", "dellogpath"): elif inC[2] in ("logpath", "addlogpath", "dellogpath"):
if len(response) == 0: if len(response) == 0:
msg = "No file is currently monitored" msg = "No file is currently monitored"

View File

@ -44,7 +44,7 @@ protocol = [
["set logtarget <TARGET>", "sets logging target to <TARGET>. Can be STDOUT, STDERR, SYSLOG or a file"], ["set logtarget <TARGET>", "sets logging target to <TARGET>. Can be STDOUT, STDERR, SYSLOG or a file"],
["get logtarget", "gets logging target"], ["get logtarget", "gets logging target"],
['', "DATABASE", ""], ['', "DATABASE", ""],
["set dbfile <FILE>", "set the location of fail2ban persistent datastore"], ["set dbfile <FILE>", "set the location of fail2ban persistent datastore. Set to \"None\" to disable"],
["get dbfile", "get the location of fail2ban persistent datastore"], ["get dbfile", "get the location of fail2ban persistent datastore"],
["set dbpurgeage <SECONDS>", "sets the max age in <SECONDS> that history of bans will be kept"], ["set dbpurgeage <SECONDS>", "sets the max age in <SECONDS> that history of bans will be kept"],
["get dbpurgeage", "gets the max age in seconds that history of bans will be kept"], ["get dbpurgeage", "gets the max age in seconds that history of bans will be kept"],

View File

@ -527,9 +527,11 @@ class FileFilter(Filter):
logSys.error(path + " already exists") logSys.error(path + " already exists")
else: else:
container = FileContainer(path, self.getLogEncoding(), tail) container = FileContainer(path, self.getLogEncoding(), tail)
lastpos = self.jail.getDatabase().addLog(self.jail, container) db = self.jail.getDatabase()
if lastpos and not tail: if db is not None:
container.setPos(lastpos) lastpos = db.addLog(self.jail, container)
if lastpos and not tail:
container.setPos(lastpos)
self.__logPath.append(container) self.__logPath.append(container)
logSys.info("Added logfile = %s" % path) logSys.info("Added logfile = %s" % path)
self._addLogPath(path) # backend specific self._addLogPath(path) # backend specific
@ -549,7 +551,9 @@ class FileFilter(Filter):
for log in self.__logPath: for log in self.__logPath:
if log.getFileName() == path: if log.getFileName() == path:
self.__logPath.remove(log) 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) logSys.info("Removed logfile = %s" % path)
self._delLogPath(path) self._delLogPath(path)
return return
@ -648,7 +652,9 @@ class FileFilter(Filter):
break break
self.processLineAndAdd(line) self.processLineAndAdd(line)
container.close() 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 return True
def status(self): def status(self):

View File

@ -37,7 +37,7 @@ class Jail:
# list had .index until 2.6 # list had .index until 2.6
_BACKENDS = ['pyinotify', 'gamin', 'polling', 'systemd'] _BACKENDS = ['pyinotify', 'gamin', 'polling', 'systemd']
def __init__(self, db, name, backend = "auto"): def __init__(self, name, backend = "auto", db=None):
self.__db = db self.__db = db
self.setName(name) self.setName(name)
self.__queue = Queue.Queue() self.__queue = Queue.Queue()
@ -130,7 +130,8 @@ class Jail:
def putFailTicket(self, ticket): def putFailTicket(self, ticket):
self.__queue.put(ticket) self.__queue.put(ticket)
self.__db.addBan(self, ticket) if self.__db is not None:
self.__db.addBan(self, ticket)
def getFailTicket(self): def getFailTicket(self):
try: try:
@ -142,8 +143,9 @@ class Jail:
self.__filter.start() self.__filter.start()
self.__action.start() self.__action.start()
# Restore any previous valid bans from the database # Restore any previous valid bans from the database
for ticket in self.__db.getBans(self, self.__action.getBanTime()): if self.__db is not None:
self.__queue.put(ticket) for ticket in self.__db.getBans(self, self.__action.getBanTime()):
self.__queue.put(ticket)
logSys.info("Jail '%s' started" % self.__name) logSys.info("Jail '%s' started" % self.__name)
def stop(self): def stop(self):

View File

@ -50,13 +50,13 @@ class Jails:
# @param name The name of the jail # @param name The name of the jail
# @param backend The backend to use # @param backend The backend to use
def add(self, db, name, backend): def add(self, name, backend, db=None):
try: try:
self.__lock.acquire() self.__lock.acquire()
if self.__jails.has_key(name): if self.__jails.has_key(name):
raise DuplicateJailException(name) raise DuplicateJailException(name)
else: else:
self.__jails[name] = Jail(db, name, backend) self.__jails[name] = Jail(name, backend, db=None)
finally: finally:
self.__lock.release() self.__lock.release()

View File

@ -43,6 +43,7 @@ class Server:
self.__loggingLock = Lock() self.__loggingLock = Lock()
self.__lock = RLock() self.__lock = RLock()
self.__jails = Jails() self.__jails = Jails()
self.__db = None
self.__daemon = daemon self.__daemon = daemon
self.__transm = Transmitter(self) self.__transm = Transmitter(self)
self.__asyncServer = AsyncServer(self.__transm) self.__asyncServer = AsyncServer(self.__transm)
@ -51,9 +52,6 @@ class Server:
# Set logging level # Set logging level
self.setLogLevel(3) self.setLogLevel(3)
self.setLogTarget("STDOUT") self.setLogTarget("STDOUT")
# Create database, initially in memory
self.setDatabase(":memory:")
def __sigTERMhandler(self, signum, frame): def __sigTERMhandler(self, signum, frame):
logSys.debug("Caught signal %d. Exiting" % signum) logSys.debug("Caught signal %d. Exiting" % signum)
@ -121,12 +119,14 @@ class Server:
def addJail(self, name, backend): def addJail(self, name, backend):
self.__jails.add(self.__db, name, backend) self.__jails.add(name, backend, self.__db)
self.__db.addJail(self.__jails.get(name)) if self.__db is not None:
self.__db.addJail(self.__jails.get(name))
def delJail(self, name): def delJail(self, name):
self.__jails.remove(name) self.__jails.remove(name)
self.__db.delJailName(name) if self.__db is not None:
self.__db.delJailName(name)
def startJail(self, name): def startJail(self, name):
try: try:
@ -468,8 +468,11 @@ class Server:
def setDatabase(self, filename): def setDatabase(self, filename):
if self.__jails.size() == 0: if self.__jails.size() == 0:
self.__db = Fail2BanDb(filename) if filename.lower() == "none":
self.__db.delAllJails() self.__db = None
else:
self.__db = Fail2BanDb(filename)
self.__db.delAllJails()
else: else:
raise RuntimeError( raise RuntimeError(
"Cannot change database when there are jails present") "Cannot change database when there are jails present")

View File

@ -116,10 +116,18 @@ class Transmitter:
#Database #Database
elif name == "dbfile": elif name == "dbfile":
self.__server.setDatabase(command[1]) 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": elif name == "dbpurgeage":
self.__server.getDatabase().setPurgeAge(command[1]) db = self.__server.getDatabase()
return self.__server.getDatabase().getPurgeAge() if db is None:
return None
else:
db.setPurgeAge(command[1])
return db.getPurgeAge()
# Jail # Jail
elif command[1] == "idle": elif command[1] == "idle":
if command[2] == "on": if command[2] == "on":
@ -266,9 +274,17 @@ class Transmitter:
return self.__server.getLogTarget() return self.__server.getLogTarget()
#Database #Database
elif name == "dbfile": 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": elif name == "dbpurgeage":
return self.__server.getDatabase().getPurgeAge() db = self.__server.getDatabase()
if db is None:
return None
else:
return db.getPurgeAge()
# Filter # Filter
elif command[1] == "logpath": elif command[1] == "logpath":
return self.__server.getLogPath(name) return self.__server.getLogPath(name)

View File

@ -24,16 +24,12 @@ __license__ = "GPL"
from threading import Lock from threading import Lock
from fail2ban.server.database import Fail2BanDb
class DummyJail(object): class DummyJail(object):
"""A simple 'jail' to suck in all the tickets generated by Filter's """A simple 'jail' to suck in all the tickets generated by Filter's
""" """
def __init__(self): def __init__(self):
self.lock = Lock() self.lock = Lock()
self.queue = [] self.queue = []
self.__db = Fail2BanDb(":memory:")
self.__db.addJail(self)
def __len__(self): def __len__(self):
try: try:
@ -69,5 +65,5 @@ class DummyJail(object):
return "DummyJail #%s with %d tickets" % (id(self), len(self)) return "DummyJail #%s with %d tickets" % (id(self), len(self))
def getDatabase(self): def getDatabase(self):
return self.__db return None

View File

@ -40,7 +40,6 @@ from fail2ban.server.filter import FileFilter, DNSUtils
from fail2ban.server.failmanager import FailManager from fail2ban.server.failmanager import FailManager
from fail2ban.server.failmanager import FailManagerEmpty from fail2ban.server.failmanager import FailManagerEmpty
from fail2ban.server.mytime import MyTime from fail2ban.server.mytime import MyTime
from fail2ban.server.database import Fail2BanDb
from fail2ban.tests.utils import setUpMyTime, tearDownMyTime from fail2ban.tests.utils import setUpMyTime, tearDownMyTime
TEST_FILES_DIR = os.path.join(os.path.dirname(__file__), "files") 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 # tail written before, so let's not copy anything yet
#_copy_lines_between_files(GetFailures.FILENAME_01, self.name, n=100) #_copy_lines_between_files(GetFailures.FILENAME_01, self.name, n=100)
# we should detect the failures # 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 # now copy and get even more
_copy_lines_between_files(GetFailures.FILENAME_01, self.file, n=100) _copy_lines_between_files(GetFailures.FILENAME_01, self.file, n=100)
@ -920,5 +919,5 @@ class JailTests(unittest.TestCase):
def testSetBackend_gh83(self): def testSetBackend_gh83(self):
# smoke test # smoke test
# Must not fail to initiate # Must not fail to initiate
jail = Jail(Fail2BanDb(":memory:"), 'test', backend='polling') jail = Jail('test', backend='polling')

View File

@ -28,7 +28,6 @@ import unittest, socket, time, tempfile, os, locale, sys
from fail2ban.server.server import Server from fail2ban.server.server import Server
from fail2ban.server.jail import Jail from fail2ban.server.jail import Jail
from fail2ban.server.database import Fail2BanDb
from fail2ban.exceptions import UnknownJailException from fail2ban.exceptions import UnknownJailException
try: try:
from fail2ban.server import filtersystemd from fail2ban.server import filtersystemd
@ -175,9 +174,23 @@ class Transmitter(TransmitterBase):
self.setGetTestNOK("dbfile", tmpFilename) self.setGetTestNOK("dbfile", tmpFilename)
self.server.delJail(self.jailName) self.server.delJail(self.jailName)
self.setGetTest("dbfile", tmpFilename) self.setGetTest("dbfile", tmpFilename)
self.setGetTest("dbpurgeage", 600) self.setGetTest("dbpurgeage", "600", 600)
self.setGetTestNOK("dbpurgeage", "LIZARD") 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): def testAddJail(self):
jail2 = "TestJail2" jail2 = "TestJail2"
jail3 = "TestJail3" jail3 = "TestJail3"
@ -651,5 +664,5 @@ class JailTests(unittest.TestCase):
def testLongName(self): def testLongName(self):
# Just a smoke test for now # Just a smoke test for now
longname = "veryveryverylongname" longname = "veryveryverylongname"
jail = Jail(Fail2BanDb(":memory:"), longname) jail = Jail(longname)
self.assertEqual(jail.getName(), longname) self.assertEqual(jail.getName(), longname)