From 1971fd4bd3eda0bfe91ee688ee41906af93043fc Mon Sep 17 00:00:00 2001 From: sebres Date: Tue, 21 Mar 2017 00:30:40 +0100 Subject: [PATCH] don't remove MLFID from cache (can recognize multiple attempt within the same connection) --- fail2ban/client/jailreader.py | 4 ++-- fail2ban/server/filter.py | 26 +++++++++++--------------- fail2ban/tests/files/logs/sshd | 4 +++- fail2ban/tests/samplestestcase.py | 6 +++--- 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/fail2ban/client/jailreader.py b/fail2ban/client/jailreader.py index 2bef2c4f..bdb3564f 100644 --- a/fail2ban/client/jailreader.py +++ b/fail2ban/client/jailreader.py @@ -220,8 +220,8 @@ class JailReader(ConfigReader): if self.__filter: stream.extend(self.__filter.convert()) for opt, value in self.__opts.iteritems(): - if opt == "logpath" and \ - not self.__opts.get('backend', None).startswith("systemd"): + if opt == "logpath": + if self.__opts.get('backend', None).startswith("systemd"): continue found_files = 0 for path in value.split("\n"): path = path.rsplit(" ", 1) diff --git a/fail2ban/server/filter.py b/fail2ban/server/filter.py index cbdb4857..76787cc7 100644 --- a/fail2ban/server/filter.py +++ b/fail2ban/server/filter.py @@ -550,26 +550,22 @@ class Filter(JailThread): def _mergeFailure(self, mlfid, fail, failRegex): mlfidFail = self.mlfidCache.get(mlfid) if self.__mlfidCache else None + # if multi-line failure id (connection id) known: if mlfidFail: mlfidGroups = mlfidFail[1] - # if current line not failure, but previous was failure: - if fail.get('nofail') and not mlfidGroups.get('nofail'): - del fail['nofail'] # remove nofail flag - completed with fid (host, ip) - self.mlfidCache.unset(mlfid) # remove cache entry - # if current line is failure, but previous was not: - elif not fail.get('nofail') and mlfidGroups.get('nofail'): - del mlfidGroups['nofail'] # remove nofail flag - completed as failure - self.mlfidCache.unset(mlfid) # remove cache entry + # update - if not forget (disconnect/reset): + if not fail.get('mlfforget'): + mlfidGroups.update(fail) else: - # cache this line info (if not forget): - if not fail.get('mlfforget'): - mlfidFail = [self.__lastDate, fail] - self.mlfidCache.set(mlfid, mlfidFail) - else: - self.mlfidCache.unset(mlfid) # remove cache entry - return fail + self.mlfidCache.unset(mlfid) # remove cached entry + # merge with previous info: fail2 = mlfidGroups.copy() fail2.update(fail) + if not fail.get('nofail'): # be sure we've correct current state + try: + del fail2['nofail'] + except KeyError: + pass fail2["matches"] = fail.get("matches", []) + failRegex.getMatchedTupleLines() fail = fail2 elif not fail.get('mlfforget'): diff --git a/fail2ban/tests/files/logs/sshd b/fail2ban/tests/files/logs/sshd index 6f9a1468..f465c9a7 100644 --- a/fail2ban/tests/files/logs/sshd +++ b/fail2ban/tests/files/logs/sshd @@ -238,4 +238,6 @@ Nov 26 13:03:30 srv sshd[45]: fatal: Unable to negotiate with 192.0.2.2 port 554 # failJSON: { "match": false } Nov 26 15:03:30 host sshd[22440]: Connection from 192.0.2.3 port 39678 on 192.168.1.9 port 22 # failJSON: { "time": "2004-11-26T15:03:31", "match": true , "host": "192.0.2.3", "desc": "Multiline - no matching key exchange method" } -Nov 26 15:03:31 host sshd[22440]: fatal: Unable to negotiate a key exchange method [preauth] \ No newline at end of file +Nov 26 15:03:31 host sshd[22440]: fatal: Unable to negotiate a key exchange method [preauth] +# failJSON: { "time": "2004-11-26T15:03:32", "match": true , "host": "192.0.2.3", "desc": "Second attempt within the same connect" } +Nov 26 15:03:32 host sshd[22440]: fatal: Unable to negotiate a key exchange method [preauth] \ No newline at end of file diff --git a/fail2ban/tests/samplestestcase.py b/fail2ban/tests/samplestestcase.py index 00a8f305..5c45e729 100644 --- a/fail2ban/tests/samplestestcase.py +++ b/fail2ban/tests/samplestestcase.py @@ -200,13 +200,13 @@ def testSampleRegexsFactory(name, basedir): self.assertEqual(len(ret), 1, "Multiple regexs matched %r" % (map(lambda x: x[0], ret))) - # Fallback for backwards compatibility (previously no fid, was host only): - if faildata.get("host", None) is not None and fail.get("host", None) is None: - fail["host"] = fid # Verify match captures (at least fid/host) and timestamp as expected for k, v in faildata.iteritems(): if k not in ("time", "match", "desc"): fv = fail.get(k, None) + # Fallback for backwards compatibility (previously no fid, was host only): + if k == "host" and fv is None: + fv = fid self.assertEqual(fv, v) t = faildata.get("time", None)