From df97fd33cfb475f2833024eceaba7de7ca1a8f56 Mon Sep 17 00:00:00 2001 From: sebres Date: Sun, 6 Jan 2019 22:31:23 +0100 Subject: [PATCH] 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