From a1e9cc552c1f8c2afaed42954b58dc4afc12b70f Mon Sep 17 00:00:00 2001 From: sebres Date: Wed, 29 Mar 2017 23:05:52 +0200 Subject: [PATCH] bulk unban: introduced new command `actionflush`: executed in order to flush all bans at once (e. g. by unban all, reload with removing action, stop, shutdown the system); the actions having `actionflush` do not execute `actionunban` for each single ticket --- fail2ban/client/actionreader.py | 1 + fail2ban/server/action.py | 21 +++++++++++++++++++++ fail2ban/server/actions.py | 18 +++++++++++++++--- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/fail2ban/client/actionreader.py b/fail2ban/client/actionreader.py index b85f22a0..ace0b898 100644 --- a/fail2ban/client/actionreader.py +++ b/fail2ban/client/actionreader.py @@ -40,6 +40,7 @@ class ActionReader(DefinitionInitConfigReader): "actionstart": ["string", None], "actionstart_on_demand": ["string", None], "actionstop": ["string", None], + "actionflush": ["string", None], "actionreload": ["string", None], "actioncheck": ["string", None], "actionrepair": ["string", None], diff --git a/fail2ban/server/action.py b/fail2ban/server/action.py index 9d518cfd..d00458ba 100644 --- a/fail2ban/server/action.py +++ b/fail2ban/server/action.py @@ -281,6 +281,8 @@ class CommandAction(ActionBase): self.actioncheck = '' ## Command executed in order to restore sane environment in error case. self.actionrepair = '' + ## Command executed in order to flush all bans at once (e. g. by stop/shutdown the system). + self.actionflush = '' ## Command executed in order to stop the system. self.actionstop = '' ## Command executed in case of reloading action. @@ -447,6 +449,25 @@ class CommandAction(ActionBase): if not self._processCmd('', aInfo): raise RuntimeError("Error unbanning %(ip)s" % aInfo) + def flush(self): + """Executes the "actionflush" command. + + Command executed in order to flush all bans at once (e. g. by stop/shutdown + the system), instead of unbunning of each single ticket. + + Replaces the tags in the action command with actions properties + and executes the resulting command. + """ + family = [] + # cumulate started families, if started on demand (conditional): + if self._startOnDemand: + for f in CommandAction.COND_FAMILIES: + if self.__started.get(f) == 1: # only real started: + family.append(f) + # if no started (on demand) actions: + if not family: return True + return self._executeOperation('', 'flushing', family=family) + def stop(self): """Executes the "actionstop" command. diff --git a/fail2ban/server/actions.py b/fail2ban/server/actions.py index 3a85e569..e652872e 100644 --- a/fail2ban/server/actions.py +++ b/fail2ban/server/actions.py @@ -447,25 +447,37 @@ class Actions(JailThread, Mapping): If actions specified, don't flush list - just execute unban for given actions (reload, obsolete resp. removed actions). """ + log = True if actions is None: logSys.debug("Flush ban list") lst = self.__banManager.flushBanList() else: + log = False lst = iter(self.__banManager) cnt = 0 + # first we'll execute flush for actions supporting this operation: + unbactions = {} + for name, action in (actions if actions is not None else self._actions).iteritems(): + if hasattr(action, 'flush') and action.actionflush: + logSys.notice("[%s] Flush ticket(s) with %s", self._jail.name, name) + action.flush() + else: + unbactions[name] = action + actions = unbactions + # unban each ticket with non-flasheable actions: for ticket in lst: # delete ip from database also: if db and self._jail.database is not None: ip = str(ticket.getIP()) self._jail.database.delBan(self._jail, ip) # unban ip: - self.__unBan(ticket, actions=actions) + self.__unBan(ticket, actions=actions, log=log) cnt += 1 logSys.debug("Unbanned %s, %s ticket(s) in %r", cnt, self.__banManager.size(), self._jail.name) return cnt - def __unBan(self, ticket, actions=None): + def __unBan(self, ticket, actions=None, log=True): """Unbans host corresponding to the ticket. Executes the actions in order to unban the host given in the @@ -482,7 +494,7 @@ class Actions(JailThread, Mapping): unbactions = actions ip = ticket.getIP() aInfo = self.__getActionInfo(ticket) - if actions is None: + if log: logSys.notice("[%s] Unban %s", self._jail.name, aInfo["ip"]) for name, action in unbactions.iteritems(): try: