Merge branch '0.11'

pull/3216/head
sebres 2022-02-09 15:45:17 +01:00
commit 13520a0494
7 changed files with 51 additions and 30 deletions

View File

@ -11,7 +11,7 @@ before = common.conf
_daemon = (?:courier)?(?:imapd?|pop3d?)(?:login)?(?:-ssl)? _daemon = (?:courier)?(?:imapd?|pop3d?)(?:login)?(?:-ssl)?
failregex = ^%(__prefix_line)sLOGIN FAILED, (?:user|method)=.*, ip=\[<HOST>\]$ failregex = ^%(__prefix_line)sLOGIN FAILED, (?:(?!ip=)(?:user=<F-USER>[^,]*</F-USER>|\w+=[^,]*), )*ip=\[<HOST>\]
ignoreregex = ignoreregex =

View File

@ -8,7 +8,7 @@ before = common.conf
[Definition] [Definition]
_auth_worker = (?:dovecot: )?auth(?:-worker)? _auth_worker = (?:dovecot: )?auth(?:-worker)?
_auth_worker_info = (?:conn \w+:auth(?:-worker)? \(uid=\w+\): auth(?:-worker)?<\d+>: )? _auth_worker_info = (?:conn \w+:auth(?:-worker)? \([^\)]+\): auth(?:-worker)?<\d+>: )?
_daemon = (?:dovecot(?:-auth)?|auth) _daemon = (?:dovecot(?:-auth)?|auth)
prefregex = ^%(__prefix_line)s(?:%(_auth_worker)s(?:\([^\)]+\))?: )?(?:%(__pam_auth)s(?:\(dovecot:auth\))?: |(?:pop3|imap|managesieve|submission)-login: )?(?:Info: )?%(_auth_worker_info)s<F-CONTENT>.+</F-CONTENT>$ prefregex = ^%(__prefix_line)s(?:%(_auth_worker)s(?:\([^\)]+\))?: )?(?:%(__pam_auth)s(?:\(dovecot:auth\))?: |(?:pop3|imap|managesieve|submission)-login: )?(?:Info: )?%(_auth_worker_info)s<F-CONTENT>.+</F-CONTENT>$

View File

