From 5e93bf9bd3ec66707cb961c851a0ce448be3a02b Mon Sep 17 00:00:00 2001 From: sebres Date: Thu, 23 Mar 2017 15:52:31 +0100 Subject: [PATCH 1/5] Introduced new option "ignoreself", specifies whether the local resp. own IP addresses should be ignored (default is true). Fail2ban will not ban a host which matches such addresses. Option "ignoreip" affects additionally to "ignoreself" and don't need to include the DNS resp. IPs of the host self. --- config/jail.conf | 12 +++++++---- fail2ban/client/jailreader.py | 1 + fail2ban/protocol.py | 2 ++ fail2ban/server/filter.py | 18 ++++++++++++++++ fail2ban/server/ipdns.py | 36 ++++++++++++++++++++++++++++++++ fail2ban/server/server.py | 6 ++++++ fail2ban/server/transmitter.py | 6 ++++++ fail2ban/tests/filtertestcase.py | 11 ++++++++++ fail2ban/tests/servertestcase.py | 10 +++++++++ 9 files changed, 98 insertions(+), 4 deletions(-) diff --git a/config/jail.conf b/config/jail.conf index c5440b71..7e5cc9b7 100644 --- a/config/jail.conf +++ b/config/jail.conf @@ -44,10 +44,14 @@ before = paths-debian.conf # MISCELLANEOUS OPTIONS # -# "ignoreip" can be an IP address, a CIDR mask or a DNS host. Fail2ban will not -# ban a host which matches an address in this list. Several addresses can be -# defined using space (and/or comma) separator. -ignoreip = 127.0.0.1/8 ::1 +# "ignorself" specifies whether the local resp. own IP addresses should be ignored +# (default is true). Fail2ban will not ban a host which matches such addresses. +#ignorself = true + +# "ignoreip" can be a list of IP addresses, CIDR masks or DNS hosts. Fail2ban +# will not ban a host which matches an address in this list. Several addresses +# can be defined using space (and/or comma) separator. +#ignoreip = 127.0.0.1/8 ::1 # External command that will take an tagged arguments to ignore, e.g. , # and return true if the IP is to be ignored. False otherwise. diff --git a/fail2ban/client/jailreader.py b/fail2ban/client/jailreader.py index 7f69155f..ca092990 100644 --- a/fail2ban/client/jailreader.py +++ b/fail2ban/client/jailreader.py @@ -110,6 +110,7 @@ class JailReader(ConfigReader): ["string", "failregex", None], ["string", "ignoreregex", None], ["string", "ignorecommand", None], + ["bool", "ignoreself", None], ["string", "ignoreip", None], ["string", "filter", ""], ["string", "datepattern", None], diff --git a/fail2ban/protocol.py b/fail2ban/protocol.py index d1c33d88..3625ec01 100644 --- a/fail2ban/protocol.py +++ b/fail2ban/protocol.py @@ -81,6 +81,7 @@ protocol = [ ["status [FLAVOR]", "gets the current status of , with optional flavor or extended info"], ['', "JAIL CONFIGURATION", ""], ["set idle on|off", "sets the idle state of "], +["set ignoreself true|false", "allows the ignoring of own IP addresses"], ["set addignoreip ", "adds to the ignore list of "], ["set delignoreip ", "removes from the ignore list of "], ["set addlogpath ['tail']", "adds to the monitoring list of , optionally starting at the 'tail' of the file (default 'head')."], @@ -117,6 +118,7 @@ protocol = [ ["get logpath", "gets the list of the monitored files for "], ["get logencoding", "gets the encoding of the log files for "], ["get journalmatch", "gets the journal filter match for "], +["get ignoreself", "gets the current value of the ignoring the own IP addresses"], ["get ignoreip", "gets the list of ignored IP addresses for "], ["get ignorecommand", "gets ignorecommand of "], ["get failregex", "gets the list of regular expressions which matches the failures for "], diff --git a/fail2ban/server/filter.py b/fail2ban/server/filter.py index 72bf47d8..b425060a 100644 --- a/fail2ban/server/filter.py +++ b/fail2ban/server/filter.py @@ -76,6 +76,8 @@ class Filter(JailThread): self.setUseDns(useDns) ## The amount of time to look back. self.__findTime = 600 + ## Ignore own IPs flag: + self.__ignoreSelf = True ## The ignore IP list. self.__ignoreIpList = [] ## Size of line buffer @@ -413,6 +415,17 @@ class Filter(JailThread): return ip + ## + # Ignore own IP/DNS. + # + @property + def ignoreSelf(self): + return self.__ignoreSelf + + @ignoreSelf.setter + def ignoreSelf(self, value): + self.__ignoreSelf = value + ## # Add an IP/DNS to the ignore list. # @@ -458,6 +471,11 @@ class Filter(JailThread): def inIgnoreIPList(self, ip, log_ignore=False): if not isinstance(ip, IPAddr): ip = IPAddr(ip) + + # check own IPs should be ignored and 'ip' is self IP: + if self.__ignoreSelf and ip in DNSUtils.getSelfIPs(): + return True + for net in self.__ignoreIpList: # check if the IP is covered by ignore IP if ip.isInNet(net): diff --git a/fail2ban/server/ipdns.py b/fail2ban/server/ipdns.py index bd3b812d..bda32ae8 100644 --- a/fail2ban/server/ipdns.py +++ b/fail2ban/server/ipdns.py @@ -118,6 +118,42 @@ class DNSUtils: return ipList + @staticmethod + def getSelfNames(): + """Get own host names of self""" + # try find cached own hostnames (this tuple-key cannot be used elsewhere): + key = ('self','dns') + names = DNSUtils.CACHE_ipToName.get(key) + # get it using different ways (a set with names of localhost, hostname, fully qualified): + if names is None: + names = set(['localhost']) + for hostname in (socket.gethostname, socket.getfqdn): + try: + names |= set([hostname()]) + except Exception as e: # pragma: no cover + logSys.warning("Retrieving own hostnames failed: %s", e) + # cache and return : + DNSUtils.CACHE_ipToName.set(key, names) + return names + + @staticmethod + def getSelfIPs(): + """Get own IP addresses of self""" + # try find cached own IPs (this tuple-key cannot be used elsewhere): + key = ('self','ips') + ips = DNSUtils.CACHE_nameToIp.get(key) + # get it using different ways (a set with IPs of localhost, hostname, fully qualified): + if ips is None: + ips = set() + for hostname in DNSUtils.getSelfNames(): + try: + ips |= set(DNSUtils.textToIp(hostname, 'yes')) + except Exception as e: # pragma: no cover + logSys.warning("Retrieving own IPs of %s failed: %s", hostname, e) + # cache and return : + DNSUtils.CACHE_nameToIp.set(key, ips) + return ips + ## # Class for IP address handling. diff --git a/fail2ban/server/server.py b/fail2ban/server/server.py index dfab1e38..facbe393 100644 --- a/fail2ban/server/server.py +++ b/fail2ban/server/server.py @@ -308,6 +308,12 @@ class Server: return self.__jails[name].idle # Filter + def setIgnoreSelf(self, name, value): + self.__jails[name].filter.ignoreSelf = value + + def getIgnoreSelf(self, name): + return self.__jails[name].filter.ignoreSelf + def addIgnoreIP(self, name, ip): self.__jails[name].filter.addIgnoreIP(ip) diff --git a/fail2ban/server/transmitter.py b/fail2ban/server/transmitter.py index ad21b851..bc9edd43 100644 --- a/fail2ban/server/transmitter.py +++ b/fail2ban/server/transmitter.py @@ -181,6 +181,10 @@ class Transmitter: raise Exception("Invalid idle option, must be 'on' or 'off'") return self.__server.getIdleJail(name) # Filter + elif command[1] == "ignoreself": + value = command[2] + self.__server.setIgnoreSelf(name, value) + return self.__server.getIgnoreSelf(name) elif command[1] == "addignoreip": value = command[2] self.__server.addIgnoreIP(name, value) @@ -341,6 +345,8 @@ class Transmitter: return self.__server.getLogEncoding(name) elif command[1] == "journalmatch": # pragma: systemd no cover return self.__server.getJournalMatch(name) + elif command[1] == "ignoreself": + return self.__server.getIgnoreSelf(name) elif command[1] == "ignoreip": return self.__server.getIgnoreIP(name) elif command[1] == "ignorecommand": diff --git a/fail2ban/tests/filtertestcase.py b/fail2ban/tests/filtertestcase.py index d3217555..10310a5d 100644 --- a/fail2ban/tests/filtertestcase.py +++ b/fail2ban/tests/filtertestcase.py @@ -325,6 +325,17 @@ class IgnoreIP(LogCaptureTestCase): LogCaptureTestCase.setUp(self) self.jail = DummyJail() self.filter = FileFilter(self.jail) + self.filter.ignoreSelf = False + + def testIgnoreSelfIP(self): + ipList = ("127.0.0.1",) + # test ignoreSelf is false: + for ip in ipList: + self.assertFalse(self.filter.inIgnoreIPList(ip)) + # test ignoreSelf with true: + self.filter.ignoreSelf = True + for ip in ipList: + self.assertTrue(self.filter.inIgnoreIPList(ip)) def testIgnoreIPOK(self): ipList = "127.0.0.1", "192.168.0.1", "255.255.255.255", "99.99.99.99" diff --git a/fail2ban/tests/servertestcase.py b/fail2ban/tests/servertestcase.py index 8d1cfa62..51ff8880 100644 --- a/fail2ban/tests/servertestcase.py +++ b/fail2ban/tests/servertestcase.py @@ -449,6 +449,16 @@ class Transmitter(TransmitterBase): self.transm.proceed(["set", self.jailName, "delignoreip", value]), (0, [value])) + self.assertEqual( + self.transm.proceed(["get", self.jailName, "ignoreself"]), + (0, True)) + self.assertEqual( + self.transm.proceed(["set", self.jailName, "ignoreself", False]), + (0, False)) + self.assertEqual( + self.transm.proceed(["get", self.jailName, "ignoreself"]), + (0, False)) + def testJailIgnoreCommand(self): self.setGetTest("ignorecommand", "bin ", jail=self.jailName) From 6c4b1c720475613b4765d040b4d86857a4ccf52b Mon Sep 17 00:00:00 2001 From: sebres Date: Thu, 23 Mar 2017 15:54:53 +0100 Subject: [PATCH 2/5] Update ChangeLog --- ChangeLog | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ChangeLog b/ChangeLog index 7629fb2f..b6e7bacd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -68,6 +68,10 @@ TODO: implementing of options resp. other tasks from PR #1346 * Samples test case factory extended with filter options - dict in JSON to control filter options (e. g. mode, etc.): # filterOptions: {"mode": "aggressive"} +* Introduced new jail option "ignoreself", specifies whether the local resp. own IP addresses + should be ignored (default is true). Fail2ban will not ban a host which matches such addresses. + Option "ignoreip" affects additionally to "ignoreself" and don't need to include the DNS + resp. IPs of the host self. ver. 0.10.0-alpha-1 (2016/07/14) - ipv6-support-etc From 663bc9903d69e95e19b67a0bf77c462a4f5fb5ad Mon Sep 17 00:00:00 2001 From: sebres Date: Thu, 23 Mar 2017 16:19:21 +0100 Subject: [PATCH 3/5] increase coverage (was decreased since "ignoreip" was set to default empty) --- fail2ban/tests/fail2banclienttestcase.py | 1 + 1 file changed, 1 insertion(+) diff --git a/fail2ban/tests/fail2banclienttestcase.py b/fail2ban/tests/fail2banclienttestcase.py index 683368ee..35e00421 100644 --- a/fail2ban/tests/fail2banclienttestcase.py +++ b/fail2ban/tests/fail2banclienttestcase.py @@ -780,6 +780,7 @@ class Fail2banServerTest(Fail2banClientServerBase): "findtime = 10m", "failregex = ^\s*failure 401|403 from ", "datepattern = {^LN-BEG}EPOCH", + "ignoreip = 127.0.0.1/8 ::1", # just to cover ignoreip in jailreader/transmitter "", "[test-jail1]", "backend = " + backend, "filter =", "action = ", From 30352c5f03263600e809fb3fa857cd99df1ba40d Mon Sep 17 00:00:00 2001 From: sebres Date: Thu, 23 Mar 2017 17:48:52 +0100 Subject: [PATCH 4/5] fix sporadic coverage changes (sometimes produces "no such process" in popen.poll after terminate/kill in timeout test cases) --- fail2ban/server/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fail2ban/server/utils.py b/fail2ban/server/utils.py index 58363e76..58363ff0 100644 --- a/fail2ban/server/utils.py +++ b/fail2ban/server/utils.py @@ -211,7 +211,8 @@ class Utils(): if retcode is None or tout_kill_tree: # Still going... os.killpg(pgid, signal.SIGKILL) # Kill the process time.sleep(Utils.DEFAULT_SLEEP_INTERVAL) - retcode = popen.poll() + if retcode is None: # pragma: no cover - too sporadic + retcode = popen.poll() #logSys.debug("%s -- killed %s ", realCmd, retcode) if retcode is None and not Utils.pid_exists(pgid): # pragma: no cover retcode = signal.SIGKILL From e7052e9625926370365de5cef02e08e8ac548e3b Mon Sep 17 00:00:00 2001 From: sebres Date: Fri, 24 Mar 2017 09:55:20 +0100 Subject: [PATCH 5/5] update man/jail.conf.5 (docu for the ignoreself) --- man/jail.conf.5 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/man/jail.conf.5 b/man/jail.conf.5 index 2e333e5a..5a75369c 100644 --- a/man/jail.conf.5 +++ b/man/jail.conf.5 @@ -199,11 +199,14 @@ Arguments can be passed to actions to override the default values from the [Init Values can also be quoted (required when value includes a ","). More that one action can be specified (in separate lines). .RE .TP +.B ignoreself +boolean value (default true) indicates the banning of own IP addresses should be prevented +.TP .B ignoreip -list of IPs not to ban. They can include a CIDR mask too. +list of IPs not to ban. They can include a DNS resp. CIDR mask too. The option affects additionally to \fBignoreself\fR (if true) and don't need to contain own DNS resp. IPs of the running host. .TP .B ignorecommand -command that is executed to determine if the current candidate IP for banning should not be banned. +command that is executed to determine if the current candidate IP for banning (or failure-ID for raw IDs) should not be banned. The option affects additionally to \fBignoreself\fR and \fBignoreip\fR and will be first executed if both don't hit. .br IP will not be banned if command returns successfully (exit code 0). Like ACTION FILES, tags like are can be included in the ignorecommand value and will be substituted before execution. Currently only is supported however more will be added later.