Merge branch '0.10-extend-proto-banned' into 0.10

pull/2814/head
sebres 2020-05-25 15:04:12 +02:00
commit b8e2b77265
8 changed files with 113 additions and 6 deletions

View File

@ -55,6 +55,8 @@ protocol = [
["stop", "stops all jails and terminate the server"], ["stop", "stops all jails and terminate the server"],
["unban --all", "unbans all IP addresses (in all jails and database)"], ["unban --all", "unbans all IP addresses (in all jails and database)"],
["unban <IP> ... <IP>", "unbans <IP> (in all jails and database)"], ["unban <IP> ... <IP>", "unbans <IP> (in all jails and database)"],
["banned", "return jails with banned IPs as dictionary"],
["banned <IP> ... <IP>]", "return list(s) of jails where given IP(s) are banned"],
["status", "gets the current status of the server"], ["status", "gets the current status of the server"],
["ping", "tests if the server is alive"], ["ping", "tests if the server is alive"],
["echo", "for internal usage, returns back and outputs a given string"], ["echo", "for internal usage, returns back and outputs a given string"],
@ -120,6 +122,8 @@ protocol = [
["set <JAIL> action <ACT> <PROPERTY> <VALUE>", "sets the <VALUE> of <PROPERTY> for the action <ACT> for <JAIL>"], ["set <JAIL> action <ACT> <PROPERTY> <VALUE>", "sets the <VALUE> of <PROPERTY> for the action <ACT> for <JAIL>"],
["set <JAIL> action <ACT> <METHOD>[ <JSONKWARGS>]", "calls the <METHOD> with <JSONKWARGS> for the action <ACT> for <JAIL>"], ["set <JAIL> action <ACT> <METHOD>[ <JSONKWARGS>]", "calls the <METHOD> with <JSONKWARGS> for the action <ACT> for <JAIL>"],
['', "JAIL INFORMATION", ""], ['', "JAIL INFORMATION", ""],
["get <JAIL> banned", "return banned IPs of <JAIL>"],
["get <JAIL> banned <IP> ... <IP>]", "return 1 if IP is banned in <JAIL> otherwise 0, or a list of 1/0 for multiple IPs"],
["get <JAIL> logpath", "gets the list of the monitored files for <JAIL>"], ["get <JAIL> logpath", "gets the list of the monitored files for <JAIL>"],
["get <JAIL> logencoding", "gets the encoding of the log files for <JAIL>"], ["get <JAIL> logencoding", "gets the encoding of the log files for <JAIL>"],
["get <JAIL> journalmatch", "gets the journal filter match for <JAIL>"], ["get <JAIL> journalmatch", "gets the journal filter match for <JAIL>"],

View File

@ -210,6 +210,14 @@ class Actions(JailThread, Mapping):
def getBanTime(self): def getBanTime(self):
return self.__banManager.getBanTime() return self.__banManager.getBanTime()
def getBanned(self, ids):
lst = self.__banManager.getBanList()
if not ids:
return lst
if len(ids) == 1:
return 1 if ids[0] in lst else 0
return map(lambda ip: 1 if ip in lst else 0, ids)
def addBannedIP(self, ip): def addBannedIP(self, ip):
"""Ban an IP or list of IPs.""" """Ban an IP or list of IPs."""
unixTime = MyTime.time() unixTime = MyTime.time()

View File

@ -521,6 +521,32 @@ class Server:
cnt += jail.actions.removeBannedIP(value, ifexists=ifexists) cnt += jail.actions.removeBannedIP(value, ifexists=ifexists)
return cnt return cnt
def banned(self, name=None, ids=None):
if name is not None:
# single jail:
jails = [self.__jails[name]]
else:
# in all jails:
jails = self.__jails.values()
# check banned ids:
res = []
if name is None and ids:
for ip in ids:
ret = []
for jail in jails:
if jail.actions.getBanned([ip]):
ret.append(jail.name)
res.append(ret)
else:
for jail in jails:
ret = jail.actions.getBanned(ids)
if name is not None:
return ret
res.append(ret)
else:
res.append({jail.name: ret})
return res
def getBanTime(self, name): def getBanTime(self, name):
return self.__jails[name].actions.getBanTime() return self.__jails[name].actions.getBanTime()

View File

@ -118,6 +118,9 @@ class Transmitter:
if len(value) == 1 and value[0] == "--all": if len(value) == 1 and value[0] == "--all":
return self.__server.setUnbanIP() return self.__server.setUnbanIP()
return self.__server.setUnbanIP(None, value) return self.__server.setUnbanIP(None, value)
elif name == "banned":
# check IP is banned in all jails:
return self.__server.banned(None, command[1:])
elif name == "echo": elif name == "echo":
return command[1:] return command[1:]
elif name == "server-status": elif name == "server-status":
@ -424,7 +427,10 @@ class Transmitter:
return None return None
else: else:
return db.purgeage return db.purgeage
# Filter # Jail, Filter
elif command[1] == "banned":
# check IP is banned in all jails:
return self.__server.banned(name, command[2:])
elif command[1] == "logpath": elif command[1] == "logpath":
return self.__server.getLogPath(name) return self.__server.getLogPath(name)
elif command[1] == "logencoding": elif command[1] == "logencoding":

View File

@ -37,7 +37,7 @@ from threading import Thread
from ..client import fail2banclient, fail2banserver, fail2bancmdline from ..client import fail2banclient, fail2banserver, fail2bancmdline
from ..client.fail2bancmdline import Fail2banCmdLine from ..client.fail2bancmdline import Fail2banCmdLine
from ..client.fail2banclient import exec_command_line as _exec_client, VisualWait from ..client.fail2banclient import exec_command_line as _exec_client, CSocket, VisualWait
from ..client.fail2banserver import Fail2banServer, exec_command_line as _exec_server from ..client.fail2banserver import Fail2banServer, exec_command_line as _exec_server
from .. import protocol from .. import protocol
from ..server import server from ..server import server
@ -421,6 +421,14 @@ class Fail2banClientServerBase(LogCaptureTestCase):
self.assertRaises(exitType, self.exec_command_line[0], self.assertRaises(exitType, self.exec_command_line[0],
(self.exec_command_line[1:] + startparams + args)) (self.exec_command_line[1:] + startparams + args))
def execCmdDirect(self, startparams, *args):
sock = startparams[startparams.index('-s')+1]
s = CSocket(sock)
try:
return s.send(args)
finally:
s.close()
# #
# Common tests # Common tests
# #
@ -1007,6 +1015,30 @@ class Fail2banServerTest(Fail2banClientServerBase):
"[test-jail2] Found 192.0.2.3", "[test-jail2] Found 192.0.2.3",
"[test-jail2] Ban 192.0.2.3", "[test-jail2] Ban 192.0.2.3",
all=True) all=True)
# test banned command:
self.assertSortedEqual(self.execCmdDirect(startparams,
'banned'), (0, [
{'test-jail1': ['192.0.2.4', '192.0.2.1', '192.0.2.8', '192.0.2.3', '192.0.2.2']},
{'test-jail2': ['192.0.2.4', '192.0.2.9', '192.0.2.8']}
]
))
self.assertSortedEqual(self.execCmdDirect(startparams,
'banned', '192.0.2.1', '192.0.2.4', '192.0.2.222'), (0, [
['test-jail1'], ['test-jail1', 'test-jail2'], []
]
))
self.assertSortedEqual(self.execCmdDirect(startparams,
'get', 'test-jail1', 'banned')[1], [
'192.0.2.4', '192.0.2.1', '192.0.2.8', '192.0.2.3', '192.0.2.2'])
self.assertSortedEqual(self.execCmdDirect(startparams,
'get', 'test-jail2', 'banned')[1], [
'192.0.2.4', '192.0.2.9', '192.0.2.8'])
self.assertEqual(self.execCmdDirect(startparams,
'get', 'test-jail1', 'banned', '192.0.2.3')[1], 1)
self.assertEqual(self.execCmdDirect(startparams,
'get', 'test-jail1', 'banned', '192.0.2.9')[1], 0)
self.assertEqual(self.execCmdDirect(startparams,
'get', 'test-jail1', 'banned', '192.0.2.3', '192.0.2.9')[1], [1, 0])
# rotate logs: # rotate logs:
_write_file(test1log, "w+") _write_file(test1log, "w+")