@ -547,9 +547,10 @@ class Actions(JailThread, Mapping):
if bTicket.banEpoch == self.banEpoch and diftm > 3: if bTicket.banEpoch == self.banEpoch and diftm > 3:
# avoid too often checks: # avoid too often checks:
if not rebanacts and MyTime.time() > self.__lastConsistencyCheckTM + 3: if not rebanacts and MyTime.time() > self.__lastConsistencyCheckTM + 3:
for action in self._actions.itervalues():
action.consistencyCheck()
self.__lastConsistencyCheckTM = MyTime.time() self.__lastConsistencyCheckTM = MyTime.time()
for action in self._actions.itervalues():
if hasattr(action, 'consistencyCheck'):
action.consistencyCheck()
# check epoch in order to reban it: # check epoch in order to reban it:
if bTicket.banEpoch < self.banEpoch: if bTicket.banEpoch < self.banEpoch:
if not rebanacts: rebanacts = dict( if not rebanacts: rebanacts = dict(

View File

@ -22,7 +22,6 @@ __author__ = "Steven Hiscocks"
__copyright__ = "Copyright (c) 2013 Steven Hiscocks" __copyright__ = "Copyright (c) 2013 Steven Hiscocks"
__license__ = "GPL" __license__ = "GPL"
import datetime
import os import os
import time import time
from distutils.version import LooseVersion from distutils.version import LooseVersion
@ -254,8 +253,8 @@ class FilterSystemd(JournalFilter): # pragma: systemd no cover
return ((logline[:0], date[0], logline.replace('\n', '\\n')), date[1]) return ((logline[:0], date[0], logline.replace('\n', '\\n')), date[1])
def seekToTime(self, date): def seekToTime(self, date):
if not isinstance(date, datetime.datetime): if isinstance(date, (int, long)):
date = datetime.datetime.fromtimestamp(date) date = float(date)
self.__journal.seek_realtime(date) self.__journal.seek_realtime(date)
def inOperationMode(self): def inOperationMode(self):
@ -281,6 +280,7 @@ class FilterSystemd(JournalFilter): # pragma: systemd no cover
try: try:
self.__journal.seek_tail() self.__journal.seek_tail()
logentry = self.__journal.get_previous() logentry = self.__journal.get_previous()
if logentry:
self.__journal.get_next() self.__journal.get_next()
except OSError: except OSError:
logentry = None # Reading failure, so safe to ignore logentry = None # Reading failure, so safe to ignore
@ -296,12 +296,6 @@ class FilterSystemd(JournalFilter): # pragma: systemd no cover
self.inOperation = False self.inOperation = False
# Save current time in order to check time to switch "in operation" mode # Save current time in order to check time to switch "in operation" mode
startTime = (1, MyTime.time(), logentry.get('__CURSOR')) startTime = (1, MyTime.time(), logentry.get('__CURSOR'))
# Move back one entry to ensure do not end up in dead space
# if start time beyond end of journal
try:
self.__journal.get_previous()
except OSError:
pass # Reading failure, so safe to ignore
else: else:
# empty journal or no entries for current filter: # empty journal or no entries for current filter:
self.inOperationMode() self.inOperationMode()
@ -311,6 +305,13 @@ class FilterSystemd(JournalFilter): # pragma: systemd no cover
# for possible future switches of in-operation mode: # for possible future switches of in-operation mode:
startTime = (0, startTime) startTime = (0, startTime)
# Move back one entry to ensure do not end up in dead space
# if start time beyond end of journal
try:
self.__journal.get_previous()
except OSError:
pass # Reading failure, so safe to ignore
line = None line = None
while self.active: while self.active:
# wait for records (or for timeout in sleeptime seconds): # wait for records (or for timeout in sleeptime seconds):

View File

@ -8,3 +8,5 @@ Nov 13 08:11:53 server imapd-ssl: LOGIN FAILED, user=user@domain.tld, ip=[::ffff
Apr 17 19:17:11 SERVER courierpop3login: LOGIN FAILED, user=USER@EXAMPLE.org, ip=[::ffff:1.2.3.4] Apr 17 19:17:11 SERVER courierpop3login: LOGIN FAILED, user=USER@EXAMPLE.org, ip=[::ffff:1.2.3.4]
# failJSON: { "time": "2005-04-17T19:17:12", "match": true , "host": "192.0.2.4" } # failJSON: { "time": "2005-04-17T19:17:12", "match": true , "host": "192.0.2.4" }
Apr 17 19:17:12 server imapd-ssl: LOGIN FAILED, method=PLAIN, ip=[::ffff:192.0.2.4] Apr 17 19:17:12 server imapd-ssl: LOGIN FAILED, method=PLAIN, ip=[::ffff:192.0.2.4]
# failJSON: { "time": "2005-04-27T09:00:00", "match": true , "user": "tester", "host": "192.0.2.5" }
Apr 27 09:00:00 servername imapd: LOGIN FAILED, user=tester, ip=[::ffff:192.0.2.5], port=[255]

View File

@ -60,6 +60,11 @@ Jun 12 11:48:12 auth-worker(80180): Info: conn unix:auth-worker (uid=143): auth-
# failJSON: { "time": "2005-06-12T23:06:05", "match": true , "host": "192.0.2.7" } # failJSON: { "time": "2005-06-12T23:06:05", "match": true , "host": "192.0.2.7" }
Jun 12 23:06:05 auth-worker(57065): Info: conn unix:auth-worker (uid=143): auth-worker<225622>: sql(user@domain.com,192.0.2.7,<Yx7+W8+Io>): Password mismatch Jun 12 23:06:05 auth-worker(57065): Info: conn unix:auth-worker (uid=143): auth-worker<225622>: sql(user@domain.com,192.0.2.7,<Yx7+W8+Io>): Password mismatch
# failJSON: { "time": "2005-06-15T11:28:21", "match": true , "host": "192.0.2.7" }
Jun 15 11:28:21 hostname dovecot: auth-worker(5787): conn unix:auth-worker (pid=27359,uid=97): auth-worker<55>: pam(webapps,192.0.2.7): unknown user
# failJSON: { "time": "2005-06-15T13:57:41", "match": true , "host": "192.0.2.7" }
Jun 15 13:57:41 hostname dovecot: auth-worker(3270): conn unix:auth-worker (pid=27359,uid=97): auth-worker<128>: pam(webapps,192.0.2.7): pam_authenticate() failed: Authentication failure (Password mismatch?)
# failJSON: { "time": "2005-01-29T14:38:51", "match": true , "host": "192.0.2.6", "desc": "PAM Permission denied (gh-1897)" } # failJSON: { "time": "2005-01-29T14:38:51", "match": true , "host": "192.0.2.6", "desc": "PAM Permission denied (gh-1897)" }
Jan 29 14:38:51 example.com dovecot[24941]: auth-worker(30165): pam(user@example.com,192.0.2.6,<PNHQq8pZhqIKAQGd>): pam_authenticate() failed: Permission denied Jan 29 14:38:51 example.com dovecot[24941]: auth-worker(30165): pam(user@example.com,192.0.2.6,<PNHQq8pZhqIKAQGd>): pam_authenticate() failed: Permission denied

View File

@ -1368,7 +1368,6 @@ def get_monitor_failures_journal_testcase(Filter_): # pragma: systemd no cover
def setUp(self): def setUp(self):
"""Call before every test case.""" """Call before every test case."""
super(MonitorJournalFailures, self).setUp() super(MonitorJournalFailures, self).setUp()
self._runtimeJournal = None
self.test_file = os.path.join(TEST_FILES_DIR, "testcase-journal.log") self.test_file = os.path.join(TEST_FILES_DIR, "testcase-journal.log")
self.jail = DummyJail() self.jail = DummyJail()
self.filter = None self.filter = None
@ -1406,7 +1405,7 @@ def get_monitor_failures_journal_testcase(Filter_): # pragma: systemd no cover
If not found, SkipTest exception will be raised. If not found, SkipTest exception will be raised.
""" """
# we can cache it: # we can cache it:
if self._runtimeJournal is None: if not hasattr(MonitorJournalFailures, "_runtimeJournal"):
# Depending on the system, it could be found under /run or /var/log (e.g. Debian) # Depending on the system, it could be found under /run or /var/log (e.g. Debian)
# which are pointed by different systemd-path variables. We will # which are pointed by different systemd-path variables. We will
# check one at at time until the first hit # check one at at time until the first hit
@ -1418,9 +1417,14 @@ def get_monitor_failures_journal_testcase(Filter_): # pragma: systemd no cover
self.assertTrue(tmp) self.assertTrue(tmp)
out = str(tmp[1].decode('utf-8')).split('\n')[0] out = str(tmp[1].decode('utf-8')).split('\n')[0]
if out: break if out: break
self._runtimeJournal = out # additional check appropriate default settings (if not root/sudoer and not already set):
if self._runtimeJournal: if os.geteuid() != 0 and os.getenv("F2B_SYSTEMD_DEFAULT_FLAGS", None) is None:
return self._runtimeJournal # filter default SYSTEM_ONLY(4) is hardly usable for not root/sudoer tester,
# so back to default LOCAL_ONLY(1):
os.environ["F2B_SYSTEMD_DEFAULT_FLAGS"] = "0"; # or "1", what will be similar to journalflags=0 or ...=1
MonitorJournalFailures._runtimeJournal = out
if MonitorJournalFailures._runtimeJournal:
return MonitorJournalFailures._runtimeJournal
raise unittest.SkipTest('systemd journal seems to be not available (e. g. no rights to read)') raise unittest.SkipTest('systemd journal seems to be not available (e. g. no rights to read)')
def testJournalFilesArg(self): def testJournalFilesArg(self):
@ -1526,7 +1530,7 @@ def get_monitor_failures_journal_testcase(Filter_): # pragma: systemd no cover
# stop: # stop:
self.filter.stop() self.filter.stop()
self.filter.join() self.filter.join()
MyTime.setTime(time.time() + 2) MyTime.setTime(time.time() + 10)
# update log manually (should cause a seek to end of log without wait for next second): # update log manually (should cause a seek to end of log without wait for next second):
self.jail.database.updateJournal(self.jail, 'systemd-journal', MyTime.time(), 'TEST') self.jail.database.updateJournal(self.jail, 'systemd-journal', MyTime.time(), 'TEST')
# check seek to last (simulated) position succeeds (without bans of previous copied tickets): # check seek to last (simulated) position succeeds (without bans of previous copied tickets):
@ -1534,7 +1538,7 @@ def get_monitor_failures_journal_testcase(Filter_): # pragma: systemd no cover
self._initFilter() self._initFilter()
self.filter.setMaxRetry(1) self.filter.setMaxRetry(1)
self.filter.start() self.filter.start()
self.waitForTicks(1) self.waitForTicks(2)
# check new IP but no old IPs found: # check new IP but no old IPs found:
_gen_falure("192.0.2.5") _gen_falure("192.0.2.5")
self.assertFalse(self.jail.getFailTicket()) self.assertFalse(self.jail.getFailTicket())
@ -1547,8 +1551,8 @@ def get_monitor_failures_journal_testcase(Filter_): # pragma: systemd no cover
self._initFilter() self._initFilter()
self.filter.setMaxRetry(1) self.filter.setMaxRetry(1)
self.filter.start() self.filter.start()
self.waitForTicks(1) self.waitForTicks(2)
MyTime.setTime(time.time() + 3) MyTime.setTime(time.time() + 20)
# check new IP but no old IPs found: # check new IP but no old IPs found:
_gen_falure("192.0.2.6") _gen_falure("192.0.2.6")
self.assertFalse(self.jail.getFailTicket()) self.assertFalse(self.jail.getFailTicket())
@ -1561,15 +1565,23 @@ def get_monitor_failures_journal_testcase(Filter_): # pragma: systemd no cover
self.filter.setMaxRetry(1) self.filter.setMaxRetry(1)
states = [] states = []
def _state(*args): def _state(*args):
try:
self.assertNotIn("** in operation", states) self.assertNotIn("** in operation", states)
self.assertFalse(self.filter.inOperation) self.assertFalse(self.filter.inOperation)
states.append("** process line: %r" % (args,)) states.append("** process line: %r" % (args,))
except Exception as e:
states.append("** failed: %r" % (e,))
raise
self.filter.processLineAndAdd = _state self.filter.processLineAndAdd = _state
def _inoper(): def _inoper():
try:
self.assertNotIn("** in operation", states) self.assertNotIn("** in operation", states)
self.assertEqual(len(states), 11) self.assertEqual(len(states), 11)
states.append("** in operation") states.append("** in operation")
self.filter.__class__.inOperationMode(self.filter) self.filter.__class__.inOperationMode(self.filter)
except Exception as e:
states.append("** failed: %r" % (e,))
raise
self.filter.inOperationMode = _inoper self.filter.inOperationMode = _inoper
self.filter.start() self.filter.start()
self.waitForTicks(12) self.waitForTicks(12)