mirror of https://github.com/fail2ban/fail2ban
Merge pull request #2315 from psvi/ban_list
New command `fail2ban-client get <JAIL> banip`pull/2319/merge
commit
53684af0e3
|
@ -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 <JAIL> banip ?sep-char|--with-time?` to get the banned ip addresses (gh-1916).
|
||||
|
||||
|
||||
ver. 0.10.4-dev-1 (20??/??/??) - development edition
|
||||
|
|
|
@ -180,6 +180,12 @@ 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":
|
||||
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,
|
||||
|
|
|
@ -128,6 +128,7 @@ protocol = [
|
|||
["get <JAIL> bantime", "gets the time a host is banned for <JAIL>"],
|
||||
["get <JAIL> datepattern", "gets the patern used to match date/times for <JAIL>"],
|
||||
["get <JAIL> usedns", "gets the usedns setting for <JAIL>"],
|
||||
["get <JAIL> banip [<SEP>|--with-time]", "gets the list of of banned IP addresses for <JAIL>. Optionally the separator character ('<SEP>', 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 <JAIL> maxretry", "gets the number of failures allowed for <JAIL>"],
|
||||
["get <JAIL> maxlines", "gets the number of lines to buffer for <JAIL>"],
|
||||
["get <JAIL> actions", "gets a list of actions for <JAIL>"],
|
||||
|
|
|
@ -204,6 +204,16 @@ class Actions(JailThread, Mapping):
|
|||
def getBanTime(self):
|
||||
return self.__banManager.getBanTime()
|
||||
|
||||
def getBanList(self, withTime=False):
|
||||
"""Returns the list of banned IP addresses.
|
||||
|
||||
Returns
|
||||
-------
|
||||
list
|
||||
The list of banned IP addresses.
|
||||
"""
|
||||
return self.__banManager.getBanList(ordered=True, withTime=withTime)
|
||||
|
||||
def removeBannedIP(self, ip=None, db=True, ifexists=False):
|
||||
"""Removes banned IP calling actions' unban method
|
||||
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -113,6 +113,16 @@ class MyTime:
|
|||
return time.localtime(x)
|
||||
else:
|
||||
return time.localtime(MyTime.myTime)
|
||||
|
||||
@staticmethod
|
||||
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(format)
|
||||
|
||||
## precreate/precompile primitives used in str2seconds:
|
||||
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -510,6 +510,21 @@ class Server:
|
|||
def getBanTime(self, name):
|
||||
return self.__jails[name].actions.getBanTime()
|
||||
|
||||
def getBanList(self, name, withTime=False):
|
||||
"""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(withTime)
|
||||
|
||||
def setBanTimeExtra(self, name, opt, value):
|
||||
self.__jails[name].setBanTimeExtra(opt, value)
|
||||
|
||||
|
|
|
@ -390,6 +390,9 @@ class Transmitter:
|
|||
# Action
|
||||
elif command[1] == "bantime":
|
||||
return self.__server.getBanTime(name)
|
||||
elif command[1] == "banip":
|
||||
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)
|
||||
|
|
|
@ -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,
|
||||
|
@ -1397,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()
|
||||
|
@ -1411,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()
|
||||
|
|
|
@ -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))
|
||||
|
||||
|
|
|
@ -347,6 +347,46 @@ 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)
|
||||
|
||||
# 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"])
|
||||
_getBanListTest(jail, banip="192.168.1.10",
|
||||
outList=["127.0.0.1", "192.168.0.1", "192.168.1.10"])
|
||||
_getBanListTest(jail, unbanip="127.0.0.1",
|
||||
outList=["192.168.0.1", "192.168.1.10"])
|
||||
_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)
|
||||
self.setGetTest("maxretry", "2", 2, jail=self.jailName)
|
||||
|
|
|
@ -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 <JAIL>
|
|||
\fBget <JAIL> usedns\fR
|
||||
gets the usedns setting for <JAIL>
|
||||
.TP
|
||||
\fBget <JAIL> banip [<SEP>|\-\-with\-time]\fR
|
||||
gets the list of of banned IP
|
||||
addresses for <JAIL>. Optionally
|
||||
the separator character ('<SEP>',
|
||||
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 <JAIL> maxretry\fR
|
||||
gets the number of failures
|
||||
allowed for <JAIL>
|
||||
|
|
Loading…
Reference in New Issue