View File

@ -390,7 +390,15 @@ class TestsUtilsTest(LogCaptureTestCase):
self.assertSortedEqual(['Z', {'A': ['B', 'C'], 'B': ['E', 'F']}], [{'B': ['F', 'E'], 'A': ['C', 'B']}, 'Z'], self.assertSortedEqual(['Z', {'A': ['B', 'C'], 'B': ['E', 'F']}], [{'B': ['F', 'E'], 'A': ['C', 'B']}, 'Z'],
level=-1) level=-1)
self.assertRaises(AssertionError, lambda: self.assertSortedEqual( self.assertRaises(AssertionError, lambda: self.assertSortedEqual(
['Z', {'A': ['B', 'C'], 'B': ['E', 'F']}], [{'B': ['F', 'E'], 'A': ['C', 'B']}, 'Z'])) ['Z', {'A': ['B', 'C'], 'B': ['E', 'F']}], [{'B': ['F', 'E'], 'A': ['C', 'B']}, 'Z'],
nestedOnly=True))
self.assertSortedEqual(
(0, [['A1'], ['A2', 'A1'], []]),
(0, [['A1'], ['A1', 'A2'], []]),
)
self.assertSortedEqual(list('ABC'), list('CBA'))
self.assertRaises(AssertionError, self.assertSortedEqual, ['ABC'], ['CBA'])
self.assertRaises(AssertionError, self.assertSortedEqual, [['ABC']], [['CBA']])
self._testAssertionErrorRE(r"\['A'\] != \['C', 'B'\]", self._testAssertionErrorRE(r"\['A'\] != \['C', 'B'\]",
self.assertSortedEqual, ['A'], ['C', 'B']) self.assertSortedEqual, ['A'], ['C', 'B'])
self._testAssertionErrorRE(r"\['A', 'B'\] != \['B', 'C'\]", self._testAssertionErrorRE(r"\['A', 'B'\] != \['B', 'C'\]",

View File

@ -556,7 +556,7 @@ if not hasattr(unittest.TestCase, 'assertDictEqual'):
self.fail(msg) self.fail(msg)
unittest.TestCase.assertDictEqual = assertDictEqual unittest.TestCase.assertDictEqual = assertDictEqual
def assertSortedEqual(self, a, b, level=1, nestedOnly=True, key=repr, msg=None): def assertSortedEqual(self, a, b, level=1, nestedOnly=False, key=repr, msg=None):
"""Compare complex elements (like dict, list or tuple) in sorted order until """Compare complex elements (like dict, list or tuple) in sorted order until
level 0 not reached (initial level = -1 meant all levels), level 0 not reached (initial level = -1 meant all levels),
or if nestedOnly set to True and some of the objects still contains nested lists or dicts. or if nestedOnly set to True and some of the objects still contains nested lists or dicts.
@ -566,6 +566,13 @@ def assertSortedEqual(self, a, b, level=1, nestedOnly=True, key=repr, msg=None):
if isinstance(v, dict): if isinstance(v, dict):
return any(isinstance(v, (dict, list, tuple)) for v in v.itervalues()) return any(isinstance(v, (dict, list, tuple)) for v in v.itervalues())
return any(isinstance(v, (dict, list, tuple)) for v in v) return any(isinstance(v, (dict, list, tuple)) for v in v)
if nestedOnly:
_nest_sorted = sorted
else:
def _nest_sorted(v, key=key):
if isinstance(v, (set, list, tuple)):
return sorted(list(_nest_sorted(v, key) for v in v), key=key)
return v
# level comparison routine: # level comparison routine:
def _assertSortedEqual(a, b, level, nestedOnly, key): def _assertSortedEqual(a, b, level, nestedOnly, key):
# first the lengths: # first the lengths:
@ -584,8 +591,8 @@ def assertSortedEqual(self, a, b, level=1, nestedOnly=True, key=repr, msg=None):
elif v1 != v2: elif v1 != v2:
raise ValueError('%r != %r' % (a, b)) raise ValueError('%r != %r' % (a, b))
else: # list, tuple, something iterable: else: # list, tuple, something iterable:
a = sorted(a, key=key) a = _nest_sorted(a, key=key)
b = sorted(b, key=key) b = _nest_sorted(b, key=key)
for v1, v2 in zip(a, b): for v1, v2 in zip(a, b):
if isinstance(v1, (dict, list, tuple)) and isinstance(v2, (dict, list, tuple)): if isinstance(v1, (dict, list, tuple)) and isinstance(v2, (dict, list, tuple)):
_assertSortedEqual(v1, v2, level-1 if level != 0 else 0, nestedOnly, key) _assertSortedEqual(v1, v2, level-1 if level != 0 else 0, nestedOnly, key)

View File

@ -111,6 +111,14 @@ jails and database)
unbans <IP> (in all jails and unbans <IP> (in all jails and
database) database)
.TP .TP
\fBbanned\fR
return jails with banned IPs as
dictionary
.TP
\fBbanned <IP> ... <IP>]\fR
return list(s) of jails where
given IP(s) are banned
.TP
\fBstatus\fR \fBstatus\fR
gets the current status of the gets the current status of the
server server
@ -356,6 +364,14 @@ for <JAIL>
.IP .IP
JAIL INFORMATION JAIL INFORMATION
.TP .TP
\fBget <JAIL> banned\fR
return banned IPs of <JAIL>
.TP
\fBget <JAIL> banned <IP> ... <IP>]\fR
return 1 if IP is banned in <JAIL>
otherwise 0, or a list of 1/0 for
multiple IPs
.TP
\fBget <JAIL> logpath\fR \fBget <JAIL> logpath\fR
gets the list of the monitored gets the list of the monitored
files for <JAIL> files for <JAIL>