summaryrefslogtreecommitdiff
path: root/fail2ban/server/database.py
diff options
context:
space:
mode:
Diffstat (limited to 'fail2ban/server/database.py')
-rw-r--r--fail2ban/server/database.py129
1 files changed, 69 insertions, 60 deletions
diff --git a/fail2ban/server/database.py b/fail2ban/server/database.py
index ed736a7a..59eeb8fd 100644
--- a/fail2ban/server/database.py
+++ b/fail2ban/server/database.py
@@ -104,7 +104,11 @@ def commitandrollback(f):
def wrapper(self, *args, **kwargs):
with self._lock: # Threading lock
with self._db: # Auto commit and rollback on exception
- return f(self, self._db.cursor(), *args, **kwargs)
+ cur = self._db.cursor()
+ try:
+ return f(self, cur, *args, **kwargs)
+ finally:
+ cur.close()
return wrapper
@@ -253,7 +257,7 @@ class Fail2BanDb(object):
self.repairDB()
else:
version = cur.fetchone()[0]
- if version < Fail2BanDb.__version__:
+ if version != Fail2BanDb.__version__:
newversion = self.updateDb(version)
if newversion == Fail2BanDb.__version__:
logSys.warning( "Database updated from '%r' to '%r'",
@@ -301,9 +305,11 @@ class Fail2BanDb(object):
try:
# backup
logSys.info("Trying to repair database %s", self._dbFilename)
- shutil.move(self._dbFilename, self._dbBackupFilename)
- logSys.info(" Database backup created: %s", self._dbBackupFilename)
-
+ if not os.path.isfile(self._dbBackupFilename):
+ shutil.move(self._dbFilename, self._dbBackupFilename)
+ logSys.info(" Database backup created: %s", self._dbBackupFilename)
+ elif os.path.isfile(self._dbFilename):
+ os.remove(self._dbFilename)
# first try to repair using dump/restore in order
Utils.executeCmd((r"""f2b_db=$0; f2b_dbbk=$1; sqlite3 "$f2b_dbbk" ".dump" | sqlite3 "$f2b_db" """,
self._dbFilename, self._dbBackupFilename))
@@ -415,7 +421,7 @@ class Fail2BanDb(object):
logSys.error("Failed to upgrade database '%s': %s",
self._dbFilename, e.args[0],
exc_info=logSys.getEffectiveLevel() <= 10)
- raise
+ self.repairDB()
@commitandrollback
def addJail(self, cur, jail):
@@ -789,7 +795,6 @@ class Fail2BanDb(object):
queryArgs.append(fromtime)
if overalljails or jail is None:
query += " GROUP BY ip ORDER BY timeofban DESC LIMIT 1"
- cur = self._db.cursor()
# repack iterator as long as in lock:
return list(cur.execute(query, queryArgs))
@@ -812,11 +817,9 @@ class Fail2BanDb(object):
query += " GROUP BY ip ORDER BY ip, timeofban DESC"
else:
query += " ORDER BY timeofban DESC LIMIT 1"
- cur = self._db.cursor()
return cur.execute(query, queryArgs)
- @commitandrollback
- def getCurrentBans(self, cur, jail=None, ip=None, forbantime=None, fromtime=None,
+ def getCurrentBans(self, jail=None, ip=None, forbantime=None, fromtime=None,
correctBanTime=True, maxmatches=None
):
"""Reads tickets (with merged info) currently affected from ban from the database.
@@ -828,57 +831,63 @@ class Fail2BanDb(object):
(and therefore endOfBan) of the ticket (normally it is ban-time of jail as maximum)
for all tickets with ban-time greater (or persistent).
"""
- if fromtime is None:
- fromtime = MyTime.time()
- tickets = []
- ticket = None
- if correctBanTime is True:
- correctBanTime = jail.getMaxBanTime() if jail is not None else None
- # don't change if persistent allowed:
- if correctBanTime == -1: correctBanTime = None
-
- for ticket in self._getCurrentBans(cur, jail=jail, ip=ip,
- forbantime=forbantime, fromtime=fromtime
- ):
- # can produce unpack error (database may return sporadical wrong-empty row):
- try:
- banip, timeofban, bantime, bancount, data = ticket
- # additionally check for empty values:
- if banip is None or banip == "": # pragma: no cover
- raise ValueError('unexpected value %r' % (banip,))
- # if bantime unknown (after upgrade-db from earlier version), just use min known ban-time:
- if bantime == -2: # todo: remove it in future version
- bantime = jail.actions.getBanTime() if jail is not None else (
- correctBanTime if correctBanTime else 600)
- elif correctBanTime and correctBanTime >= 0:
- # if persistent ban (or greater as max), use current max-bantime of the jail:
- if bantime == -1 or bantime > correctBanTime:
- bantime = correctBanTime
- # after correction check the end of ban again:
- if bantime != -1 and timeofban + bantime <= fromtime:
- # not persistent and too old - ignore it:
- logSys.debug("ignore ticket (with new max ban-time %r): too old %r <= %r, ticket: %r",
- bantime, timeofban + bantime, fromtime, ticket)
+ cur = self._db.cursor()
+ try:
+ if fromtime is None:
+ fromtime = MyTime.time()
+ tickets = []
+ ticket = None
+ if correctBanTime is True:
+ correctBanTime = jail.getMaxBanTime() if jail is not None else None
+ # don't change if persistent allowed:
+ if correctBanTime == -1: correctBanTime = None
+
+ with self._lock:
+ bans = self._getCurrentBans(cur, jail=jail, ip=ip,
+ forbantime=forbantime, fromtime=fromtime
+ )
+ for ticket in bans:
+ # can produce unpack error (database may return sporadical wrong-empty row):
+ try:
+ banip, timeofban, bantime, bancount, data = ticket
+ # additionally check for empty values:
+ if banip is None or banip == "": # pragma: no cover
+ raise ValueError('unexpected value %r' % (banip,))
+ # if bantime unknown (after upgrade-db from earlier version), just use min known ban-time:
+ if bantime == -2: # todo: remove it in future version
+ bantime = jail.actions.getBanTime() if jail is not None else (
+ correctBanTime if correctBanTime else 600)
+ elif correctBanTime and correctBanTime >= 0:
+ # if persistent ban (or greater as max), use current max-bantime of the jail:
+ if bantime == -1 or bantime > correctBanTime:
+ bantime = correctBanTime
+ # after correction check the end of ban again:
+ if bantime != -1 and timeofban + bantime <= fromtime:
+ # not persistent and too old - ignore it:
+ logSys.debug("ignore ticket (with new max ban-time %r): too old %r <= %r, ticket: %r",
+ bantime, timeofban + bantime, fromtime, ticket)
+ continue
+ except ValueError as e: # pragma: no cover
+ logSys.debug("get current bans: ignore row %r - %s", ticket, e)
continue
- except ValueError as e: # pragma: no cover
- logSys.debug("get current bans: ignore row %r - %s", ticket, e)
- continue
- # logSys.debug('restore ticket %r, %r, %r', banip, timeofban, data)
- ticket = FailTicket(banip, timeofban, data=data)
- # filter matches if expected (current count > as maxmatches specified):
- if maxmatches is None:
- maxmatches = self.maxMatches
- if maxmatches:
- matches = ticket.getMatches()
- if matches and len(matches) > maxmatches:
- ticket.setMatches(matches[-maxmatches:])
- else:
- ticket.setMatches(None)
- # logSys.debug('restored ticket: %r', ticket)
- ticket.setBanTime(bantime)
- ticket.setBanCount(bancount)
- if ip is not None: return ticket
- tickets.append(ticket)
+ # logSys.debug('restore ticket %r, %r, %r', banip, timeofban, data)
+ ticket = FailTicket(banip, timeofban, data=data)
+ # filter matches if expected (current count > as maxmatches specified):
+ if maxmatches is None:
+ maxmatches = self.maxMatches
+ if maxmatches:
+ matches = ticket.getMatches()
+ if matches and len(matches) > maxmatches:
+ ticket.setMatches(matches[-maxmatches:])
+ else:
+ ticket.setMatches(None)
+ # logSys.debug('restored ticket: %r', ticket)
+ ticket.setBanTime(bantime)
+ ticket.setBanCount(bancount)
+ if ip is not None: return ticket
+ tickets.append(ticket)
+ finally:
+ cur.close()
return tickets