diff --git a/fail2ban/server/filter.py b/fail2ban/server/filter.py index e327228e..16e7d2a6 100644 --- a/fail2ban/server/filter.py +++ b/fail2ban/server/filter.py @@ -662,13 +662,22 @@ class Filter(JailThread): delta = date - MyTime.time() if (date is None or abs(delta) > 60): latency = "This could indicate a latency problem." + synchronization = "This could be a clock synchronization" + \ + " problem; are you sure that NTP is set up?" timezone = "This looks like a timezone problem." + msg = "" + if -15*60 <= delta <= 0: + msg = latency + elif 0 < delta <= 15*60: + msg = synchronization + else: + msg = timezone # log time zone issue as warning once per day: self._logWarnOnce("_next_simByTimeWarn", ("Found a log entry with a timestamp %ss %s the current time. %s", abs(delta), "before" if delta <= 0 else "after", - latency if -15*60 <= delta <= 0 else timezone), + msg), ("Please check this jail and associated logs as there" + " is potentially a timezone or latency problem: %s", line)) diff --git a/fail2ban/tests/filtertestcase.py b/fail2ban/tests/filtertestcase.py index 9e81006b..6b0218f0 100644 --- a/fail2ban/tests/filtertestcase.py +++ b/fail2ban/tests/filtertestcase.py @@ -479,6 +479,36 @@ class IgnoreIP(LogCaptureTestCase): finally: tearDownMyTime() + def testWrongTimeDelta(self): + try: + self.filter.addFailRegex('fail from $') + self.filter.setDatePattern(r'{^LN-BEG}%Y-%m-%d %H:%M:%S(?:\s*%Z)?\s') + self.filter.setMaxRetry(5); # don't ban here + self.filter.inOperation = True; # real processing (all messages are new) + expectable_messages = ["timezone problem.", "clock synchronization problem;", "latency problem."] + cases = [(-90*60, 0), + (-10*60, 1), + (-30, None), + (30, None), + (10*60, 2), + (90*60, 0)] + for idx, (delta, expect) in enumerate(cases): + MyTime.setTime(1572138000+delta) + setattr(self.filter, "_next_simByTimeWarn", -1) + self.pruneLog('[phase {phase}] log entries offset by {delta}s'.format(phase=idx+1, delta=delta)) + for i in (1,2,3): + self.filter.processLineAndAdd('2019-10-27 02:00:00 fail from 192.0.2.15'); + for (idx, msg) in enumerate(expectable_messages): + if idx == expect: + self.assertLogged(expectable_messages[idx]) + else: + self.assertNotLogged(expectable_messages[idx]) + self.assertLogged( + "192.0.2.15:1", "192.0.2.15:2", "192.0.2.15:3", + "Total # of detected failures: 3.", wait=True) + finally: + tearDownMyTime() + def testAddAttempt(self): self.filter.setMaxRetry(3) for i in xrange(1, 1+3):