From ec873e2dc38af51d7da41d65fb28a1daf707b557 Mon Sep 17 00:00:00 2001 From: benrubson <6764151+benrubson@users.noreply.github.com> Date: Thu, 5 Nov 2020 23:56:30 +0100 Subject: [PATCH 1/8] Add SoftEtherVPN jail --- ChangeLog | 1 + config/filter.d/softethervpn.conf | 9 +++++++++ config/jail.conf | 5 +++++ fail2ban/tests/files/logs/softethervpn | 7 +++++++ 4 files changed, 22 insertions(+) create mode 100644 config/filter.d/softethervpn.conf create mode 100644 fail2ban/tests/files/logs/softethervpn diff --git a/ChangeLog b/ChangeLog index c3e2c6d4..d1aa66c5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -71,6 +71,7 @@ ver. 0.10.6-dev (20??/??/??) - development edition * parsing of action in jail-configs considers space between action-names as separator also (previously only new-line was allowed), for example `action = a b` would specify 2 actions `a` and `b` * new filter and jail for GitLab recognizing failed application logins (gh-2689) +* new filter and jail for SoftEtherVPN recognizing failed application logins (gh-2723) * `filter.d/guacamole.conf` extended with `logging` parameter to follow webapp-logging if it's configured (gh-2631) * introduced new prefix `{UNB}` for `datepattern` to disable word boundaries in regex; * datetemplate: improved anchor detection for capturing groups `(^...)`; diff --git a/config/filter.d/softethervpn.conf b/config/filter.d/softethervpn.conf new file mode 100644 index 00000000..0cbf5c94 --- /dev/null +++ b/config/filter.d/softethervpn.conf @@ -0,0 +1,9 @@ +# Fail2Ban filter for SoftEtherVPN +# Detecting unauthorized access to SoftEtherVPN +# typically logged in /usr/local/vpnserver/security_log/*/sec.log, or in syslog, depending on configuration + +[INCLUDES] +before = common.conf + +[Definition] +failregex = ^%(__prefix_line)s(?:\([0-9 :.-]{23}\) :)? Connection ".+": User authentication failed. The user name that has been provided was ".+", from .$ diff --git a/config/jail.conf b/config/jail.conf index 8fbd23a1..67f39e40 100644 --- a/config/jail.conf +++ b/config/jail.conf @@ -820,6 +820,11 @@ udpport = 1200,27000,27001,27002,27003,27004,27005,27006,27007,27008,27009,27010 action_ = %(default/action_)s[name=%(__name__)s-tcp, port="%(tcpport)s", protocol="tcp"] %(default/action_)s[name=%(__name__)s-udp, port="%(udpport)s", protocol="udp"] +[softethervpn] +port = 500,4500 +protocol = udp +logpath = /usr/local/vpnserver/security_log/*/sec.log + [gitlab] port = http,https logpath = /var/log/gitlab/gitlab-rails/application.log diff --git a/fail2ban/tests/files/logs/softethervpn b/fail2ban/tests/files/logs/softethervpn new file mode 100644 index 00000000..dd2a798b --- /dev/null +++ b/fail2ban/tests/files/logs/softethervpn @@ -0,0 +1,7 @@ +# Access of unauthorized host in /usr/local/vpnserver/security_log/*/sec.log +# failJSON: { "time": "2020-05-12T10:53:19", "match": true , "host": "80.10.11.12" } +2020-05-12 10:53:19.781 Connection "CID-72": User authentication failed. The user name that has been provided was "bob", from 80.10.11.12. + +# Access of unauthorized host in syslog +# failJSON: { "time": "2020-05-13T10:53:19", "match": true , "host": "80.10.11.13" } +2020-05-13T10:53:19 localhost [myserver.com/VPN/defaultvpn] (2020-05-13 10:53:19.591) : Connection "CID-594": User authentication failed. The user name that has been provided was "alice", from 80.10.11.13. From 010e76406fd2aac83b8cf6da27e9d380cc75dad4 Mon Sep 17 00:00:00 2001 From: "Sergey G. Brester" Date: Mon, 9 Nov 2020 13:19:25 +0100 Subject: [PATCH 2/8] small tweaks (both 2nd time and facility are optional, avoid catch-all, etc) --- config/filter.d/softethervpn.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/filter.d/softethervpn.conf b/config/filter.d/softethervpn.conf index 0cbf5c94..f7e7c0c3 100644 --- a/config/filter.d/softethervpn.conf +++ b/config/filter.d/softethervpn.conf @@ -6,4 +6,4 @@ before = common.conf [Definition] -failregex = ^%(__prefix_line)s(?:\([0-9 :.-]{23}\) :)? Connection ".+": User authentication failed. The user name that has been provided was ".+", from .$ +failregex = ^%(__prefix_line)s(?:(?:\([\d\-]+ [\d:.]+\) )?: )?Connection "[^"]+": User authentication failed. The user name that has been provided was "(?:[^"]+|.+)", from \.$ From df659a0cbc68ad7f8233f16edf64ddddec6dd1d7 Mon Sep 17 00:00:00 2001 From: Mart124 <37041094+Mart124@users.noreply.github.com> Date: Sun, 18 Oct 2020 19:56:30 +0200 Subject: [PATCH 3/8] Add Bitwarden syslog support --- ChangeLog | 1 + config/filter.d/bitwarden.conf | 8 +++++++- fail2ban/tests/files/logs/bitwarden | 3 +++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index d1aa66c5..96c58bb5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -73,6 +73,7 @@ ver. 0.10.6-dev (20??/??/??) - development edition * new filter and jail for GitLab recognizing failed application logins (gh-2689) * new filter and jail for SoftEtherVPN recognizing failed application logins (gh-2723) * `filter.d/guacamole.conf` extended with `logging` parameter to follow webapp-logging if it's configured (gh-2631) +* `filter.d/bitwarden.conf` enhanced to support syslog (gh-2778) * introduced new prefix `{UNB}` for `datepattern` to disable word boundaries in regex; * datetemplate: improved anchor detection for capturing groups `(^...)`; * datepattern: improved handling with wrong recognized timestamps (timezones, no datepattern, etc) diff --git a/config/filter.d/bitwarden.conf b/config/filter.d/bitwarden.conf index 29bd4be8..4a836cbb 100644 --- a/config/filter.d/bitwarden.conf +++ b/config/filter.d/bitwarden.conf @@ -2,5 +2,11 @@ # Detecting failed login attempts # Logged in bwdata/logs/identity/Identity/log.txt +[INCLUDES] +before = common.conf + [Definition] -failregex = ^\s*\[WRN\]\s+Failed login attempt(?:, 2FA invalid)?\. $ +failregex = ^%(__prefix_line)s\s*\[[^\s]+\]\s+Failed login attempt(?:, 2FA invalid)?\. $ + +# DEV Notes: +# __prefix_line can result to an empty string, so it can support syslog and non-syslog at once. diff --git a/fail2ban/tests/files/logs/bitwarden b/fail2ban/tests/files/logs/bitwarden index 3642b3bf..9deb2c07 100644 --- a/fail2ban/tests/files/logs/bitwarden +++ b/fail2ban/tests/files/logs/bitwarden @@ -3,3 +3,6 @@ # failJSON: { "time": "2019-11-25T21:39:58", "match": true , "host": "192.168.0.21" } 2019-11-25 21:39:58.464 +01:00 [WRN] Failed login attempt, 2FA invalid. 192.168.0.21 + +# failJSON: { "time": "2019-09-24T13:16:50", "match": true , "host": "192.168.0.23" } +2019-09-24T13:16:50 e5a81dbf7fd1 Bitwarden-Identity[1]: [Bit.Core.IdentityServer.ResourceOwnerPasswordValidator] Failed login attempt. 192.168.0.23 From 2a18b82f5f92ca50b63dcb01b6f4231cd4220f9f Mon Sep 17 00:00:00 2001 From: Mart124 <37041094+Mart124@users.noreply.github.com> Date: Tue, 20 Oct 2020 18:18:03 +0200 Subject: [PATCH 4/8] Support alternative Bitwarden log format --- fail2ban/tests/files/logs/bitwarden | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fail2ban/tests/files/logs/bitwarden b/fail2ban/tests/files/logs/bitwarden index 9deb2c07..27a22854 100644 --- a/fail2ban/tests/files/logs/bitwarden +++ b/fail2ban/tests/files/logs/bitwarden @@ -2,7 +2,7 @@ 2019-11-26 01:04:49.008 +08:00 [WRN] Failed login attempt. 192.168.0.16 # failJSON: { "time": "2019-11-25T21:39:58", "match": true , "host": "192.168.0.21" } -2019-11-25 21:39:58.464 +01:00 [WRN] Failed login attempt, 2FA invalid. 192.168.0.21 +2019-11-25 21:39:58.464 +01:00 [Warning] Failed login attempt, 2FA invalid. 192.168.0.21 # failJSON: { "time": "2019-09-24T13:16:50", "match": true , "host": "192.168.0.23" } 2019-09-24T13:16:50 e5a81dbf7fd1 Bitwarden-Identity[1]: [Bit.Core.IdentityServer.ResourceOwnerPasswordValidator] Failed login attempt. 192.168.0.23 From 25e006e137172c96c25864f8050b191efaaba3d8 Mon Sep 17 00:00:00 2001 From: sebres Date: Mon, 9 Nov 2020 13:43:59 +0100 Subject: [PATCH 5/8] review and small tweaks (more precise and safe RE) --- config/filter.d/bitwarden.conf | 3 ++- fail2ban/tests/files/logs/bitwarden | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/config/filter.d/bitwarden.conf b/config/filter.d/bitwarden.conf index 4a836cbb..b0651c8e 100644 --- a/config/filter.d/bitwarden.conf +++ b/config/filter.d/bitwarden.conf @@ -6,7 +6,8 @@ before = common.conf [Definition] -failregex = ^%(__prefix_line)s\s*\[[^\s]+\]\s+Failed login attempt(?:, 2FA invalid)?\. $ +_daemon = Bitwarden-Identity +failregex = ^%(__prefix_line)s\s*\[(?:W(?:RN|arning)|Bit\.Core\.[^\]]+)\]\s+Failed login attempt(?:, 2FA invalid)?\. $ # DEV Notes: # __prefix_line can result to an empty string, so it can support syslog and non-syslog at once. diff --git a/fail2ban/tests/files/logs/bitwarden b/fail2ban/tests/files/logs/bitwarden index 27a22854..0fede6c6 100644 --- a/fail2ban/tests/files/logs/bitwarden +++ b/fail2ban/tests/files/logs/bitwarden @@ -1,6 +1,9 @@ # failJSON: { "time": "2019-11-25T18:04:49", "match": true , "host": "192.168.0.16" } 2019-11-26 01:04:49.008 +08:00 [WRN] Failed login attempt. 192.168.0.16 +# failJSON: { "time": "2019-11-25T21:39:58", "match": true , "host": "192.168.0.21" } +2019-11-25 21:39:58.464 +01:00 [WRN] Failed login attempt, 2FA invalid. 192.168.0.21 + # failJSON: { "time": "2019-11-25T21:39:58", "match": true , "host": "192.168.0.21" } 2019-11-25 21:39:58.464 +01:00 [Warning] Failed login attempt, 2FA invalid. 192.168.0.21 From 840f0ff10a5edb14afaba8b2c13bc18d2514715d Mon Sep 17 00:00:00 2001 From: benrubson <6764151+benrubson@users.noreply.github.com> Date: Mon, 9 Nov 2020 15:31:06 +0100 Subject: [PATCH 6/8] Add Grafana jail --- ChangeLog | 1 + config/filter.d/grafana.conf | 9 +++++++++ config/jail.conf | 4 ++++ fail2ban/tests/files/logs/grafana | 5 +++++ 4 files changed, 19 insertions(+) create mode 100644 config/filter.d/grafana.conf create mode 100644 fail2ban/tests/files/logs/grafana diff --git a/ChangeLog b/ChangeLog index 96c58bb5..51ba4f90 100644 --- a/ChangeLog +++ b/ChangeLog @@ -71,6 +71,7 @@ ver. 0.10.6-dev (20??/??/??) - development edition * parsing of action in jail-configs considers space between action-names as separator also (previously only new-line was allowed), for example `action = a b` would specify 2 actions `a` and `b` * new filter and jail for GitLab recognizing failed application logins (gh-2689) +* new filter and jail for Grafana recognizing failed application logins (gh-2855) * new filter and jail for SoftEtherVPN recognizing failed application logins (gh-2723) * `filter.d/guacamole.conf` extended with `logging` parameter to follow webapp-logging if it's configured (gh-2631) * `filter.d/bitwarden.conf` enhanced to support syslog (gh-2778) diff --git a/config/filter.d/grafana.conf b/config/filter.d/grafana.conf new file mode 100644 index 00000000..78ded075 --- /dev/null +++ b/config/filter.d/grafana.conf @@ -0,0 +1,9 @@ +# Fail2Ban filter for Grafana +# Detecting unauthorized access +# Typically logged in /var/log/grafana/grafana.log + +[Init] +datepattern = ^t=%%Y-%%m-%%dT%%H:%%M:%%S%%z + +[Definition] +failregex = ^.*msg="Invalid username or password".* remote_addr=$ diff --git a/config/jail.conf b/config/jail.conf index 5ca67749..ddbcf61e 100644 --- a/config/jail.conf +++ b/config/jail.conf @@ -828,6 +828,10 @@ logpath = /usr/local/vpnserver/security_log/*/sec.log port = http,https logpath = /var/log/gitlab/gitlab-rails/application.log +[grafana] +port = http,https +logpath = /var/log/grafana/grafana.log + [bitwarden] port = http,https logpath = /home/*/bwdata/logs/identity/Identity/log.txt diff --git a/fail2ban/tests/files/logs/grafana b/fail2ban/tests/files/logs/grafana new file mode 100644 index 00000000..aac86ebc --- /dev/null +++ b/fail2ban/tests/files/logs/grafana @@ -0,0 +1,5 @@ +# Access of unauthorized host in /var/log/grafana/grafana.log +# failJSON: { "time": "2020-10-19T17:44:33", "match": true , "host": "182.56.23.12" } +t=2020-10-19T17:44:33+0200 lvl=eror msg="Invalid username or password" logger=context userId=0 orgId=0 uname= error="Invalid Username or Password" remote_addr=182.56.23.12 +# failJSON: { "time": "2020-10-19T18:44:33", "match": true , "host": "182.56.23.13" } +t=2020-10-19T18:44:33+0200 lvl=eror msg="Invalid username or password" logger=context userId=0 orgId=0 uname= error="User not found" remote_addr=182.56.23.13 From 1c1a9b868c6c7677c929ede5e8c1cb8f16d3be41 Mon Sep 17 00:00:00 2001 From: "Sergey G. Brester" Date: Mon, 9 Nov 2020 15:36:30 +0100 Subject: [PATCH 7/8] no catch-alls, user name and error message stored in ticket --- config/filter.d/grafana.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/filter.d/grafana.conf b/config/filter.d/grafana.conf index 78ded075..e7f0f420 100644 --- a/config/filter.d/grafana.conf +++ b/config/filter.d/grafana.conf @@ -6,4 +6,4 @@ datepattern = ^t=%%Y-%%m-%%dT%%H:%%M:%%S%%z [Definition] -failregex = ^.*msg="Invalid username or password".* remote_addr=$ +failregex = ^(?: lvl=err?or)? msg="Invalid username or password"(?: uname=(?:"[^"]+"|\S+)| error="[^"]+"| \S+=(?:\S*|"[^"]+"))* remote_addr=$ From ca4da9d1d3f792562500182711abc149b2c741a1 Mon Sep 17 00:00:00 2001 From: sebres Date: Wed, 11 Nov 2020 11:08:23 +0100 Subject: [PATCH 8/8] actions: extend tags replacement in non ticket-based commands (actionstart, actionstop, etc); fixes regression by interpolation of tag `` introduced in 0.11 with dynamic bantime (due to `bantime.increment`, see #2869) --- fail2ban/server/action.py | 13 ++++++++++++- fail2ban/server/actions.py | 12 +++++++----- fail2ban/tests/servertestcase.py | 3 ++- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/fail2ban/server/action.py b/fail2ban/server/action.py index 1c313cf0..f52e0878 100644 --- a/fail2ban/server/action.py +++ b/fail2ban/server/action.py @@ -455,7 +455,18 @@ class CommandAction(ActionBase): ret = True # avoid double execution of same command for both families: if cmd and cmd not in self._operationExecuted(tag, lambda f: f != famoper): - ret = self.executeCmd(cmd, self.timeout) + realCmd = cmd + if self._jail: + # simulate action info with "empty" ticket: + aInfo = getattr(self._jail.actions, 'actionInfo', None) + if not aInfo: + aInfo = self._jail.actions._getActionInfo(None) + setattr(self._jail.actions, 'actionInfo', aInfo) + aInfo['time'] = MyTime.time() + aInfo['family'] = famoper + # replace dynamical tags, important - don't cache, no recursion and auto-escape here + realCmd = self.replaceDynamicTags(cmd, aInfo) + ret = self.executeCmd(realCmd, self.timeout) res &= ret if afterExec: afterExec(famoper, ret) self._operationExecuted(tag, famoper, cmd if ret else None) diff --git a/fail2ban/server/actions.py b/fail2ban/server/actions.py index 49db5df7..967908af 100644 --- a/fail2ban/server/actions.py +++ b/fail2ban/server/actions.py @@ -457,7 +457,9 @@ class Actions(JailThread, Mapping): return mi[idx] if mi[idx] is not None else self.__ticket - def __getActionInfo(self, ticket): + def _getActionInfo(self, ticket): + if not ticket: + ticket = BanTicket("", MyTime.time()) aInfo = Actions.ActionInfo(ticket, self._jail) return aInfo @@ -491,7 +493,7 @@ class Actions(JailThread, Mapping): bTicket = BanTicket.wrap(ticket) btime = ticket.getBanTime(self.__banManager.getBanTime()) ip = bTicket.getIP() - aInfo = self.__getActionInfo(bTicket) + aInfo = self._getActionInfo(bTicket) reason = {} if self.__banManager.addBanTicket(bTicket, reason=reason): cnt += 1 @@ -568,7 +570,7 @@ class Actions(JailThread, Mapping): """ actions = actions or self._actions ip = ticket.getIP() - aInfo = self.__getActionInfo(ticket) + aInfo = self._getActionInfo(ticket) if log: logSys.notice("[%s] Reban %s%s", self._jail.name, aInfo["ip"], (', action %r' % actions.keys()[0] if len(actions) == 1 else '')) for name, action in actions.iteritems(): @@ -602,7 +604,7 @@ class Actions(JailThread, Mapping): if not action._prolongable: continue if aInfo is None: - aInfo = self.__getActionInfo(ticket) + aInfo = self._getActionInfo(ticket) if not aInfo.immutable: aInfo.reset() action.prolong(aInfo) except Exception as e: @@ -696,7 +698,7 @@ class Actions(JailThread, Mapping): else: unbactions = actions ip = ticket.getIP() - aInfo = self.__getActionInfo(ticket) + aInfo = self._getActionInfo(ticket) if log: logSys.notice("[%s] Unban %s", self._jail.name, aInfo["ip"]) for name, action in unbactions.iteritems(): diff --git a/fail2ban/tests/servertestcase.py b/fail2ban/tests/servertestcase.py index 3bd4501b..d2bf8bdc 100644 --- a/fail2ban/tests/servertestcase.py +++ b/fail2ban/tests/servertestcase.py @@ -1448,9 +1448,10 @@ class ServerConfigReaderTests(LogCaptureTestCase): ), }), # dummy -- - ('j-dummy', 'dummy[name=%(__name__)s, init="==", target="/tmp/fail2ban.dummy"]', { + ('j-dummy', '''dummy[name=%(__name__)s, init="=='/'==bt:==bc:==", target="/tmp/fail2ban.dummy"]''', { 'ip4': ('family: inet4',), 'ip6': ('family: inet6',), 'start': ( + '''`printf %b "=='/'==bt:600==bc:0==\\n"''', ## empty family (independent in this action, same for both), no ip on start, initial bantime and bancount '`echo "[j-dummy] dummy /tmp/fail2ban.dummy -- started"`', ), 'flush': (