mirror of https://github.com/fail2ban/fail2ban
fail2ban-client: extended with new feature which allows to inform fail2ban about single or multiple attempts (failure) for IP (failure-ID), syntax:
set <JAIL> attempt <IP> [<failure1> ... <failureN>]pull/2351/head
parent
84cec5e861
commit
00a6717953
|
@ -99,6 +99,7 @@ protocol = [
|
||||||
["set <JAIL> bantime <TIME>", "sets the number of seconds <TIME> a host will be banned for <JAIL>"],
|
["set <JAIL> bantime <TIME>", "sets the number of seconds <TIME> a host will be banned for <JAIL>"],
|
||||||
["set <JAIL> datepattern <PATTERN>", "sets the <PATTERN> used to match date/times for <JAIL>"],
|
["set <JAIL> datepattern <PATTERN>", "sets the <PATTERN> used to match date/times for <JAIL>"],
|
||||||
["set <JAIL> usedns <VALUE>", "sets the usedns mode for <JAIL>"],
|
["set <JAIL> usedns <VALUE>", "sets the usedns mode for <JAIL>"],
|
||||||
|
["set <JAIL> attempt <IP> [<failure1> ... <failureN>]", "manually notify about <IP> failure"],
|
||||||
["set <JAIL> banip <IP> ... <IP>", "manually Ban <IP> for <JAIL>"],
|
["set <JAIL> banip <IP> ... <IP>", "manually Ban <IP> for <JAIL>"],
|
||||||
["set <JAIL> unbanip [--report-absent] <IP> ... <IP>", "manually Unban <IP> in <JAIL>"],
|
["set <JAIL> unbanip [--report-absent] <IP> ... <IP>", "manually Unban <IP> in <JAIL>"],
|
||||||
["set <JAIL> maxretry <RETRY>", "sets the number of failures <RETRY> before banning the host for <JAIL>"],
|
["set <JAIL> maxretry <RETRY>", "sets the number of failures <RETRY> before banning the host for <JAIL>"],
|
||||||
|
|
|
@ -428,6 +428,34 @@ class Filter(JailThread):
|
||||||
else:
|
else:
|
||||||
self.__ignoreCache = None
|
self.__ignoreCache = None
|
||||||
|
|
||||||
|
def performBan(self, ip=None):
|
||||||
|
"""Performs a ban for IPs (or given ip) that are reached maxretry of the jail."""
|
||||||
|
try: # pragma: no branch - exception is the only way out
|
||||||
|
while True:
|
||||||
|
ticket = self.failManager.toBan(ip)
|
||||||
|
self.jail.putFailTicket(ticket)
|
||||||
|
except FailManagerEmpty:
|
||||||
|
self.failManager.cleanup(MyTime.time())
|
||||||
|
|
||||||
|
def addAttempt(self, ip, *matches):
|
||||||
|
"""Generate a failed attempt for ip"""
|
||||||
|
if not isinstance(ip, IPAddr):
|
||||||
|
ip = IPAddr(ip)
|
||||||
|
matches = list(matches) # tuple to list
|
||||||
|
|
||||||
|
# Generate the failure attempt for the IP:
|
||||||
|
unixTime = MyTime.time()
|
||||||
|
ticket = FailTicket(ip, unixTime, matches=matches)
|
||||||
|
logSys.info(
|
||||||
|
"[%s] Attempt %s - %s", self.jailName, ip, datetime.datetime.fromtimestamp(unixTime).strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
)
|
||||||
|
self.failManager.addFailure(ticket, len(matches) or 1)
|
||||||
|
|
||||||
|
# Perform the ban if this attempt is resulted to:
|
||||||
|
self.performBan(ip)
|
||||||
|
|
||||||
|
return 1
|
||||||
|
|
||||||
##
|
##
|
||||||
# Ignore own IP/DNS.
|
# Ignore own IP/DNS.
|
||||||
#
|
#
|
||||||
|
|
|
@ -473,9 +473,12 @@ class Server:
|
||||||
def setBanTime(self, name, value):
|
def setBanTime(self, name, value):
|
||||||
self.__jails[name].actions.setBanTime(value)
|
self.__jails[name].actions.setBanTime(value)
|
||||||
|
|
||||||
|
def addAttemptIP(self, name, *args):
|
||||||
|
return self.__jails[name].filter.addAttempt(*args)
|
||||||
|
|
||||||
def setBanIP(self, name, value):
|
def setBanIP(self, name, value):
|
||||||
return self.__jails[name].actions.addBannedIP(value)
|
return self.__jails[name].actions.addBannedIP(value)
|
||||||
|
|
||||||
def setUnbanIP(self, name=None, value=None, ifexists=True):
|
def setUnbanIP(self, name=None, value=None, ifexists=True):
|
||||||
if name is not None:
|
if name is not None:
|
||||||
# single jail:
|
# single jail:
|
||||||
|
|
|
@ -282,6 +282,9 @@ class Transmitter:
|
||||||
value = command[2]
|
value = command[2]
|
||||||
self.__server.setBanTime(name, value)
|
self.__server.setBanTime(name, value)
|
||||||
return self.__server.getBanTime(name)
|
return self.__server.getBanTime(name)
|
||||||
|
elif command[1] == "attempt":
|
||||||
|
value = command[2:]
|
||||||
|
return self.__server.addAttemptIP(name, *value)
|
||||||
elif command[1] == "banip":
|
elif command[1] == "banip":
|
||||||
value = command[2:]
|
value = command[2:]
|
||||||
return self.__server.setBanIP(name,value)
|
return self.__server.setBanIP(name,value)
|
||||||
|
|
|
@ -394,6 +394,14 @@ class IgnoreIP(LogCaptureTestCase):
|
||||||
self.assertLogged('Ignore 192.168.1.32')
|
self.assertLogged('Ignore 192.168.1.32')
|
||||||
tearDownMyTime()
|
tearDownMyTime()
|
||||||
|
|
||||||
|
def testAddAttempt(self):
|
||||||
|
self.filter.setMaxRetry(3)
|
||||||
|
for i in xrange(1, 1+3):
|
||||||
|
self.filter.addAttempt('192.0.2.1')
|
||||||
|
self.assertLogged('Attempt 192.0.2.1', '192.0.2.1:%d' % i, all=True, wait=True)
|
||||||
|
self.jail.actions._Actions__checkBan()
|
||||||
|
self.assertLogged('Ban 192.0.2.1', wait=True)
|
||||||
|
|
||||||
def testIgnoreCommand(self):
|
def testIgnoreCommand(self):
|
||||||
self.filter.ignoreCommand = sys.executable + ' ' + os.path.join(TEST_FILES_DIR, "ignorecommand.py <ip>")
|
self.filter.ignoreCommand = sys.executable + ' ' + os.path.join(TEST_FILES_DIR, "ignorecommand.py <ip>")
|
||||||
self.assertTrue(self.filter.inIgnoreIPList("10.0.0.1"))
|
self.assertTrue(self.filter.inIgnoreIPList("10.0.0.1"))
|
||||||
|
|
|
@ -347,6 +347,25 @@ class Transmitter(TransmitterBase):
|
||||||
self.transm.proceed(
|
self.transm.proceed(
|
||||||
["set", self.jailName, "unbanip", "--report-absent", "192.0.2.255"])[0],1)
|
["set", self.jailName, "unbanip", "--report-absent", "192.0.2.255"])[0],1)
|
||||||
|
|
||||||
|
def testJailAttemptIP(self):
|
||||||
|
self.server.startJail(self.jailName) # Jail must be started
|
||||||
|
|
||||||
|
def attempt(ip, matches):
|
||||||
|
return self.transm.proceed(["set", self.jailName, "attempt", ip] + matches)
|
||||||
|
|
||||||
|
self.setGetTest("maxretry", "5", 5, jail=self.jailName)
|
||||||
|
# produce 2 single attempts per IP:
|
||||||
|
for i in (1, 2):
|
||||||
|
for ip in ("192.0.2.1", "192.0.2.2"):
|
||||||
|
self.assertEqual(attempt(ip, ["test failure %d" % i]), (0, 1))
|
||||||
|
self.assertLogged("192.0.2.1:2", "192.0.2.2:2", all=True, wait=True)
|
||||||
|
# this 3 attempts at once should cause a ban:
|
||||||
|
self.assertEqual(attempt(ip, ["test failure %d" % i for i in (3,4,5)]), (0, 1))
|
||||||
|
self.assertLogged("192.0.2.2:5", wait=True)
|
||||||
|
# resulted to ban for "192.0.2.2" but not for "192.0.2.1":
|
||||||
|
self.assertLogged("Ban 192.0.2.2", wait=True)
|
||||||
|
self.assertNotLogged("Ban 192.0.2.1")
|
||||||
|
|
||||||
def testJailMaxRetry(self):
|
def testJailMaxRetry(self):
|
||||||
self.setGetTest("maxretry", "5", 5, jail=self.jailName)
|
self.setGetTest("maxretry", "5", 5, jail=self.jailName)
|
||||||
self.setGetTest("maxretry", "2", 2, jail=self.jailName)
|
self.setGetTest("maxretry", "2", 2, jail=self.jailName)
|
||||||
|
|
|
@ -272,6 +272,9 @@ date/times for <JAIL>
|
||||||
\fBset <JAIL> usedns <VALUE>\fR
|
\fBset <JAIL> usedns <VALUE>\fR
|
||||||
sets the usedns mode for <JAIL>
|
sets the usedns mode for <JAIL>
|
||||||
.TP
|
.TP
|
||||||
|
\fBset <JAIL> attempt <IP> [<failure1> ... <failureN>]\fR
|
||||||
|
manually notify about <IP> failure
|
||||||
|
.TP
|
||||||
\fBset <JAIL> banip <IP> ... <IP>\fR
|
\fBset <JAIL> banip <IP> ... <IP>\fR
|
||||||
manually Ban <IP> for <JAIL>
|
manually Ban <IP> for <JAIL>
|
||||||
.TP
|
.TP
|
||||||
|
|
Loading…
Reference in New Issue