From a51f82770bf26745f67b877021bd2d8b0ee020af Mon Sep 17 00:00:00 2001 From: CrazyMax Date: Sat, 24 Nov 2018 22:44:44 +0100 Subject: [PATCH 01/13] New filter `traefik-auth` --- ChangeLog | 1 + config/filter.d/traefik-auth.conf | 56 ++++++++++++++++++++++++++ config/jail.conf | 8 ++++ fail2ban/tests/files/logs/traefik-auth | 3 ++ 4 files changed, 68 insertions(+) create mode 100644 config/filter.d/traefik-auth.conf create mode 100644 fail2ban/tests/files/logs/traefik-auth diff --git a/ChangeLog b/ChangeLog index a45de141..c3aec224 100644 --- a/ChangeLog +++ b/ChangeLog @@ -42,6 +42,7 @@ ver. 0.10.5-dev-1 (20??/??/??) - development edition ### New Features * 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) +* `filter.d/traefik-auth.conf`: used to ban hosts, that were failed through traefik ### Enhancements diff --git a/config/filter.d/traefik-auth.conf b/config/filter.d/traefik-auth.conf new file mode 100644 index 00000000..4ebab0e9 --- /dev/null +++ b/config/filter.d/traefik-auth.conf @@ -0,0 +1,56 @@ +# Fail2ban filter configuration for traefik :: auth +# used to ban hosts, that were failed through traefik +# +# Author: CrazyMax +# +# To use 'traefik-auth' filter you have to configure your Traefik instance to write +# the access logs as describe in https://docs.traefik.io/configuration/logs/#access-logs +# into a log file on host and specifiy users for Basic Authentication +# https://docs.traefik.io/configuration/entrypoints/#basic-authentication +# +# Example: +# +# version: "3.2" +# +# services: +# traefik: +# image: traefik:latest +# command: +# - "--loglevel=INFO" +# - "--accesslog=true" +# - "--accessLog.filePath=/var/log/access.log" +# # - "--accessLog.filters.statusCodes=400-499" +# - "--defaultentrypoints=http,https" +# - "--entryPoints=Name:http Address::80" +# - "--entryPoints=Name:https Address::443 TLS" +# - "--docker.domain=example.com" +# - "--docker.watch=true" +# - "--docker.exposedbydefault=false" +# - "--api=true" +# - "--api.dashboard=true" +# ports: +# - target: 80 +# published: 80 +# protocol: tcp +# mode: host +# - target: 443 +# published: 443 +# protocol: tcp +# mode: host +# labels: +# - "traefik.enable=true" +# - "traefik.port=8080" +# - "traefik.backend=traefik" +# - "traefik.frontend.rule=Host:traefik.example.com" +# - "traefik.frontend.auth.basic.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/" +# volumes: +# - "/var/log/traefik:/var/log" +# - "/var/run/docker.sock:/var/run/docker.sock" +# restart: always +# + +[Definition] + +failregex = ^ \- \S+ \[\] \"(GET|POST|HEAD) .+\" 401 .+$ + +ignoreregex = diff --git a/config/jail.conf b/config/jail.conf index 8b7d3d9b..8fdd1ab2 100644 --- a/config/jail.conf +++ b/config/jail.conf @@ -888,3 +888,11 @@ backend = %(syslog_backend)s port = http,https logpath = %(apache_error_log)s +# To use 'traefik-auth' filter you have to configure your Traefik instance to write +# the access logs as describe in https://docs.traefik.io/configuration/logs/#access-logs +# into a log file on host and specifiy users for Basic Authentication +# https://docs.traefik.io/configuration/entrypoints/#basic-authentication +# Service example in 'config/filter.d/traefik-auth.conf' +[traefik-auth] +port = http,https +logpath = /var/log/traefik/access.log diff --git a/fail2ban/tests/files/logs/traefik-auth b/fail2ban/tests/files/logs/traefik-auth new file mode 100644 index 00000000..6f8be2ea --- /dev/null +++ b/fail2ban/tests/files/logs/traefik-auth @@ -0,0 +1,3 @@ + +# failJSON: { "time": "2018-11-18T21:34:34", "match": true , "host": "10.0.0.2" } +10.0.0.2 - - [18/Nov/2018:21:34:34 +0000] "GET /dashboard/ HTTP/2.0" 401 17 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0" 72 "Auth for frontend-Host-traefik-0" "/dashboard/" 0ms From a8fbdd6a872fe6d2c49c27c924c0c71442a3b771 Mon Sep 17 00:00:00 2001 From: CrazyMax Date: Sat, 24 Nov 2018 23:13:50 +0100 Subject: [PATCH 02/13] Fix UTC Time mismatch --- fail2ban/tests/files/logs/traefik-auth | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fail2ban/tests/files/logs/traefik-auth b/fail2ban/tests/files/logs/traefik-auth index 6f8be2ea..9164d35c 100644 --- a/fail2ban/tests/files/logs/traefik-auth +++ b/fail2ban/tests/files/logs/traefik-auth @@ -1,3 +1,3 @@ -# failJSON: { "time": "2018-11-18T21:34:34", "match": true , "host": "10.0.0.2" } +# failJSON: { "time": "2018-11-18T20:34:34", "match": true , "host": "10.0.0.2" } 10.0.0.2 - - [18/Nov/2018:21:34:34 +0000] "GET /dashboard/ HTTP/2.0" 401 17 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0" 72 "Auth for frontend-Host-traefik-0" "/dashboard/" 0ms From a160c38211877029461d11d1614d6c37a6a2d2ad Mon Sep 17 00:00:00 2001 From: CrazyMax Date: Sat, 24 Nov 2018 23:16:27 +0100 Subject: [PATCH 03/13] Fix UTC Time mismatch --- fail2ban/tests/files/logs/traefik-auth | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fail2ban/tests/files/logs/traefik-auth b/fail2ban/tests/files/logs/traefik-auth index 9164d35c..f3e2cdfc 100644 --- a/fail2ban/tests/files/logs/traefik-auth +++ b/fail2ban/tests/files/logs/traefik-auth @@ -1,3 +1,3 @@ -# failJSON: { "time": "2018-11-18T20:34:34", "match": true , "host": "10.0.0.2" } +# failJSON: { "time": "2018-11-18T22:34:34", "match": true , "host": "10.0.0.2" } 10.0.0.2 - - [18/Nov/2018:21:34:34 +0000] "GET /dashboard/ HTTP/2.0" 401 17 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0" 72 "Auth for frontend-Host-traefik-0" "/dashboard/" 0ms From 90516d6b671aa35897d5affa1655e99569a448e3 Mon Sep 17 00:00:00 2001 From: CrazyMax Date: Wed, 28 Nov 2018 00:37:24 +0100 Subject: [PATCH 04/13] Add login success example for traefik-auth --- fail2ban/tests/files/logs/traefik-auth | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fail2ban/tests/files/logs/traefik-auth b/fail2ban/tests/files/logs/traefik-auth index f3e2cdfc..fbd7732f 100644 --- a/fail2ban/tests/files/logs/traefik-auth +++ b/fail2ban/tests/files/logs/traefik-auth @@ -1,3 +1,5 @@ # failJSON: { "time": "2018-11-18T22:34:34", "match": true , "host": "10.0.0.2" } 10.0.0.2 - - [18/Nov/2018:21:34:34 +0000] "GET /dashboard/ HTTP/2.0" 401 17 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0" 72 "Auth for frontend-Host-traefik-0" "/dashboard/" 0ms +# failJSON: { "match": false } +10.0.0.2 - username [27/Nov/2018:23:33:31 +0000] "GET /dashboard/ HTTP/2.0" 200 716 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0" 118 "Host-traefik-0" "http://172.16.0.3:8080" 4ms \ No newline at end of file From 7cdabdd7aed3fea525529c364d5d9f628741f548 Mon Sep 17 00:00:00 2001 From: CrazyMax Date: Fri, 14 Dec 2018 19:06:09 +0100 Subject: [PATCH 05/13] Update traefik-auth failregex --- config/filter.d/traefik-auth.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/filter.d/traefik-auth.conf b/config/filter.d/traefik-auth.conf index 4ebab0e9..9a9b90a7 100644 --- a/config/filter.d/traefik-auth.conf +++ b/config/filter.d/traefik-auth.conf @@ -51,6 +51,6 @@ [Definition] -failregex = ^ \- \S+ \[\] \"(GET|POST|HEAD) .+\" 401 .+$ +failregex = ^ \- (?!- )\S+ \[\] \"(GET|POST|HEAD) .+\" 401 .+$ ignoreregex = From c540babfb6ca5cab5277f621b5fcb035d8d973d6 Mon Sep 17 00:00:00 2001 From: "Sergey G. Brester" Date: Mon, 17 Dec 2018 12:30:46 +0100 Subject: [PATCH 06/13] matches not empty username only --- fail2ban/tests/files/logs/traefik-auth | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fail2ban/tests/files/logs/traefik-auth b/fail2ban/tests/files/logs/traefik-auth index fbd7732f..3e7a8987 100644 --- a/fail2ban/tests/files/logs/traefik-auth +++ b/fail2ban/tests/files/logs/traefik-auth @@ -1,5 +1,6 @@ - +# failJSON: { "match": false } +10.0.0.2 - - [18/Nov/2018:21:34:30 +0000] "GET /dashboard/ HTTP/2.0" 401 17 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0" 72 "Auth for frontend-Host-traefik-0" "/dashboard/" 0ms # failJSON: { "time": "2018-11-18T22:34:34", "match": true , "host": "10.0.0.2" } -10.0.0.2 - - [18/Nov/2018:21:34:34 +0000] "GET /dashboard/ HTTP/2.0" 401 17 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0" 72 "Auth for frontend-Host-traefik-0" "/dashboard/" 0ms +10.0.0.2 - username [18/Nov/2018:21:34:34 +0000] "GET /dashboard/ HTTP/2.0" 401 17 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0" 72 "Auth for frontend-Host-traefik-0" "/dashboard/" 0ms # failJSON: { "match": false } -10.0.0.2 - username [27/Nov/2018:23:33:31 +0000] "GET /dashboard/ HTTP/2.0" 200 716 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0" 118 "Host-traefik-0" "http://172.16.0.3:8080" 4ms \ No newline at end of file +10.0.0.2 - username [27/Nov/2018:23:33:31 +0000] "GET /dashboard/ HTTP/2.0" 200 716 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0" 118 "Host-traefik-0" "/dashboard/" 4ms From 6b4404b1bcfd77c0cace893cc9251612e4412454 Mon Sep 17 00:00:00 2001 From: Yannik Sembritzki Date: Thu, 3 Jan 2019 23:55:42 +0100 Subject: [PATCH 07/13] Fix asterisk filter not catching attackers when port is logged (Fixes #2316) --- config/filter.d/asterisk.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/filter.d/asterisk.conf b/config/filter.d/asterisk.conf index 0cb1b70a..fb8e4b01 100644 --- a/config/filter.d/asterisk.conf +++ b/config/filter.d/asterisk.conf @@ -24,7 +24,7 @@ failregex = ^%(__prefix_line)s%(log_prefix)s Registration from '[^']*' failed fo ^%(__prefix_line)s%(log_prefix)s No registration for peer '[^']*' \(from \)$ ^%(__prefix_line)s%(log_prefix)s hacking attempt detected ''$ ^%(__prefix_line)s%(log_prefix)s SecurityEvent="(?:FailedACL|InvalidAccountID|ChallengeResponseFailed|InvalidPassword)"(?:(?:,(?!RemoteAddress=)\w+="[^"]*")*|.*?),RemoteAddress="IPV[46]/(UDP|TCP|WS)//\d+"(?:,(?!RemoteAddress=)\w+="[^"]*")*$ - ^%(__prefix_line)s%(log_prefix)s "Rejecting unknown SIP connection from "$ + ^%(__prefix_line)s%(log_prefix)s "Rejecting unknown SIP connection from (?::\d+)?"$ ^%(__prefix_line)s%(log_prefix)s Request (?:'[^']*' )?from '(?:[^']*|.*?)' failed for '(?::\d+)?'\s\(callid: [^\)]*\) - (?:No matching endpoint found|Not match Endpoint(?: Contact)? ACL|(?:Failed|Error) to authenticate)\s*$ # FreePBX (todo: make optional in v.0.10): From 547504873ee58a1b2c860c529b0038a64682865a Mon Sep 17 00:00:00 2001 From: Yannik Sembritzki Date: Thu, 3 Jan 2019 23:59:38 +0100 Subject: [PATCH 08/13] Add test case for new asterisk pjsip log syntax which includes the port --- fail2ban/tests/files/logs/asterisk | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fail2ban/tests/files/logs/asterisk b/fail2ban/tests/files/logs/asterisk index 0955cfe7..3edea535 100644 --- a/fail2ban/tests/files/logs/asterisk +++ b/fail2ban/tests/files/logs/asterisk @@ -35,7 +35,8 @@ # failJSON: { "time": "2013-11-11T14:33:38", "match": true , "host": "192.168.55.152" } [2013-11-11 14:33:38] WARNING[6756][C-0000001d] Ext. s: "Rejecting unknown SIP connection from 192.168.55.152" - +# failJSON: { "time": "2013-11-11T14:33:38", "match": true , "host": "192.168.55.152" } +[2013-11-11 14:33:38] WARNING[8447][C-00000244] Ext. s: "Rejecting unknown SIP connection from 192.168.55.152:52126" # failJSON: { "time": "2004-11-04T18:30:40", "match": true , "host": "192.168.200.100" } Nov 4 18:30:40 localhost asterisk[32229]: NOTICE[32257]: chan_sip.c:23417 in handle_request_register: Registration from '' failed for '192.168.200.100:36998' - Wrong password From d84fb8a4b1111e2730a4e5d1adff3356e055d10f Mon Sep 17 00:00:00 2001 From: "Sergey G. Brester" Date: Thu, 21 Feb 2019 22:19:04 +0100 Subject: [PATCH 09/13] regex rewritten (more secure now, resolves catch-all vulni) --- config/filter.d/traefik-auth.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/filter.d/traefik-auth.conf b/config/filter.d/traefik-auth.conf index 9a9b90a7..8321a138 100644 --- a/config/filter.d/traefik-auth.conf +++ b/config/filter.d/traefik-auth.conf @@ -51,6 +51,6 @@ [Definition] -failregex = ^ \- (?!- )\S+ \[\] \"(GET|POST|HEAD) .+\" 401 .+$ +failregex = ^ \- (?!- )\S+ \[\] \"(GET|POST|HEAD) [^\"]+\" 401\b ignoreregex = From dcede9b3f144e92b80908a6e111bb7d827cdc0bf Mon Sep 17 00:00:00 2001 From: "Sergey G. Brester" Date: Thu, 21 Feb 2019 22:26:28 +0100 Subject: [PATCH 10/13] comment rewritten (belongs to the filter) --- config/jail.conf | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/config/jail.conf b/config/jail.conf index 8fdd1ab2..e486ebbc 100644 --- a/config/jail.conf +++ b/config/jail.conf @@ -888,11 +888,8 @@ backend = %(syslog_backend)s port = http,https logpath = %(apache_error_log)s -# To use 'traefik-auth' filter you have to configure your Traefik instance to write -# the access logs as describe in https://docs.traefik.io/configuration/logs/#access-logs -# into a log file on host and specifiy users for Basic Authentication -# https://docs.traefik.io/configuration/entrypoints/#basic-authentication -# Service example in 'config/filter.d/traefik-auth.conf' [traefik-auth] +# to use 'traefik-auth' filter you have to configure your Traefik instance, +# see `filter.d/traefik-auth.conf` for details and service example. port = http,https logpath = /var/log/traefik/access.log From 62acaae32758e87cfa805bca98c001932b160cb4 Mon Sep 17 00:00:00 2001 From: Yannik Sembritzki Date: Fri, 22 Feb 2019 00:06:55 +0100 Subject: [PATCH 11/13] Add asterisk ipv6 test cases with and without port (related to #2317) --- fail2ban/tests/files/logs/asterisk | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fail2ban/tests/files/logs/asterisk b/fail2ban/tests/files/logs/asterisk index 02213306..5c9de3dc 100644 --- a/fail2ban/tests/files/logs/asterisk +++ b/fail2ban/tests/files/logs/asterisk @@ -37,6 +37,10 @@ [2013-11-11 14:33:38] WARNING[6756][C-0000001d] Ext. s: "Rejecting unknown SIP connection from 192.168.55.152" # failJSON: { "time": "2013-11-11T14:33:38", "match": true , "host": "192.168.55.152" } [2013-11-11 14:33:38] WARNING[8447][C-00000244] Ext. s: "Rejecting unknown SIP connection from 192.168.55.152:52126" +# failJSON: { "time": "2013-11-11T14:33:38", "match": true , "host": "2003:e7:aaaa:aaaa:aaaa:aaaa:aaaa:aaaa" } +[2013-11-11 14:33:38] WARNING[12124][C-00000001] Ext. s: "Rejecting unknown SIP connection from 2003:e7:aaaa:aaaa:aaaa:aaaa:aaaa:aaaa" +# failJSON: { "time": "2013-11-11T14:33:38", "match": true , "host": "2003:e7:aaaa:aaaa:aaaa:aaaa:aaaa:aaaa" } +[2013-11-11 14:33:38] WARNING[12124][C-00000001] Ext. s: "Rejecting unknown SIP connection from [2003:e7:aaaa:aaaa:aaaa:aaaa:aaaa:aaaa]:5060" # failJSON: { "time": "2004-11-04T18:30:40", "match": true , "host": "192.168.200.100" } Nov 4 18:30:40 localhost asterisk[32229]: NOTICE[32257]: chan_sip.c:23417 in handle_request_register: Registration from '' failed for '192.168.200.100:36998' - Wrong password From 3d7b072a15b3dec584eac46a894e17473f289ab4 Mon Sep 17 00:00:00 2001 From: sebres Date: Fri, 22 Feb 2019 12:50:34 +0100 Subject: [PATCH 12/13] covering short form of IPv6 (written-out full form of IPv6 is safe, no matter with or without square brackets) --- fail2ban/tests/files/logs/asterisk | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fail2ban/tests/files/logs/asterisk b/fail2ban/tests/files/logs/asterisk index 5c9de3dc..82092ec4 100644 --- a/fail2ban/tests/files/logs/asterisk +++ b/fail2ban/tests/files/logs/asterisk @@ -37,10 +37,10 @@ [2013-11-11 14:33:38] WARNING[6756][C-0000001d] Ext. s: "Rejecting unknown SIP connection from 192.168.55.152" # failJSON: { "time": "2013-11-11T14:33:38", "match": true , "host": "192.168.55.152" } [2013-11-11 14:33:38] WARNING[8447][C-00000244] Ext. s: "Rejecting unknown SIP connection from 192.168.55.152:52126" -# failJSON: { "time": "2013-11-11T14:33:38", "match": true , "host": "2003:e7:aaaa:aaaa:aaaa:aaaa:aaaa:aaaa" } -[2013-11-11 14:33:38] WARNING[12124][C-00000001] Ext. s: "Rejecting unknown SIP connection from 2003:e7:aaaa:aaaa:aaaa:aaaa:aaaa:aaaa" -# failJSON: { "time": "2013-11-11T14:33:38", "match": true , "host": "2003:e7:aaaa:aaaa:aaaa:aaaa:aaaa:aaaa" } -[2013-11-11 14:33:38] WARNING[12124][C-00000001] Ext. s: "Rejecting unknown SIP connection from [2003:e7:aaaa:aaaa:aaaa:aaaa:aaaa:aaaa]:5060" +# failJSON: { "time": "2013-11-11T14:33:38", "match": true , "host": "2001:db8::1" } +[2013-11-11 14:33:38] WARNING[12124][C-00000001] Ext. s: "Rejecting unknown SIP connection from 2001:db8::1" +# failJSON: { "time": "2013-11-11T14:33:38", "match": true , "host": "2001:db8::1" } +[2013-11-11 14:33:38] WARNING[12124][C-00000001] Ext. s: "Rejecting unknown SIP connection from [2001:db8::1]:5060" # failJSON: { "time": "2004-11-04T18:30:40", "match": true , "host": "192.168.200.100" } Nov 4 18:30:40 localhost asterisk[32229]: NOTICE[32257]: chan_sip.c:23417 in handle_request_register: Registration from '' failed for '192.168.200.100:36998' - Wrong password From 140243328fa1ae563d3d5cbfa527a6cbb836e5e0 Mon Sep 17 00:00:00 2001 From: sebres Date: Fri, 22 Feb 2019 13:20:40 +0100 Subject: [PATCH 13/13] coverage: try to avoid sporadic "coverage decreased" in CI --- config/action.d/smtp.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/config/action.d/smtp.py b/config/action.d/smtp.py index 9cdfe327..5c27d0ff 100644 --- a/config/action.d/smtp.py +++ b/config/action.d/smtp.py @@ -159,25 +159,25 @@ class SMTPAction(ActionBase): try: self._logSys.debug("Connected to SMTP '%s', response: %i: %s", self.host, *smtp.connect(self.host)) - if self.user and self.password: + if self.user and self.password: # pragma: no cover (ATM no tests covering that) smtp.login(self.user, self.password) failed_recipients = smtp.sendmail( self.fromaddr, self.toaddr.split(", "), msg.as_string()) - except smtplib.SMTPConnectError: + except smtplib.SMTPConnectError: # pragma: no cover self._logSys.error("Error connecting to host '%s'", self.host) raise - except smtplib.SMTPAuthenticationError: + except smtplib.SMTPAuthenticationError: # pragma: no cover self._logSys.error( "Failed to authenticate with host '%s' user '%s'", self.host, self.user) raise - except smtplib.SMTPException: + except smtplib.SMTPException: # pragma: no cover self._logSys.error( "Error sending mail to host '%s' from '%s' to '%s'", self.host, self.fromaddr, self.toaddr) raise else: - if failed_recipients: + if failed_recipients: # pragma: no cover self._logSys.warning( "Email to '%s' failed to following recipients: %r", self.toaddr, failed_recipients) @@ -186,7 +186,7 @@ class SMTPAction(ActionBase): try: self._logSys.debug("Disconnected from '%s', response %i: %s", self.host, *smtp.quit()) - except smtplib.SMTPServerDisconnected: + except smtplib.SMTPServerDisconnected: # pragma: no cover pass # Not connected def start(self):