From 18ba714f97ed6b7ac49c15290683fb4b8eafea5f Mon Sep 17 00:00:00 2001 From: CPbN <40244829+CPbN@users.noreply.github.com> Date: Wed, 23 Oct 2019 09:14:26 +0200 Subject: [PATCH 01/10] Add Centreon jail --- config/filter.d/centreon.conf | 9 +++++++++ config/jail.conf | 3 +++ fail2ban/tests/files/logs/centreon | 4 ++++ 3 files changed, 16 insertions(+) create mode 100644 config/filter.d/centreon.conf create mode 100644 fail2ban/tests/files/logs/centreon diff --git a/config/filter.d/centreon.conf b/config/filter.d/centreon.conf new file mode 100644 index 00000000..68460363 --- /dev/null +++ b/config/filter.d/centreon.conf @@ -0,0 +1,9 @@ +# Fail2Ban filter for Centreon Web +# Detecting unauthorized access to the Centreon Web portal +# typically logged in /var/log/centreon/login.log + +[Init] +datepattern = ^%%Y-%%m-%%d %%H:%%M:%%S + +[Definition] +failregex = ^\[0-9-]*\|[0-9-]*\|\[0-9-]*|\[[^]]*\] \[\] Authentication failed for '.+' : diff --git a/config/jail.conf b/config/jail.conf index dcb9a7fc..71535411 100644 --- a/config/jail.conf +++ b/config/jail.conf @@ -820,6 +820,9 @@ udpport = 1200,27000,27001,27002,27003,27004,27005,27006,27007,27008,27009,27010 action = %(banaction)s[name=%(__name__)s-tcp, port="%(tcpport)s", protocol="tcp", chain="%(chain)s", actname=%(banaction)s-tcp] %(banaction)s[name=%(__name__)s-udp, port="%(udpport)s", protocol="udp", chain="%(chain)s", actname=%(banaction)s-udp] +[centreon] +logpath = /var/log/centreon/login.log + # consider low maxretry and a long bantime # nobody except your own Nagios server should ever probe nrpe [nagios] diff --git a/fail2ban/tests/files/logs/centreon b/fail2ban/tests/files/logs/centreon new file mode 100644 index 00000000..fc6fe4fe --- /dev/null +++ b/fail2ban/tests/files/logs/centreon @@ -0,0 +1,4 @@ +# Access of unauthorized host in /var/log/centreon/login.log +# failJSON: { "time": "2019-10-21T18:55:15", "match": true , "host": "50.97.225.132" } +2019-10-21 18:55:15|-1|0|0|[WEB] [50.97.225.132] Authentication failed for 'admin' : password mismatch + From 9e699646f8b901973ff5c6725deca4a7d9bd97e7 Mon Sep 17 00:00:00 2001 From: CPbN <40244829+CPbN@users.noreply.github.com> Date: Thu, 24 Oct 2019 14:37:18 +0200 Subject: [PATCH 02/10] Add Centreon jail --- config/filter.d/centreon.conf | 2 +- config/jail.conf | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/config/filter.d/centreon.conf b/config/filter.d/centreon.conf index 68460363..fd3c8482 100644 --- a/config/filter.d/centreon.conf +++ b/config/filter.d/centreon.conf @@ -6,4 +6,4 @@ datepattern = ^%%Y-%%m-%%d %%H:%%M:%%S [Definition] -failregex = ^\[0-9-]*\|[0-9-]*\|\[0-9-]*|\[[^]]*\] \[\] Authentication failed for '.+' : +failregex = ^(?:\|-?\d+){3}\|\[[^\]]*\] \[\] Authentication failed for '[^']+' diff --git a/config/jail.conf b/config/jail.conf index 71535411..8027f4d0 100644 --- a/config/jail.conf +++ b/config/jail.conf @@ -821,6 +821,7 @@ action = %(banaction)s[name=%(__name__)s-tcp, port="%(tcpport)s", protocol="tcp %(banaction)s[name=%(__name__)s-udp, port="%(udpport)s", protocol="udp", chain="%(chain)s", actname=%(banaction)s-udp] [centreon] +port = http,https logpath = /var/log/centreon/login.log # consider low maxretry and a long bantime From d44607a161854cbed9f15041b36c60cf67ad8cbb Mon Sep 17 00:00:00 2001 From: sebres Date: Fri, 1 Nov 2019 16:29:17 +0100 Subject: [PATCH 03/10] part of #927 - filter enhancement to parse IP sub-nets (IP/CIDR with correct recognition of IP-family), provides new replacement tags for failregex to match subnets in form of IP-addresses with CIDR mask (gh-2559): - `` - helper regex to match CIDR (simple integer form of net-mask); - `` - regex to match sub-net adresses (in form of IP/CIDR, also single IP is matched, so part /CIDR is optional); --- ChangeLog | 3 +++ fail2ban/server/failregex.py | 22 ++++++++++++++++--- fail2ban/server/filter.py | 4 ++-- fail2ban/tests/fail2banregextestcase.py | 13 ++++++++++++ fail2ban/tests/servertestcase.py | 28 +++++++++++++++++++++++++ man/jail.conf.5 | 22 ++++++++++++++++--- 6 files changed, 84 insertions(+), 8 deletions(-) diff --git a/ChangeLog b/ChangeLog index 67182248..95727168 100644 --- a/ChangeLog +++ b/ChangeLog @@ -79,6 +79,9 @@ ver. 0.10.5-dev-1 (20??/??/??) - development edition - `prefregex` extended, more selective now (denied/NOTAUTH suffix moved from failregex, so no catch-all there anymore) ### New Features +* new replacement tags for failregex to match subnets in form of IP-addresses with CIDR mask (gh-2559): + - `` - helper regex to match CIDR (simple integer form of net-mask); + - `` - regex to match sub-net adresses (in form of IP/CIDR, also single IP is matched, so part /CIDR is optional); * new failregex-flag tag `` for failregex, signaled that the access to service was gained (ATM used similar to tag ``, but it does not add the log-line to matches, gh-2279) * filters: introduced new configuration parameter `logtype` (default `file` for file-backends, and diff --git a/fail2ban/server/failregex.py b/fail2ban/server/failregex.py index 672bc32a..bb8a2b29 100644 --- a/fail2ban/server/failregex.py +++ b/fail2ban/server/failregex.py @@ -44,7 +44,11 @@ R_HOST = [ # place-holder for ADDR tag-replacement (joined): "", # place-holder for HOST tag replacement (joined): - "" + "", + # CIDR in simplest integer form: + r"(?P\d+)", + # place-holder for SUBNET tag-replacement + "", ] RI_IPV4 = 0 RI_IPV6 = 1 @@ -52,10 +56,13 @@ RI_IPV6BR = 2 RI_DNS = 3 RI_ADDR = 4 RI_HOST = 5 +RI_CIDR = 6 +RI_SUBNET = 7 R_HOST[RI_IPV6BR] = r"""\[?%s\]?""" % (R_HOST[RI_IPV6],) -R_HOST[RI_ADDR] = "(?:%s)" % ("|".join((R_HOST[RI_IPV4], R_HOST[RI_IPV6BR])),) -R_HOST[RI_HOST] = "(?:%s)" % ("|".join((R_HOST[RI_IPV4], R_HOST[RI_IPV6BR], R_HOST[RI_DNS])),) +R_HOST[RI_ADDR] = "(?:%s|%s)" % (R_HOST[RI_IPV4], R_HOST[RI_IPV6BR],) +R_HOST[RI_HOST] = "(?:%s|%s|%s)" % (R_HOST[RI_IPV4], R_HOST[RI_IPV6BR], R_HOST[RI_DNS],) +R_HOST[RI_SUBNET] = r"""(?:%s|%s)(?:/%s)?""" % (R_HOST[RI_IPV4], R_HOST[RI_IPV6], R_HOST[RI_CIDR],) RH4TAG = { # separated ipv4 (self closed, closed): @@ -68,6 +75,11 @@ RH4TAG = { # for separate usage of 2 address groups only (regardless of `usedns`), `ip4` and `ip6` together "ADDR": R_HOST[RI_ADDR], "F-ADDR/": R_HOST[RI_ADDR], + # subnet tags for usage as `/` or ``: + "CIDR": R_HOST[RI_CIDR], + "F-CIDR/": R_HOST[RI_CIDR], + "SUBNET": R_HOST[RI_SUBNET], + "F-SUBNET/":R_HOST[RI_SUBNET], # separated dns (self closed, closed): "DNS": R_HOST[RI_DNS], "F-DNS/": R_HOST[RI_DNS], @@ -416,3 +428,7 @@ class FailRegex(Regex): def getHost(self): return self.getFailID(("ip4", "ip6", "dns")) + + def getIP(self): + fail = self.getGroups() + return IPAddr(self.getFailID(("ip4", "ip6")), int(fail.get("cidr") or IPAddr.CIDR_UNSPEC)) diff --git a/fail2ban/server/filter.py b/fail2ban/server/filter.py index 41b9fd3d..94342b31 100644 --- a/fail2ban/server/filter.py +++ b/fail2ban/server/filter.py @@ -865,12 +865,12 @@ class Filter(JailThread): # ip-address or host: host = fail.get('ip4') if host is not None: - cidr = IPAddr.FAM_IPv4 + cidr = int(fail.get('cidr') or IPAddr.FAM_IPv4) raw = True else: host = fail.get('ip6') if host is not None: - cidr = IPAddr.FAM_IPv6 + cidr = int(fail.get('cidr') or IPAddr.FAM_IPv6) raw = True if host is None: host = fail.get('dns') diff --git a/fail2ban/tests/fail2banregextestcase.py b/fail2ban/tests/fail2banregextestcase.py index c751b13d..05db2a24 100644 --- a/fail2ban/tests/fail2banregextestcase.py +++ b/fail2ban/tests/fail2banregextestcase.py @@ -315,6 +315,19 @@ class Fail2banRegexTest(LogCaptureTestCase): self.assertTrue(fail2banRegex.start(args)) self.assertLogged('Lines: 4 lines, 0 ignored, 4 matched, 0 missed') + def testRegexSubnet(self): + (opts, args, fail2banRegex) = _Fail2banRegex( + "-vv", "-d", r"^\[{LEPOCH}\]\s+", "--maxlines", "5", + "[1516469849] 192.0.2.1 FAIL: failure\n" + "[1516469849] 192.0.2.1/24 FAIL: failure\n" + "[1516469849] 2001:DB8:FF:FF::1 FAIL: failure\n" + "[1516469849] 2001:DB8:FF:FF::1/60 FAIL: failure\n", + r"^ FAIL\b" + ) + self.assertTrue(fail2banRegex.start(args)) + self.assertLogged('Lines: 4 lines, 0 ignored, 4 matched, 0 missed') + self.assertLogged('192.0.2.0/24', '2001:db8:ff:f0::/60', all=True) + def testWrongFilterFile(self): # use test log as filter file to cover eror cases... (opts, args, fail2banRegex) = _Fail2banRegex( diff --git a/fail2ban/tests/servertestcase.py b/fail2ban/tests/servertestcase.py index 19c93145..f1121a57 100644 --- a/fail2ban/tests/servertestcase.py +++ b/fail2ban/tests/servertestcase.py @@ -1055,6 +1055,34 @@ class RegexTests(unittest.TestCase): fr.search([('test id group: user:(test login name)',"","")]) self.assertTrue(fr.hasMatched()) self.assertEqual(fr.getFailID(), 'test login name') + # Success case: subnet with IPAddr (IP and subnet) conversion: + fr = FailRegex(r'%%net=') + fr.search([('%%net=192.0.2.1',"","")]) + ip = fr.getIP() + self.assertEqual((ip, ip.familyStr), ('192.0.2.1', 'inet4')) + fr.search([('%%net=192.0.2.1/24',"","")]) + ip = fr.getIP() + self.assertEqual((ip, ip.familyStr), ('192.0.2.0/24', 'inet4')) + fr.search([('%%net=2001:DB8:FF:FF::1',"","")]) + ip = fr.getIP() + self.assertEqual((ip, ip.familyStr), ('2001:db8:ff:ff::1', 'inet6')) + fr.search([('%%net=2001:DB8:FF:FF::1/60',"","")]) + ip = fr.getIP() + self.assertEqual((ip, ip.familyStr), ('2001:db8:ff:f0::/60', 'inet6')) + # CIDR: + fr = FailRegex(r'%%ip="", mask="?"') + fr.search([('%%ip="192.0.2.2", mask=""',"","")]) + ip = fr.getIP() + self.assertEqual((ip, ip.familyStr), ('192.0.2.2', 'inet4')) + fr.search([('%%ip="192.0.2.2", mask="24"',"","")]) + ip = fr.getIP() + self.assertEqual((ip, ip.familyStr), ('192.0.2.0/24', 'inet4')) + fr.search([('%%ip="2001:DB8:2FF:FF::1", mask=""',"","")]) + ip = fr.getIP() + self.assertEqual((ip, ip.familyStr), ('2001:db8:2ff:ff::1', 'inet6')) + fr.search([('%%ip="2001:DB8:2FF:FF::1", mask="60"',"","")]) + ip = fr.getIP() + self.assertEqual((ip, ip.familyStr), ('2001:db8:2ff:f0::/60', 'inet6')) class _BadThread(JailThread): diff --git a/man/jail.conf.5 b/man/jail.conf.5 index 4f5c48e0..662b3f48 100644 --- a/man/jail.conf.5 +++ b/man/jail.conf.5 @@ -277,7 +277,7 @@ It defaults to "auto" which will try "pyinotify", "gamin", "systemd" before "pol use DNS to resolve HOST names that appear in the logs. By default it is "warn" which will resolve hostnames to IPs however it will also log a warning. If you are using DNS here you could be blocking the wrong IPs due to the asymmetric nature of reverse DNS (that the application used to write the domain name to log) compared to forward DNS that fail2ban uses to resolve this back to an IP (but not necessarily the same one). Ideally you should configure your applications to log a real IP. This can be set to "yes" to prevent warnings in the log or "no" to disable DNS resolution altogether (thus ignoring entries where hostname, not an IP is logged).. .TP .B failregex -regex (Python \fBreg\fRular \fBex\fRpression) to be added to the filter's failregexes. If this is useful for others using your application please share you regular expression with the fail2ban developers by reporting an issue (see REPORTING BUGS below). +regex (Python \fBreg\fRular \fBex\fRpression) to be added to the filter's failregexes (see \fBfailregex\fR in section FILTER FILES for details). If this is useful for others using your application please share you regular expression with the fail2ban developers by reporting an issue (see REPORTING BUGS below). .TP .B ignoreregex regex which, if the log line matches, would cause Fail2Ban not consider that line. This line will be ignored even if it matches a failregex of the jail or any of its filters. @@ -428,8 +428,24 @@ Like action files, filter files are ini files. The main section is the [Definiti There are two filter definitions used in the [Definition] section: .TP .B failregex -is the regex (\fBreg\fRular \fBex\fRpression) that will match failed attempts. The tag \fI\fR is used as part of the regex and is itself a regex -for IPv4 addresses (and hostnames if \fBusedns\fR). Fail2Ban will work out which one of these it actually is. +is the regex (\fBreg\fRular \fBex\fRpression) that will match failed attempts. The standard replacement tags can be used as part of the regex: +.RS +.IP +\fI\fR - common regex for IP addresses and hostnames (if \fBusedns\fR is enabled). Fail2Ban will work out which one of these it actually is. +.IP +\fI\fR - regex for IP addresses (both families). +.IP +\fI\fR - regex for IPv4 addresses. +.IP +\fI\fR - regex for IPv6 addresses (also IP enclosed in brackets). +.IP +\fI\fR - regex to match hostnames. +.IP +\fI\fR - helper regex to match CIDR (simple integer form of net-mask). +.IP +\fI\fR - regex to match sub-net adresses (in form of IP/CIDR, also single IP is matched, so part /CIDR is optional). +.RE +.TP For multiline regexs the tag \fI\fR should be used to separate lines. This allows lines between the matched lines to continue to be searched for other failures. The tag can be used multiple times. .TP From e5d02bc2e93662b088b0e815fd6498a01cefd563 Mon Sep 17 00:00:00 2001 From: sebres Date: Mon, 4 Nov 2019 11:33:11 +0100 Subject: [PATCH 04/10] grouped tags (``, ``, ``) recognize IP addresses enclosed in square brackets, closes gh-2494 --- ChangeLog | 2 ++ fail2ban/server/failregex.py | 20 ++++++++------------ fail2ban/tests/files/logs/monit | 3 +++ 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/ChangeLog b/ChangeLog index 95727168..59c71b01 100644 --- a/ChangeLog +++ b/ChangeLog @@ -77,11 +77,13 @@ ver. 0.10.5-dev-1 (20??/??/??) - development edition * `filter.d/named-refused.conf`: - support BIND 9.11.0 log format (includes an additional field @0xXXX..., gh-2406); - `prefregex` extended, more selective now (denied/NOTAUTH suffix moved from failregex, so no catch-all there anymore) +* all filters would accept square brackets around IPv4 addresses also (e. g. monit-filter, gh-2494) ### New Features * new replacement tags for failregex to match subnets in form of IP-addresses with CIDR mask (gh-2559): - `` - helper regex to match CIDR (simple integer form of net-mask); - `` - regex to match sub-net adresses (in form of IP/CIDR, also single IP is matched, so part /CIDR is optional); +* grouped tags (``, ``, ``) recognize IP addresses enclosed in square brackets * new failregex-flag tag `` for failregex, signaled that the access to service was gained (ATM used similar to tag ``, but it does not add the log-line to matches, gh-2279) * filters: introduced new configuration parameter `logtype` (default `file` for file-backends, and diff --git a/fail2ban/server/failregex.py b/fail2ban/server/failregex.py index bb8a2b29..f7dafbef 100644 --- a/fail2ban/server/failregex.py +++ b/fail2ban/server/failregex.py @@ -37,8 +37,6 @@ R_HOST = [ r"""(?:::f{4,6}:)?(?P%s)""" % (IPAddr.IP_4_RE,), # separated ipv6: r"""(?P%s)""" % (IPAddr.IP_6_RE,), - # place-holder for ipv6 enclosed in optional [] (used in addr-, host-regex) - "", # separated dns: r"""(?P[\w\-.^_]*\w)""", # place-holder for ADDR tag-replacement (joined): @@ -52,17 +50,15 @@ R_HOST = [ ] RI_IPV4 = 0 RI_IPV6 = 1 -RI_IPV6BR = 2 -RI_DNS = 3 -RI_ADDR = 4 -RI_HOST = 5 -RI_CIDR = 6 -RI_SUBNET = 7 +RI_DNS = 2 +RI_ADDR = 3 +RI_HOST = 4 +RI_CIDR = 5 +RI_SUBNET = 6 -R_HOST[RI_IPV6BR] = r"""\[?%s\]?""" % (R_HOST[RI_IPV6],) -R_HOST[RI_ADDR] = "(?:%s|%s)" % (R_HOST[RI_IPV4], R_HOST[RI_IPV6BR],) -R_HOST[RI_HOST] = "(?:%s|%s|%s)" % (R_HOST[RI_IPV4], R_HOST[RI_IPV6BR], R_HOST[RI_DNS],) -R_HOST[RI_SUBNET] = r"""(?:%s|%s)(?:/%s)?""" % (R_HOST[RI_IPV4], R_HOST[RI_IPV6], R_HOST[RI_CIDR],) +R_HOST[RI_ADDR] = r"\[?(?:%s|%s)\]?" % (R_HOST[RI_IPV4], R_HOST[RI_IPV6],) +R_HOST[RI_HOST] = r"(?:%s|%s)" % (R_HOST[RI_ADDR], R_HOST[RI_DNS],) +R_HOST[RI_SUBNET] = r"\[?(?:%s|%s)(?:/%s)?\]?" % (R_HOST[RI_IPV4], R_HOST[RI_IPV6], R_HOST[RI_CIDR],) RH4TAG = { # separated ipv4 (self closed, closed): diff --git a/fail2ban/tests/files/logs/monit b/fail2ban/tests/files/logs/monit index 57437046..8dbddaf6 100644 --- a/fail2ban/tests/files/logs/monit +++ b/fail2ban/tests/files/logs/monit @@ -19,3 +19,6 @@ Mar 9 09:18:32 hostname monit[5731]: HttpRequest: access denied -- client 1.2.3 Mar 9 09:18:33 hostname monit[5731]: HttpRequest: access denied -- client 1.2.3.4: unknown user 'test1' # failJSON: { "time": "2005-03-09T09:18:34", "match": true, "host": "1.2.3.4", "desc": "wrong password try" } Mar 9 09:18:34 hostname monit[5731]: HttpRequest: access denied -- client 1.2.3.4: wrong password for user 'test2' + +# failJSON: { "time": "2005-08-06T10:14:52", "match": true, "host": "192.168.1.85", "desc": "IP in brackets, gh-2494" } +[CEST Aug 6 10:14:52] error : HttpRequest: access denied -- client [192.168.1.85]: wrong password for user 'root' From 5cf064a1126416b1143ea3925b290e824355170e Mon Sep 17 00:00:00 2001 From: sebres Date: Mon, 4 Nov 2019 12:17:44 +0100 Subject: [PATCH 05/10] monit: accepting both logpath's: monit and monit.log, closes gh-2495 --- config/jail.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/config/jail.conf b/config/jail.conf index dcb9a7fc..ba5a54b8 100644 --- a/config/jail.conf +++ b/config/jail.conf @@ -445,6 +445,7 @@ logpath = /var/log/tomcat*/catalina.out #Ban clients brute-forcing the monit gui login port = 2812 logpath = /var/log/monit + /var/log/monit.log [webmin-auth] From 548e2e0054c0a17bef97e7a57ac0a85c9fcb2e28 Mon Sep 17 00:00:00 2001 From: Henry van Megen Date: Wed, 6 Nov 2019 15:28:17 +0100 Subject: [PATCH 06/10] sendmail-auth.conf: filter updated for longer mail IDs (up to 20, see gh-2562) --- config/filter.d/sendmail-auth.conf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config/filter.d/sendmail-auth.conf b/config/filter.d/sendmail-auth.conf index a370eea2..752621f7 100644 --- a/config/filter.d/sendmail-auth.conf +++ b/config/filter.d/sendmail-auth.conf @@ -9,7 +9,8 @@ before = common.conf _daemon = (?:sendmail|sm-(?:mta|acceptingconnections)) -failregex = ^%(__prefix_line)s\w{14}: (\S+ )?\[(?:IPv6:|)\]( \(may be forged\))?: possible SMTP attack: command=AUTH, count=\d+$ +# "w{14,20}" will give support for IDs from 14 up to 20 characters long +failregex = ^%(__prefix_line)s\w{14,20}: (\S+ )?\[(?:IPv6:|)\]( \(may be forged\))?: possible SMTP attack: command=AUTH, count=\d+$ ignoreregex = From a9200c54567fcd02d8fa5e91c269481e5d8e4b80 Mon Sep 17 00:00:00 2001 From: Henry van Megen Date: Fri, 8 Nov 2019 09:50:11 +0100 Subject: [PATCH 07/10] Added logline that fails at IDs with 15 chars (see gh-2563) --- fail2ban/tests/files/logs/sendmail-auth | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fail2ban/tests/files/logs/sendmail-auth b/fail2ban/tests/files/logs/sendmail-auth index 835508f6..ec4b1722 100644 --- a/fail2ban/tests/files/logs/sendmail-auth +++ b/fail2ban/tests/files/logs/sendmail-auth @@ -14,3 +14,7 @@ Feb 24 13:00:17 kismet sm-acceptingconnections[1499]: s1OHxxSn001499: 192.241.70 # gh-1632, Fedora 24/RHEL - the daemon name is "sendmail": # failJSON: { "time": "2005-02-24T14:00:00", "match": true , "host": "192.0.2.1" } Feb 24 14:00:00 server sendmail[26592]: u0CB32qX026592: [192.0.2.1]: possible SMTP attack: command=AUTH, count=5 + +# github pull request 2563: failing with IDs longer than 14 characters (15 in this case): +# failJSON: { "time": "2019-11-07T03:27:08", "match": true , "host": "156.156.156.156" } +Nov 3 03:27:08 servername sendmail[3529565]: xA32R2PQ3529565: [156.156.156.156] did not issue MAIL/EXPN/VRFY/ETRN during connection to MTA From 0e8a8edb5e365093321f8b3a336c38a11f4a1bb2 Mon Sep 17 00:00:00 2001 From: sebres Date: Fri, 8 Nov 2019 13:15:40 +0100 Subject: [PATCH 08/10] filter.d/sendmail-*.conf: both filters have same `__prefix_line` now (and same RE for ID, 14-20 chars long, optional) + adjusted test cases (gh-2563) --- config/filter.d/sendmail-auth.conf | 3 ++- config/filter.d/sendmail-reject.conf | 3 ++- fail2ban/tests/files/logs/sendmail-auth | 5 ++--- fail2ban/tests/files/logs/sendmail-reject | 3 +++ 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/config/filter.d/sendmail-auth.conf b/config/filter.d/sendmail-auth.conf index 752621f7..14995fed 100644 --- a/config/filter.d/sendmail-auth.conf +++ b/config/filter.d/sendmail-auth.conf @@ -8,9 +8,10 @@ before = common.conf [Definition] _daemon = (?:sendmail|sm-(?:mta|acceptingconnections)) +__prefix_line = %(known/__prefix_line)s(?:\w{14,20}: )? # "w{14,20}" will give support for IDs from 14 up to 20 characters long -failregex = ^%(__prefix_line)s\w{14,20}: (\S+ )?\[(?:IPv6:|)\]( \(may be forged\))?: possible SMTP attack: command=AUTH, count=\d+$ +failregex = ^%(__prefix_line)s(\S+ )?\[(?:IPv6:|)\]( \(may be forged\))?: possible SMTP attack: command=AUTH, count=\d+$ ignoreregex = diff --git a/config/filter.d/sendmail-reject.conf b/config/filter.d/sendmail-reject.conf index 5c1b1fce..ca171915 100644 --- a/config/filter.d/sendmail-reject.conf +++ b/config/filter.d/sendmail-reject.conf @@ -20,8 +20,9 @@ before = common.conf [Definition] _daemon = (?:(sm-(mta|acceptingconnections)|sendmail)) +__prefix_line = %(known/__prefix_line)s(?:\w{14,20}: )? -prefregex = ^%(__prefix_line)s(?:\w{14}: )?.+$ +prefregex = ^%(__prefix_line)s.+$ cmnfailre = ^ruleset=check_rcpt, arg1=(?P<\S+@\S+>), relay=(\S+ )?\[(?:IPv6:|)\](?: \(may be forged\))?, reject=(550 5\.7\.1 (?P=email)\.\.\. Relaying denied\. (IP name possibly forged \[(\d+\.){3}\d+\]|Proper authentication required\.|IP name lookup failed \[(\d+\.){3}\d+\])|553 5\.1\.8 (?P=email)\.\.\. Domain of sender address \S+ does not exist|550 5\.[71]\.1 (?P=email)\.\.\. (Rejected: .*|User unknown))$ ^ruleset=check_relay, arg1=(?P\S+), arg2=(?:IPv6:|), relay=((?P=dom) )?\[(\d+\.){3}\d+\](?: \(may be forged\))?, reject=421 4\.3\.2 (Connection rate limit exceeded\.|Too many open connections\.)$ diff --git a/fail2ban/tests/files/logs/sendmail-auth b/fail2ban/tests/files/logs/sendmail-auth index ec4b1722..a7ddd6f8 100644 --- a/fail2ban/tests/files/logs/sendmail-auth +++ b/fail2ban/tests/files/logs/sendmail-auth @@ -15,6 +15,5 @@ Feb 24 13:00:17 kismet sm-acceptingconnections[1499]: s1OHxxSn001499: 192.241.70 # failJSON: { "time": "2005-02-24T14:00:00", "match": true , "host": "192.0.2.1" } Feb 24 14:00:00 server sendmail[26592]: u0CB32qX026592: [192.0.2.1]: possible SMTP attack: command=AUTH, count=5 -# github pull request 2563: failing with IDs longer than 14 characters (15 in this case): -# failJSON: { "time": "2019-11-07T03:27:08", "match": true , "host": "156.156.156.156" } -Nov 3 03:27:08 servername sendmail[3529565]: xA32R2PQ3529565: [156.156.156.156] did not issue MAIL/EXPN/VRFY/ETRN during connection to MTA +# failJSON: { "time": "2005-02-24T14:00:01", "match": true , "host": "192.0.2.2", "desc": "long PID, ID longer as 14 chars (gh-2563)" } +Feb 24 14:00:01 server sendmail[3529566]: xA32R2PQ3529566: [192.0.2.2]: possible SMTP attack: command=AUTH, count=5 diff --git a/fail2ban/tests/files/logs/sendmail-reject b/fail2ban/tests/files/logs/sendmail-reject index b6911c4d..f69e4531 100644 --- a/fail2ban/tests/files/logs/sendmail-reject +++ b/fail2ban/tests/files/logs/sendmail-reject @@ -100,3 +100,6 @@ Mar 7 15:04:37 s192-168-0-1 sm-mta[18624]: v27K4Vj8018624: some-host-24.example Mar 29 22:33:47 kismet sm-mta[23221]: x2TMXH7Y023221: internettl.org [104.152.52.29] (may be forged) did not issue MAIL/EXPN/VRFY/ETRN during connection to TLSMTA # failJSON: { "time": "2005-03-29T22:51:42", "match": true , "host": "104.152.52.29", "desc": "wrong resp. non RFC compiant (ddos prelude?), MSA-mode" } Mar 29 22:51:42 kismet sm-mta[24202]: x2TMpAlI024202: internettl.org [104.152.52.29] (may be forged) did not issue MAIL/EXPN/VRFY/ETRN during connection to MSA + +# failJSON: { "time": "2005-03-29T22:51:43", "match": true , "host": "192.0.2.2", "desc": "long PID, ID longer as 14 chars (gh-2563)" } +Mar 29 22:51:43 server sendmail[3529565]: xA32R2PQ3529565: [192.0.2.2] did not issue MAIL/EXPN/VRFY/ETRN during connection to MTA From 27e6b0021c59bf35135a0003bf215bd62edae84a Mon Sep 17 00:00:00 2001 From: sebres Date: Fri, 8 Nov 2019 13:18:33 +0100 Subject: [PATCH 09/10] ChangeLog update gh-2563 --- ChangeLog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ChangeLog b/ChangeLog index 67182248..e589ea1a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -77,6 +77,8 @@ ver. 0.10.5-dev-1 (20??/??/??) - development edition * `filter.d/named-refused.conf`: - support BIND 9.11.0 log format (includes an additional field @0xXXX..., gh-2406); - `prefregex` extended, more selective now (denied/NOTAUTH suffix moved from failregex, so no catch-all there anymore) +* `filter.d/sendmail-auth.conf`, `filter.d/sendmail-reject.conf` : + - ID in prefix can be longer as 14 characters (gh-2563); ### New Features * new failregex-flag tag `` for failregex, signaled that the access to service was gained From d5144e380e66c0923409ca8d36763c18f77ea183 Mon Sep 17 00:00:00 2001 From: sebres Date: Mon, 25 Nov 2019 01:46:07 +0100 Subject: [PATCH 10/10] filter: testing proper handling after time-drift or time-jump (DST-hole, NTP time correction backwards, etc), gh-2566 --- fail2ban/tests/filtertestcase.py | 50 ++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/fail2ban/tests/filtertestcase.py b/fail2ban/tests/filtertestcase.py index 46a0d0b5..38d77d0b 100644 --- a/fail2ban/tests/filtertestcase.py +++ b/fail2ban/tests/filtertestcase.py @@ -387,12 +387,50 @@ class IgnoreIP(LogCaptureTestCase): def testIgnoreInProcessLine(self): setUpMyTime() - self.filter.addIgnoreIP('192.168.1.0/25') - self.filter.addFailRegex('') - self.filter.setDatePattern(r'{^LN-BEG}EPOCH') - self.filter.processLineAndAdd('1387203300.222 192.168.1.32') - self.assertLogged('Ignore 192.168.1.32') - tearDownMyTime() + try: + self.filter.addIgnoreIP('192.168.1.0/25') + self.filter.addFailRegex('') + self.filter.setDatePattern(r'{^LN-BEG}EPOCH') + self.filter.processLineAndAdd('1387203300.222 192.168.1.32') + self.assertLogged('Ignore 192.168.1.32') + finally: + tearDownMyTime() + + def testTimeJump(self): + try: + self.filter.addFailRegex('^') + self.filter.setDatePattern(r'{^LN-BEG}%Y-%m-%d %H:%M:%S(?:\s*%Z)?\s') + self.filter.setFindTime(10); # max 10 seconds back + # + self.pruneLog('[phase 1] DST time jump') + # check local time jump (DST hole): + MyTime.setTime(1572137999) + self.filter.processLineAndAdd('2019-10-27 02:59:59 192.0.2.5'); # +1 = 1 + MyTime.setTime(1572138000) + self.filter.processLineAndAdd('2019-10-27 02:00:00 192.0.2.5'); # +1 = 2 + MyTime.setTime(1572138001) + self.filter.processLineAndAdd('2019-10-27 02:00:01 192.0.2.5'); # +1 = 3 + self.assertLogged( + 'Current failures from 1 IPs (IP:count): 192.0.2.5:1', + 'Current failures from 1 IPs (IP:count): 192.0.2.5:2', + 'Current failures from 1 IPs (IP:count): 192.0.2.5:3', + "Total # of detected failures: 3.", all=True, wait=True) + self.assertNotLogged('Ignore line') + # + self.pruneLog('[phase 2] UTC time jump (NTP correction)') + # check time drifting backwards (NTP correction): + MyTime.setTime(1572210000) + self.filter.processLineAndAdd('2019-10-27 22:00:00 CET 192.0.2.6'); # +1 = 1 + MyTime.setTime(1572200000) + self.filter.processLineAndAdd('2019-10-27 22:00:01 CET 192.0.2.6'); # +1 = 2 (logged before correction) + self.filter.processLineAndAdd('2019-10-27 19:13:20 CET 192.0.2.6'); # +1 = 3 (logged after correction) + self.filter.processLineAndAdd('2019-10-27 19:13:21 CET 192.0.2.6'); # +1 = 4 + self.assertLogged( + '192.0.2.6:1', '192.0.2.6:2', '192.0.2.6:3', '192.0.2.6:4', + "Total # of detected failures: 7.", all=True, wait=True) + self.assertNotLogged('Ignore line') + finally: + tearDownMyTime() def testAddAttempt(self): self.filter.setMaxRetry(3)