From 68f827e1f3945b735f214e15b224a926bb4bf170 Mon Sep 17 00:00:00 2001 From: sebres <serg.brester@sebres.de> Date: Fri, 13 Mar 2020 18:03:27 +0100 Subject: [PATCH 1/4] small optimization for manually (via client / protocol) signaled attempt (performBan only if maxretry gets reached) --- fail2ban/server/filter.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fail2ban/server/filter.py b/fail2ban/server/filter.py index c668e77d..e7f3e01d 100644 --- a/fail2ban/server/filter.py +++ b/fail2ban/server/filter.py @@ -457,10 +457,10 @@ class Filter(JailThread): logSys.info( "[%s] Attempt %s - %s", self.jailName, ip, datetime.datetime.fromtimestamp(unixTime).strftime("%Y-%m-%d %H:%M:%S") ) - self.failManager.addFailure(ticket, len(matches) or 1) - + attempts = self.failManager.addFailure(ticket, len(matches) or 1) # Perform the ban if this attempt is resulted to: - self.performBan(ip) + if attempts >= self.failManager.getMaxRetry(): + self.performBan(ip) return 1 From bc2b81133c2c5a460673ddab54fe30bcc2af9ecd Mon Sep 17 00:00:00 2001 From: sebres <serg.brester@sebres.de> Date: Fri, 13 Mar 2020 22:07:32 +0100 Subject: [PATCH 2/4] pyinotify backend: guarantees initial scanning of log-file by start (retarded via pending event if filter not yet active) --- ChangeLog | 1 + fail2ban/server/filterpyinotify.py | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 6a6b4451..3781f467 100644 --- a/ChangeLog +++ b/ChangeLog @@ -34,6 +34,7 @@ ver. 0.10.6-dev (20??/??/??) - development edition ### Fixes * [stability] prevent race condition - no ban if filter (backend) is continuously busy if too many messages will be found in log, e. g. initial scan of large log-file or journal (gh-2660) +* pyinotify-backend sporadically avoided initial scanning of log-file by start * python 3.9 compatibility (and Travis CI support) * restoring a large number (500+ depending on files ulimit) of current bans when using PyPy fixed * manual ban is written to database, so can be restored by restart (gh-2647) diff --git a/fail2ban/server/filterpyinotify.py b/fail2ban/server/filterpyinotify.py index 185305ca..6d0172da 100644 --- a/fail2ban/server/filterpyinotify.py +++ b/fail2ban/server/filterpyinotify.py @@ -271,7 +271,13 @@ class FilterPyinotify(FileFilter): def _addLogPath(self, path): self._addFileWatcher(path) - self._process_file(path) + # initial scan: + if self.active: + # we can execute it right now: + self._process_file(path) + else: + # retard until filter gets started: + self._addPending(path, ('INITIAL', path)) ## # Delete a log path From b43dc147b5177019a1dcb51de9833139e904cc21 Mon Sep 17 00:00:00 2001 From: sebres <serg.brester@sebres.de> Date: Fri, 13 Mar 2020 22:20:01 +0100 Subject: [PATCH 3/4] amend to RC-fix 9f1c6f1617018a0e00fc8bf7bfd62db2c17fa11a (gh-2660): resolves bottleneck by initial scanning of a lot of messages (or evildoers generating many messages) causes repeated ban, that will be ignored but could cause entering of "long" sleep in actions thread previously; speedup recognition banning queue has entries to begin check-ban process in actions thread --- fail2ban/server/actions.py | 10 ++++++---- fail2ban/server/jail.py | 6 ++++++ fail2ban/tests/dummyjail.py | 4 ++++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/fail2ban/server/actions.py b/fail2ban/server/actions.py index d1f46ac0..902d7aa6 100644 --- a/fail2ban/server/actions.py +++ b/fail2ban/server/actions.py @@ -318,8 +318,10 @@ class Actions(JailThread, Mapping): logSys.debug("Actions: leave idle mode") continue # wait for ban (stop if gets inactive): - bancnt = Utils.wait_for(lambda: not self.active or self.__checkBan(), self.sleeptime) - cnt += bancnt + bancnt = 0 + if Utils.wait_for(lambda: not self.active or self._jail.hasFailTickets, self.sleeptime): + bancnt = self.__checkBan() + cnt += bancnt # unban if nothing is banned not later than banned tickets >= banPrecedence if not bancnt or cnt >= self.banPrecedence: if self.active: @@ -495,8 +497,8 @@ class Actions(JailThread, Mapping): cnt += self.__reBan(bTicket, actions=rebanacts) else: # pragma: no cover - unexpected: ticket is not banned for some reasons - reban using all actions: cnt += self.__reBan(bTicket) - # add ban to database: - if not bTicket.restored and self._jail.database is not None: + # add ban to database (and ignore too old tickets, replace it with inOperation later): + if not bTicket.restored and self._jail.database is not None and bTicket.getTime() >= MyTime.time() - 60: self._jail.database.addBan(self._jail, bTicket) if cnt: logSys.debug("Banned %s / %s, %s ticket(s) in %r", cnt, diff --git a/fail2ban/server/jail.py b/fail2ban/server/jail.py index b8a1c1f4..048aded9 100644 --- a/fail2ban/server/jail.py +++ b/fail2ban/server/jail.py @@ -191,6 +191,12 @@ class Jail(object): ("Actions", self.actions.status(flavor=flavor)), ] + @property + def hasFailTickets(self): + """Retrieve whether queue has tickets to ban. + """ + return not self.__queue.empty() + def putFailTicket(self, ticket): """Add a fail ticket to the jail. diff --git a/fail2ban/tests/dummyjail.py b/fail2ban/tests/dummyjail.py index 9e9aaeed..eaa4a564 100644 --- a/fail2ban/tests/dummyjail.py +++ b/fail2ban/tests/dummyjail.py @@ -49,6 +49,10 @@ class DummyJail(Jail): with self.lock: return bool(self.queue) + @property + def hasFailTickets(self): + return bool(self.queue) + def putFailTicket(self, ticket): with self.lock: self.queue.append(ticket) From b64a435b0eac0a1298235e320f1450382a2ac9fe Mon Sep 17 00:00:00 2001 From: sebres <serg.brester@sebres.de> Date: Fri, 13 Mar 2020 22:34:15 +0100 Subject: [PATCH 4/4] ignore only not banned old (repeated and ignored) tickets --- fail2ban/server/actions.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/fail2ban/server/actions.py b/fail2ban/server/actions.py index 902d7aa6..35b027ae 100644 --- a/fail2ban/server/actions.py +++ b/fail2ban/server/actions.py @@ -497,9 +497,12 @@ class Actions(JailThread, Mapping): cnt += self.__reBan(bTicket, actions=rebanacts) else: # pragma: no cover - unexpected: ticket is not banned for some reasons - reban using all actions: cnt += self.__reBan(bTicket) - # add ban to database (and ignore too old tickets, replace it with inOperation later): - if not bTicket.restored and self._jail.database is not None and bTicket.getTime() >= MyTime.time() - 60: - self._jail.database.addBan(self._jail, bTicket) + # add ban to database: + if not bTicket.restored and self._jail.database is not None: + # ignore too old (repeated and ignored) tickets, + # [todo] replace it with inOperation later (once it gets back-ported): + if not reason and bTicket.getTime() >= MyTime.time() - 60: + self._jail.database.addBan(self._jail, bTicket) if cnt: logSys.debug("Banned %s / %s, %s ticket(s) in %r", cnt, self.__banManager.getBanTotal(), self.__banManager.size(), self._jail.name)