From 884f708bd7f4b48c90503752d927975ba10dafbc Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sun, 15 Jul 2018 09:07:28 -0400 Subject: fail2ban/files: rename "gentoo" files to "openrc". We ship a service script and configuration file for "gentoo" that are actually more generally applicable: they work on any system where OpenRC is used. This commit simply renames the files from "gentoo" to "openrc" to reflect the fact that they are in no way Gentoo-specific. --- MANIFEST | 4 ++-- files/fail2ban-openrc.conf | 8 +++++++ files/fail2ban-openrc.init | 60 ++++++++++++++++++++++++++++++++++++++++++++++ files/gentoo-confd | 8 ------- files/gentoo-initd | 60 ---------------------------------------------- 5 files changed, 70 insertions(+), 70 deletions(-) create mode 100644 files/fail2ban-openrc.conf create mode 100755 files/fail2ban-openrc.init delete mode 100644 files/gentoo-confd delete mode 100755 files/gentoo-initd diff --git a/MANIFEST b/MANIFEST index 3ea2816b..ca9be123 100644 --- a/MANIFEST +++ b/MANIFEST @@ -375,8 +375,8 @@ files/fail2ban.service.in files/fail2ban-tmpfiles.conf files/fail2ban.upstart files/gen_badbots -files/gentoo-confd -files/gentoo-initd +files/fail2ban-openrc.conf +files/fail2ban-openrc.init files/ipmasq-ZZZzzz_fail2ban.rul files/logwatch/fail2ban files/logwatch/fail2ban-0.8.log diff --git a/files/fail2ban-openrc.conf b/files/fail2ban-openrc.conf new file mode 100644 index 00000000..00d19f8b --- /dev/null +++ b/files/fail2ban-openrc.conf @@ -0,0 +1,8 @@ +# Config file for /etc/init.d/fail2ban +# +# For information on options, see "/usr/bin/fail2ban-client -h". + +FAIL2BAN_OPTIONS="" + +# Force execution of the server even if the socket already exists: +#FAIL2BAN_OPTIONS="-x" diff --git a/files/fail2ban-openrc.init b/files/fail2ban-openrc.init new file mode 100755 index 00000000..0fb157cd --- /dev/null +++ b/files/fail2ban-openrc.init @@ -0,0 +1,60 @@ +#!/sbin/openrc-run +# This file is part of Fail2Ban. +# +# Fail2Ban is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Fail2Ban is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Fail2Ban; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# Author: Sireyessire, Cyril Jaquier +# + +description="Daemon to ban hosts that cause multiple authentication errors" +description_reload="reload configuration" +description_showlog="show fail2ban logs" +extra_started_commands="reload showlog" + +FAIL2BAN="/usr/bin/fail2ban-client ${FAIL2BAN_OPTIONS}" + +depend() { + need net + need logger + after iptables +} + +start() { + ebegin "Starting fail2ban" + mkdir -p /var/run/fail2ban || return 1 + # remove stalled sock file after system crash + # bug 347477 + rm -f /var/run/fail2ban/fail2ban.sock || return 1 + start-stop-daemon --start --pidfile /var/run/fail2ban/fail2ban.pid \ + -- ${FAIL2BAN} start + eend $? "Failed to start fail2ban" +} + +stop() { + ebegin "Stopping fail2ban" + start-stop-daemon --stop --pidfile /var/run/fail2ban/fail2ban.pid --retry 30 \ + -- ${FAIL2BAN} stop + eend $? "Failed to stop fail2ban" +} + +reload() { + ebegin "Reloading fail2ban" + ${FAIL2BAN} reload + eend $? "Failed to reload fail2ban" +} + +showlog(){ + less /var/log/fail2ban.log +} diff --git a/files/gentoo-confd b/files/gentoo-confd deleted file mode 100644 index 00d19f8b..00000000 --- a/files/gentoo-confd +++ /dev/null @@ -1,8 +0,0 @@ -# Config file for /etc/init.d/fail2ban -# -# For information on options, see "/usr/bin/fail2ban-client -h". - -FAIL2BAN_OPTIONS="" - -# Force execution of the server even if the socket already exists: -#FAIL2BAN_OPTIONS="-x" diff --git a/files/gentoo-initd b/files/gentoo-initd deleted file mode 100755 index 0fb157cd..00000000 --- a/files/gentoo-initd +++ /dev/null @@ -1,60 +0,0 @@ -#!/sbin/openrc-run -# This file is part of Fail2Ban. -# -# Fail2Ban is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# Fail2Ban is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Fail2Ban; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# Author: Sireyessire, Cyril Jaquier -# - -description="Daemon to ban hosts that cause multiple authentication errors" -description_reload="reload configuration" -description_showlog="show fail2ban logs" -extra_started_commands="reload showlog" - -FAIL2BAN="/usr/bin/fail2ban-client ${FAIL2BAN_OPTIONS}" - -depend() { - need net - need logger - after iptables -} - -start() { - ebegin "Starting fail2ban" - mkdir -p /var/run/fail2ban || return 1 - # remove stalled sock file after system crash - # bug 347477 - rm -f /var/run/fail2ban/fail2ban.sock || return 1 - start-stop-daemon --start --pidfile /var/run/fail2ban/fail2ban.pid \ - -- ${FAIL2BAN} start - eend $? "Failed to start fail2ban" -} - -stop() { - ebegin "Stopping fail2ban" - start-stop-daemon --stop --pidfile /var/run/fail2ban/fail2ban.pid --retry 30 \ - -- ${FAIL2BAN} stop - eend $? "Failed to stop fail2ban" -} - -reload() { - ebegin "Reloading fail2ban" - ${FAIL2BAN} reload - eend $? "Failed to reload fail2ban" -} - -showlog(){ - less /var/log/fail2ban.log -} -- cgit v1.2.1 From 1cec3d05b8a052423a7ef4f8205c9d63b1ce9b07 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sun, 15 Jul 2018 09:11:53 -0400 Subject: files/fail2ban-openrc.conf: remove hard-coded paths. There were two paths mentioned in comments in the fail2ban OpenRC conf file, but those paths aren't guaranteed to be correct (until/unless we integrate the conf file with the build system). The first comment referenced the physical location of the associated init script, and in my opinion is not useful to an end user in the first place. It has been removed: OpenRC users know what this file is for, there's no reason to repeat it in a comment. The second comment contained an absolute path to fail2ban-client, and I've removed the leading path components because "fail2ban-client" is generally run from your $PATH. --- files/fail2ban-openrc.conf | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/files/fail2ban-openrc.conf b/files/fail2ban-openrc.conf index 00d19f8b..1c589763 100644 --- a/files/fail2ban-openrc.conf +++ b/files/fail2ban-openrc.conf @@ -1,6 +1,4 @@ -# Config file for /etc/init.d/fail2ban -# -# For information on options, see "/usr/bin/fail2ban-client -h". +# For available options, plase run "fail2ban-client -h". FAIL2BAN_OPTIONS="" -- cgit v1.2.1 From eb58e90ba9ccd049fae8bd92d8f828df6fdb287e Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sun, 15 Jul 2018 09:17:29 -0400 Subject: files/fail2ban-openrc.conf: remove a commented example setting. Our OpenRC conf file already tells users how to find the available options that can be placed in the FAIL2BAN_OPTIONS variable, so having a specific example of, FAIL2BAN_OPTIONS="-x" doesn't provide much more information. In fact, it makes you wonder why it's there in the first place: does the init script have some kind of problem with stale sockets? It used to, but that problem has been fixed. This commit removes the redundant example. --- files/fail2ban-openrc.conf | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/files/fail2ban-openrc.conf b/files/fail2ban-openrc.conf index 1c589763..1a2450e2 100644 --- a/files/fail2ban-openrc.conf +++ b/files/fail2ban-openrc.conf @@ -1,6 +1,2 @@ # For available options, plase run "fail2ban-client -h". - -FAIL2BAN_OPTIONS="" - -# Force execution of the server even if the socket already exists: -#FAIL2BAN_OPTIONS="-x" +#FAIL2BAN_OPTIONS="" -- cgit v1.2.1 From 64ec399542f20582090a69b77f4c5b90f7b41019 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sun, 15 Jul 2018 12:59:18 -0400 Subject: files/fail2ban-openrc.init: drop "need net" dependency. The "need net" dependency in our OpenRC service script was incorrect: the fail2ban service does not need a working WAN to function. This issue is well-documented and is covered in the OpenRC Service Script Guide, currently located at https://github.com/OpenRC/openrc/blob/master/service-script-guide.md --- files/fail2ban-openrc.init | 1 - 1 file changed, 1 deletion(-) diff --git a/files/fail2ban-openrc.init b/files/fail2ban-openrc.init index 0fb157cd..69025499 100755 --- a/files/fail2ban-openrc.init +++ b/files/fail2ban-openrc.init @@ -26,7 +26,6 @@ extra_started_commands="reload showlog" FAIL2BAN="/usr/bin/fail2ban-client ${FAIL2BAN_OPTIONS}" depend() { - need net need logger after iptables } -- cgit v1.2.1 From af24c52558296e52e90cb55fad449b2bf9e996c6 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sun, 15 Jul 2018 13:05:11 -0400 Subject: files/fail2ban-openrc.init: change "need logger" dependency to "use logger". Our OpenRC service script contained a "need logger" dependency, which meant that the life cycle of the fail2ban service was tied to that of the system logger service. That isn't quite correct: fail2ban functions fine even if the system logger is stopped: 1. fail2ban is capable of analyzing non-syslog log files. 2. Even if fail2ban is solely analyzing syslog files, we don't want to stop the fail2ban service simply because syslog was stopped -- fail2ban just won't see any new log lines until syslog is started again. This commit changes the "need net" dependency to "use net", which will still attempt to start the system logger service, but which won't kill fail2ban if the system logger is ever stopped. --- files/fail2ban-openrc.init | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/fail2ban-openrc.init b/files/fail2ban-openrc.init index 69025499..138bff30 100755 --- a/files/fail2ban-openrc.init +++ b/files/fail2ban-openrc.init @@ -26,7 +26,7 @@ extra_started_commands="reload showlog" FAIL2BAN="/usr/bin/fail2ban-client ${FAIL2BAN_OPTIONS}" depend() { - need logger + use logger after iptables } -- cgit v1.2.1 From bc4a742e32f610879fcdb1af7677e46007ac0682 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sun, 15 Jul 2018 13:13:13 -0400 Subject: files/fail2ban-openrc.init: replace FAIL2BAN with standard OpenRC variables. The FAIL2BAN variable in our OpenRC service script was a combination of two standard OpenRC variables, "command" and "command_args". This commit simply replaces the custom variable with the two standard ones. This will aid future simplifications of the service script. --- files/fail2ban-openrc.init | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/files/fail2ban-openrc.init b/files/fail2ban-openrc.init index 138bff30..1374da3d 100755 --- a/files/fail2ban-openrc.init +++ b/files/fail2ban-openrc.init @@ -23,7 +23,8 @@ description_reload="reload configuration" description_showlog="show fail2ban logs" extra_started_commands="reload showlog" -FAIL2BAN="/usr/bin/fail2ban-client ${FAIL2BAN_OPTIONS}" +command="/usr/bin/fail2ban-client" +command_args="${FAIL2BAN_OPTIONS}" depend() { use logger @@ -37,20 +38,20 @@ start() { # bug 347477 rm -f /var/run/fail2ban/fail2ban.sock || return 1 start-stop-daemon --start --pidfile /var/run/fail2ban/fail2ban.pid \ - -- ${FAIL2BAN} start + -- ${command} ${command_args} start eend $? "Failed to start fail2ban" } stop() { ebegin "Stopping fail2ban" start-stop-daemon --stop --pidfile /var/run/fail2ban/fail2ban.pid --retry 30 \ - -- ${FAIL2BAN} stop + -- ${command} ${command_args} stop eend $? "Failed to stop fail2ban" } reload() { ebegin "Reloading fail2ban" - ${FAIL2BAN} reload + ${command} ${command_args} reload eend $? "Failed to reload fail2ban" } -- cgit v1.2.1 From 115024d14a0b1a5a4b96175712f257b408883f38 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sun, 15 Jul 2018 13:15:44 -0400 Subject: files/fail2ban-openrc.init: use a variable for the pid file location. OpenRC has a special variable "pidfile" that should be used to store the location of the daemon's PID file. This commit replaces two instances of said location with one variable. --- files/fail2ban-openrc.init | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/files/fail2ban-openrc.init b/files/fail2ban-openrc.init index 1374da3d..9969a3e0 100755 --- a/files/fail2ban-openrc.init +++ b/files/fail2ban-openrc.init @@ -25,6 +25,7 @@ extra_started_commands="reload showlog" command="/usr/bin/fail2ban-client" command_args="${FAIL2BAN_OPTIONS}" +pidfile="/run/${RC_SVCNAME}/${RC_SVCNAME}.pid" depend() { use logger @@ -37,14 +38,14 @@ start() { # remove stalled sock file after system crash # bug 347477 rm -f /var/run/fail2ban/fail2ban.sock || return 1 - start-stop-daemon --start --pidfile /var/run/fail2ban/fail2ban.pid \ + start-stop-daemon --start --pidfile "${pidfile}" \ -- ${command} ${command_args} start eend $? "Failed to start fail2ban" } stop() { ebegin "Stopping fail2ban" - start-stop-daemon --stop --pidfile /var/run/fail2ban/fail2ban.pid --retry 30 \ + start-stop-daemon --stop --pidfile "${pidfile}" --retry 30 \ -- ${command} ${command_args} stop eend $? "Failed to stop fail2ban" } -- cgit v1.2.1 From 0b146208eb3a6f3ceb04d59e9e89c1cc914854ea Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sun, 15 Jul 2018 13:32:03 -0400 Subject: files/fail2ban-openrc.init: move pre-flight checks into start_pre(). Our OpenRC service script performs two tasks before starting the service: 1. It removes any stake sockets (from e.g. a system crash). 2. It ensures that the PID file directory exists. These have both been moved into the "start_pre" phase, which is designed to do such things (and will allow us to simplify the "start" phase in the future). The existing "mkdir -p" has also been converted into a "checkpath -d" command which is built-in to OpenRC. --- files/fail2ban-openrc.init | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/files/fail2ban-openrc.init b/files/fail2ban-openrc.init index 9969a3e0..e1cf5273 100755 --- a/files/fail2ban-openrc.init +++ b/files/fail2ban-openrc.init @@ -32,12 +32,16 @@ depend() { after iptables } +start_pre() { + checkpath -d "${pidfile%/*}" || return 1 + + # Remove stale socket after system crash, Gentoo bug 347477 + rm -f /var/run/fail2ban/fail2ban.sock || return 1 +} + start() { ebegin "Starting fail2ban" - mkdir -p /var/run/fail2ban || return 1 - # remove stalled sock file after system crash - # bug 347477 - rm -f /var/run/fail2ban/fail2ban.sock || return 1 + start-stop-daemon --start --pidfile "${pidfile}" \ -- ${command} ${command_args} start eend $? "Failed to start fail2ban" -- cgit v1.2.1 From e0097aefb95bac48c992f6b5ae58fc99140ecc03 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sun, 15 Jul 2018 13:37:00 -0400 Subject: files/fail2ban-openrc.init: use RC_SVCNAME instead of hard-coding the name. If our service is installed under some other name, then we don't want the service script to say things like "Starting fail2ban..." because the name "fail2ban" won't make any sense at that point. Instead, we use the $RC_SVCNAME variable to ensure that the service name matches what we tell the user. Typically, however, $RC_SVCNAME will still be "fail2ban". --- files/fail2ban-openrc.init | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/files/fail2ban-openrc.init b/files/fail2ban-openrc.init index e1cf5273..bcbdf8fd 100755 --- a/files/fail2ban-openrc.init +++ b/files/fail2ban-openrc.init @@ -40,24 +40,24 @@ start_pre() { } start() { - ebegin "Starting fail2ban" + ebegin "Starting ${RC_SVCNAME}" start-stop-daemon --start --pidfile "${pidfile}" \ -- ${command} ${command_args} start - eend $? "Failed to start fail2ban" + eend $? "Failed to start ${RC_SVCNAME}" } stop() { - ebegin "Stopping fail2ban" + ebegin "Stopping ${RC_SVCNAME}" start-stop-daemon --stop --pidfile "${pidfile}" --retry 30 \ -- ${command} ${command_args} stop - eend $? "Failed to stop fail2ban" + eend $? "Failed to stop ${RC_SVCNAME}" } reload() { - ebegin "Reloading fail2ban" + ebegin "Reloading ${RC_SVCNAME}" ${command} ${command_args} reload - eend $? "Failed to reload fail2ban" + eend $? "Failed to reload ${RC_SVCNAME}" } showlog(){ -- cgit v1.2.1 From c8ed0e0d913feeb361de800d1af6fd56e96bb840 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sun, 15 Jul 2018 13:44:53 -0400 Subject: files/fail2ban-openrc.init: use the standard OpenRC "retry" variable. If the "retry" variable is set in the service script, we don't have to pass it to start-stop-daemon explicitly. While we can't immediately eliminate any code with this change, it will be necessary later to adopt the default OpenRC stop() function. --- files/fail2ban-openrc.init | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/files/fail2ban-openrc.init b/files/fail2ban-openrc.init index bcbdf8fd..e3ddfe1a 100755 --- a/files/fail2ban-openrc.init +++ b/files/fail2ban-openrc.init @@ -26,6 +26,7 @@ extra_started_commands="reload showlog" command="/usr/bin/fail2ban-client" command_args="${FAIL2BAN_OPTIONS}" pidfile="/run/${RC_SVCNAME}/${RC_SVCNAME}.pid" +retry="30" depend() { use logger @@ -49,7 +50,7 @@ start() { stop() { ebegin "Stopping ${RC_SVCNAME}" - start-stop-daemon --stop --pidfile "${pidfile}" --retry 30 \ + start-stop-daemon --stop --pidfile "${pidfile}" --retry "${retry}" \ -- ${command} ${command_args} stop eend $? "Failed to stop ${RC_SVCNAME}" } -- cgit v1.2.1 From ec4c4b12c1f86769f021216482f4a380ca02d4bc Mon Sep 17 00:00:00 2001 From: Ben RUBSON Date: Sun, 19 Aug 2018 22:35:09 +0200 Subject: Add yes/no log option to badips.py --- config/action.d/badips.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/config/action.d/badips.py b/config/action.d/badips.py index 4e50890c..95bfbe14 100644 --- a/config/action.d/badips.py +++ b/config/action.d/badips.py @@ -70,6 +70,9 @@ class BadIPsAction(ActionBase): # pragma: no cover - may be unavailable updateperiod : int, optional Time in seconds between updating bad IPs blacklist. Default 900 (15 minutes) + log : str, optional + Whether or not to log when an IP id (un)banned. + Default `yes`. agent : str, optional User agent transmitted to server. Default `Fail2Ban/ver.` @@ -86,7 +89,7 @@ class BadIPsAction(ActionBase): # pragma: no cover - may be unavailable return Request(url, headers={'User-Agent': self.agent}, **argv) def __init__(self, jail, name, category, score=3, age="24h", key=None, - banaction=None, bancategory=None, bankey=None, updateperiod=900, agent="Fail2Ban", + banaction=None, bancategory=None, bankey=None, updateperiod=900, log="yes", agent="Fail2Ban", timeout=TIMEOUT): super(BadIPsAction, self).__init__(jail, name) @@ -99,6 +102,7 @@ class BadIPsAction(ActionBase): # pragma: no cover - may be unavailable self.banaction = banaction self.bancategory = bancategory or category self.bankey = bankey + self.log = log self.updateperiod = updateperiod self._bannedips = set() @@ -289,9 +293,10 @@ class BadIPsAction(ActionBase): # pragma: no cover - may be unavailable exc_info=self._logSys.getEffectiveLevel()<=logging.DEBUG) else: self._bannedips.add(ip) - self._logSys.debug( - "Banned IP %s for jail '%s' with action '%s'", - ip, self._jail.name, self.banaction) + if self.log is "yes": + self._logSys.notice( + "Banned IP %s for jail '%s' with action '%s'", + ip, self._jail.name, self.banaction) def _unbanIPs(self, ips): for ip in ips: @@ -304,14 +309,15 @@ class BadIPsAction(ActionBase): # pragma: no cover - may be unavailable 'ipjailmatches': "", }) except Exception as e: - self._logSys.info( + self._logSys.error( "Error unbanning IP %s for jail '%s' with action '%s': %s", ip, self._jail.name, self.banaction, e, exc_info=self._logSys.getEffectiveLevel()<=logging.DEBUG) else: - self._logSys.debug( - "Unbanned IP %s for jail '%s' with action '%s'", - ip, self._jail.name, self.banaction) + if self.log is "yes": + self._logSys.notice( + "Unbanned IP %s for jail '%s' with action '%s'", + ip, self._jail.name, self.banaction) finally: self._bannedips.remove(ip) -- cgit v1.2.1 From 70e53b55c558e9ccdb1a59d86031385dcd3023b0 Mon Sep 17 00:00:00 2001 From: Ben RUBSON Date: Sun, 19 Aug 2018 22:39:18 +0200 Subject: Typo --- config/action.d/badips.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/action.d/badips.py b/config/action.d/badips.py index 95bfbe14..c1c46ae3 100644 --- a/config/action.d/badips.py +++ b/config/action.d/badips.py @@ -71,7 +71,7 @@ class BadIPsAction(ActionBase): # pragma: no cover - may be unavailable Time in seconds between updating bad IPs blacklist. Default 900 (15 minutes) log : str, optional - Whether or not to log when an IP id (un)banned. + Whether or not to log when an IP is (un)banned. Default `yes`. agent : str, optional User agent transmitted to server. -- cgit v1.2.1 From 9d7c0e00c132c04a4d77eb5d684116780300e207 Mon Sep 17 00:00:00 2001 From: Ben RUBSON Date: Sat, 8 Sep 2018 09:28:42 +0200 Subject: Also log number of IPs removed/added --- config/action.d/badips.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/config/action.d/badips.py b/config/action.d/badips.py index c1c46ae3..97b45fa8 100644 --- a/config/action.d/badips.py +++ b/config/action.d/badips.py @@ -343,13 +343,16 @@ class BadIPsAction(ActionBase): # pragma: no cover - may be unavailable ips = self.getList( self.bancategory, self.score, self.age, self.bankey) # Remove old IPs no longer listed - self._unbanIPs(self._bannedips - ips) + s = self._bannedips - ips + m = len(s) + self._unbanIPs(s) # Add new IPs which are now listed - self._banIPs(ips - self._bannedips) - - self._logSys.debug( - "Updated IPs for jail '%s'. Update again in %i seconds", - self._jail.name, self.updateperiod) + s = ips - self._bannedips + p = len(s) + self._banIPs(s) + self._logSys.info( + "Updated IPs for jail '%s' (-%d/+%d). Update again in %i seconds", + self._jail.name, m, p, self.updateperiod) finally: self._timer = threading.Timer(self.updateperiod, self.update) self._timer.start() -- cgit v1.2.1 From 58b510a5be3b2399a602680d62a15118dad45cd3 Mon Sep 17 00:00:00 2001 From: sebres Date: Fri, 21 Sep 2018 14:14:00 +0200 Subject: filter.d/domino-smtp.conf: - recognizes failures logged using another format (something like session-id, IP enclosed in square brackets); - failregex extended to catch connections rejected for policy reasons (gh-2228); --- ChangeLog | 3 +++ config/filter.d/domino-smtp.conf | 9 ++++++--- fail2ban/tests/files/logs/domino-smtp | 5 +++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index e18b5ec0..30cffd14 100644 --- a/ChangeLog +++ b/ChangeLog @@ -46,6 +46,9 @@ ver. 0.10.4-dev-1 (20??/??/??) - development edition - extended with mode parameter, allows to avoid matching of messages like `auth challenge (REGISTER)` (see gh-2163) (currently `extra` as default to be backwards-compatible), see comments in filter how to set it to mode `normal`. +* `filter.d/domino-smtp.conf`: + - recognizes failures logged using another format (something like session-id, IP enclosed in square brackets); + - failregex extended to catch connections rejected for policy reasons (gh-2228); * `action.d/hostsdeny.conf`: fix parameter in config (dynamic parameters stating with '_' are protected and don't allowed in command-actions), see gh-2114; * decoding stability fix by wrong encoded characters like utf-8 surrogate pairs, etc (gh-2171): diff --git a/config/filter.d/domino-smtp.conf b/config/filter.d/domino-smtp.conf index cdc17736..638cd7c5 100644 --- a/config/filter.d/domino-smtp.conf +++ b/config/filter.d/domino-smtp.conf @@ -35,9 +35,12 @@ # 08-09-2014 06:14:27 smtp: postmaster [1.2.3.4] authentication failure using internet password # 08-09-2014 06:14:27 SMTP Server: Authentication failed for user postmaster ; connecting host 1.2.3.4 -__prefix = (?:\[[^\]]+\])?\s+ -failregex = ^%(__prefix)sSMTP Server: Authentication failed for user .*? \; connecting host $ - ^%(__prefix)ssmtp: (?:[^\[]+ )*\[\] authentication failure using internet password\s*$ +__prefix = (?:\[[^\]]+\])?\s* +__opt_data = (?::|\s+\[[^\]]+\]) +failregex = ^%(__prefix)sSMTP Server%(__opt_data)s Authentication failed for user .*? \; connecting host \[?\]?$ + ^%(__prefix)ssmtp: (?:[^\[]+ )*\[?\]? authentication failure using internet password\s*$ + ^%(__prefix)sSMTP Server%(__opt_data)s Connection from \[?\]? rejected for policy reasons\. + # Option: ignoreregex # Notes.: regex to ignore. If this regex matches, the line is ignored. # Values: TEXT diff --git a/fail2ban/tests/files/logs/domino-smtp b/fail2ban/tests/files/logs/domino-smtp index 4987e7ea..957d593f 100644 --- a/fail2ban/tests/files/logs/domino-smtp +++ b/fail2ban/tests/files/logs/domino-smtp @@ -6,3 +6,8 @@ 08-09-2014 06:14:27 smtp: postmaster [1.2.3.4] authentication failure using internet password # failJSON: { "time": "2016-11-07T22:21:20", "match": true , "host": "1.2.3.4" } 2016-11-07 22:21:20 smtp: postmaster [1.2.3.4] authentication failure using internet password + +# failJSON: { "time": "2018-09-19T17:25:50", "match": true , "host": "192.0.2.1", "desc":"different log-format" } +2018-09-19 17:25:50 SMTP Server [0D14:0027-1334] Authentication failed for user Bad Hacker ; connecting host [192.0.2.1] +# failJSON: { "time": "2018-09-19T17:25:52", "match": true , "host": "192.0.2.2", "desc":"gh-2228, rejected for policy reasons" } +2018-09-19 17:25:52 SMTP Server [000527:000013-0000001227564800] Connection from [192.0.2.2] rejected for policy reasons. IP address of connecting host not found in reverse DNS lookup. -- cgit v1.2.1 From 2a4c47ea32b2e483746c757896ff0d63a1403f6b Mon Sep 17 00:00:00 2001 From: sebres Date: Fri, 21 Sep 2018 14:47:40 +0200 Subject: .travis.yml: coveralls doesn't support python 2.6 now --- .travis.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 16a71249..ebfcd68e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,8 +29,11 @@ install: # Install Python packages / dependencies # coverage - travis_retry pip install coverage - # coveralls - - travis_retry pip install coveralls codecov + # coveralls (note coveralls doesn't support 2.6 now): + - if [[ $TRAVIS_PYTHON_VERSION != 2.6* ]]; then F2B_COV=1; else F2B_COV=0; fi + - if [[ "$F2B_COV" = 1 ]]; then travis_retry pip install coveralls; fi + # codecov: + - travis_retry pip install codecov # dnspython or dnspython3 - if [[ "$F2B_PY" = 2 ]]; then travis_retry pip install dnspython; fi - if [[ "$F2B_PY" = 3 ]]; then travis_retry pip install dnspython3; fi @@ -51,7 +54,7 @@ script: # Doc files should get installed on Travis under Linux - test -e /usr/share/doc/fail2ban/FILTERS after_success: - - coveralls + - if [[ "$F2B_COV" = 1 ]]; then coveralls; fi - codecov matrix: fast_finish: true -- cgit v1.2.1 From 17da4943df0716b394e2b40a973357fa0b423183 Mon Sep 17 00:00:00 2001 From: sebres Date: Wed, 26 Sep 2018 21:00:51 +0200 Subject: use short log-names for special pure numeric log-level (e.g. "Level 25" could be truncated by short formats) --- fail2ban/__init__.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fail2ban/__init__.py b/fail2ban/__init__.py index 317f53e7..fa6dcf77 100644 --- a/fail2ban/__init__.py +++ b/fail2ban/__init__.py @@ -79,3 +79,10 @@ logging.handlers.SysLogHandler.priority_map['NOTICE'] = 'notice' from time import strptime # strptime thread safety hack-around - http://bugs.python.org/issue7980 strptime("2012", "%Y") + +# short names for pure numeric log-level ("Level 25" could be truncated by short formats): +def _init(): + for i in xrange(50): + if logging.getLevelName(i).startswith('Level'): + logging.addLevelName(i, '#%02d-Lev.' % i) +_init() -- cgit v1.2.1 From 6067579464f32e81f4810692cf465219ae6b2537 Mon Sep 17 00:00:00 2001 From: sebres Date: Thu, 27 Sep 2018 12:51:57 +0200 Subject: Fixed action parameter `timeout`: it is a time (integer), so avoid to convert it to string (for replacement); fix substituteRecursiveTags using auto-convert to string. Closes gh-2241. --- fail2ban/helpers.py | 2 +- fail2ban/server/action.py | 2 +- fail2ban/tests/servertestcase.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fail2ban/helpers.py b/fail2ban/helpers.py index 49a5ca47..c6da8f30 100644 --- a/fail2ban/helpers.py +++ b/fail2ban/helpers.py @@ -429,7 +429,7 @@ def substituteRecursiveTags(inptags, conditional='', m = tre_search(value, m.end()) continue # if calling map - be sure we've string: - if noRecRepl: repl = uni_string(repl) + if not isinstance(repl, basestring): repl = uni_string(repl) value = value.replace('<%s>' % rtag, repl) #logSys.log(5, 'value now: %s' % value) # increment reference count: diff --git a/fail2ban/server/action.py b/fail2ban/server/action.py index 10d1acd9..ef953eff 100644 --- a/fail2ban/server/action.py +++ b/fail2ban/server/action.py @@ -331,7 +331,7 @@ class CommandAction(ActionBase): if not name.startswith('_') and not self.__init and not callable(value): # special case for some parameters: if name in ('timeout', 'bantime'): - value = str(MyTime.str2seconds(value)) + value = MyTime.str2seconds(value) # parameters changed - clear properties and substitution cache: self.__properties = None self.__substCache.clear() diff --git a/fail2ban/tests/servertestcase.py b/fail2ban/tests/servertestcase.py index 425ab757..610a1a5d 100644 --- a/fail2ban/tests/servertestcase.py +++ b/fail2ban/tests/servertestcase.py @@ -661,11 +661,11 @@ class Transmitter(TransmitterBase): self.assertEqual( self.transm.proceed( ["set", self.jailName, "action", action, "timeout", "10"]), - (0, "10")) + (0, 10)) self.assertEqual( self.transm.proceed( ["get", self.jailName, "action", action, "timeout"]), - (0, "10")) + (0, 10)) self.assertEqual( self.transm.proceed(["set", self.jailName, "delaction", action]), (0, None)) -- cgit v1.2.1 From 4b751c84c353ac0254addad6bb89167df2dcdd25 Mon Sep 17 00:00:00 2001 From: "Sergey G. Brester" Date: Tue, 2 Oct 2018 12:32:15 +0200 Subject: badips.py: Rewrite new bool option "log" as "loglevel" and revert default to log-level (DEBUG). --- config/action.d/badips.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/config/action.d/badips.py b/config/action.d/badips.py index 97b45fa8..0d03f1d1 100644 --- a/config/action.d/badips.py +++ b/config/action.d/badips.py @@ -32,6 +32,8 @@ else: # pragma: 3.x no cover from urllib import urlencode from fail2ban.server.actions import ActionBase +from fail2ban.helpers import str2LogLevel + class BadIPsAction(ActionBase): # pragma: no cover - may be unavailable @@ -70,9 +72,9 @@ class BadIPsAction(ActionBase): # pragma: no cover - may be unavailable updateperiod : int, optional Time in seconds between updating bad IPs blacklist. Default 900 (15 minutes) - log : str, optional - Whether or not to log when an IP is (un)banned. - Default `yes`. + loglevel : int/str, optional + Log level of the message when an IP is (un)banned. + Default `DEBUG`. agent : str, optional User agent transmitted to server. Default `Fail2Ban/ver.` @@ -89,7 +91,7 @@ class BadIPsAction(ActionBase): # pragma: no cover - may be unavailable return Request(url, headers={'User-Agent': self.agent}, **argv) def __init__(self, jail, name, category, score=3, age="24h", key=None, - banaction=None, bancategory=None, bankey=None, updateperiod=900, log="yes", agent="Fail2Ban", + banaction=None, bancategory=None, bankey=None, updateperiod=900, loglevel='DEBUG', agent="Fail2Ban", timeout=TIMEOUT): super(BadIPsAction, self).__init__(jail, name) @@ -102,7 +104,7 @@ class BadIPsAction(ActionBase): # pragma: no cover - may be unavailable self.banaction = banaction self.bancategory = bancategory or category self.bankey = bankey - self.log = log + self.loglevel = str2LogLevel(loglevel) if isinstance(val, basestring) else loglevel self.updateperiod = updateperiod self._bannedips = set() @@ -293,10 +295,9 @@ class BadIPsAction(ActionBase): # pragma: no cover - may be unavailable exc_info=self._logSys.getEffectiveLevel()<=logging.DEBUG) else: self._bannedips.add(ip) - if self.log is "yes": - self._logSys.notice( - "Banned IP %s for jail '%s' with action '%s'", - ip, self._jail.name, self.banaction) + self._logSys.log(self.loglevel, + "Banned IP %s for jail '%s' with action '%s'", + ip, self._jail.name, self.banaction) def _unbanIPs(self, ips): for ip in ips: @@ -314,10 +315,9 @@ class BadIPsAction(ActionBase): # pragma: no cover - may be unavailable ip, self._jail.name, self.banaction, e, exc_info=self._logSys.getEffectiveLevel()<=logging.DEBUG) else: - if self.log is "yes": - self._logSys.notice( - "Unbanned IP %s for jail '%s' with action '%s'", - ip, self._jail.name, self.banaction) + self._logSys.log(self.loglevel, + "Unbanned IP %s for jail '%s' with action '%s'", + ip, self._jail.name, self.banaction) finally: self._bannedips.remove(ip) -- cgit v1.2.1 From 65676baf8c5eb3917269f99e6f8fb2fb8e422291 Mon Sep 17 00:00:00 2001 From: "Sergey G. Brester" Date: Tue, 2 Oct 2018 12:38:29 +0200 Subject: fixed py3 incompatibility (for some reasons this file seems to be excluded from 2to3), anyway not needed, because int-type is already checked in str2LogLevel --- config/action.d/badips.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/action.d/badips.py b/config/action.d/badips.py index 0d03f1d1..1ad711f4 100644 --- a/config/action.d/badips.py +++ b/config/action.d/badips.py @@ -104,7 +104,7 @@ class BadIPsAction(ActionBase): # pragma: no cover - may be unavailable self.banaction = banaction self.bancategory = bancategory or category self.bankey = bankey - self.loglevel = str2LogLevel(loglevel) if isinstance(val, basestring) else loglevel + self.loglevel = str2LogLevel(loglevel) self.updateperiod = updateperiod self._bannedips = set() @@ -350,7 +350,7 @@ class BadIPsAction(ActionBase): # pragma: no cover - may be unavailable s = ips - self._bannedips p = len(s) self._banIPs(s) - self._logSys.info( + self._logSys.log(self.loglevel, "Updated IPs for jail '%s' (-%d/+%d). Update again in %i seconds", self._jail.name, m, p, self.updateperiod) finally: -- cgit v1.2.1 From 8614ca8c41a0d868f0d54d219666374216fb70d4 Mon Sep 17 00:00:00 2001 From: Shane Forsythe <2287983+shaneforsythe@users.noreply.github.com> Date: Tue, 2 Oct 2018 17:24:33 -0400 Subject: Update proftpd.conf proftpd 1.3.5e can leave inconsistent error message if ftp or mod_sftp is used Oct 2 15:45:31 ftp01 proftpd[5516]: 10.10.2.13 (10.10.2.189[10.10.2.189]) - SECURITY VIOLATION: Root login attempted Oct 2 15:45:44 ftp01 proftpd[5517]: 10.10.2.13 (10.10.2.189[10.10.2.189]) - SECURITY VIOLATION: Root login attempted. Fix regex to make trailing period optional, otherwise brute force attacks against root account using ftp are not blocked correctly. --- config/filter.d/proftpd.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/filter.d/proftpd.conf b/config/filter.d/proftpd.conf index 303be5e5..feb59f11 100644 --- a/config/filter.d/proftpd.conf +++ b/config/filter.d/proftpd.conf @@ -18,7 +18,7 @@ __suffix_failed_login = (User not authorized for login|No such user found|Incorr failregex = ^%(__prefix_line)s%(__hostname)s \(\S+\[\]\)[: -]+ USER .*: no such user found from \S+ \[\S+\] to \S+:\S+ *$ ^%(__prefix_line)s%(__hostname)s \(\S+\[\]\)[: -]+ USER .* \(Login failed\): %(__suffix_failed_login)s\s*$ - ^%(__prefix_line)s%(__hostname)s \(\S+\[\]\)[: -]+ SECURITY VIOLATION: .* login attempted\. *$ + ^%(__prefix_line)s%(__hostname)s \(\S+\[\]\)[: -]+ SECURITY VIOLATION: .* login attempted\.? *$ ^%(__prefix_line)s%(__hostname)s \(\S+\[\]\)[: -]+ Maximum login attempts \(\d+\) exceeded *$ ignoreregex = -- cgit v1.2.1 From aa565eb80ec6043317e8430cabcaf9c3f4e61578 Mon Sep 17 00:00:00 2001 From: sebres Date: Thu, 4 Oct 2018 11:26:22 +0200 Subject: release 0.10.4 - ten-four-on-due-date-ten-four --- ChangeLog | 2 +- fail2ban/version.py | 2 +- man/fail2ban-client.1 | 20 +++++++++----------- man/fail2ban-python.1 | 2 +- man/fail2ban-regex.1 | 5 ++++- man/fail2ban-server.1 | 11 +++-------- man/fail2ban-testcases.1 | 2 +- 7 files changed, 20 insertions(+), 24 deletions(-) diff --git a/ChangeLog b/ChangeLog index 30cffd14..02634e72 100644 --- a/ChangeLog +++ b/ChangeLog @@ -31,7 +31,7 @@ Incompatibility list (compared to v.0.9): IPv6-capable now. -ver. 0.10.4-dev-1 (20??/??/??) - development edition +ver. 0.10.4 (2018/10/04) - ten-four-on-due-date-ten-four ----------- ### Fixes diff --git a/fail2ban/version.py b/fail2ban/version.py index 2a515592..78a40761 100644 --- a/fail2ban/version.py +++ b/fail2ban/version.py @@ -24,7 +24,7 @@ __author__ = "Cyril Jaquier, Yaroslav Halchenko, Steven Hiscocks, Daniel Black" __copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2005-2016 Yaroslav Halchenko, 2013-2014 Steven Hiscocks, Daniel Black" __license__ = "GPL-v2+" -version = "0.10.4.dev1" +version = "0.10.4" def normVersion(): """ Returns fail2ban version in normalized machine-readable format""" diff --git a/man/fail2ban-client.1 b/man/fail2ban-client.1 index 6ed4ea4c..d11c4ee8 100644 --- a/man/fail2ban-client.1 +++ b/man/fail2ban-client.1 @@ -1,12 +1,12 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH FAIL2BAN-CLIENT "1" "April 2018" "fail2ban-client v0.10.4.dev1" "User Commands" +.TH FAIL2BAN-CLIENT "1" "October 2018" "fail2ban-client v0.10.4" "User Commands" .SH NAME fail2ban-client \- configure and control the server .SH SYNOPSIS .B fail2ban-client [\fI\,OPTIONS\/\fR] \fI\,\/\fR .SH DESCRIPTION -Fail2Ban v0.10.4.dev1 reads log file that contains password failure report +Fail2Ban v0.10.4 reads log file that contains password failure report and bans the corresponding IP addresses using firewall rules. .SH OPTIONS .TP @@ -67,7 +67,7 @@ convert time abbreviation format to seconds display this help message .TP \fB\-V\fR, \fB\-\-version\fR -print the version +print the version (\fB\-V\fR returns machine\-readable short format) .SH COMMAND .IP BASIC @@ -210,6 +210,12 @@ adds to the ignore list of removes from the ignore list of .TP +\fBset ignorecommand \fR +sets ignorecommand of +.TP +\fBset ignorecache \fR +sets ignorecache of +.TP \fBset addlogpath ['tail']\fR adds to the monitoring list of , optionally starting at @@ -241,9 +247,6 @@ for removes the regular expression at for failregex .TP -\fBset ignorecommand \fR -sets ignorecommand of -.TP \fBset addignoreregex \fR adds the regular expression which should match pattern @@ -438,11 +441,6 @@ the action for \fI/etc/fail2ban/*\fR .SH "REPORTING BUGS" Report bugs to https://github.com/fail2ban/fail2ban/issues -.SH COPYRIGHT -Copyright \(co 2004\-2008 Cyril Jaquier, 2008\- Fail2Ban Contributors -.br -Copyright of modifications held by their respective authors. -Licensed under the GNU General Public License v2 (GPL). .SH "SEE ALSO" .br fail2ban-server(1) diff --git a/man/fail2ban-python.1 b/man/fail2ban-python.1 index 9880726e..137bc5c6 100644 --- a/man/fail2ban-python.1 +++ b/man/fail2ban-python.1 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH FAIL2BAN-PYTHON "1" "April 2018" "fail2ban-python f2bversion" "User Commands" +.TH FAIL2BAN-PYTHON "1" "October 2018" "fail2ban-python f2bversion" "User Commands" .SH NAME fail2ban-python \- a helper for Fail2Ban to assure that the same Python is used .SH DESCRIPTION diff --git a/man/fail2ban-regex.1 b/man/fail2ban-regex.1 index ebc9fa4c..0ef8056e 100644 --- a/man/fail2ban-regex.1 +++ b/man/fail2ban-regex.1 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH FAIL2BAN-REGEX "1" "April 2018" "fail2ban-regex 0.10.4.dev1" "User Commands" +.TH FAIL2BAN-REGEX "1" "October 2018" "fail2ban-regex 0.10.4" "User Commands" .SH NAME fail2ban-regex \- test Fail2ban "failregex" option .SH SYNOPSIS @@ -72,6 +72,9 @@ journalctl style matches overriding filter file. \fB\-l\fR LOG_LEVEL, \fB\-\-log\-level\fR=\fI\,LOG_LEVEL\/\fR Log level for the Fail2Ban logger to use .TP +\fB\-V\fR +get version in machine\-readable short format +.TP \fB\-v\fR, \fB\-\-verbose\fR Increase verbosity .TP diff --git a/man/fail2ban-server.1 b/man/fail2ban-server.1 index 075fb378..6390c991 100644 --- a/man/fail2ban-server.1 +++ b/man/fail2ban-server.1 @@ -1,12 +1,12 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH FAIL2BAN-SERVER "1" "April 2018" "fail2ban-server v0.10.4.dev1" "User Commands" +.TH FAIL2BAN-SERVER "1" "October 2018" "fail2ban-server v0.10.4" "User Commands" .SH NAME fail2ban-server \- start the server .SH SYNOPSIS .B fail2ban-server [\fI\,OPTIONS\/\fR] .SH DESCRIPTION -Fail2Ban v0.10.4.dev1 reads log file that contains password failure report +Fail2Ban v0.10.4 reads log file that contains password failure report and bans the corresponding IP addresses using firewall rules. .SH OPTIONS .TP @@ -67,14 +67,9 @@ convert time abbreviation format to seconds display this help message .TP \fB\-V\fR, \fB\-\-version\fR -print the version +print the version (\fB\-V\fR returns machine\-readable short format) .SH "REPORTING BUGS" Report bugs to https://github.com/fail2ban/fail2ban/issues -.SH COPYRIGHT -Copyright \(co 2004\-2008 Cyril Jaquier, 2008\- Fail2Ban Contributors -.br -Copyright of modifications held by their respective authors. -Licensed under the GNU General Public License v2 (GPL). .SH "SEE ALSO" .br fail2ban-client(1) diff --git a/man/fail2ban-testcases.1 b/man/fail2ban-testcases.1 index 56abc600..95a3604c 100644 --- a/man/fail2ban-testcases.1 +++ b/man/fail2ban-testcases.1 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH FAIL2BAN-TESTCASES "1" "April 2018" "fail2ban-testcases 0.10.4.dev1" "User Commands" +.TH FAIL2BAN-TESTCASES "1" "October 2018" "fail2ban-testcases 0.10.4" "User Commands" .SH NAME fail2ban-testcases \- run Fail2Ban unit-tests .SH SYNOPSIS -- cgit v1.2.1 From 0ae02ba2a1d7ab842278a8bb8d5f5481c8d04bf5 Mon Sep 17 00:00:00 2001 From: sebres Date: Thu, 4 Oct 2018 11:57:56 +0200 Subject: version bump (back to dev-version) --- ChangeLog | 10 ++++++++++ fail2ban/version.py | 2 +- man/fail2ban-client.1 | 4 ++-- man/fail2ban-regex.1 | 2 +- man/fail2ban-server.1 | 4 ++-- man/fail2ban-testcases.1 | 2 +- 6 files changed, 17 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index 02634e72..0cda45fa 100644 --- a/ChangeLog +++ b/ChangeLog @@ -31,6 +31,16 @@ Incompatibility list (compared to v.0.9): IPv6-capable now. +ver. 0.10.5-dev-1 (20??/??/??) - development edition +----------- + +### Fixes + +### New Features + +### Enhancements + + ver. 0.10.4 (2018/10/04) - ten-four-on-due-date-ten-four ----------- diff --git a/fail2ban/version.py b/fail2ban/version.py index 78a40761..42adf49b 100644 --- a/fail2ban/version.py +++ b/fail2ban/version.py @@ -24,7 +24,7 @@ __author__ = "Cyril Jaquier, Yaroslav Halchenko, Steven Hiscocks, Daniel Black" __copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2005-2016 Yaroslav Halchenko, 2013-2014 Steven Hiscocks, Daniel Black" __license__ = "GPL-v2+" -version = "0.10.4" +version = "0.10.5.dev1" def normVersion(): """ Returns fail2ban version in normalized machine-readable format""" diff --git a/man/fail2ban-client.1 b/man/fail2ban-client.1 index d11c4ee8..a2d8d999 100644 --- a/man/fail2ban-client.1 +++ b/man/fail2ban-client.1 @@ -1,12 +1,12 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH FAIL2BAN-CLIENT "1" "October 2018" "fail2ban-client v0.10.4" "User Commands" +.TH FAIL2BAN-CLIENT "1" "October 2018" "fail2ban-client v0.10.5.dev1" "User Commands" .SH NAME fail2ban-client \- configure and control the server .SH SYNOPSIS .B fail2ban-client [\fI\,OPTIONS\/\fR] \fI\,\/\fR .SH DESCRIPTION -Fail2Ban v0.10.4 reads log file that contains password failure report +Fail2Ban v0.10.5.dev1 reads log file that contains password failure report and bans the corresponding IP addresses using firewall rules. .SH OPTIONS .TP diff --git a/man/fail2ban-regex.1 b/man/fail2ban-regex.1 index 0ef8056e..fe7d37ab 100644 --- a/man/fail2ban-regex.1 +++ b/man/fail2ban-regex.1 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH FAIL2BAN-REGEX "1" "October 2018" "fail2ban-regex 0.10.4" "User Commands" +.TH FAIL2BAN-REGEX "1" "October 2018" "fail2ban-regex 0.10.5.dev1" "User Commands" .SH NAME fail2ban-regex \- test Fail2ban "failregex" option .SH SYNOPSIS diff --git a/man/fail2ban-server.1 b/man/fail2ban-server.1 index 6390c991..098c3eac 100644 --- a/man/fail2ban-server.1 +++ b/man/fail2ban-server.1 @@ -1,12 +1,12 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH FAIL2BAN-SERVER "1" "October 2018" "fail2ban-server v0.10.4" "User Commands" +.TH FAIL2BAN-SERVER "1" "October 2018" "fail2ban-server v0.10.5.dev1" "User Commands" .SH NAME fail2ban-server \- start the server .SH SYNOPSIS .B fail2ban-server [\fI\,OPTIONS\/\fR] .SH DESCRIPTION -Fail2Ban v0.10.4 reads log file that contains password failure report +Fail2Ban v0.10.5.dev1 reads log file that contains password failure report and bans the corresponding IP addresses using firewall rules. .SH OPTIONS .TP diff --git a/man/fail2ban-testcases.1 b/man/fail2ban-testcases.1 index 95a3604c..c61d4b3f 100644 --- a/man/fail2ban-testcases.1 +++ b/man/fail2ban-testcases.1 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH FAIL2BAN-TESTCASES "1" "October 2018" "fail2ban-testcases 0.10.4" "User Commands" +.TH FAIL2BAN-TESTCASES "1" "October 2018" "fail2ban-testcases 0.10.5.dev1" "User Commands" .SH NAME fail2ban-testcases \- run Fail2Ban unit-tests .SH SYNOPSIS -- cgit v1.2.1 From e99635650af10da80eca55872d69b38a5badeb91 Mon Sep 17 00:00:00 2001 From: sebres Date: Tue, 9 Oct 2018 18:24:50 +0200 Subject: dnsToIp and other DNSUtils primitives uses sets instead of lists now (speed-up search of ip, e. g. ignoreself/ignoreip check process) --- fail2ban/server/ipdns.py | 10 +++++----- fail2ban/tests/filtertestcase.py | 9 +++++---- fail2ban/tests/utils.py | 12 ++++++++---- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/fail2ban/server/ipdns.py b/fail2ban/server/ipdns.py index 2841eac1..959e39c4 100644 --- a/fail2ban/server/ipdns.py +++ b/fail2ban/server/ipdns.py @@ -64,7 +64,7 @@ class DNSUtils: if ips is not None: return ips # retrieve ips - ips = list() + ips = set() saveerr = None for fam, ipfam in ((socket.AF_INET, IPAddr.FAM_IPv4), (socket.AF_INET6, IPAddr.FAM_IPv6)): try: @@ -75,7 +75,7 @@ class DNSUtils: # (some python-versions resp. host configurations causes returning of integer there): ip = IPAddr(str(result[4][0]), ipfam) if ip.isValid: - ips.append(ip) + ips.add(ip) except Exception as e: saveerr = e if not ips and saveerr: @@ -103,19 +103,19 @@ class DNSUtils: def textToIp(text, useDns): """ Return the IP of DNS found in a given text. """ - ipList = list() + ipList = set() # Search for plain IP plainIP = IPAddr.searchIP(text) if plainIP is not None: ip = IPAddr(plainIP) if ip.isValid: - ipList.append(ip) + ipList.add(ip) # If we are allowed to resolve -- give it a try if nothing was found if useDns in ("yes", "warn") and not ipList: # Try to get IP from possible DNS ip = DNSUtils.dnsToIp(text) - ipList.extend(ip) + ipList.update(ip) if ip and useDns == "warn": logSys.warning("Determined IP using DNS Lookup: %s = %s", text, ipList) diff --git a/fail2ban/tests/filtertestcase.py b/fail2ban/tests/filtertestcase.py index 350d46b0..b22cd0f8 100644 --- a/fail2ban/tests/filtertestcase.py +++ b/fail2ban/tests/filtertestcase.py @@ -1800,7 +1800,7 @@ class DNSUtilsNetworkTests(unittest.TestCase): def testUseDns(self): res = DNSUtils.textToIp('www.example.com', 'no') - self.assertEqual(res, []) + self.assertSortedEqual(res, []) res = DNSUtils.textToIp('www.example.com', 'warn') # sort ipaddr, IPv4 is always smaller as IPv6 self.assertSortedEqual(res, ['93.184.216.34', '2606:2800:220:1:248:1893:25c8:1946']) @@ -1821,12 +1821,13 @@ class DNSUtilsNetworkTests(unittest.TestCase): # sort ipaddr, IPv4 is always smaller as IPv6 self.assertSortedEqual(res, ['93.184.216.34', '2606:2800:220:1:248:1893:25c8:1946']) else: - self.assertEqual(res, []) + self.assertSortedEqual(res, []) # pure ips: for s in ('93.184.216.34', '2606:2800:220:1:248:1893:25c8:1946'): ips = DNSUtils.textToIp(s, 'yes') - self.assertEqual(ips, [s]) - self.assertTrue(isinstance(ips[0], IPAddr)) + self.assertSortedEqual(ips, [s]) + for ip in ips: + self.assertTrue(isinstance(ip, IPAddr)) def testIpToName(self): unittest.F2B.SkipIfNoNetwork() diff --git a/fail2ban/tests/utils.py b/fail2ban/tests/utils.py index 8bf42994..65e09005 100644 --- a/fail2ban/tests/utils.py +++ b/fail2ban/tests/utils.py @@ -322,12 +322,16 @@ def initTests(opts): # precache all wrong dns to ip's used in test cases: c = DNSUtils.CACHE_nameToIp for i in ( - ('999.999.999.999', []), - ('abcdef.abcdef', []), - ('192.168.0.', []), - ('failed.dns.ch', []), + ('999.999.999.999', set()), + ('abcdef.abcdef', set()), + ('192.168.0.', set()), + ('failed.dns.ch', set()), ): c.set(*i) + # if fast - precache all host names as localhost addresses (speed-up getSelfIPs/ignoreself): + if unittest.F2B.fast: # pragma: no cover + for i in DNSUtils.getSelfNames(): + c.set(i, DNSUtils.dnsToIp('localhost')) def mtimesleep(): -- cgit v1.2.1 From 657b147c0d7830f3600f3dc7feaa4815a7e19fde Mon Sep 17 00:00:00 2001 From: sebres Date: Wed, 10 Oct 2018 12:25:53 +0200 Subject: fixed dependency issue if setup invoked using python 3.x: invocation of 2to3 takes place after setup (and __init__.py) loaded; closes gh-2255. --- fail2ban/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fail2ban/__init__.py b/fail2ban/__init__.py index fa6dcf77..61789a45 100644 --- a/fail2ban/__init__.py +++ b/fail2ban/__init__.py @@ -82,7 +82,7 @@ strptime("2012", "%Y") # short names for pure numeric log-level ("Level 25" could be truncated by short formats): def _init(): - for i in xrange(50): + for i in range(50): if logging.getLevelName(i).startswith('Level'): logging.addLevelName(i, '#%02d-Lev.' % i) _init() -- cgit v1.2.1 From 0df221b54b170ac6fbd204c0e6cf1c7b80f54e67 Mon Sep 17 00:00:00 2001 From: dienteperro Date: Thu, 15 Nov 2018 14:34:51 -0500 Subject: "be" instead of "me" in shorewall.conf --- config/action.d/shorewall.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/action.d/shorewall.conf b/config/action.d/shorewall.conf index f5f2c775..5626c596 100644 --- a/config/action.d/shorewall.conf +++ b/config/action.d/shorewall.conf @@ -9,7 +9,7 @@ # connections. So if the attempter goes on trying using the same connection # he could even log in. In order to get the same behavior of the iptable # action (so that the ban is immediate) the /etc/shorewall/shorewall.conf -# file should me modified with "BLACKLISTNEWONLY=No". Note that as of +# file should be modified with "BLACKLISTNEWONLY=No". Note that as of # Shorewall 4.5.13 BLACKLISTNEWONLY is deprecated; however the equivalent # of BLACKLISTNEWONLY=No can now be achieved by setting BLACKLIST="ALL". # -- cgit v1.2.1 From 1c1d2cc435d6e8f1eb4a1b60c935a1385a82e295 Mon Sep 17 00:00:00 2001 From: sebres Date: Mon, 19 Nov 2018 21:19:57 +0100 Subject: introduces new failregex-flag tag `` signaled that the access to service was gained (ATM used similar to , but does not added to matches); filter.d/sshd.conf: extended with new rules: - Disconnecting ...: Change of username or service not allowed - Disconnected from ... [preauth] (extra/aggressive mode only) --- config/filter.d/sshd.conf | 4 +++- fail2ban/server/filter.py | 14 ++++++++++---- .../tests/config/filter.d/zzz-sshd-obsolete-multiline.conf | 1 + fail2ban/tests/files/logs/sshd | 10 ++++++++++ 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/config/filter.d/sshd.conf b/config/filter.d/sshd.conf index 60efead7..906e46cf 100644 --- a/config/filter.d/sshd.conf +++ b/config/filter.d/sshd.conf @@ -54,10 +54,11 @@ cmnfailre = ^[aA]uthentication (?:failure|error|failed) for .* ^%(__pam_auth)s\(sshd:auth\):\s+authentication failure;(?:\s+(?:(?:logname|e?uid|tty)=\S*)){0,4}\s+ruser=\S*\s+rhost=(?:\s+user=\S*)?%(__suff)s$ ^(error: )?maximum authentication attempts exceeded for .* from %(__on_port_opt)s(?: ssh\d*)?%(__suff)s$ ^User .+ not allowed because account is locked%(__suff)s + ^Disconnecting(?: from)?(?: (?:invalid|authenticating)) user \S+ %(__on_port_opt)s:\s*Change of username or service not allowed:\s*.*\[preauth\]\s*$ ^Disconnecting: Too many authentication failures(?: for .+?)?%(__suff)s$ ^Received disconnect from %(__on_port_opt)s:\s*11: ^Connection closed by%(__authng_user)s -suff-onclosed> - ^Accepted \w+ for \S+ from (?:\s|$) + ^Accepted \w+ for \S+ from (?:\s|$) mdre-normal = # used to differentiate "connection closed" with and without `[preauth]` (fail/nofail cases in ddos mode) @@ -74,6 +75,7 @@ mdre-extra = ^Received disconnect from %(__on_p ^Unable to negotiate with %(__on_port_opt)s: no matching <__alg_match> found. ^Unable to negotiate a <__alg_match> ^no matching <__alg_match> found: + ^Disconnected(?: from)?(?: (?:invalid|authenticating)) user \S+ %(__on_port_opt)s \[preauth\]\s*$ mdrp-extra-suff-onclosed = %(mdrp-normal-suff-onclosed)s mdre-aggressive = %(mdre-ddos)s diff --git a/fail2ban/server/filter.py b/fail2ban/server/filter.py index 681e708c..1e7d5e76 100644 --- a/fail2ban/server/filter.py +++ b/fail2ban/server/filter.py @@ -670,16 +670,21 @@ class Filter(JailThread): mlfidFail = self.mlfidCache.get(mlfid) if self.__mlfidCache else None users = None nfflgs = 0 - if fail.get('nofail'): nfflgs |= 1 + if fail.get("mlfgained"): + nfflgs |= 9 + if not fail.get('nofail'): + fail['nofail'] = fail["mlfgained"] + elif fail.get('nofail'): nfflgs |= 1 if fail.get('mlfforget'): nfflgs |= 2 # if multi-line failure id (connection id) known: if mlfidFail: mlfidGroups = mlfidFail[1] # update users set (hold all users of connect): users = self._updateUsers(mlfidGroups, fail.get('user')) - # be sure we've correct current state ('nofail' only from last failure) + # be sure we've correct current state ('nofail' and 'mlfgained' only from last failure) try: del mlfidGroups['nofail'] + del mlfidGroups['mlfgained'] except KeyError: pass # # ATM incremental (non-empty only) merge deactivated (for future version only), @@ -703,16 +708,17 @@ class Filter(JailThread): # we've new user, reset 'nofail' because of multiple users attempts: try: del fail['nofail'] + nfflgs &= ~1 # reset nofail except KeyError: pass # merge matches: - if not fail.get('nofail'): # current state (corresponding users) + if not (nfflgs & 1): # current nofail state (corresponding users) try: m = fail.pop("nofail-matches") m += fail.get("matches", []) except KeyError: m = fail.get("matches", []) - if not (nfflgs & 2): # not mlfforget: + if not (nfflgs & 8): # no gain signaled m += failRegex.getMatchedTupleLines() fail["matches"] = m elif not (nfflgs & 2) and (nfflgs & 1): # not mlfforget and nofail: diff --git a/fail2ban/tests/config/filter.d/zzz-sshd-obsolete-multiline.conf b/fail2ban/tests/config/filter.d/zzz-sshd-obsolete-multiline.conf index 98fca7f5..cc7737ec 100644 --- a/fail2ban/tests/config/filter.d/zzz-sshd-obsolete-multiline.conf +++ b/fail2ban/tests/config/filter.d/zzz-sshd-obsolete-multiline.conf @@ -64,6 +64,7 @@ mdre-extra = ^%(__prefix_line_sl)sReceived disconnect from %(__on_port_opt ^%(__prefix_line_sl)sUnable to negotiate with %(__on_port_opt)s: no matching <__alg_match> found. ^%(__prefix_line_ml1)sConnection from %(__on_port_opt)s%(__prefix_line_ml2)sUnable to negotiate a <__alg_match> ^%(__prefix_line_ml1)sConnection from %(__on_port_opt)s%(__prefix_line_ml2)sno matching <__alg_match> found: + ^%(__prefix_line_sl)sDisconnected(?: from)?(?: (?:invalid|authenticating)) user \S+ %(__on_port_opt)s \[preauth\]\s*$ mdre-aggressive = %(mdre-ddos)s %(mdre-extra)s diff --git a/fail2ban/tests/files/logs/sshd b/fail2ban/tests/files/logs/sshd index e2b3d456..f32f3462 100644 --- a/fail2ban/tests/files/logs/sshd +++ b/fail2ban/tests/files/logs/sshd @@ -253,6 +253,13 @@ Mar 7 18:53:34 bar sshd[1559]: Accepted password for known from 192.0.2.116 por # failJSON: { "match": false , "desc": "No failure" } Mar 7 18:53:38 bar sshd[1559]: Connection closed by 192.0.2.116 +# failJSON: { "time": "2005-03-19T16:47:48", "match": true , "attempts": 1, "user": "admin", "host": "192.0.2.117", "desc": "Failure: attempt invalid user" } +Mar 19 16:47:48 test sshd[5672]: Invalid user admin from 192.0.2.117 port 44004 +# failJSON: { "time": "2005-03-19T16:47:49", "match": true , "attempts": 2, "user": "admin", "host": "192.0.2.117", "desc": "Failure: attempt to change user (disallowed)" } +Mar 19 16:47:49 test sshd[5672]: Disconnecting invalid user admin 192.0.2.117 port 44004: Change of username or service not allowed: (admin,ssh-connection) -> (user,ssh-connection) [preauth] +# failJSON: { "time": "2005-03-19T16:47:50", "match": false, "desc": "Disconnected during preauth phase (no failure in normal mode)" } +Mar 19 16:47:50 srv sshd[5672]: Disconnected from authenticating user admin 192.0.2.6 port 33553 [preauth] + # filterOptions: [{"mode": "ddos"}, {"mode": "aggressive"}] # http://forums.powervps.com/showthread.php?t=1667 @@ -334,3 +341,6 @@ Oct 26 15:30:40 localhost sshd[14737]: Unable to negotiate with 192.0.2.2 port 5 Nov 26 13:03:38 srv sshd[14737]: Unable to negotiate with 192.0.2.4 port 50404: no matching host key type found. Their offer: ssh-dss # failJSON: { "time": "2004-11-26T13:03:39", "match": true , "host": "192.0.2.5", "desc": "No matching everything ... found." } Nov 26 13:03:39 srv sshd[14738]: fatal: Unable to negotiate with 192.0.2.5 port 55555: no matching everything new here found. Their offer: ... + +# failJSON: { "time": "2004-11-26T16:47:51", "match": true , "host": "192.0.2.6", "desc": "Disconnected during preauth phase (in extra/aggressive mode)" } +Nov 26 16:47:51 srv sshd[19320]: Disconnected from authenticating user root 192.0.2.6 port 33553 [preauth] -- cgit v1.2.1 From 0ac5c8941c5ddab69bd7b251c70d714d5eafe3be Mon Sep 17 00:00:00 2001 From: "Sergey G. Brester" Date: Tue, 20 Nov 2018 12:39:38 +0100 Subject: Update ChangeLog --- ChangeLog | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ChangeLog b/ChangeLog index 0cda45fa..a45de141 100644 --- a/ChangeLog +++ b/ChangeLog @@ -35,8 +35,13 @@ ver. 0.10.5-dev-1 (20??/??/??) - development edition ----------- ### Fixes +* `filter.d/sshd.conf`: + - captures `Disconnecting ...: Change of username or service not allowed` (gh-2239, gh-2279) + - captures `Disconnected from ... [preauth]` (`extra`/`aggressive` mode and preauth phase only, gh-2239, gh-2279) ### 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) ### Enhancements -- cgit v1.2.1 From 52f6bfc2df96fa4258c742a7369d25e8cf0c5f2b Mon Sep 17 00:00:00 2001 From: Jens Diemer Date: Fri, 23 Nov 2018 15:47:37 +0100 Subject: Add link to https://fail2ban.readthedocs.io in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aa3203c1..88ae1d05 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ mechanisms if you really want to protect services. ------|------ This README is a quick introduction to Fail2Ban. More documentation, FAQ, and HOWTOs -to be found on fail2ban(1) manpage, [Wiki](https://github.com/fail2ban/fail2ban/wiki) +to be found on fail2ban(1) manpage, [Documentation](https://fail2ban.readthedocs.io/), [Wiki](https://github.com/fail2ban/fail2ban/wiki) and the website: https://www.fail2ban.org Installation: -- cgit v1.2.1 From a51f82770bf26745f67b877021bd2d8b0ee020af Mon Sep 17 00:00:00 2001 From: CrazyMax Date: Sat, 24 Nov 2018 22:44:44 +0100 Subject: 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 -- cgit v1.2.1 From a8fbdd6a872fe6d2c49c27c924c0c71442a3b771 Mon Sep 17 00:00:00 2001 From: CrazyMax Date: Sat, 24 Nov 2018 23:13:50 +0100 Subject: 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 -- cgit v1.2.1 From a160c38211877029461d11d1614d6c37a6a2d2ad Mon Sep 17 00:00:00 2001 From: CrazyMax Date: Sat, 24 Nov 2018 23:16:27 +0100 Subject: 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 -- cgit v1.2.1 From 9b918bba2f672780fb4469294d80ba7deb6b8cab Mon Sep 17 00:00:00 2001 From: Angelo Compagnucci Date: Sun, 25 Nov 2018 21:13:55 +0100 Subject: setup.py: adding option to install without tests Tests files are not always needed especially when installing on low resource systems like an embedded one. This patch adds the --without-tests option to skip building and installing of tests files. Signed-off-by: Angelo Compagnucci --- setup.py | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/setup.py b/setup.py index 8da29268..bde979e0 100755 --- a/setup.py +++ b/setup.py @@ -119,9 +119,11 @@ class install_scripts_f2b(install_scripts): class install_command_f2b(install): user_options = install.user_options + [ ('disable-2to3', None, 'Specify to deactivate 2to3, e.g. if the install runs from fail2ban test-cases.'), + ('without-tests', None, 'without tests files installation'), ] def initialize_options(self): self.disable_2to3 = None + self.without_tests = None install.initialize_options(self) def finalize_options(self): global _2to3 @@ -132,6 +134,28 @@ class install_command_f2b(install): cmdclass = self.distribution.cmdclass cmdclass['build_py'] = build_py_2to3 cmdclass['build_scripts'] = build_scripts_2to3 + if self.without_tests is None: + self.distribution.scripts += [ + 'bin/fail2ban-testcases', + ] + + self.distribution.packages += [ + 'fail2ban.tests', + 'fail2ban.tests.action_d', + ] + + self.distribution.package_data = { + 'fail2ban.tests': + [ join(w[0], f).replace("fail2ban/tests/", "", 1) + for w in os.walk('fail2ban/tests/files') + for f in w[2]] + + [ join(w[0], f).replace("fail2ban/tests/", "", 1) + for w in os.walk('fail2ban/tests/config') + for f in w[2]] + + [ join(w[0], f).replace("fail2ban/tests/", "", 1) + for w in os.walk('fail2ban/tests/action_d') + for f in w[2]] + } install.finalize_options(self) def run(self): install.run(self) @@ -208,35 +232,20 @@ setup( license = "GPL", platforms = "Posix", cmdclass = { - 'build_py': build_py, 'build_scripts': build_scripts, + 'build_py': build_py, 'build_scripts': build_scripts, 'install_scripts': install_scripts_f2b, 'install': install_command_f2b }, scripts = [ 'bin/fail2ban-client', 'bin/fail2ban-server', 'bin/fail2ban-regex', - 'bin/fail2ban-testcases', # 'bin/fail2ban-python', -- link (binary), will be installed via install_scripts_f2b wrapper ], packages = [ 'fail2ban', 'fail2ban.client', 'fail2ban.server', - 'fail2ban.tests', - 'fail2ban.tests.action_d', ], - package_data = { - 'fail2ban.tests': - [ join(w[0], f).replace("fail2ban/tests/", "", 1) - for w in os.walk('fail2ban/tests/files') - for f in w[2]] + - [ join(w[0], f).replace("fail2ban/tests/", "", 1) - for w in os.walk('fail2ban/tests/config') - for f in w[2]] + - [ join(w[0], f).replace("fail2ban/tests/", "", 1) - for w in os.walk('fail2ban/tests/action_d') - for f in w[2]] - }, data_files = [ ('/etc/fail2ban', glob("config/*.conf") -- cgit v1.2.1 From c4c713ca6ef37d3587e11acaa4d708013121cc8a Mon Sep 17 00:00:00 2001 From: "Sergey G. Brester" Date: Mon, 26 Nov 2018 13:10:30 +0100 Subject: Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 88ae1d05..8e9f5c3a 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,8 @@ mechanisms if you really want to protect services. ------|------ This README is a quick introduction to Fail2Ban. More documentation, FAQ, and HOWTOs -to be found on fail2ban(1) manpage, [Documentation](https://fail2ban.readthedocs.io/), [Wiki](https://github.com/fail2ban/fail2ban/wiki) +to be found on fail2ban(1) manpage, [Wiki](https://github.com/fail2ban/fail2ban/wiki), +[Developers documentation](https://fail2ban.readthedocs.io/) and the website: https://www.fail2ban.org Installation: -- cgit v1.2.1 From 90516d6b671aa35897d5affa1655e99569a448e3 Mon Sep 17 00:00:00 2001 From: CrazyMax Date: Wed, 28 Nov 2018 00:37:24 +0100 Subject: 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 -- cgit v1.2.1 From 0245777c845315d2cc3e716d897059091ee91b22 Mon Sep 17 00:00:00 2001 From: sebres Date: Tue, 11 Dec 2018 14:48:48 +0100 Subject: SafeConfigParserWithIncludes: fixed read of included config-files (expands with localized version, so `inc.local` overwrites options of `inc.conf` for config-files included with before/after); added new test to cover this case. --- fail2ban/client/configparserinc.py | 12 ++++++++ fail2ban/tests/clientreadertestcase.py | 51 +++++++++++++++++++++++++++++++++- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/fail2ban/client/configparserinc.py b/fail2ban/client/configparserinc.py index 70dfd91b..e0f39579 100644 --- a/fail2ban/client/configparserinc.py +++ b/fail2ban/client/configparserinc.py @@ -73,6 +73,17 @@ else: # pragma: no cover return self._cp_interpolate_some(option, accum, rest, section, map, *args, **kwargs) SafeConfigParser._interpolate_some = _interpolate_some +def _expandConfFilesWithLocal(filenames): + """Expands config files with local extension. + """ + newFilenames = [] + for filename in filenames: + newFilenames.append(filename) + localname = os.path.splitext(filename)[0] + '.local' + if localname not in filenames and os.path.isfile(localname): + newFilenames.append(localname) + return newFilenames + # Gets the instance of the logger. logSys = getLogger(__name__) logLevel = 7 @@ -245,6 +256,7 @@ after = 1.conf def _getIncludes(self, filenames, seen=[]): if not isinstance(filenames, list): filenames = [ filenames ] + filenames = _expandConfFilesWithLocal(filenames) # retrieve or cache include paths: if self._cfg_share: # cache/share include list: diff --git a/fail2ban/tests/clientreadertestcase.py b/fail2ban/tests/clientreadertestcase.py index 184595ab..96e6c7a4 100644 --- a/fail2ban/tests/clientreadertestcase.py +++ b/fail2ban/tests/clientreadertestcase.py @@ -28,7 +28,8 @@ import re import shutil import tempfile import unittest -from ..client.configreader import ConfigReader, ConfigReaderUnshared, NoSectionError +from ..client.configreader import ConfigReader, ConfigReaderUnshared, \ + DefinitionInitConfigReader, NoSectionError from ..client import configparserinc from ..client.jailreader import JailReader, extractOptions from ..client.filterreader import FilterReader @@ -125,6 +126,54 @@ option = %s self._remove("c.d/90.conf") self.assertEqual(self._getoption(), 2) + def testLocalInIncludes(self): + self._write("c.conf", value=None, content=""" +[INCLUDES] +before = ib.conf +after = ia.conf +[Definition] +test = %(default/test)s +""") + self._write("ib.conf", value=None, content=""" +[DEFAULT] +test = A +[Definition] +option = 1 +""") + self._write("ib.local", value=None, content=""" +[DEFAULT] +test = B +[Definition] +option = 2 +""") + self._write("ia.conf", value=None, content=""" +[DEFAULT] +test = C +[Definition] +oafter = 3 +""") + self._write("ia.local", value=None, content=""" +[DEFAULT] +test = D +[Definition] +oafter = 4 +""") + class TestDefConfReader(DefinitionInitConfigReader): + _configOpts = { + "option": ["int", None], + "oafter": ["int", None], + "test": ["string", None], + } + self.c = TestDefConfReader('c', 'option', {}) + self.c.setBaseDir(self.d) + self.assertTrue(self.c.read()) + self.c.getOptions({}, all=True) + o = self.c.getCombined() + # test local wins (overwrite all options): + self.assertEqual(o.get('option'), 2) + self.assertEqual(o.get('oafter'), 4) + self.assertEqual(o.get('test'), 'D') + def testInterpolations(self): self.assertFalse(self.c.read('i')) # nothing is there yet self._write("i.conf", value=None, content=""" -- cgit v1.2.1 From 9b96a7de89e1d3c75b2057627ce58b394101797d Mon Sep 17 00:00:00 2001 From: sebres Date: Tue, 11 Dec 2018 15:39:43 +0100 Subject: fix of SafeConfigParserWithIncludes --- ChangeLog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ChangeLog b/ChangeLog index a45de141..ea71326d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -35,6 +35,8 @@ ver. 0.10.5-dev-1 (20??/??/??) - development edition ----------- ### Fixes +* fixed read of included config-files (`.local` overwrites options of `.conf` for config-files + included with before/after) * `filter.d/sshd.conf`: - captures `Disconnecting ...: Change of username or service not allowed` (gh-2239, gh-2279) - captures `Disconnected from ... [preauth]` (`extra`/`aggressive` mode and preauth phase only, gh-2239, gh-2279) -- cgit v1.2.1 From 7cdabdd7aed3fea525529c364d5d9f628741f548 Mon Sep 17 00:00:00 2001 From: CrazyMax Date: Fri, 14 Dec 2018 19:06:09 +0100 Subject: 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 = -- cgit v1.2.1 From c540babfb6ca5cab5277f621b5fcb035d8d973d6 Mon Sep 17 00:00:00 2001 From: "Sergey G. Brester" Date: Mon, 17 Dec 2018 12:30:46 +0100 Subject: 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 -- cgit v1.2.1 From 3fa54559e554fa6fce1d271838ef01da35b6c658 Mon Sep 17 00:00:00 2001 From: "Sergey G. Brester" Date: Mon, 17 Dec 2018 18:39:31 +0100 Subject: Update ChangeLog --- ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog b/ChangeLog index 033d53db..7b6f5d49 100644 --- a/ChangeLog +++ b/ChangeLog @@ -55,6 +55,7 @@ ver. 0.11.0-dev-0 (20??/??/??) - development nightly edition * algorithm of restore current bans after restart changed: update the restored ban-time (and therefore end of ban) of the ticket with ban-time of jail (as maximum), for all tickets with ban-time greater (or persistent); not affected if ban-time of the jail is unchanged between stop/start. +* added new setup-option `--without-tests` to skip building and installing of tests files (gh-2287). ver. 0.10.4-dev-1 (20??/??/??) - development edition -- cgit v1.2.1 From cadcc69bc02c9a85edbc2ade00c3ea2f4cf2e0ba Mon Sep 17 00:00:00 2001 From: "Sergey G. Brester" Date: Mon, 17 Dec 2018 18:42:28 +0100 Subject: minor amend for better readability --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index bde979e0..e476c5dd 100755 --- a/setup.py +++ b/setup.py @@ -134,7 +134,7 @@ class install_command_f2b(install): cmdclass = self.distribution.cmdclass cmdclass['build_py'] = build_py_2to3 cmdclass['build_scripts'] = build_scripts_2to3 - if self.without_tests is None: + if not self.without_tests: self.distribution.scripts += [ 'bin/fail2ban-testcases', ] -- cgit v1.2.1 From df9b352baca7b492f73581899a2a82637f5c15b7 Mon Sep 17 00:00:00 2001 From: Alexander Koeppe Date: Tue, 18 Dec 2018 20:21:36 +0100 Subject: Update information reg. ipdns.py as successor for dnsutils.py --- DEVELOP | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/DEVELOP b/DEVELOP index bb7de5c8..f3f9819a 100644 --- a/DEVELOP +++ b/DEVELOP @@ -262,12 +262,16 @@ FileContainer Keeps the position pointer -dnsutils.py -~~~~~~~~~~~ +ipdns.py +~~~~~~~~ DNSUtils - Utility class for DNS and IP handling + Utility class for DNS handling + +IPAddr + + Object-class for IP address handling filter*.py -- cgit v1.2.1 From 0298c8a31e666e789583f350f27e718724a3591b Mon Sep 17 00:00:00 2001 From: sebres Date: Thu, 27 Dec 2018 18:07:23 +0100 Subject: closes gh-2277: fixed cache-object clean-up process (if max-size reached) used multi-threaded (del can throw KeyError if get/unset changes the list); additionally OrderedDict is used now for cache (if available, so >= 2.7) - avoids (slow) search of expired items in full cache and always prefers older objects to remove (like FIFO). --- fail2ban/server/utils.py | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/fail2ban/server/utils.py b/fail2ban/server/utils.py index 8569a3f2..7104bb45 100644 --- a/fail2ban/server/utils.py +++ b/fail2ban/server/utils.py @@ -27,9 +27,15 @@ import os import signal import subprocess import sys +from threading import Lock import time from ..helpers import getLogger, _merge_dicts, uni_decode +try: + from collections import OrderedDict +except ImportError: # pragma: 3.x no cover + OrderedDict = dict + if sys.version_info >= (3, 3): import importlib.machinery else: @@ -69,7 +75,8 @@ class Utils(): def __init__(self, *args, **kwargs): self.setOptions(*args, **kwargs) - self._cache = {} + self._cache = OrderedDict() + self.__lock = Lock() def setOptions(self, maxCount=1000, maxTime=60): self.maxCount = maxCount @@ -83,7 +90,7 @@ class Utils(): if v: if v[1] > time.time(): return v[0] - del self._cache[k] + self.unset(k) return defv def set(self, k, v): @@ -91,12 +98,21 @@ class Utils(): cache = self._cache # for shorter local access # clean cache if max count reached: if len(cache) >= self.maxCount: - for (ck, cv) in cache.items(): - if cv[1] < t: - del cache[ck] - # if still max count - remove any one: - if len(cache) >= self.maxCount: - cache.popitem() + # avoid multiple modification of list multi-threaded: + with self.__lock: + if len(cache) >= self.maxCount: + for (ck, cv) in cache.items(): + # if expired: + if cv[1] <= t: + self.unset(ck) + elif OrderedDict is not dict: + break + # if still max count - remove any one: + if len(cache) >= self.maxCount: + if OrderedDict is not dict: # first (older): + cache.popitem(False) + else: + cache.popitem() cache[k] = (v, t + self.maxTime) def unset(self, k): -- cgit v1.2.1 From 4a4780be0456ec958c93833f9c2beb02dd08e5e2 Mon Sep 17 00:00:00 2001 From: sebres Date: Thu, 27 Dec 2018 18:10:09 +0100 Subject: test-cases: prevent sporadic timing errors (unban if ban still not occurred) --- fail2ban/tests/servertestcase.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fail2ban/tests/servertestcase.py b/fail2ban/tests/servertestcase.py index 610a1a5d..8c2d0e3e 100644 --- a/fail2ban/tests/servertestcase.py +++ b/fail2ban/tests/servertestcase.py @@ -64,7 +64,7 @@ class TestServer(Server): pass -class TransmitterBase(unittest.TestCase): +class TransmitterBase(LogCaptureTestCase): def setUp(self): """Call before every test case.""" @@ -332,11 +332,11 @@ class Transmitter(TransmitterBase): self.assertEqual( self.transm.proceed(["set", self.jailName, "banip", "127.0.0.1"]), (0, "127.0.0.1")) - time.sleep(Utils.DEFAULT_SLEEP_TIME) # Give chance to ban + self.assertLogged("Ban 127.0.0.1", wait=True) # Give chance to ban self.assertEqual( self.transm.proceed(["set", self.jailName, "banip", "Badger"]), (0, "Badger")) #NOTE: Is IP address validated? Is DNS Lookup done? - time.sleep(Utils.DEFAULT_SLEEP_TIME) # Give chance to ban + self.assertLogged("Ban Badger", wait=True) # Give chance to ban # Unban IP self.assertEqual( self.transm.proceed( -- cgit v1.2.1 From c9ba695ba349db84fd4302e0c089e0d9a4423d3b Mon Sep 17 00:00:00 2001 From: sebres Date: Fri, 28 Dec 2018 00:04:15 +0100 Subject: minor, no cover for 3.x (2.6 only) --- fail2ban/server/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fail2ban/server/utils.py b/fail2ban/server/utils.py index 7104bb45..46bb33b8 100644 --- a/fail2ban/server/utils.py +++ b/fail2ban/server/utils.py @@ -111,7 +111,7 @@ class Utils(): if len(cache) >= self.maxCount: if OrderedDict is not dict: # first (older): cache.popitem(False) - else: + else: # pragma: 3.x no cover cache.popitem() cache[k] = (v, t + self.maxTime) -- cgit v1.2.1 From 3d477d229d169a3dedcb7144bb24f4ea08d93c02 Mon Sep 17 00:00:00 2001 From: SP Date: Thu, 3 Jan 2019 22:47:24 +0300 Subject: ENH: added new command `fail2ban-client get banip` to get the banned ip addresses (gh-1916) --- ChangeLog | 1 + fail2ban/client/beautifier.py | 2 ++ fail2ban/server/actions.py | 10 ++++++++ fail2ban/server/server.py | 15 ++++++++++++ fail2ban/server/transmitter.py | 2 ++ fail2ban/tests/servertestcase.py | 50 ++++++++++++++++++++++++++++++++++++++++ man/fail2ban-client.1 | 4 ++++ 7 files changed, 84 insertions(+) diff --git a/ChangeLog b/ChangeLog index b9efbc10..8f12b49a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -56,6 +56,7 @@ ver. 0.11.0-dev-0 (20??/??/??) - development nightly edition end of ban) of the ticket with ban-time of jail (as maximum), for all tickets with ban-time greater (or persistent); not affected if ban-time of the jail is unchanged between stop/start. * added new setup-option `--without-tests` to skip building and installing of tests files (gh-2287). +* added new command `fail2ban-client get banip` to get the banned ip addresses (gh-1916). ver. 0.10.4-dev-1 (20??/??/??) - development edition diff --git a/fail2ban/client/beautifier.py b/fail2ban/client/beautifier.py index 4d9e549f..607c0ade 100644 --- a/fail2ban/client/beautifier.py +++ b/fail2ban/client/beautifier.py @@ -180,6 +180,8 @@ class Beautifier: msg = "The jail %s action %s has the following " \ "methods:\n" % (inC[1], inC[3]) msg += ", ".join(response) + elif inC[2] == "banip" and inC[0] == "get": + msg = " ".join(response) except Exception: logSys.warning("Beautifier error. Please report the error") logSys.error("Beautify %r with %r failed", response, self.__inputCmd, diff --git a/fail2ban/server/actions.py b/fail2ban/server/actions.py index 3d862275..11a905be 100644 --- a/fail2ban/server/actions.py +++ b/fail2ban/server/actions.py @@ -204,6 +204,16 @@ class Actions(JailThread, Mapping): def getBanTime(self): return self.__banManager.getBanTime() + def getBanList(self): + """Returns the list of banned IP addresses. + + Returns + ------- + list + The list of banned IP addresses. + """ + return self.__banManager.getBanList() + def removeBannedIP(self, ip=None, db=True, ifexists=False): """Removes banned IP calling actions' unban method diff --git a/fail2ban/server/server.py b/fail2ban/server/server.py index dfbbd5d7..5370808c 100644 --- a/fail2ban/server/server.py +++ b/fail2ban/server/server.py @@ -510,6 +510,21 @@ class Server: def getBanTime(self, name): return self.__jails[name].actions.getBanTime() + def getBanList(self, name): + """Returns the list of banned IP addresses for a jail. + + Parameters + ---------- + name : str + The name of a jail. + + Returns + ------- + list + The list of banned IP addresses. + """ + return self.__jails[name].actions.getBanList() + def setBanTimeExtra(self, name, opt, value): self.__jails[name].setBanTimeExtra(opt, value) diff --git a/fail2ban/server/transmitter.py b/fail2ban/server/transmitter.py index c24408c4..e3e05eb6 100644 --- a/fail2ban/server/transmitter.py +++ b/fail2ban/server/transmitter.py @@ -390,6 +390,8 @@ class Transmitter: # Action elif command[1] == "bantime": return self.__server.getBanTime(name) + elif command[1] == "banip": + return self.__server.getBanList(name) elif command[1].startswith("bantime."): opt = command[1][len("bantime."):] return self.__server.getBanTimeExtra(name, opt) diff --git a/fail2ban/tests/servertestcase.py b/fail2ban/tests/servertestcase.py index 8b616abc..14905b76 100644 --- a/fail2ban/tests/servertestcase.py +++ b/fail2ban/tests/servertestcase.py @@ -103,6 +103,34 @@ class TransmitterBase(LogCaptureTestCase): # if we expected to get it set without problem, check new value self.assertEqual(v(self.transm.proceed(getCmd)), v((0, outValue))) + def getBanListTest(self, jail, banip=None, unbanip=None, outList=None): + """Process set banip/set unbanip commands and compare the list of + banned IP addresses with outList.""" + def v(value): + """Prepare value for comparison.""" + if value[1] is None: + tmp = [] + else: + tmp = map(str, value[1]) + return (value[0], sorted(tmp)) + + # Ban IP address + if banip is not None: + self.assertEqual( + self.transm.proceed(["set", jail, "banip", banip]), + (0, banip)) + time.sleep(Utils.DEFAULT_SLEEP_TIME) # Give chance to ban + # Unban IP address + if unbanip is not None: + self.assertEqual( + self.transm.proceed(["set", jail, "unbanip", unbanip]), + (0, unbanip)) + time.sleep(Utils.DEFAULT_SLEEP_TIME) # Give chance to unban + # Compare the list of banned IP addresses with outList + self.assertEqual( + v(self.transm.proceed(["get", jail, "banip"])), + v((0, outList))) + def setGetTestNOK(self, cmd, inValue, jail=None): setCmd = ["set", cmd, inValue] getCmd = ["get", cmd] @@ -347,6 +375,28 @@ class Transmitter(TransmitterBase): self.transm.proceed( ["set", self.jailName, "unbanip", "192.168.1.1"])[0],1) + def testJailBanList(self): + jail = "TestJailBanList" + self.server.addJail(jail, FAST_BACKEND) + self.server.startJail(jail) + + self.getBanListTest(jail) + self.getBanListTest( + jail, banip="127.0.0.1", outList=["127.0.0.1"]) + self.getBanListTest( + jail, banip="192.168.0.1", + outList=["127.0.0.1", "192.168.0.1"]) + self.getBanListTest( + jail, banip="192.168.1.10", + outList=["127.0.0.1", "192.168.0.1", "192.168.1.10"]) + self.getBanListTest( + jail, unbanip="127.0.0.1", + outList=["192.168.0.1", "192.168.1.10"]) + self.getBanListTest( + jail, unbanip="192.168.1.10", outList=["192.168.0.1"]) + self.getBanListTest(jail, unbanip="192.168.0.1", outList=[]) + self.getBanListTest(jail) + def testJailMaxRetry(self): self.setGetTest("maxretry", "5", 5, jail=self.jailName) self.setGetTest("maxretry", "2", 2, jail=self.jailName) diff --git a/man/fail2ban-client.1 b/man/fail2ban-client.1 index d1226d56..81883ce2 100644 --- a/man/fail2ban-client.1 +++ b/man/fail2ban-client.1 @@ -379,6 +379,10 @@ will look back for failures for gets the time a host is banned for .TP +\fBget banip\fR +gets the list of banned IP +addresses for +.TP \fBget datepattern\fR gets the patern used to match date/times for -- cgit v1.2.1 From 6b4404b1bcfd77c0cace893cc9251612e4412454 Mon Sep 17 00:00:00 2001 From: Yannik Sembritzki Date: Thu, 3 Jan 2019 23:55:42 +0100 Subject: 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): -- cgit v1.2.1 From 547504873ee58a1b2c860c529b0038a64682865a Mon Sep 17 00:00:00 2001 From: Yannik Sembritzki Date: Thu, 3 Jan 2019 23:59:38 +0100 Subject: 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 -- cgit v1.2.1 From 7f5f7017dbaf9410149d262e4ec15fdc23da60f8 Mon Sep 17 00:00:00 2001 From: SP Date: Fri, 4 Jan 2019 17:06:47 +0300 Subject: ENH: added new test cases for `fail2ban-client get banip` command (gh-1916) --- fail2ban/tests/clientbeautifiertestcase.py | 7 +++++++ fail2ban/tests/fail2banclienttestcase.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/fail2ban/tests/clientbeautifiertestcase.py b/fail2ban/tests/clientbeautifiertestcase.py index 79a0ff54..863da7f8 100644 --- a/fail2ban/tests/clientbeautifiertestcase.py +++ b/fail2ban/tests/clientbeautifiertestcase.py @@ -261,3 +261,10 @@ class BeautifierTest(unittest.TestCase): output = "Sorry but the command is invalid" self.assertEqual(self.b.beautifyError(IndexError()), output) + + def testJailBanList(self): + self.b.setInputCmd(["get", "ssh", "banip"]) + response = ["192.168.0.1", "192.168.1.10"] + output = "192.168.0.1 192.168.1.10" + self.assertEqual(self.b.beautify(response), output) + self.assertEqual(self.b.beautify([]), "") diff --git a/fail2ban/tests/fail2banclienttestcase.py b/fail2ban/tests/fail2banclienttestcase.py index c120128b..b8fb6d0c 100644 --- a/fail2ban/tests/fail2banclienttestcase.py +++ b/fail2ban/tests/fail2banclienttestcase.py @@ -1227,6 +1227,36 @@ class Fail2banServerTest(Fail2banClientServerBase): "Jail 'test-jail1' stopped", "Jail 'test-jail1' started", all=True, wait=MID_WAITTIME) + # test the list of banned IP addresses, step 0: prepare + self.pruneLog("[test-phase 9a]") + self.execCmd(SUCCESS, startparams, "reload", "--unban", "test-jail1") + self.assertLogged( + "Jail 'test-jail1' reloaded", wait=MID_WAITTIME) + # test the list of banned IP addresses, step 1: ban IP addresses + self.pruneLog("[test-phase 9b]") + self.execCmd(SUCCESS, startparams, + "set", "test-jail1", "banip", "192.168.0.1") + self.assertLogged("[test-jail1] Ban 192.168.0.1", wait=MID_WAITTIME) + self.execCmd(SUCCESS, startparams, + "set", "test-jail1", "banip", "192.168.1.10") + self.assertLogged("[test-jail1] Ban 192.168.1.10", wait=MID_WAITTIME) + self.execCmd(SUCCESS, startparams, "get", "test-jail1", "banip") + self.assertLogged( + "192.168.1.10 192.168.0.1", + "192.168.0.1 192.168.1.10", wait=MID_WAITTIME) + # test the list of banned IP addresses, step 2: unban IP addresses + self.pruneLog("[test-phase 9c]") + self.execCmd(SUCCESS, startparams, + "set", "test-jail1", "unbanip", "192.168.0.1") + self.assertLogged("[test-jail1] Unban 192.168.0.1", wait=MID_WAITTIME) + self.execCmd(SUCCESS, startparams, + "set", "test-jail1", "unbanip", "192.168.1.10") + self.assertLogged("[test-jail1] Unban 192.168.1.10", wait=MID_WAITTIME) + self.execCmd(SUCCESS, startparams, "get", "test-jail1", "banip") + self.assertNotLogged( + "192.168.1.10 192.168.0.1", + "192.168.0.1 192.168.1.10", wait=MID_WAITTIME) + # test action.d/nginx-block-map.conf -- @unittest.F2B.skip_if_cfg_missing(action="nginx-block-map") @with_foreground_server_thread(startextra={ -- cgit v1.2.1 From 67247999ff9aeb38ef376f53faffcc4c74a0919f Mon Sep 17 00:00:00 2001 From: "Sergey G. Brester" Date: Sun, 6 Jan 2019 17:03:09 +0100 Subject: closes #2313: missing dependency to nftables.service --- files/fail2ban.service.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/files/fail2ban.service.in b/files/fail2ban.service.in index 24dcb51e..f00a3a23 100644 --- a/files/fail2ban.service.in +++ b/files/fail2ban.service.in @@ -1,8 +1,8 @@ [Unit] Description=Fail2Ban Service Documentation=man:fail2ban(1) -After=network.target iptables.service firewalld.service ip6tables.service ipset.service -PartOf=iptables.service firewalld.service ip6tables.service ipset.service +After=network.target iptables.service firewalld.service ip6tables.service ipset.service nftables.service +PartOf=iptables.service firewalld.service ip6tables.service ipset.service nftables.service [Service] Type=simple -- cgit v1.2.1 From df97fd33cfb475f2833024eceaba7de7ca1a8f56 Mon Sep 17 00:00:00 2001 From: sebres Date: Sun, 6 Jan 2019 22:31:23 +0100 Subject: ip-list is sorted now (by end of ban) per default; extended with new option `--with-time` to provide more pretty and informative result (separated by new-line, including time strings: time of ban + ban-time = end of ban): 192.0.2.1 2019-01-06 22:24:48 + 300 = 2019-01-06 22:29:48 192.0.2.2 2019-01-06 22:24:48 + 600 = 2019-01-06 22:34:48 also it is possible now to provide separator-character as extra-parameter after `get banip ?sep-char?` (default is space). removed unneeded test-cases (test code-base minimization) and unexpected manually changed files. --- ChangeLog | 2 +- fail2ban/client/beautifier.py | 6 +++- fail2ban/server/actions.py | 4 +-- fail2ban/server/banmanager.py | 17 ++++++++-- fail2ban/server/mytime.py | 10 ++++++ fail2ban/server/server.py | 4 +-- fail2ban/server/transmitter.py | 3 +- fail2ban/tests/clientbeautifiertestcase.py | 7 ---- fail2ban/tests/fail2banclienttestcase.py | 53 +++++++++++++----------------- man/fail2ban-client.1 | 4 --- 10 files changed, 60 insertions(+), 50 deletions(-) diff --git a/ChangeLog b/ChangeLog index 8f12b49a..d18876e2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -56,7 +56,7 @@ ver. 0.11.0-dev-0 (20??/??/??) - development nightly edition end of ban) of the ticket with ban-time of jail (as maximum), for all tickets with ban-time greater (or persistent); not affected if ban-time of the jail is unchanged between stop/start. * added new setup-option `--without-tests` to skip building and installing of tests files (gh-2287). -* added new command `fail2ban-client get banip` to get the banned ip addresses (gh-1916). +* added new command `fail2ban-client get banip ?--with-time|sep-char?` to get the banned ip addresses (gh-1916). ver. 0.10.4-dev-1 (20??/??/??) - development edition diff --git a/fail2ban/client/beautifier.py b/fail2ban/client/beautifier.py index 607c0ade..97cd38b2 100644 --- a/fail2ban/client/beautifier.py +++ b/fail2ban/client/beautifier.py @@ -181,7 +181,11 @@ class Beautifier: "methods:\n" % (inC[1], inC[3]) msg += ", ".join(response) elif inC[2] == "banip" and inC[0] == "get": - msg = " ".join(response) + if isinstance(response, list): + sep = " " if len(inC) <= 3 else inC[3] + if sep == "--with-time": + sep = "\n" + msg = sep.join(response) except Exception: logSys.warning("Beautifier error. Please report the error") logSys.error("Beautify %r with %r failed", response, self.__inputCmd, diff --git a/fail2ban/server/actions.py b/fail2ban/server/actions.py index 11a905be..3a92dcda 100644 --- a/fail2ban/server/actions.py +++ b/fail2ban/server/actions.py @@ -204,7 +204,7 @@ class Actions(JailThread, Mapping): def getBanTime(self): return self.__banManager.getBanTime() - def getBanList(self): + def getBanList(self, withTime=False): """Returns the list of banned IP addresses. Returns @@ -212,7 +212,7 @@ class Actions(JailThread, Mapping): list The list of banned IP addresses. """ - return self.__banManager.getBanList() + return self.__banManager.getBanList(ordered=True, withTime=withTime) def removeBannedIP(self, ip=None, db=True, ifexists=False): """Removes banned IP calling actions' unban method diff --git a/fail2ban/server/banmanager.py b/fail2ban/server/banmanager.py index 1340fb52..ffbcf766 100644 --- a/fail2ban/server/banmanager.py +++ b/fail2ban/server/banmanager.py @@ -102,9 +102,22 @@ class BanManager: # # @return IP list - def getBanList(self): + def getBanList(self, ordered=False, withTime=False): with self.__lock: - return self.__banList.keys() + if not ordered: + return self.__banList.keys() + lst = [] + for ticket in self.__banList.itervalues(): + eob = ticket.getEndOfBanTime(self.__banTime) + lst.append((ticket,eob)) + lst.sort(key=lambda t: t[1]) + t2s = MyTime.time2str + if withTime: + return ['%s \t%s + %d = %s' % ( + t[0].getID(), + t2s(t[0].getTime()), t[0].getBanTime(self.__banTime), t2s(t[1]) + ) for t in lst] + return [t[0].getID() for t in lst] ## # Returns a iterator to ban list (used in reload, so idle). diff --git a/fail2ban/server/mytime.py b/fail2ban/server/mytime.py index 49199887..e20e9690 100644 --- a/fail2ban/server/mytime.py +++ b/fail2ban/server/mytime.py @@ -113,6 +113,16 @@ class MyTime: return time.localtime(x) else: return time.localtime(MyTime.myTime) + + @staticmethod + def time2str(unixTime): + """Convert time to a string representing as date and time in ISO 8601 + format, YYYY-MM-DD HH:MM:SS without microseconds. + + @return ISO-capable string representation of given unixTime + """ + return datetime.datetime.fromtimestamp( + unixTime).replace(microsecond=0).strftime("%Y-%m-%d %H:%M:%S") ## precreate/precompile primitives used in str2seconds: diff --git a/fail2ban/server/server.py b/fail2ban/server/server.py index 5370808c..9cc17b5b 100644 --- a/fail2ban/server/server.py +++ b/fail2ban/server/server.py @@ -510,7 +510,7 @@ class Server: def getBanTime(self, name): return self.__jails[name].actions.getBanTime() - def getBanList(self, name): + def getBanList(self, name, withTime=False): """Returns the list of banned IP addresses for a jail. Parameters @@ -523,7 +523,7 @@ class Server: list The list of banned IP addresses. """ - return self.__jails[name].actions.getBanList() + return self.__jails[name].actions.getBanList(withTime) def setBanTimeExtra(self, name, opt, value): self.__jails[name].setBanTimeExtra(opt, value) diff --git a/fail2ban/server/transmitter.py b/fail2ban/server/transmitter.py index e3e05eb6..0c0cfba8 100644 --- a/fail2ban/server/transmitter.py +++ b/fail2ban/server/transmitter.py @@ -391,7 +391,8 @@ class Transmitter: elif command[1] == "bantime": return self.__server.getBanTime(name) elif command[1] == "banip": - return self.__server.getBanList(name) + return self.__server.getBanList(name, + withTime=len(command) > 2 and command[2] == "--with-time") elif command[1].startswith("bantime."): opt = command[1][len("bantime."):] return self.__server.getBanTimeExtra(name, opt) diff --git a/fail2ban/tests/clientbeautifiertestcase.py b/fail2ban/tests/clientbeautifiertestcase.py index 863da7f8..79a0ff54 100644 --- a/fail2ban/tests/clientbeautifiertestcase.py +++ b/fail2ban/tests/clientbeautifiertestcase.py @@ -261,10 +261,3 @@ class BeautifierTest(unittest.TestCase): output = "Sorry but the command is invalid" self.assertEqual(self.b.beautifyError(IndexError()), output) - - def testJailBanList(self): - self.b.setInputCmd(["get", "ssh", "banip"]) - response = ["192.168.0.1", "192.168.1.10"] - output = "192.168.0.1 192.168.1.10" - self.assertEqual(self.b.beautify(response), output) - self.assertEqual(self.b.beautify([]), "") diff --git a/fail2ban/tests/fail2banclienttestcase.py b/fail2ban/tests/fail2banclienttestcase.py index b8fb6d0c..4480c71c 100644 --- a/fail2ban/tests/fail2banclienttestcase.py +++ b/fail2ban/tests/fail2banclienttestcase.py @@ -1064,6 +1064,17 @@ class Fail2banServerTest(Fail2banClientServerBase): "stdout: '[test-jail2] test-action3: ++ ban 192.0.2.22", "stdout: '[test-jail2] test-action3: ++ ban 192.0.2.22 ", all=True, wait=MID_WAITTIME) + # get banned ips: + _observer_wait_idle() + self.pruneLog("[test-phase 2d.1]") + self.execCmd(SUCCESS, startparams, "get", "test-jail2", "banip", "\n") + self.assertLogged( + "192.0.2.4", "192.0.2.8", "192.0.2.21", "192.0.2.22", all=True, wait=MID_WAITTIME) + self.pruneLog("[test-phase 2d.2]") + self.execCmd(SUCCESS, startparams, "get", "test-jail1", "banip") + self.assertLogged( + "192.0.2.1", "192.0.2.2", "192.0.2.3", "192.0.2.4", "192.0.2.8", all=True, wait=MID_WAITTIME) + # restart jail with unban all: self.pruneLog("[test-phase 2e]") self.execCmd(SUCCESS, startparams, @@ -1227,36 +1238,6 @@ class Fail2banServerTest(Fail2banClientServerBase): "Jail 'test-jail1' stopped", "Jail 'test-jail1' started", all=True, wait=MID_WAITTIME) - # test the list of banned IP addresses, step 0: prepare - self.pruneLog("[test-phase 9a]") - self.execCmd(SUCCESS, startparams, "reload", "--unban", "test-jail1") - self.assertLogged( - "Jail 'test-jail1' reloaded", wait=MID_WAITTIME) - # test the list of banned IP addresses, step 1: ban IP addresses - self.pruneLog("[test-phase 9b]") - self.execCmd(SUCCESS, startparams, - "set", "test-jail1", "banip", "192.168.0.1") - self.assertLogged("[test-jail1] Ban 192.168.0.1", wait=MID_WAITTIME) - self.execCmd(SUCCESS, startparams, - "set", "test-jail1", "banip", "192.168.1.10") - self.assertLogged("[test-jail1] Ban 192.168.1.10", wait=MID_WAITTIME) - self.execCmd(SUCCESS, startparams, "get", "test-jail1", "banip") - self.assertLogged( - "192.168.1.10 192.168.0.1", - "192.168.0.1 192.168.1.10", wait=MID_WAITTIME) - # test the list of banned IP addresses, step 2: unban IP addresses - self.pruneLog("[test-phase 9c]") - self.execCmd(SUCCESS, startparams, - "set", "test-jail1", "unbanip", "192.168.0.1") - self.assertLogged("[test-jail1] Unban 192.168.0.1", wait=MID_WAITTIME) - self.execCmd(SUCCESS, startparams, - "set", "test-jail1", "unbanip", "192.168.1.10") - self.assertLogged("[test-jail1] Unban 192.168.1.10", wait=MID_WAITTIME) - self.execCmd(SUCCESS, startparams, "get", "test-jail1", "banip") - self.assertNotLogged( - "192.168.1.10 192.168.0.1", - "192.168.0.1 192.168.1.10", wait=MID_WAITTIME) - # test action.d/nginx-block-map.conf -- @unittest.F2B.skip_if_cfg_missing(action="nginx-block-map") @with_foreground_server_thread(startextra={ @@ -1427,6 +1408,11 @@ class Fail2banServerTest(Fail2banClientServerBase): "stdout: '[test-jail1] test-action1: ++ ban 192.0.2.11 -c 2 -t 300 : ", "stdout: '[test-jail1] test-action2: ++ ban 192.0.2.11 -c 2 -t 300 : ", all=True, wait=MID_WAITTIME) + # get banned ips with time: + self.pruneLog("[test-phase 2) time+10m - get-ips]") + self.execCmd(SUCCESS, startparams, "get", "test-jail1", "banip", "--with-time") + self.assertLogged( + "192.0.2.11", "+ 300 =", all=True, wait=MID_WAITTIME) # unblock observer here and wait it is done: wakeObs = True _observer_wait_idle() @@ -1441,6 +1427,13 @@ class Fail2banServerTest(Fail2banClientServerBase): "stdout: '[test-jail1] test-action2: ++ prolong 192.0.2.11 -c 2 -t 600 : ", all=True, wait=MID_WAITTIME) + # get banned ips with time: + _observer_wait_idle() + self.pruneLog("[test-phase 2) time+11m - get-ips]") + self.execCmd(SUCCESS, startparams, "get", "test-jail1", "banip", "--with-time") + self.assertLogged( + "192.0.2.11", "+ 600 =", all=True, wait=MID_WAITTIME) + # test multiple start/stop of the server (threaded in foreground) -- if False: # pragma: no cover @with_foreground_server_thread() diff --git a/man/fail2ban-client.1 b/man/fail2ban-client.1 index 81883ce2..d1226d56 100644 --- a/man/fail2ban-client.1 +++ b/man/fail2ban-client.1 @@ -379,10 +379,6 @@ will look back for failures for gets the time a host is banned for .TP -\fBget banip\fR -gets the list of banned IP -addresses for -.TP \fBget datepattern\fR gets the patern used to match date/times for -- cgit v1.2.1 From f959f58e15c46fdb6f33fc30e08ce6b7316c3c83 Mon Sep 17 00:00:00 2001 From: sebres Date: Sun, 6 Jan 2019 22:45:48 +0100 Subject: extend protocol (command-line) and regenerate man's --- ChangeLog | 2 +- fail2ban/protocol.py | 1 + man/fail2ban-client.1 | 11 ++++++++++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index d18876e2..20a37d4a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -56,7 +56,7 @@ ver. 0.11.0-dev-0 (20??/??/??) - development nightly edition end of ban) of the ticket with ban-time of jail (as maximum), for all tickets with ban-time greater (or persistent); not affected if ban-time of the jail is unchanged between stop/start. * added new setup-option `--without-tests` to skip building and installing of tests files (gh-2287). -* added new command `fail2ban-client get banip ?--with-time|sep-char?` to get the banned ip addresses (gh-1916). +* added new command `fail2ban-client get banip ?sep-char|--with-time?` to get the banned ip addresses (gh-1916). ver. 0.10.4-dev-1 (20??/??/??) - development edition diff --git a/fail2ban/protocol.py b/fail2ban/protocol.py index b21ab848..f9ec5b71 100644 --- a/fail2ban/protocol.py +++ b/fail2ban/protocol.py @@ -128,6 +128,7 @@ protocol = [ ["get bantime", "gets the time a host is banned for "], ["get datepattern", "gets the patern used to match date/times for "], ["get usedns", "gets the usedns setting for "], +["get banip [|--with-time]", "gets the list of of banned IP addresses for . Optionally the separator character ('', default is space) or the option '--with-time' (printing the times of ban) may be specified. The IPs are ordered by end of ban."], ["get maxretry", "gets the number of failures allowed for "], ["get maxlines", "gets the number of lines to buffer for "], ["get actions", "gets a list of actions for "], diff --git a/man/fail2ban-client.1 b/man/fail2ban-client.1 index d1226d56..af2af054 100644 --- a/man/fail2ban-client.1 +++ b/man/fail2ban-client.1 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH FAIL2BAN-CLIENT "1" "October 2018" "fail2ban-client v0.11.0.dev3" "User Commands" +.TH FAIL2BAN-CLIENT "1" "January 2019" "fail2ban-client v0.11.0.dev3" "User Commands" .SH NAME fail2ban-client \- configure and control the server .SH SYNOPSIS @@ -386,6 +386,15 @@ date/times for \fBget usedns\fR gets the usedns setting for .TP +\fBget banip [|\-\-with\-time]\fR +gets the list of of banned IP +addresses for . Optionally +the separator character ('', +default is space) or the option +\&'\-\-with\-time' (printing the times +of ban) may be specified. The IPs +are ordered by end of ban. +.TP \fBget maxretry\fR gets the number of failures allowed for -- cgit v1.2.1 From 4b934c784d5329f884d1005338a9d84705f87839 Mon Sep 17 00:00:00 2001 From: sebres Date: Sun, 6 Jan 2019 23:33:28 +0100 Subject: normalized time to string calls. --- fail2ban/server/filter.py | 6 +++--- fail2ban/server/mytime.py | 8 ++++---- fail2ban/server/observer.py | 6 +++--- fail2ban/tests/filtertestcase.py | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/fail2ban/server/filter.py b/fail2ban/server/filter.py index 9265a58b..001b35b7 100644 --- a/fail2ban/server/filter.py +++ b/fail2ban/server/filter.py @@ -602,7 +602,7 @@ class Filter(JailThread): if self._inIgnoreIPList(ip, tick): continue logSys.info( - "[%s] Found %s - %s", self.jailName, ip, datetime.datetime.fromtimestamp(unixTime).strftime("%Y-%m-%d %H:%M:%S") + "[%s] Found %s - %s", self.jailName, ip, MyTime.time2str(unixTime) ) self.failManager.addFailure(tick) # report to observer - failure was found, for possibly increasing of it retry counter (asynchronous) @@ -1092,7 +1092,7 @@ class FileFilter(Filter): fs = container.getFileSize() if logSys.getEffectiveLevel() <= logging.DEBUG: logSys.debug("Seek to find time %s (%s), file size %s", date, - datetime.datetime.fromtimestamp(date).strftime("%Y-%m-%d %H:%M:%S"), fs) + MyTime.time2str(date), fs) minp = container.getPos() maxp = fs tryPos = minp @@ -1171,7 +1171,7 @@ class FileFilter(Filter): container.setPos(foundPos) if logSys.getEffectiveLevel() <= logging.DEBUG: logSys.debug("Position %s from %s, found time %s (%s) within %s seeks", lastPos, fs, foundTime, - (datetime.datetime.fromtimestamp(foundTime).strftime("%Y-%m-%d %H:%M:%S") if foundTime is not None else ''), cntr) + (MyTime.time2str(foundTime) if foundTime is not None else ''), cntr) def status(self, flavor="basic"): """Status of Filter plus files being monitored. diff --git a/fail2ban/server/mytime.py b/fail2ban/server/mytime.py index e20e9690..98b69bd4 100644 --- a/fail2ban/server/mytime.py +++ b/fail2ban/server/mytime.py @@ -115,14 +115,14 @@ class MyTime: return time.localtime(MyTime.myTime) @staticmethod - def time2str(unixTime): - """Convert time to a string representing as date and time in ISO 8601 - format, YYYY-MM-DD HH:MM:SS without microseconds. + def time2str(unixTime, format="%Y-%m-%d %H:%M:%S"): + """Convert time to a string representing as date and time using given format. + Default format is ISO 8601, YYYY-MM-DD HH:MM:SS without microseconds. @return ISO-capable string representation of given unixTime """ return datetime.datetime.fromtimestamp( - unixTime).replace(microsecond=0).strftime("%Y-%m-%d %H:%M:%S") + unixTime).replace(microsecond=0).strftime(format) ## precreate/precompile primitives used in str2seconds: diff --git a/fail2ban/server/observer.py b/fail2ban/server/observer.py index c3fa7d54..ffeeec71 100644 --- a/fail2ban/server/observer.py +++ b/fail2ban/server/observer.py @@ -393,7 +393,7 @@ class ObserverThread(JailThread): return # retry counter was increased - add it again: logSys.info("[%s] Found %s, bad - %s, %s # -> %s%s", jail.name, ip, - datetime.datetime.fromtimestamp(unixTime).strftime("%Y-%m-%d %H:%M:%S"), banCount, retryCount, + MyTime.time2str(unixTime), banCount, retryCount, (', Ban' if retryCount >= maxRetry else '')) # retryCount-1, because a ticket was already once incremented by filter self retryCount = failManager.addFailure(ticket, retryCount - 1, True) @@ -454,7 +454,7 @@ class ObserverThread(JailThread): # check current ticket time to prevent increasing for twice read tickets (restored from log file besides database after restart) if ticket.getTime() > timeOfBan: logSys.info('[%s] IP %s is bad: %s # last %s - incr %s to %s' % (jail.name, ip, banCount, - datetime.datetime.fromtimestamp(timeOfBan).strftime("%Y-%m-%d %H:%M:%S"), + MyTime.time2str(timeOfBan), datetime.timedelta(seconds=int(orgBanTime)), datetime.timedelta(seconds=int(banTime)))); else: ticket.restored = True @@ -485,7 +485,7 @@ class ObserverThread(JailThread): if btime != -1: bendtime = ticket.getTime() + btime logtime = (datetime.timedelta(seconds=int(btime)), - datetime.datetime.fromtimestamp(bendtime).strftime("%Y-%m-%d %H:%M:%S")) + MyTime.time2str(bendtime)) # check ban is not too old : if bendtime < MyTime.time(): logSys.debug('Ignore old bantime %s', logtime[1]) diff --git a/fail2ban/tests/filtertestcase.py b/fail2ban/tests/filtertestcase.py index b22cd0f8..cae1e173 100644 --- a/fail2ban/tests/filtertestcase.py +++ b/fail2ban/tests/filtertestcase.py @@ -94,7 +94,7 @@ class _tmSerial(): @staticmethod def _tm(time): # ## strftime it too slow for large time serializer : - # return datetime.datetime.fromtimestamp(time).strftime("%Y-%m-%d %H:%M:%S") + # return MyTime.time2str(time) c = _tmSerial sec = (time % 60) if c._last_s == time - sec: @@ -306,7 +306,7 @@ class BasicFilter(unittest.TestCase): unittest.F2B.SkipIfFast() ## test function "_tm" works correct (returns the same as slow strftime): for i in xrange(1417512352, (1417512352 // 3600 + 3) * 3600): - tm = datetime.datetime.fromtimestamp(i).strftime("%Y-%m-%d %H:%M:%S") + tm = MyTime.time2str(i) if _tm(i) != tm: # pragma: no cover - never reachable self.assertEqual((_tm(i), i), (tm, i)) -- cgit v1.2.1 From 963e14c6855bca7e6ac470cce9554a71565e425d Mon Sep 17 00:00:00 2001 From: sebres Date: Sun, 6 Jan 2019 23:44:42 +0100 Subject: resolve sporadic timing errors (unban if ban still not occurred, resp. get list of IPs if not yet banned); simplify helper procedure for testJailBanList. --- fail2ban/tests/servertestcase.py | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/fail2ban/tests/servertestcase.py b/fail2ban/tests/servertestcase.py index 14905b76..7198464c 100644 --- a/fail2ban/tests/servertestcase.py +++ b/fail2ban/tests/servertestcase.py @@ -103,33 +103,25 @@ class TransmitterBase(LogCaptureTestCase): # if we expected to get it set without problem, check new value self.assertEqual(v(self.transm.proceed(getCmd)), v((0, outValue))) - def getBanListTest(self, jail, banip=None, unbanip=None, outList=None): + def getBanListTest(self, jail, banip=None, unbanip=None, outList=[]): """Process set banip/set unbanip commands and compare the list of banned IP addresses with outList.""" - def v(value): - """Prepare value for comparison.""" - if value[1] is None: - tmp = [] - else: - tmp = map(str, value[1]) - return (value[0], sorted(tmp)) - # Ban IP address if banip is not None: self.assertEqual( self.transm.proceed(["set", jail, "banip", banip]), (0, banip)) - time.sleep(Utils.DEFAULT_SLEEP_TIME) # Give chance to ban + self.assertLogged("Ban %s" % banip, wait=True) # Give chance to ban # Unban IP address if unbanip is not None: self.assertEqual( self.transm.proceed(["set", jail, "unbanip", unbanip]), (0, unbanip)) - time.sleep(Utils.DEFAULT_SLEEP_TIME) # Give chance to unban + self.assertLogged("Unban %s" % unbanip, wait=True) # Give chance to unban # Compare the list of banned IP addresses with outList - self.assertEqual( - v(self.transm.proceed(["get", jail, "banip"])), - v((0, outList))) + self.assertSortedEqual( + self.transm.proceed(["get", jail, "banip"]), + (0, outList)) def setGetTestNOK(self, cmd, inValue, jail=None): setCmd = ["set", cmd, inValue] -- cgit v1.2.1 From 59688d7cd563a930263c9dd6ba50d9b8251813ab Mon Sep 17 00:00:00 2001 From: sebres Date: Mon, 7 Jan 2019 00:05:27 +0100 Subject: move helper to test, normalize invocations in order to emphasize assert comparison. --- fail2ban/tests/servertestcase.py | 64 +++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/fail2ban/tests/servertestcase.py b/fail2ban/tests/servertestcase.py index 7198464c..166cd438 100644 --- a/fail2ban/tests/servertestcase.py +++ b/fail2ban/tests/servertestcase.py @@ -103,26 +103,6 @@ class TransmitterBase(LogCaptureTestCase): # if we expected to get it set without problem, check new value self.assertEqual(v(self.transm.proceed(getCmd)), v((0, outValue))) - def getBanListTest(self, jail, banip=None, unbanip=None, outList=[]): - """Process set banip/set unbanip commands and compare the list of - banned IP addresses with outList.""" - # Ban IP address - if banip is not None: - self.assertEqual( - self.transm.proceed(["set", jail, "banip", banip]), - (0, banip)) - self.assertLogged("Ban %s" % banip, wait=True) # Give chance to ban - # Unban IP address - if unbanip is not None: - self.assertEqual( - self.transm.proceed(["set", jail, "unbanip", unbanip]), - (0, unbanip)) - self.assertLogged("Unban %s" % unbanip, wait=True) # Give chance to unban - # Compare the list of banned IP addresses with outList - self.assertSortedEqual( - self.transm.proceed(["get", jail, "banip"]), - (0, outList)) - def setGetTestNOK(self, cmd, inValue, jail=None): setCmd = ["set", cmd, inValue] getCmd = ["get", cmd] @@ -372,22 +352,40 @@ class Transmitter(TransmitterBase): self.server.addJail(jail, FAST_BACKEND) self.server.startJail(jail) - self.getBanListTest(jail) - self.getBanListTest( - jail, banip="127.0.0.1", outList=["127.0.0.1"]) - self.getBanListTest( - jail, banip="192.168.0.1", + # Helper to process set banip/set unbanip commands and compare the list of + # banned IP addresses with outList. + def _getBanListTest(jail, banip=None, unbanip=None, outList=[]): + # Ban IP address + if banip is not None: + self.assertEqual( + self.transm.proceed(["set", jail, "banip", banip]), + (0, banip)) + self.assertLogged("Ban %s" % banip, wait=True) # Give chance to ban + # Unban IP address + if unbanip is not None: + self.assertEqual( + self.transm.proceed(["set", jail, "unbanip", unbanip]), + (0, unbanip)) + self.assertLogged("Unban %s" % unbanip, wait=True) # Give chance to unban + # Compare the list of banned IP addresses with outList + self.assertSortedEqual( + self.transm.proceed(["get", jail, "banip"]), + (0, outList)) + + _getBanListTest(jail, + outList=[]) + _getBanListTest(jail, banip="127.0.0.1", + outList=["127.0.0.1"]) + _getBanListTest(jail, banip="192.168.0.1", outList=["127.0.0.1", "192.168.0.1"]) - self.getBanListTest( - jail, banip="192.168.1.10", + _getBanListTest(jail, banip="192.168.1.10", outList=["127.0.0.1", "192.168.0.1", "192.168.1.10"]) - self.getBanListTest( - jail, unbanip="127.0.0.1", + _getBanListTest(jail, unbanip="127.0.0.1", outList=["192.168.0.1", "192.168.1.10"]) - self.getBanListTest( - jail, unbanip="192.168.1.10", outList=["192.168.0.1"]) - self.getBanListTest(jail, unbanip="192.168.0.1", outList=[]) - self.getBanListTest(jail) + _getBanListTest(jail, unbanip="192.168.1.10", + outList=["192.168.0.1"]) + _getBanListTest(jail, unbanip="192.168.0.1", + outList=[]) def testJailMaxRetry(self): self.setGetTest("maxretry", "5", 5, jail=self.jailName) -- cgit v1.2.1 From a13fdcf4f7ae0f1660ad047dddda3b92e25dcae2 Mon Sep 17 00:00:00 2001 From: sebres Date: Mon, 7 Jan 2019 01:34:12 +0100 Subject: closes gh-2314: extended regex for mysql 8.0.13 if used logging with details (e. g. log-error-verbosity = 3, so log output has few additional words enclosed in brackets after "[Note]"). --- config/filter.d/mysqld-auth.conf | 2 +- fail2ban/tests/files/logs/mysqld-auth | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/config/filter.d/mysqld-auth.conf b/config/filter.d/mysqld-auth.conf index 31bd2056..940298cc 100644 --- a/config/filter.d/mysqld-auth.conf +++ b/config/filter.d/mysqld-auth.conf @@ -17,7 +17,7 @@ before = common.conf _daemon = mysqld -failregex = ^%(__prefix_line)s(?:\d+ |\d{6} \s?\d{1,2}:\d{2}:\d{2} )?\[\w+\] Access denied for user '[^']+'@'' (to database '[^']*'|\(using password: (YES|NO)\))*\s*$ +failregex = ^%(__prefix_line)s(?:\d+ |\d{6} \s?\d{1,2}:\d{2}:\d{2} )?\[\w+\] (?:\[[^\]]+\] )*Access denied for user '[^']+'@'' (to database '[^']*'|\(using password: (YES|NO)\))*\s*$ ignoreregex = diff --git a/fail2ban/tests/files/logs/mysqld-auth b/fail2ban/tests/files/logs/mysqld-auth index ebb8c0c4..3f4c2436 100644 --- a/fail2ban/tests/files/logs/mysqld-auth +++ b/fail2ban/tests/files/logs/mysqld-auth @@ -25,3 +25,6 @@ Sep 16 21:30:32 catinthehat mysqld: 130916 21:30:32 [Warning] Access denied for # failJSON: { "time": "2016-02-24T15:26:18", "match": false , "host": "localhost", "desc": "A hypothetical example of injection having full log line first (for paranoid yoh)" } 2016-02-24T15:26:18.237955 6 [Note] Access denied for user 'root'@'localhost' (using password: YES) condition lead to a hypothetical failure + +# failJSON: { "time": "2019-01-03T09:50:04", "match": true , "host": "192.0.2.1", "desc": "mysql 8.0.13 logging with details, (log-error-verbosity = 3, gh-2314)" } +2019-01-03T08:50:04.634875Z 113 [Note] [MY-010926] [Server] Access denied for user 'root'@'192.0.2.1' (using password: NO) -- cgit v1.2.1 From 4108e04ab404cc9b2965669d0c9cab0909ecc018 Mon Sep 17 00:00:00 2001 From: "Sergey G. Brester" Date: Mon, 7 Jan 2019 01:50:44 +0100 Subject: Update ChangeLog --- ChangeLog | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ChangeLog b/ChangeLog index ea71326d..f24c43ae 100644 --- a/ChangeLog +++ b/ChangeLog @@ -40,6 +40,10 @@ ver. 0.10.5-dev-1 (20??/??/??) - development edition * `filter.d/sshd.conf`: - captures `Disconnecting ...: Change of username or service not allowed` (gh-2239, gh-2279) - captures `Disconnected from ... [preauth]` (`extra`/`aggressive` mode and preauth phase only, gh-2239, gh-2279) +* `filter.d/mysqld-auth.conf`: + - MYSQL 8.0.13 compatibility (log-error-verbosity = 3), log-format contains few additional words + enclosed in brackets after "[Note]" (gh-2314) +* `files/fail2ban.service.in`: fixed systemd-unit template - missing nftables dependency (gh-2313) ### New Features * new failregex-flag tag `` for failregex, signaled that the access to service was gained -- cgit v1.2.1 From 89c611064db13caf485c13e6464fff52bb8f8056 Mon Sep 17 00:00:00 2001 From: sebres Date: Mon, 14 Jan 2019 19:00:42 +0100 Subject: test-cases: be sure the test-files always written with new-line at end --- fail2ban/tests/fail2banclienttestcase.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fail2ban/tests/fail2banclienttestcase.py b/fail2ban/tests/fail2banclienttestcase.py index c9c5ea41..fb820a5a 100644 --- a/fail2ban/tests/fail2banclienttestcase.py +++ b/fail2ban/tests/fail2banclienttestcase.py @@ -126,7 +126,7 @@ def _out_file(fn, handle=logSys.debug): def _write_file(fn, mode, *lines): f = open(fn, mode) - f.write('\n'.join(lines)) + f.write('\n'.join(lines)+('\n' if lines else '')) f.close() def _read_file(fn): -- cgit v1.2.1 From 39ed016a1ea3ed8c2910eaf76c28b07cb239b9f4 Mon Sep 17 00:00:00 2001 From: todgru Date: Mon, 14 Jan 2019 22:08:38 -0800 Subject: fix: correct spelling category --- config/action.d/abuseipdb.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/action.d/abuseipdb.conf b/config/action.d/abuseipdb.conf index c53ed489..0e5f4029 100644 --- a/config/action.d/abuseipdb.conf +++ b/config/action.d/abuseipdb.conf @@ -101,5 +101,5 @@ actionunban = # Notes Your API key from abuseipdb.com # Values: STRING Default: None # Register for abuseipdb [https://www.abuseipdb.com], get api key and set below. -# You will need to set the catagory in the action call. +# You will need to set the category in the action call. abuseipdb_apikey = -- cgit v1.2.1 From e651bc7866f7c0cc32db1fe01d7c93abebd303c5 Mon Sep 17 00:00:00 2001 From: sebres Date: Mon, 11 Feb 2019 11:54:58 +0100 Subject: amend to #1622: jail-reader supports now multi-line option for multi-line action parameter: logpath = a.log b.log c.log action = ban[...] = log[logpath="%(logpath)s"] closes gh-2341, ultimate fix for gh-976 --- config/jail.conf | 6 +++--- fail2ban/client/jailreader.py | 11 ++++++++++- fail2ban/tests/clientreadertestcase.py | 24 ++++++++++++++++++++++++ fail2ban/tests/config/action.d/action.conf | 4 ++++ fail2ban/tests/config/jail.conf | 12 ++++++++++++ 5 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 fail2ban/tests/config/action.d/action.conf diff --git a/config/jail.conf b/config/jail.conf index 8b7d3d9b..daebf48b 100644 --- a/config/jail.conf +++ b/config/jail.conf @@ -177,19 +177,19 @@ action_mw = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port # ban & send an e-mail with whois report and relevant log lines # to the destemail. action_mwl = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"] - %(mta)s-whois-lines[name=%(__name__)s, sender="%(sender)s", dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s"] + %(mta)s-whois-lines[name=%(__name__)s, sender="%(sender)s", dest="%(destemail)s", logpath="%(logpath)s", chain="%(chain)s"] # See the IMPORTANT note in action.d/xarf-login-attack for when to use this action # # ban & send a xarf e-mail to abuse contact of IP address and include relevant log lines # to the destemail. action_xarf = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"] - xarf-login-attack[service=%(__name__)s, sender="%(sender)s", logpath=%(logpath)s, port="%(port)s"] + xarf-login-attack[service=%(__name__)s, sender="%(sender)s", logpath="%(logpath)s", port="%(port)s"] # ban IP on CloudFlare & send an e-mail with whois report and relevant log lines # to the destemail. action_cf_mwl = cloudflare[cfuser="%(cfemail)s", cftoken="%(cfapikey)s"] - %(mta)s-whois-lines[name=%(__name__)s, sender="%(sender)s", dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s"] + %(mta)s-whois-lines[name=%(__name__)s, sender="%(sender)s", dest="%(destemail)s", logpath="%(logpath)s", chain="%(chain)s"] # Report block via blocklist.de fail2ban reporting service API # diff --git a/fail2ban/client/jailreader.py b/fail2ban/client/jailreader.py index b06ba72d..b8a89380 100644 --- a/fail2ban/client/jailreader.py +++ b/fail2ban/client/jailreader.py @@ -151,12 +151,21 @@ class JailReader(ConfigReader): self.__filter.getOptions(self.__opts) # Read action - for act in self.__opts["action"].split('\n'): + prevln = '' + actlst = self.__opts["action"].split('\n') + for n, act in enumerate(actlst): try: if not act: # skip empty actions continue + # join with previous line if needed (consider possible new-line): + if prevln: act = prevln + '\n' + act actName, actOpt = extractOptions(act) + prevln = '' if not actName: + # consider possible new-line, so repeat with joined next line's: + if n < len(actlst) - 1: + prevln = act + continue raise JailDefError("Invalid action definition %r" % act) if actName.endswith(".py"): self.__actions.append([ diff --git a/fail2ban/tests/clientreadertestcase.py b/fail2ban/tests/clientreadertestcase.py index 96e6c7a4..9854417d 100644 --- a/fail2ban/tests/clientreadertestcase.py +++ b/fail2ban/tests/clientreadertestcase.py @@ -353,6 +353,30 @@ class JailReaderTest(LogCaptureTestCase): ) self.assertEqual(expected2, result) + def testMultiLineOption(self): + jail = JailReader('multi-log', force_enable=True, basedir=IMPERFECT_CONFIG, share_config=IMPERFECT_CONFIG_SHARE_CFG) + self.assertTrue(jail.read()) + self.assertTrue(jail.getOptions()) + self.assertEqual(jail.options['logpath'], 'a.log\nb.log\nc.log') + self.assertEqual(jail.options['action'], 'action[actname=\'ban\']\naction[actname=\'log\', logpath="a.log\nb.log\nc.log\nd.log"]\naction[actname=\'test\']') + self.assertSortedEqual([a.convert() for a in jail._JailReader__actions], [ + [['set', 'multi-log', 'addaction', 'ban'], ['multi-set', 'multi-log', 'action', 'ban', [ + ['actionban', 'echo "name: ban, ban: , logs: a.log\nb.log\nc.log"'], + ['actname', 'ban'], + ['name', 'multi-log'] + ]]], + [['set', 'multi-log', 'addaction', 'log'], ['multi-set', 'multi-log', 'action', 'log', [ + ['actionban', 'echo "name: log, ban: , logs: a.log\nb.log\nc.log\nd.log"'], + ['actname', 'log'], + ['logpath', 'a.log\nb.log\nc.log\nd.log'], ['name', 'multi-log'] + ]]], + [['set', 'multi-log', 'addaction', 'test'], ['multi-set', 'multi-log', 'action', 'test', [ + ['actionban', 'echo "name: test, ban: , logs: a.log\nb.log\nc.log"'], + ['actname', 'test'], + ['name', 'multi-log'] + ]]] + ]) + def testVersionAgent(self): unittest.F2B.SkipIfCfgMissing(stock=True) jail = JailReader('blocklisttest', force_enable=True, basedir=CONFIG_DIR) diff --git a/fail2ban/tests/config/action.d/action.conf b/fail2ban/tests/config/action.d/action.conf new file mode 100644 index 00000000..b26c00b8 --- /dev/null +++ b/fail2ban/tests/config/action.d/action.conf @@ -0,0 +1,4 @@ + +[Definition] + +actionban = echo "name: , ban: , logs: %(logpath)s" diff --git a/fail2ban/tests/config/jail.conf b/fail2ban/tests/config/jail.conf index 3dcbf634..6539adc1 100644 --- a/fail2ban/tests/config/jail.conf +++ b/fail2ban/tests/config/jail.conf @@ -51,3 +51,15 @@ action = [tz_correct] enabled = true logtimezone = UTC+0200 + +[multi-log] +enabled = false +filter = +logpath = a.log + b.log + c.log +log2nd = %(logpath)s + d.log +action = action[actname='ban'] + action[actname='log', logpath="%(log2nd)s"] + action[actname='test'] \ No newline at end of file -- cgit v1.2.1 From c819a18a0a0fa4eafcaadd6edcdcab8732b930c6 Mon Sep 17 00:00:00 2001 From: "Sergey G. Brester" Date: Mon, 11 Feb 2019 19:15:11 +0100 Subject: Update ChangeLog --- ChangeLog | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ChangeLog b/ChangeLog index f24c43ae..f45718d3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -44,12 +44,15 @@ ver. 0.10.5-dev-1 (20??/??/??) - development edition - MYSQL 8.0.13 compatibility (log-error-verbosity = 3), log-format contains few additional words enclosed in brackets after "[Note]" (gh-2314) * `files/fail2ban.service.in`: fixed systemd-unit template - missing nftables dependency (gh-2313) +* several `action.d/mail*`: fixed usage with multiple log files (ultimate fix for gh-976, gh-2341) ### 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) ### Enhancements +* jail-reader extended (amend to gh-1622): actions support multi-line options now (interpolations + containing new-line); ver. 0.10.4 (2018/10/04) - ten-four-on-due-date-ten-four -- cgit v1.2.1 From 5a54a445599a52fa12b583d8b5bfb33189cad0db Mon Sep 17 00:00:00 2001 From: sebres Date: Tue, 12 Feb 2019 14:30:18 +0100 Subject: provide more meaningful error-message if invalid `datepattern` set; fail2ban-regex: catch errors/exceptions by set of parameter, more verbose output if needed (`-v` or log-level `debug` would produce output of call-stack additionally). --- fail2ban/client/fail2banregex.py | 10 +++++++++- fail2ban/server/datetemplate.py | 19 +++++++++++-------- fail2ban/tests/fail2banregextestcase.py | 13 +++++++++++++ 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/fail2ban/client/fail2banregex.py b/fail2ban/client/fail2banregex.py index 29723dfb..87d359ba 100644 --- a/fail2ban/client/fail2banregex.py +++ b/fail2ban/client/fail2banregex.py @@ -694,6 +694,14 @@ def exec_command_line(*args): stdout.setFormatter(Formatter(getVerbosityFormat(opts.verbose, fmt))) logSys.addHandler(stdout) - fail2banRegex = Fail2banRegex(opts) + try: + fail2banRegex = Fail2banRegex(opts) + except Exception as e: + if opts.verbose or logSys.getEffectiveLevel()<=logging.DEBUG: + logSys.critical(e, exc_info=True) + else: + output( 'ERROR: %s' % e ) + sys.exit(255) + if not fail2banRegex.start(args): sys.exit(255) diff --git a/fail2ban/server/datetemplate.py b/fail2ban/server/datetemplate.py index e032c2b0..d827cd1a 100644 --- a/fail2ban/server/datetemplate.py +++ b/fail2ban/server/datetemplate.py @@ -301,14 +301,17 @@ class DatePatternRegex(DateTemplate): if wordBegin and RE_EXLINE_BOUND_BEG.search(pattern): pattern = RE_EXLINE_BOUND_BEG.sub('', pattern) wordBegin = 'start' - # wrap to regex: - fmt = self._patternRE.sub(r'%(\1)s', pattern) - self.name = fmt % self._patternName - regex = fmt % timeRE - # if expected add (?iu) for "ignore case" and "unicode": - if RE_ALPHA_PATTERN.search(pattern): - regex = r'(?iu)' + regex - super(DatePatternRegex, self).setRegex(regex, wordBegin, wordEnd) + try: + # wrap to regex: + fmt = self._patternRE.sub(r'%(\1)s', pattern) + self.name = fmt % self._patternName + regex = fmt % timeRE + # if expected add (?iu) for "ignore case" and "unicode": + if RE_ALPHA_PATTERN.search(pattern): + regex = r'(?iu)' + regex + super(DatePatternRegex, self).setRegex(regex, wordBegin, wordEnd) + except Exception as e: + raise TypeError("Failed to set datepattern '%s' (may be an invalid format or unescaped percent char): %s" % (pattern, e)) def getDate(self, line, dateMatch=None, default_tz=None): """Method to return the date for a log line. diff --git a/fail2ban/tests/fail2banregextestcase.py b/fail2ban/tests/fail2banregextestcase.py index 44acfd35..aa6977ed 100644 --- a/fail2ban/tests/fail2banregextestcase.py +++ b/fail2ban/tests/fail2banregextestcase.py @@ -368,3 +368,16 @@ class Fail2banRegexTest(LogCaptureTestCase): r"Authentication failure" ), 0) self.assertLogged('No failure-id group in ') + + def testExecCmdLine_ErrorParam(self): + # single line error: + self.assertNotEqual(_test_exec_command_line( + '-l', 'notice', '-d', '%:%.%-', 'LOG', 'RE' + ), 0) + self.assertLogged('ERROR: Failed to set datepattern') + # verbose (traceback/callstack): + self.pruneLog() + self.assertNotEqual(_test_exec_command_line( + '-v', '-d', '%:%.%-', 'LOG', 'RE' + ), 0) + self.assertLogged('Failed to set datepattern') -- cgit v1.2.1 From b31a018e7ca6983bb0babf83c054e9cf36085cb3 Mon Sep 17 00:00:00 2001 From: Cool Fire Date: Fri, 8 Feb 2019 16:54:11 +0100 Subject: Add override for dovecot failed logins on debian --- config/paths-debian.conf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/paths-debian.conf b/config/paths-debian.conf index 50ff948b..ae45e72e 100644 --- a/config/paths-debian.conf +++ b/config/paths-debian.conf @@ -31,6 +31,8 @@ apache_error_log = /var/log/apache2/*error.log apache_access_log = /var/log/apache2/*access.log +dovecot_log = /var/log/mail.log + # was in debian squeezy but not in wheezy # /etc/proftpd/proftpd.conf (SystemLog) proftpd_log = /var/log/proftpd/proftpd.log -- cgit v1.2.1 From 27526e431b31add8181b6dcd88ab5022021eccea Mon Sep 17 00:00:00 2001 From: Cool Fire Date: Wed, 13 Feb 2019 10:10:24 +0100 Subject: Changes static logfile string to variable Since we don't want to re-declare a log file name we already have a varialbe for, use the existing variable to set dovecot_log. --- config/paths-debian.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/paths-debian.conf b/config/paths-debian.conf index ae45e72e..0904668b 100644 --- a/config/paths-debian.conf +++ b/config/paths-debian.conf @@ -31,7 +31,7 @@ apache_error_log = /var/log/apache2/*error.log apache_access_log = /var/log/apache2/*access.log -dovecot_log = /var/log/mail.log +dovecot_log = %(syslog_mail)s # was in debian squeezy but not in wheezy # /etc/proftpd/proftpd.conf (SystemLog) -- cgit v1.2.1 From dfd2a2063d320f93b2bb7f33d38d3f8403097984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sz=C3=A9pe?= Date: Fri, 12 Oct 2018 22:08:31 +0200 Subject: Safer, nicer, uniform Debian initd script --- files/debian-initd | 124 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 70 insertions(+), 54 deletions(-) diff --git a/files/debian-initd b/files/debian-initd index 3b1745c1..24d40b87 100755 --- a/files/debian-initd +++ b/files/debian-initd @@ -1,4 +1,4 @@ -#! /bin/sh +#!/bin/sh ### BEGIN INIT INFO # Provides: fail2ban # Required-Start: $local_fs $remote_fs @@ -22,28 +22,28 @@ # rename this file: (sudo) mv /etc/init.d/fail2ban.init /etc/init.d/fail2ban # same with the logrotate file: (sudo) mv /etc/logrotate.d/fail2ban.logrotate /etc/logrotate.d/fail2ban # -PATH=/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/bin -DESC="authentication failure monitor" -NAME=fail2ban +PATH="/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/bin" +DESC="Authentication failure monitor" +NAME="fail2ban" # fail2ban-client is not a daemon itself but starts a daemon and # loads its with configuration -DAEMON=/usr/local/bin/$NAME-client -SCRIPTNAME=/etc/init.d/$NAME +DAEMON="/usr/local/bin/$NAME-client" +SCRIPTNAME="/etc/init.d/$NAME" # Ad-hoc way to parse out socket file name -SOCKFILE=`grep -h '^[^#]*socket *=' /etc/$NAME/$NAME.conf /etc/$NAME/$NAME.local 2>/dev/null \ - | tail -n 1 | sed -e 's/.*socket *= *//g' -e 's/ *$//g'` -[ -z "$SOCKFILE" ] && SOCKFILE='/var/run/fail2ban.sock' +SOCKFILE="$(grep -h '^[^#]*socket *=' "/etc/$NAME/$NAME.conf" "/etc/$NAME/$NAME.local" 2>/dev/null \ + | tail -n 1 | sed -e 's/.*socket *= *//g' -e 's/ *$//g')" +[ -z "$SOCKFILE" ] && SOCKFILE="/var/run/fail2ban.sock" # Exit if the package is not installed [ -x "$DAEMON" ] || exit 0 # Run as root by default. -FAIL2BAN_USER=root +FAIL2BAN_USER="root" # Read configuration variable file if it is present -[ -r /etc/default/$NAME ] && . /etc/default/$NAME +[ -r "/etc/default/$NAME" ] && . "/etc/default/$NAME" DAEMON_ARGS="$FAIL2BAN_OPTS" # Load the VERBOSE setting and other rcS variables @@ -51,7 +51,8 @@ DAEMON_ARGS="$FAIL2BAN_OPTS" # Predefine what can be missing from lsb source later on -- necessary to run # on sarge. Just present it in a bit more compact way from what was shipped -log_daemon_msg () { +log_daemon_msg() +{ [ -z "$1" ] && return 1 echo -n "$1:" [ -z "$2" ] || echo -n " $2" @@ -68,7 +69,7 @@ log_daemon_msg () { # report_bug() { - echo $* + echo "$*" echo "Please submit a bug report to Debian BTS (reportbug fail2ban)" exit 1 } @@ -80,10 +81,10 @@ report_bug() check_socket() { # Return - # 0 if socket is present and readable - # 1 if socket file is not present - # 2 if socket file is present but not readable - # 3 if socket file is present but is not a socket + # 0 if socket is present and readable + # 1 if socket file is not present + # 2 if socket file is present but not readable + # 3 if socket file is present but is not a socket [ -e "$SOCKFILE" ] || return 1 [ -r "$SOCKFILE" ] || return 2 [ -S "$SOCKFILE" ] || return 3 @@ -96,9 +97,9 @@ check_socket() do_start() { # Return - # 0 if daemon has been started - # 1 if daemon was already running - # 2 if daemon could not be started + # 0 if daemon has been started + # 1 if daemon was already running + # 2 if daemon could not be started do_status && return 1 if [ -e "$SOCKFILE" ]; then @@ -119,11 +120,11 @@ do_start() # Create the logfile if it doesn't exist touch /var/log/fail2ban.log chown "$FAIL2BAN_USER" /var/log/fail2ban.log - find /proc/net/xt_recent -name 'fail2ban-*' -exec chown "$FAIL2BAN_USER" {} \; + find /proc/net/xt_recent -name "fail2ban-*" -exec chown "$FAIL2BAN_USER" "{}" ";" fi - start-stop-daemon --start --quiet --chuid "$FAIL2BAN_USER" --exec $DAEMON -- \ - $DAEMON_ARGS start > /dev/null\ + start-stop-daemon --start --quiet --chuid "$FAIL2BAN_USER" --exec "$DAEMON" -- \ + $DAEMON_ARGS start >/dev/null \ || return 2 return 0 @@ -136,8 +137,8 @@ do_start() # do_status() { - $DAEMON ping > /dev/null 2>&1 - return $? + $DAEMON ping >/dev/null 2>&1 + return "$?" } # @@ -146,22 +147,22 @@ do_status() do_stop() { # Return - # 0 if daemon has been stopped - # 1 if daemon was already stopped - # 2 if daemon could not be stopped - # other if a failure occurred - $DAEMON status > /dev/null 2>&1 || return 1 - $DAEMON stop > /dev/null || return 2 + # 0 if daemon has been stopped + # 1 if daemon was already stopped + # 2 if daemon could not be stopped + # other if a failure occurred + $DAEMON status >/dev/null 2>&1 || return 1 + $DAEMON stop >/dev/null || return 2 # now we need actually to wait a bit since it might take time # for server to react on client's stop request. Especially # important for restart command on slow boxes count=1 - while do_status && [ $count -lt 60 ]; do + while do_status && [ "$count" -lt 60 ]; do sleep 1 - count=$(($count+1)) + count="$((count + 1))" done - [ $count -lt 60 ] || return 3 # failed to stop + [ "$count" -lt 60 ] || return 3 # failed to stop return 0 } @@ -169,8 +170,9 @@ do_stop() # # Function to reload configuration # -do_reload() { - $DAEMON reload > /dev/null && return 0 || return 1 +do_reload() +{ + "$DAEMON" reload >/dev/null && return 0 || return 1 return 0 } @@ -186,7 +188,7 @@ log_end_msg_wrapper() value=0 fi if [ "$3" != "no" ]; then - log_end_msg $value + log_end_msg "$value" fi if [ $value != "0" ]; then exit $1 @@ -198,13 +200,13 @@ case "$command" in start|force-start) [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" do_start "$command" - log_end_msg_wrapper $? 255 "$VERBOSE" + log_end_msg_wrapper "$?" 255 "$VERBOSE" ;; stop) [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" do_stop - log_end_msg_wrapper $? 255 "$VERBOSE" + log_end_msg_wrapper "$?" 255 "$VERBOSE" ;; restart|force-reload) @@ -213,41 +215,55 @@ case "$command" in case "$?" in 0|1) do_start - log_end_msg_wrapper $? 0 "always" + log_end_msg_wrapper "$?" 0 always ;; *) # Failed to stop log_end_msg 1 ;; - esac + esac ;; - reload|force-reload) - log_daemon_msg "Reloading $DESC" "$NAME" - do_reload - log_end_msg $? - ;; + reload) + log_daemon_msg "Reloading $DESC" "$NAME" + do_reload + log_end_msg $? + ;; status) log_daemon_msg "Status of $DESC" do_status case $? in - 0) log_success_msg " $NAME is running" ;; + 0) + log_success_msg " $NAME is running" + ;; 255) check_socket case $? in - 1) log_failure_msg " $NAME is not running" && exit 3 ;; - 0) log_failure_msg " $NAME is not running but $SOCKFILE exists" && exit 3 ;; - 2) log_failure_msg " $SOCKFILE not readable, status of $NAME is unknown" && exit 3 ;; - 3) log_failure_msg " $SOCKFILE exists but not a socket, status of $NAME is unknown" && exit 3 ;; - *) report_bug "Unknown return code from $NAME:check_socket." && exit 4 ;; + 1) + log_failure_msg " $NAME is not running" && exit 3 + ;; + 0) + log_failure_msg " $NAME is not running but $SOCKFILE exists" && exit 3 + ;; + 2) + log_failure_msg " $SOCKFILE not readable, status of $NAME is unknown" && exit 3 + ;; + 3) + log_failure_msg " $SOCKFILE exists but not a socket, status of $NAME is unknown" && exit 3 + ;; + *) + report_bug "Unknown return code from $NAME:check_socket." && exit 4 + ;; esac ;; - *) report_bug "Unknown $NAME status code" && exit 4 + *) + report_bug "Unknown $NAME status code" && exit 4 + ;; esac ;; *) - echo "Usage: $SCRIPTNAME {start|force-start|stop|restart|force-reload|status}" >&2 + echo "Usage: $SCRIPTNAME {start|force-start|stop|restart|force-reload|status}" 1>&2 exit 3 ;; esac -- cgit v1.2.1 From 62f957973df4c24300c3932b2115bd80545003ba Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Fri, 15 Feb 2019 15:00:30 -0500 Subject: ENH: disable shell check for $DAEMON_ARGS expansion --- files/debian-initd | 2 ++ 1 file changed, 2 insertions(+) diff --git a/files/debian-initd b/files/debian-initd index 24d40b87..0f353976 100755 --- a/files/debian-initd +++ b/files/debian-initd @@ -123,6 +123,8 @@ do_start() find /proc/net/xt_recent -name "fail2ban-*" -exec chown "$FAIL2BAN_USER" "{}" ";" fi + # $DAEMON_ARGS need to be expanded possibly with multiple or no options + # shellcheck disable=SC2086 start-stop-daemon --start --quiet --chuid "$FAIL2BAN_USER" --exec "$DAEMON" -- \ $DAEMON_ARGS start >/dev/null \ || return 2 -- cgit v1.2.1 From c5453151927453d5068c2befc992c8e3e6494564 Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Fri, 15 Feb 2019 15:02:51 -0500 Subject: ENH: travis - run shellcheck on files/debian-initd --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index ebfcd68e..230617c8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,6 +41,8 @@ install: - if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then travis_retry sudo apt-get install -qq python-gamin && cp /usr/share/pyshared/gamin.py /usr/lib/pyshared/python2.7/_gamin.so $VIRTUAL_ENV/lib/python2.7/site-packages/; fi # pyinotify - travis_retry pip install pyinotify + # Install helper tools + - sudo apt-get install shellcheck before_script: # Manually execute 2to3 for now - if [[ "$F2B_PY" = 3 ]]; then ./fail2ban-2to3; fi @@ -53,6 +55,8 @@ script: - sudo $VENV_BIN/pip install . # Doc files should get installed on Travis under Linux - test -e /usr/share/doc/fail2ban/FILTERS + # Test initd script + - shellcheck -s bash -e SC1090,SC1091 files/debian-initd after_success: - if [[ "$F2B_COV" = 1 ]]; then coveralls; fi - codecov -- cgit v1.2.1 From 24b0e048d18ee6cce0c544ee6e9fc3126279da56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sz=C3=A9pe?= Date: Fri, 15 Feb 2019 21:56:34 +0100 Subject: Normalizing quote usage in initd --- files/debian-initd | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/files/debian-initd b/files/debian-initd index 0f353976..35362d0d 100755 --- a/files/debian-initd +++ b/files/debian-initd @@ -104,7 +104,7 @@ do_start() if [ -e "$SOCKFILE" ]; then log_failure_msg "Socket file $SOCKFILE is present" - [ "$1" = "force-start" ] \ + [ "$1" = force-start ] \ && log_success_msg "Starting anyway as requested" \ || return 2 DAEMON_ARGS="$DAEMON_ARGS -x" @@ -113,7 +113,7 @@ do_start() # Assure that /var/run/fail2ban exists [ -d /var/run/fail2ban ] || mkdir -p /var/run/fail2ban - if [ "$FAIL2BAN_USER" != "root" ]; then + if [ "$FAIL2BAN_USER" != root ]; then # Make the socket directory, IP lists and fail2ban log # files writable by fail2ban chown "$FAIL2BAN_USER" /var/run/fail2ban @@ -184,16 +184,16 @@ do_reload() # log_end_msg_wrapper() { - if [ $1 != 0 ] && [ $1 != $2 ]; then - value=1 + if [ "$1" != 0 ] && [ "$1" != "$2" ]; then + value="1" else - value=0 + value="0" fi - if [ "$3" != "no" ]; then + if [ "$3" != no ]; then log_end_msg "$value" fi - if [ $value != "0" ]; then - exit $1 + if [ "$value" != 0 ]; then + exit "$1" fi } @@ -229,19 +229,19 @@ case "$command" in reload) log_daemon_msg "Reloading $DESC" "$NAME" do_reload - log_end_msg $? + log_end_msg "$?" ;; status) log_daemon_msg "Status of $DESC" do_status - case $? in + case "$?" in 0) log_success_msg " $NAME is running" ;; 255) check_socket - case $? in + case "$?" in 1) log_failure_msg " $NAME is not running" && exit 3 ;; -- cgit v1.2.1 From 824afbf52d951844ebaff1cc93c990867f60ffd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sz=C3=A9pe?= Date: Sun, 17 Feb 2019 09:12:30 +0000 Subject: Fix whitespaces --- .travis.yml | 2 +- files/debian-initd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 230617c8..bd095598 100644 --- a/.travis.yml +++ b/.travis.yml @@ -56,7 +56,7 @@ script: # Doc files should get installed on Travis under Linux - test -e /usr/share/doc/fail2ban/FILTERS # Test initd script - - shellcheck -s bash -e SC1090,SC1091 files/debian-initd + - shellcheck -s bash -e SC1090,SC1091 files/debian-initd after_success: - if [[ "$F2B_COV" = 1 ]]; then coveralls; fi - codecov diff --git a/files/debian-initd b/files/debian-initd index 35362d0d..a9cc584f 100755 --- a/files/debian-initd +++ b/files/debian-initd @@ -33,7 +33,7 @@ SCRIPTNAME="/etc/init.d/$NAME" # Ad-hoc way to parse out socket file name SOCKFILE="$(grep -h '^[^#]*socket *=' "/etc/$NAME/$NAME.conf" "/etc/$NAME/$NAME.local" 2>/dev/null \ - | tail -n 1 | sed -e 's/.*socket *= *//g' -e 's/ *$//g')" + | tail -n 1 | sed -e 's/.*socket *= *//g' -e 's/ *$//g')" [ -z "$SOCKFILE" ] && SOCKFILE="/var/run/fail2ban.sock" # Exit if the package is not installed -- cgit v1.2.1 From 14f997231d70314059d287691de153689125889b Mon Sep 17 00:00:00 2001 From: sebres Date: Mon, 18 Feb 2019 16:56:43 +0100 Subject: add test case to cover gh-2277, testOverflowedIPCache testing overflow of IP-cache multi-threaded (2 "parasite" threads flooding cache) --- fail2ban/tests/filtertestcase.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/fail2ban/tests/filtertestcase.py b/fail2ban/tests/filtertestcase.py index b22cd0f8..98b4d286 100644 --- a/fail2ban/tests/filtertestcase.py +++ b/fail2ban/tests/filtertestcase.py @@ -1753,6 +1753,44 @@ class DNSUtilsTests(unittest.TestCase): # here the whole cache should be empty: self.assertEqual(len(c), 0) + def testOverflowedIPCache(self): + # test overflow of IP-cache multi-threaded (2 "parasite" threads flooding cache): + from threading import Thread + from random import shuffle + # save original cache and use smaller cache during the test here: + _org_cache = IPAddr.CACHE_OBJ + cache = IPAddr.CACHE_OBJ = Utils.Cache(maxCount=5, maxTime=60) + result = list() + count = 1 if unittest.F2B.fast else 50 + try: + # tester procedure of worker: + def _TestCacheStr2IP(forw=True, result=[], random=False): + try: + c = count + while c: + c -= 1 + s = xrange(0, 256, 1) if forw else xrange(255, -1, -1) + if random: shuffle([i for i in s]) + for i in s: + IPAddr('192.0.2.'+str(i), IPAddr.FAM_IPv4) + IPAddr('2001:db8::'+str(i), IPAddr.FAM_IPv6) + result.append(None) + except Exception as e: + DefLogSys.debug(e, exc_info=True) + result.append(e) + + # 2 workers flooding it forwards and backwards: + th1 = Thread(target=_TestCacheStr2IP, args=(True, result)); th1.start() + th2 = Thread(target=_TestCacheStr2IP, args=(False, result)); th2.start() + # and here we flooding it with random IPs too: + _TestCacheStr2IP(True, result, True) + finally: + # wait for end of threads and restore cache: + th1.join() + th2.join() + IPAddr.CACHE_OBJ = _org_cache + self.assertEqual(result, [None]*3) # no errors + self.assertTrue(len(cache) <= cache.maxCount) class DNSUtilsNetworkTests(unittest.TestCase): -- cgit v1.2.1 From e30ebb1f3b408043d164a411b57661141e4fe1af Mon Sep 17 00:00:00 2001 From: sebres Date: Mon, 18 Feb 2019 17:05:11 +0100 Subject: closes gh-2277: fixed and optimized cache facilities (operations on OrderedDict are not atomic); increased max-size of IPAddr cache; don't cache raw objects (it is fast enough). --- fail2ban/server/ipdns.py | 9 +++++++-- fail2ban/server/utils.py | 44 ++++++++++++++++++++++++-------------------- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/fail2ban/server/ipdns.py b/fail2ban/server/ipdns.py index 959e39c4..7256ce4e 100644 --- a/fail2ban/server/ipdns.py +++ b/fail2ban/server/ipdns.py @@ -197,7 +197,7 @@ class IPAddr(object): __slots__ = '_family','_addr','_plen','_maskplen','_raw' # todo: make configurable the expired time and max count of cache entries: - CACHE_OBJ = Utils.Cache(maxCount=1000, maxTime=5*60) + CACHE_OBJ = Utils.Cache(maxCount=10000, maxTime=5*60) CIDR_RAW = -2 CIDR_UNSPEC = -1 @@ -205,6 +205,10 @@ class IPAddr(object): FAM_IPv6 = CIDR_RAW - socket.AF_INET6 def __new__(cls, ipstr, cidr=CIDR_UNSPEC): + if cidr == IPAddr.CIDR_RAW: # don't cache raw + ip = super(IPAddr, cls).__new__(cls) + ip.__init(ipstr, cidr) + return ip # check already cached as IPAddr args = (ipstr, cidr) ip = IPAddr.CACHE_OBJ.get(args) @@ -221,7 +225,8 @@ class IPAddr(object): return ip ip = super(IPAddr, cls).__new__(cls) ip.__init(ipstr, cidr) - IPAddr.CACHE_OBJ.set(args, ip) + if ip._family != IPAddr.CIDR_RAW: + IPAddr.CACHE_OBJ.set(args, ip) return ip @staticmethod diff --git a/fail2ban/server/utils.py b/fail2ban/server/utils.py index 46bb33b8..d88c29b8 100644 --- a/fail2ban/server/utils.py +++ b/fail2ban/server/utils.py @@ -95,31 +95,35 @@ class Utils(): def set(self, k, v): t = time.time() - cache = self._cache # for shorter local access - # clean cache if max count reached: - if len(cache) >= self.maxCount: - # avoid multiple modification of list multi-threaded: - with self.__lock: - if len(cache) >= self.maxCount: - for (ck, cv) in cache.items(): + # avoid multiple modification of dict multi-threaded: + cache = self._cache + with self.__lock: + # clean cache if max count reached: + if len(cache) >= self.maxCount: + if OrderedDict is not dict: + # ordered (so remove some from ahead, FIFO) + while cache: + (ck, cv) = cache.popitem(last=False) + # if not yet expired (but has free slot for new entry): + if cv[1] > t and len(cache) < self.maxCount: + break + else: # pragma: 3.x no cover (dict is in 2.6 only) + remlst = [] + for (ck, cv) in cache.iteritems(): # if expired: if cv[1] <= t: - self.unset(ck) - elif OrderedDict is not dict: - break + remlst.append(ck) + for ck in remlst: + self._cache.pop(ck, None) # if still max count - remove any one: - if len(cache) >= self.maxCount: - if OrderedDict is not dict: # first (older): - cache.popitem(False) - else: # pragma: 3.x no cover - cache.popitem() - cache[k] = (v, t + self.maxTime) + while cache and len(cache) >= self.maxCount: + cache.popitem() + # set now: + cache[k] = (v, t + self.maxTime) def unset(self, k): - try: - del self._cache[k] - except KeyError: - pass + with self.__lock: + self._cache.pop(k, None) @staticmethod -- cgit v1.2.1 From 84cec5e861b08bd65d6cdf11a956f1007a397a50 Mon Sep 17 00:00:00 2001 From: sebres Date: Wed, 20 Feb 2019 14:56:00 +0100 Subject: implements gh-2349: `fail2ban-client set jain banip/unbanip ip1 .. ipN` extended to ban/unban multiple tickets; reorganized banning facilities (addBannedIP moved from filter to actions in order to ban directly without implication of fail-manager in between. --- fail2ban/protocol.py | 4 +-- fail2ban/server/actions.py | 57 ++++++++++++++++++++++++++------ fail2ban/server/filter.py | 25 -------------- fail2ban/server/server.py | 11 +++--- fail2ban/server/transmitter.py | 17 +++++----- fail2ban/tests/actionstestcase.py | 10 ++++++ fail2ban/tests/fail2banclienttestcase.py | 2 +- fail2ban/tests/filtertestcase.py | 6 ---- fail2ban/tests/servertestcase.py | 14 ++++---- man/fail2ban-client.1 | 4 +-- 10 files changed, 84 insertions(+), 66 deletions(-) diff --git a/fail2ban/protocol.py b/fail2ban/protocol.py index b21ab848..d8e617a9 100644 --- a/fail2ban/protocol.py +++ b/fail2ban/protocol.py @@ -99,8 +99,8 @@ protocol = [ ["set bantime