fix sporadic time related errors after fail2ban restart resp. reload jail:

- ValueError: need more than N values to unpack
- empty banip from restoreCurrentBans, etc.
pull/1460/head
sebres 2017-01-23 11:45:29 +01:00
parent 0636b3247d
commit 8c54675750
4 changed files with 54 additions and 23 deletions

View File

@ -80,7 +80,10 @@ class RequestHandler(asynchat.async_chat):
# Deserialize # Deserialize
message = loads(message) message = loads(message)
# Gives the message to the transmitter. # Gives the message to the transmitter.
message = self.__transmitter.proceed(message) if self.__transmitter:
message = self.__transmitter.proceed(message)
else:
message = ['SHUTDOWN']
# Serializes the response. # Serializes the response.
message = dumps(message, HIGHEST_PROTOCOL) message = dumps(message, HIGHEST_PROTOCOL)
# Sends the response to the client. # Sends the response to the client.
@ -228,6 +231,13 @@ class AsyncServer(asyncore.dispatcher):
## ##
# Stops the communication server. # Stops the communication server.
def stop_communication(self):
logSys.debug("Stop communication")
self.__transmitter = None
##
# Stops the server.
def stop(self): def stop(self):
self.close() self.close()

View File

@ -643,7 +643,6 @@ class Fail2BanDb(object):
cur = self._db.cursor() cur = self._db.cursor()
return cur.execute(query, queryArgs) return cur.execute(query, queryArgs)
@commitandrollback
def _getCurrentBans(self, cur, jail = None, ip = None, forbantime=None, fromtime=None): def _getCurrentBans(self, cur, jail = None, ip = None, forbantime=None, fromtime=None):
if fromtime is None: if fromtime is None:
fromtime = MyTime.time() fromtime = MyTime.time()
@ -666,20 +665,29 @@ class Fail2BanDb(object):
cur = self._db.cursor() cur = self._db.cursor()
return cur.execute(query, queryArgs) return cur.execute(query, queryArgs)
def getCurrentBans(self, jail = None, ip = None, forbantime=None, fromtime=None): @commitandrollback
def getCurrentBans(self, cur, jail = None, ip = None, forbantime=None, fromtime=None):
tickets = [] tickets = []
ticket = None ticket = None
results = list(self._getCurrentBans(jail=jail, ip=ip, forbantime=forbantime, fromtime=fromtime)) for ticket in self._getCurrentBans(cur, jail=jail, ip=ip,
forbantime=forbantime, fromtime=fromtime
if results: ):
for banip, timeofban, bantime, bancount, data in results: # can produce unpack error (database may return sporadical wrong-empty row):
# logSys.debug('restore ticket %r, %r, %r', banip, timeofban, data) try:
ticket = FailTicket(banip, timeofban, data=data) banip, timeofban, bantime, bancount, data = ticket
# logSys.debug('restored ticket: %r', ticket) # additionally check for empty values:
ticket.setBanTime(bantime) if banip is None or banip == "": # pragma: no cover
ticket.setBanCount(bancount) raise ValueError('unexpected value %r' % (banip,))
tickets.append(ticket) 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)
# logSys.debug('restored ticket: %r', ticket)
ticket.setBanTime(bantime)
ticket.setBanCount(bancount)
tickets.append(ticket)
return tickets if ip is None else ticket return tickets if ip is None else ticket

View File

@ -274,8 +274,9 @@ class Jail(object):
if not self.getBanTimeExtra('increment'): if not self.getBanTimeExtra('increment'):
forbantime = self.actions.getBanTime() forbantime = self.actions.getBanTime()
for ticket in self.database.getCurrentBans(jail=self, forbantime=forbantime): for ticket in self.database.getCurrentBans(jail=self, forbantime=forbantime):
#logSys.debug('restored ticket: %s', ticket) try:
if not self.filter.inIgnoreIPList(ticket.getIP(), log_ignore=True): #logSys.debug('restored ticket: %s', ticket)
if self.filter.inIgnoreIPList(ticket.getIP(), log_ignore=True): continue
# mark ticked was restored from database - does not put it again into db: # mark ticked was restored from database - does not put it again into db:
ticket.restored = True ticket.restored = True
# correct start time / ban time (by the same end of ban): # correct start time / ban time (by the same end of ban):
@ -287,8 +288,12 @@ class Jail(object):
if btm != -1 and btm <= 0: if btm != -1 and btm <= 0:
continue continue
self.putFailTicket(ticket) self.putFailTicket(ticket)
except Exception as e: # pragma: no cover
logSys.error('Restore ticket failed: %s', e,
exc_info=logSys.getEffectiveLevel()<=logging.DEBUG)
except Exception as e: # pragma: no cover except Exception as e: # pragma: no cover
logSys.error('%s', e, exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) logSys.error('Restore bans failed: %s', e,
exc_info=logSys.getEffectiveLevel()<=logging.DEBUG)
def start(self): def start(self):
"""Start the jail, by starting filter and actions threads. """Start the jail, by starting filter and actions threads.

View File

@ -159,17 +159,16 @@ class Server:
self.__asyncServer.start(sock, force) self.__asyncServer.start(sock, force)
except AsyncServerException as e: except AsyncServerException as e:
logSys.error("Could not start server: %s", e) logSys.error("Could not start server: %s", e)
# Removes the PID file. # Removes the PID file.
try: try:
logSys.debug("Remove PID file %s", pidfile) logSys.debug("Remove PID file %s", pidfile)
os.remove(pidfile) os.remove(pidfile)
except (OSError, IOError) as e: # pragma: no cover except (OSError, IOError) as e: # pragma: no cover
logSys.error("Unable to remove PID file: %s", e) logSys.error("Unable to remove PID file: %s", e)
# Stop observer and exit
if Observers.Main is not None: # Stop (if not yet already executed):
Observers.Main.stop() self.quit()
Observers.Main = None
logSys.info("Exiting Fail2ban")
def quit(self): def quit(self):
# Give observer a small chance to complete its work before exit # Give observer a small chance to complete its work before exit
@ -183,8 +182,7 @@ class Server:
# are exiting) # are exiting)
# See https://github.com/fail2ban/fail2ban/issues/7 # See https://github.com/fail2ban/fail2ban/issues/7
if self.__asyncServer is not None: if self.__asyncServer is not None:
self.__asyncServer.stop() self.__asyncServer.stop_communication()
self.__asyncServer = None
# Now stop all the jails # Now stop all the jails
self.stopAllJail() self.stopAllJail()
@ -205,6 +203,16 @@ class Server:
for s, sh in self.__prev_signals.iteritems(): for s, sh in self.__prev_signals.iteritems():
signal.signal(s, sh) signal.signal(s, sh)
# Stop observer and exit
if Observers.Main is not None:
Observers.Main.stop()
Observers.Main = None
# Stop async
if self.__asyncServer is not None:
self.__asyncServer.stop()
self.__asyncServer = None
logSys.info("Exiting Fail2ban")
# Prevent to call quit twice: # Prevent to call quit twice:
self.quit = lambda: False self.quit = lambda: False