diff --git a/ChangeLog b/ChangeLog
index 1d3e3024..e93f554b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -71,10 +71,14 @@ ver. 0.10.4-dev-1 (20??/??/??) - development edition
- database: improve adapter/converter handlers working on invalid characters in sense of json and/or sqlite-database;
additionally both are exception-safe now, so avoid possible locking of database (closes gh-2137);
- logging in fail2ban is process-wide exception-safe now.
+* repaired start-time of initial seek to time (as well as other log-parsing related data),
+ if parameter `logpath` specified before `findtime`, `backend`, `datepattern`, etc (gh-2173)
+* systemd: fixed type error on option `journalflags`: an integer is required (gh-2125);
### New Features
### Enhancements
+* `filter.d/dovecot.conf`: extended with tags F-USER (and alternatives) to collect user-logins (gh-2168)
* since v.0.10.4, fail2ban-client, fail2ban-server and fail2ban-regex will return version without logo info,
additionally option `-V` can be used to get version in normalized machine-readable short format.
diff --git a/config/filter.d/dovecot.conf b/config/filter.d/dovecot.conf
index cfb38ea4..daea2ff3 100644
--- a/config/filter.d/dovecot.conf
+++ b/config/filter.d/dovecot.conf
@@ -12,8 +12,8 @@ _daemon = (?:dovecot(?:-auth)?|auth)
prefregex = ^%(__prefix_line)s(?:%(_auth_worker)s(?:\([^\)]+\))?: )?(?:%(__pam_auth)s(?:\(dovecot:auth\))?: |(?:pop3|imap)-login: )?(?:Info: )?.+$
-failregex = ^authentication failure; logname=\S* uid=\S* euid=\S* tty=dovecot ruser=\S* rhost=(?:\s+user=\S*)?\s*$
- ^(?:Aborted login|Disconnected)(?::(?: [^ \(]+)+)? \((?:auth failed, \d+ attempts(?: in \d+ secs)?|tried to use (?:disabled|disallowed) \S+ auth)\):(?: user=<[^>]*>,)?(?: method=\S+,)? rip=(?:[^>]*(?:, session=<\S+>)?)\s*$
+failregex = ^authentication failure; logname=\S* uid=\S* euid=\S* tty=dovecot ruser=\S* rhost=(?:\s+user=\S*)?\s*$
+ ^(?:Aborted login|Disconnected)(?::(?: [^ \(]+)+)? \((?:auth failed, \d+ attempts(?: in \d+ secs)?|tried to use (?:disabled|disallowed) \S+ auth)\):(?: user=<[^>]*>,)?(?: method=\S+,)? rip=(?:[^>]*(?:, session=<\S+>)?)\s*$
^pam\(\S+,(?:,\S*)?\): pam_authenticate\(\) failed: (?:User not known to the underlying authentication module: \d+ Time\(s\)|Authentication failure \(password mismatch\?\)|Permission denied)\s*$
^[a-z\-]{3,15}\(\S*,(?:,\S*)?\): (?:unknown user|invalid credentials|Password mismatch)\s*$
>
diff --git a/fail2ban/client/jailreader.py b/fail2ban/client/jailreader.py
index 42321d4e..56038bc9 100644
--- a/fail2ban/client/jailreader.py
+++ b/fail2ban/client/jailreader.py
@@ -90,9 +90,6 @@ class JailReader(ConfigReader):
opts1st = [["bool", "enabled", False],
["string", "filter", ""]]
opts = [["bool", "enabled", False],
- ["string", "logpath", None],
- ["string", "logtimezone", None],
- ["string", "logencoding", None],
["string", "backend", "auto"],
["int", "maxretry", None],
["string", "findtime", None],
@@ -112,6 +109,9 @@ class JailReader(ConfigReader):
["string", "ignoreip", None],
["string", "filter", ""],
["string", "datepattern", None],
+ ["string", "logtimezone", None],
+ ["string", "logencoding", None],
+ ["string", "logpath", None], # logpath after all log-related data (backend, date-pattern, etc)
["string", "action", ""]]
# Before interpolation (substitution) add static options always available as default:
diff --git a/fail2ban/server/banmanager.py b/fail2ban/server/banmanager.py
index 71623e3c..1340fb52 100644
--- a/fail2ban/server/banmanager.py
+++ b/fail2ban/server/banmanager.py
@@ -156,7 +156,7 @@ class BanManager:
# get cymru info:
try:
for ip in banIPs:
- # Reference: http://www.team-cymru.org/Services/ip-to-asn.html#dns
+ # Reference: https://www.team-cymru.com/IP-ASN-mapping.html#dns
question = ip.getPTR(
"origin.asn.cymru.com" if ip.isIPv4
else "origin6.asn.cymru.com"
diff --git a/fail2ban/server/filter.py b/fail2ban/server/filter.py
index d9c56852..7d037e24 100644
--- a/fail2ban/server/filter.py
+++ b/fail2ban/server/filter.py
@@ -891,9 +891,6 @@ class FileFilter(Filter):
self.__logs[path] = log
logSys.info("Added logfile: %r (pos = %s, hash = %s)" , path, log.getPos(), log.getHash())
if autoSeek:
- # if default, seek to "current time" - "find time":
- if isinstance(autoSeek, bool):
- autoSeek = MyTime.time() - self.getFindTime()
self.__autoSeek[path] = autoSeek
self._addLogPath(path) # backend specific
@@ -1003,18 +1000,21 @@ class FileFilter(Filter):
return False
# seek to find time for first usage only (prevent performance decline with polling of big files)
- if self.__autoSeek.get(filename):
- startTime = self.__autoSeek[filename]
- del self.__autoSeek[filename]
- # prevent completely read of big files first time (after start of service),
- # initial seek to start time using half-interval search algorithm:
- try:
- self.seekToTime(log, startTime)
- except Exception as e: # pragma: no cover
- logSys.error("Error during seek to start time in \"%s\"", filename)
- raise
- logSys.exception(e)
- return False
+ if self.__autoSeek:
+ startTime = self.__autoSeek.pop(filename, None)
+ if startTime:
+ # if default, seek to "current time" - "find time":
+ if isinstance(startTime, bool):
+ startTime = MyTime.time() - self.getFindTime()
+ # prevent completely read of big files first time (after start of service),
+ # initial seek to start time using half-interval search algorithm:
+ try:
+ self.seekToTime(log, startTime)
+ except Exception as e: # pragma: no cover
+ logSys.error("Error during seek to start time in \"%s\"", filename)
+ raise
+ logSys.exception(e)
+ return False
if has_content:
while not self.idle:
diff --git a/fail2ban/server/filtersystemd.py b/fail2ban/server/filtersystemd.py
index f83e7264..f87fdb4e 100644
--- a/fail2ban/server/filtersystemd.py
+++ b/fail2ban/server/filtersystemd.py
@@ -87,7 +87,7 @@ class FilterSystemd(JournalFilter): # pragma: systemd no cover
args['files'] = list(set(files))
try:
- args['flags'] = kwargs.pop('journalflags')
+ args['flags'] = int(kwargs.pop('journalflags'))
except KeyError:
pass
diff --git a/fail2ban/tests/servertestcase.py b/fail2ban/tests/servertestcase.py
index 0f8ec9e2..82b88b24 100644
--- a/fail2ban/tests/servertestcase.py
+++ b/fail2ban/tests/servertestcase.py
@@ -733,7 +733,7 @@ class Transmitter(TransmitterBase):
self.assertEqual(
self.transm.proceed(["status", "INVALID", "COMMAND"])[0],1)
- def testJournalMatch(self):
+ def testJournalMatch(self): # pragma: systemd no cover
if not filtersystemd: # pragma: no cover
raise unittest.SkipTest("systemd python interface not available")
jailName = "TestJail2"
@@ -803,6 +803,28 @@ class Transmitter(TransmitterBase):
result = self.transm.proceed(
["set", jailName, "deljournalmatch", value])
self.assertTrue(isinstance(result[1], ValueError))
+
+ def testJournalFlagsMatch(self): # pragma: systemd no cover
+ if not filtersystemd: # pragma: no cover
+ raise unittest.SkipTest("systemd python interface not available")
+ self.assertTrue(True)
+ jailName = "TestJail3"
+ self.server.addJail(jailName, "systemd[journalflags=2]")
+ values = [
+ "_SYSTEMD_UNIT=sshd.service",
+ "TEST_FIELD1=ABC",
+ "_HOSTNAME=example.com",
+ ]
+ for n, value in enumerate(values):
+ self.assertEqual(
+ self.transm.proceed(
+ ["set", jailName, "addjournalmatch", value]),
+ (0, [[val] for val in values[:n+1]]))
+ for n, value in enumerate(values):
+ self.assertEqual(
+ self.transm.proceed(
+ ["set", jailName, "deljournalmatch", value]),
+ (0, [[val] for val in values[n+1:]]))
class TransmitterLogging(TransmitterBase):