mirror of https://github.com/fail2ban/fail2ban
Merge pull request #2176 from sebres/ignore-cache
Introduces cache for ignore-facilities (for `ignoreip`, `ignoreself` and `ignorecommand`)pull/2180/head
commit
cc321b78da
|
@ -50,6 +50,10 @@ ver. 0.10.4-dev-1 (20??/??/??) - development edition
|
||||||
* systemd: fixed type error on option `journalflags`: an integer is required (gh-2125);
|
* systemd: fixed type error on option `journalflags`: an integer is required (gh-2125);
|
||||||
|
|
||||||
### New Features
|
### New Features
|
||||||
|
* new option `ignorecache` to improve performance of ignore failure check (using caching of `ignoreip`,
|
||||||
|
`ignoreself` and `ignorecommand`), see `man jail.conf` for syntax-example;
|
||||||
|
* `ignorecommand` extended to use actions-similar replacement (capable to interpolate
|
||||||
|
all possible tags like `<ip-host>`, `<family>`, `<fid>`, `F-USER` etc.)
|
||||||
|
|
||||||
### Enhancements
|
### Enhancements
|
||||||
* `filter.d/dovecot.conf`: extended with tags F-USER (and alternatives) to collect user-logins (gh-2168)
|
* `filter.d/dovecot.conf`: extended with tags F-USER (and alternatives) to collect user-logins (gh-2168)
|
||||||
|
|
|
@ -100,6 +100,7 @@ class JailReader(ConfigReader):
|
||||||
["string", "ignorecommand", None],
|
["string", "ignorecommand", None],
|
||||||
["bool", "ignoreself", None],
|
["bool", "ignoreself", None],
|
||||||
["string", "ignoreip", None],
|
["string", "ignoreip", None],
|
||||||
|
["string", "ignorecache", None],
|
||||||
["string", "filter", ""],
|
["string", "filter", ""],
|
||||||
["string", "datepattern", None],
|
["string", "datepattern", None],
|
||||||
["string", "logtimezone", None],
|
["string", "logtimezone", None],
|
||||||
|
|
|
@ -84,6 +84,8 @@ protocol = [
|
||||||
["set <JAIL> ignoreself true|false", "allows the ignoring of own IP addresses"],
|
["set <JAIL> ignoreself true|false", "allows the ignoring of own IP addresses"],
|
||||||
["set <JAIL> addignoreip <IP>", "adds <IP> to the ignore list of <JAIL>"],
|
["set <JAIL> addignoreip <IP>", "adds <IP> to the ignore list of <JAIL>"],
|
||||||
["set <JAIL> delignoreip <IP>", "removes <IP> from the ignore list of <JAIL>"],
|
["set <JAIL> delignoreip <IP>", "removes <IP> from the ignore list of <JAIL>"],
|
||||||
|
["set <JAIL> ignorecommand <VALUE>", "sets ignorecommand of <JAIL>"],
|
||||||
|
["set <JAIL> ignorecache <VALUE>", "sets ignorecache of <JAIL>"],
|
||||||
["set <JAIL> addlogpath <FILE> ['tail']", "adds <FILE> to the monitoring list of <JAIL>, optionally starting at the 'tail' of the file (default 'head')."],
|
["set <JAIL> addlogpath <FILE> ['tail']", "adds <FILE> to the monitoring list of <JAIL>, optionally starting at the 'tail' of the file (default 'head')."],
|
||||||
["set <JAIL> dellogpath <FILE>", "removes <FILE> from the monitoring list of <JAIL>"],
|
["set <JAIL> dellogpath <FILE>", "removes <FILE> from the monitoring list of <JAIL>"],
|
||||||
["set <JAIL> logencoding <ENCODING>", "sets the <ENCODING> of the log files for <JAIL>"],
|
["set <JAIL> logencoding <ENCODING>", "sets the <ENCODING> of the log files for <JAIL>"],
|
||||||
|
@ -91,7 +93,6 @@ protocol = [
|
||||||
["set <JAIL> deljournalmatch <MATCH>", "removes <MATCH> from the journal filter of <JAIL>"],
|
["set <JAIL> deljournalmatch <MATCH>", "removes <MATCH> from the journal filter of <JAIL>"],
|
||||||
["set <JAIL> addfailregex <REGEX>", "adds the regular expression <REGEX> which must match failures for <JAIL>"],
|
["set <JAIL> addfailregex <REGEX>", "adds the regular expression <REGEX> which must match failures for <JAIL>"],
|
||||||
["set <JAIL> delfailregex <INDEX>", "removes the regular expression at <INDEX> for failregex"],
|
["set <JAIL> delfailregex <INDEX>", "removes the regular expression at <INDEX> for failregex"],
|
||||||
["set <JAIL> ignorecommand <VALUE>", "sets ignorecommand of <JAIL>"],
|
|
||||||
["set <JAIL> addignoreregex <REGEX>", "adds the regular expression <REGEX> which should match pattern to exclude for <JAIL>"],
|
["set <JAIL> addignoreregex <REGEX>", "adds the regular expression <REGEX> which should match pattern to exclude for <JAIL>"],
|
||||||
["set <JAIL> delignoreregex <INDEX>", "removes the regular expression at <INDEX> for ignoreregex"],
|
["set <JAIL> delignoreregex <INDEX>", "removes the regular expression at <INDEX> for ignoreregex"],
|
||||||
["set <JAIL> findtime <TIME>", "sets the number of seconds <TIME> for which the filter will look back for <JAIL>"],
|
["set <JAIL> findtime <TIME>", "sets the number of seconds <TIME> for which the filter will look back for <JAIL>"],
|
||||||
|
|
|
@ -30,6 +30,7 @@ import re
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
from .actions import Actions
|
||||||
from .failmanager import FailManagerEmpty, FailManager
|
from .failmanager import FailManagerEmpty, FailManager
|
||||||
from .ipdns import DNSUtils, IPAddr
|
from .ipdns import DNSUtils, IPAddr
|
||||||
from .ticket import FailTicket
|
from .ticket import FailTicket
|
||||||
|
@ -80,6 +81,10 @@ class Filter(JailThread):
|
||||||
self.__ignoreSelf = True
|
self.__ignoreSelf = True
|
||||||
## The ignore IP list.
|
## The ignore IP list.
|
||||||
self.__ignoreIpList = []
|
self.__ignoreIpList = []
|
||||||
|
## External command
|
||||||
|
self.__ignoreCommand = False
|
||||||
|
## Cache for ignoreip:
|
||||||
|
self.__ignoreCache = None
|
||||||
## Size of line buffer
|
## Size of line buffer
|
||||||
self.__lineBufferSize = 1
|
self.__lineBufferSize = 1
|
||||||
## Line buffer
|
## Line buffer
|
||||||
|
@ -89,8 +94,6 @@ class Filter(JailThread):
|
||||||
self.__lastDate = None
|
self.__lastDate = None
|
||||||
## if set, treat log lines without explicit time zone to be in this time zone
|
## if set, treat log lines without explicit time zone to be in this time zone
|
||||||
self.__logtimezone = None
|
self.__logtimezone = None
|
||||||
## External command
|
|
||||||
self.__ignoreCommand = False
|
|
||||||
## Default or preferred encoding (to decode bytes from file or journal):
|
## Default or preferred encoding (to decode bytes from file or journal):
|
||||||
self.__encoding = PREFER_ENC
|
self.__encoding = PREFER_ENC
|
||||||
## Cache temporary holds failures info (used by multi-line for wrapping e. g. conn-id to host):
|
## Cache temporary holds failures info (used by multi-line for wrapping e. g. conn-id to host):
|
||||||
|
@ -396,19 +399,34 @@ class Filter(JailThread):
|
||||||
raise Exception("run() is abstract")
|
raise Exception("run() is abstract")
|
||||||
|
|
||||||
##
|
##
|
||||||
# Set external command, for ignoredips
|
# External command, for ignoredips
|
||||||
#
|
#
|
||||||
|
|
||||||
def setIgnoreCommand(self, command):
|
@property
|
||||||
|
def ignoreCommand(self):
|
||||||
|
return self.__ignoreCommand
|
||||||
|
|
||||||
|
@ignoreCommand.setter
|
||||||
|
def ignoreCommand(self, command):
|
||||||
self.__ignoreCommand = command
|
self.__ignoreCommand = command
|
||||||
|
|
||||||
##
|
##
|
||||||
# Get external command, for ignoredips
|
# Cache parameters for ignoredips
|
||||||
#
|
#
|
||||||
|
|
||||||
def getIgnoreCommand(self):
|
@property
|
||||||
return self.__ignoreCommand
|
def ignoreCache(self):
|
||||||
|
return [self.__ignoreCache[0], self.__ignoreCache[1].maxCount, self.__ignoreCache[1].maxTime] \
|
||||||
|
if self.__ignoreCache else None
|
||||||
|
|
||||||
|
@ignoreCache.setter
|
||||||
|
def ignoreCache(self, command):
|
||||||
|
if command:
|
||||||
|
self.__ignoreCache = command['key'], Utils.Cache(
|
||||||
|
maxCount=int(command.get('max-count', 100)), maxTime=MyTime.str2seconds(command.get('max-time', 5*60))
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.__ignoreCache = None
|
||||||
##
|
##
|
||||||
# Ban an IP - http://blogs.buanzo.com.ar/2009/04/fail2ban-patch-ban-ip-address-manually.html
|
# Ban an IP - http://blogs.buanzo.com.ar/2009/04/fail2ban-patch-ban-ip-address-manually.html
|
||||||
# Arturo 'Buanzo' Busleiman <buanzo@buanzo.com.ar>
|
# Arturo 'Buanzo' Busleiman <buanzo@buanzo.com.ar>
|
||||||
|
@ -418,11 +436,12 @@ class Filter(JailThread):
|
||||||
def addBannedIP(self, ip):
|
def addBannedIP(self, ip):
|
||||||
if not isinstance(ip, IPAddr):
|
if not isinstance(ip, IPAddr):
|
||||||
ip = IPAddr(ip)
|
ip = IPAddr(ip)
|
||||||
if self.inIgnoreIPList(ip, log_ignore=False):
|
|
||||||
logSys.warning('Requested to manually ban an ignored IP %s. User knows best. Proceeding to ban it.', ip)
|
|
||||||
|
|
||||||
unixTime = MyTime.time()
|
unixTime = MyTime.time()
|
||||||
self.failManager.addFailure(FailTicket(ip, unixTime), self.failManager.getMaxRetry())
|
ticket = FailTicket(ip, unixTime)
|
||||||
|
if self._inIgnoreIPList(ip, ticket, log_ignore=False):
|
||||||
|
logSys.warning('Requested to manually ban an ignored IP %s. User knows best. Proceeding to ban it.', ip)
|
||||||
|
self.failManager.addFailure(ticket, self.failManager.getMaxRetry())
|
||||||
|
|
||||||
# Perform the banning of the IP now.
|
# Perform the banning of the IP now.
|
||||||
try: # pragma: no branch - exception is the only way out
|
try: # pragma: no branch - exception is the only way out
|
||||||
|
@ -487,32 +506,61 @@ class Filter(JailThread):
|
||||||
#
|
#
|
||||||
# Check if the given IP address matches an IP address/DNS or a CIDR
|
# Check if the given IP address matches an IP address/DNS or a CIDR
|
||||||
# mask in the ignore list.
|
# mask in the ignore list.
|
||||||
# @param ip IP address object
|
# @param ip IP address object or ticket
|
||||||
# @return True if IP address is in ignore list
|
# @return True if IP address is in ignore list
|
||||||
|
|
||||||
def inIgnoreIPList(self, ip, log_ignore=True):
|
def inIgnoreIPList(self, ip, log_ignore=True):
|
||||||
if not isinstance(ip, IPAddr):
|
ticket = None
|
||||||
|
if isinstance(ip, FailTicket):
|
||||||
|
ticket = ip
|
||||||
|
ip = ticket.getIP()
|
||||||
|
elif not isinstance(ip, IPAddr):
|
||||||
ip = IPAddr(ip)
|
ip = IPAddr(ip)
|
||||||
|
return self._inIgnoreIPList(ip, ticket, log_ignore)
|
||||||
|
|
||||||
|
def _inIgnoreIPList(self, ip, ticket, log_ignore=True):
|
||||||
|
aInfo = None
|
||||||
|
# cached ?
|
||||||
|
if self.__ignoreCache:
|
||||||
|
key, c = self.__ignoreCache
|
||||||
|
if ticket:
|
||||||
|
aInfo = Actions.ActionInfo(ticket, self.jail)
|
||||||
|
key = CommandAction.replaceDynamicTags(key, aInfo)
|
||||||
|
else:
|
||||||
|
aInfo = { 'ip': ip }
|
||||||
|
key = CommandAction.replaceTag(key, aInfo)
|
||||||
|
v = c.get(key)
|
||||||
|
if v is not None:
|
||||||
|
return v
|
||||||
|
|
||||||
# check own IPs should be ignored and 'ip' is self IP:
|
# check own IPs should be ignored and 'ip' is self IP:
|
||||||
if self.__ignoreSelf and ip in DNSUtils.getSelfIPs():
|
if self.__ignoreSelf and ip in DNSUtils.getSelfIPs():
|
||||||
self.logIgnoreIp(ip, log_ignore, ignore_source="ignoreself rule")
|
self.logIgnoreIp(ip, log_ignore, ignore_source="ignoreself rule")
|
||||||
|
if self.__ignoreCache: c.set(key, True)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
for net in self.__ignoreIpList:
|
for net in self.__ignoreIpList:
|
||||||
# check if the IP is covered by ignore IP
|
# check if the IP is covered by ignore IP
|
||||||
if ip.isInNet(net):
|
if ip.isInNet(net):
|
||||||
self.logIgnoreIp(ip, log_ignore, ignore_source=("ip" if net.isValid else "dns"))
|
self.logIgnoreIp(ip, log_ignore, ignore_source=("ip" if net.isValid else "dns"))
|
||||||
|
if self.__ignoreCache: c.set(key, True)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if self.__ignoreCommand:
|
if self.__ignoreCommand:
|
||||||
command = CommandAction.replaceTag(self.__ignoreCommand, { 'ip': ip } )
|
if ticket:
|
||||||
|
if not aInfo: aInfo = Actions.ActionInfo(ticket, self.jail)
|
||||||
|
command = CommandAction.replaceDynamicTags(self.__ignoreCommand, aInfo)
|
||||||
|
else:
|
||||||
|
if not aInfo: aInfo = { 'ip': ip }
|
||||||
|
command = CommandAction.replaceTag(self.__ignoreCommand, aInfo)
|
||||||
logSys.debug('ignore command: %s', command)
|
logSys.debug('ignore command: %s', command)
|
||||||
ret, ret_ignore = CommandAction.executeCmd(command, success_codes=(0, 1))
|
ret, ret_ignore = CommandAction.executeCmd(command, success_codes=(0, 1))
|
||||||
ret_ignore = ret and ret_ignore == 0
|
ret_ignore = ret and ret_ignore == 0
|
||||||
self.logIgnoreIp(ip, log_ignore and ret_ignore, ignore_source="command")
|
self.logIgnoreIp(ip, log_ignore and ret_ignore, ignore_source="command")
|
||||||
|
if self.__ignoreCache: c.set(key, ret_ignore)
|
||||||
return ret_ignore
|
return ret_ignore
|
||||||
|
|
||||||
|
if self.__ignoreCache: c.set(key, False)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def processLine(self, line, date=None):
|
def processLine(self, line, date=None):
|
||||||
|
@ -549,12 +597,12 @@ class Filter(JailThread):
|
||||||
fail = element[3]
|
fail = element[3]
|
||||||
logSys.debug("Processing line with time:%s and ip:%s",
|
logSys.debug("Processing line with time:%s and ip:%s",
|
||||||
unixTime, ip)
|
unixTime, ip)
|
||||||
if self.inIgnoreIPList(ip):
|
tick = FailTicket(ip, unixTime, data=fail)
|
||||||
|
if self._inIgnoreIPList(ip, tick):
|
||||||
continue
|
continue
|
||||||
logSys.info(
|
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, datetime.datetime.fromtimestamp(unixTime).strftime("%Y-%m-%d %H:%M:%S")
|
||||||
)
|
)
|
||||||
tick = FailTicket(ip, unixTime, data=fail)
|
|
||||||
self.failManager.addFailure(tick)
|
self.failManager.addFailure(tick)
|
||||||
# reset (halve) error counter (successfully processed line):
|
# reset (halve) error counter (successfully processed line):
|
||||||
if self._errors:
|
if self._errors:
|
||||||
|
|
|
@ -391,10 +391,17 @@ class Server:
|
||||||
return self.__jails[name].filter.getLogTimeZone()
|
return self.__jails[name].filter.getLogTimeZone()
|
||||||
|
|
||||||
def setIgnoreCommand(self, name, value):
|
def setIgnoreCommand(self, name, value):
|
||||||
self.__jails[name].filter.setIgnoreCommand(value)
|
self.__jails[name].filter.ignoreCommand = value
|
||||||
|
|
||||||
def getIgnoreCommand(self, name):
|
def getIgnoreCommand(self, name):
|
||||||
return self.__jails[name].filter.getIgnoreCommand()
|
return self.__jails[name].filter.ignoreCommand
|
||||||
|
|
||||||
|
def setIgnoreCache(self, name, value):
|
||||||
|
value, options = extractOptions("cache["+value+"]")
|
||||||
|
self.__jails[name].filter.ignoreCache = options
|
||||||
|
|
||||||
|
def getIgnoreCache(self, name):
|
||||||
|
return self.__jails[name].filter.ignoreCache
|
||||||
|
|
||||||
def setPrefRegex(self, name, value):
|
def setPrefRegex(self, name, value):
|
||||||
flt = self.__jails[name].filter
|
flt = self.__jails[name].filter
|
||||||
|
|
|
@ -200,6 +200,10 @@ class Transmitter:
|
||||||
value = command[2]
|
value = command[2]
|
||||||
self.__server.setIgnoreCommand(name, value)
|
self.__server.setIgnoreCommand(name, value)
|
||||||
return self.__server.getIgnoreCommand(name)
|
return self.__server.getIgnoreCommand(name)
|
||||||
|
elif command[1] == "ignorecache":
|
||||||
|
value = command[2]
|
||||||
|
self.__server.setIgnoreCache(name, value)
|
||||||
|
return self.__server.getIgnoreCache(name)
|
||||||
elif command[1] == "addlogpath":
|
elif command[1] == "addlogpath":
|
||||||
value = command[2]
|
value = command[2]
|
||||||
tail = False
|
tail = False
|
||||||
|
@ -358,6 +362,8 @@ class Transmitter:
|
||||||
return self.__server.getIgnoreIP(name)
|
return self.__server.getIgnoreIP(name)
|
||||||
elif command[1] == "ignorecommand":
|
elif command[1] == "ignorecommand":
|
||||||
return self.__server.getIgnoreCommand(name)
|
return self.__server.getIgnoreCommand(name)
|
||||||
|
elif command[1] == "ignorecache":
|
||||||
|
return self.__server.getIgnoreCache(name)
|
||||||
elif command[1] == "prefregex":
|
elif command[1] == "prefregex":
|
||||||
return self.__server.getPrefRegex(name)
|
return self.__server.getPrefRegex(name)
|
||||||
elif command[1] == "failregex":
|
elif command[1] == "failregex":
|
||||||
|
|
|
@ -38,7 +38,7 @@ except ImportError:
|
||||||
|
|
||||||
from ..server.jail import Jail
|
from ..server.jail import Jail
|
||||||
from ..server.filterpoll import FilterPoll
|
from ..server.filterpoll import FilterPoll
|
||||||
from ..server.filter import Filter, FileFilter, FileContainer
|
from ..server.filter import FailTicket, Filter, FileFilter, FileContainer
|
||||||
from ..server.failmanager import FailManagerEmpty
|
from ..server.failmanager import FailManagerEmpty
|
||||||
from ..server.ipdns import DNSUtils, IPAddr
|
from ..server.ipdns import DNSUtils, IPAddr
|
||||||
from ..server.mytime import MyTime
|
from ..server.mytime import MyTime
|
||||||
|
@ -401,7 +401,7 @@ class IgnoreIP(LogCaptureTestCase):
|
||||||
self.assertLogged('Requested to manually ban an ignored IP 192.168.1.32. User knows best. Proceeding to ban it.')
|
self.assertLogged('Requested to manually ban an ignored IP 192.168.1.32. User knows best. Proceeding to ban it.')
|
||||||
|
|
||||||
def testIgnoreCommand(self):
|
def testIgnoreCommand(self):
|
||||||
self.filter.setIgnoreCommand(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"))
|
||||||
self.assertFalse(self.filter.inIgnoreIPList("10.0.0.0"))
|
self.assertFalse(self.filter.inIgnoreIPList("10.0.0.0"))
|
||||||
self.assertLogged("returned successfully 0", "returned successfully 1", all=True)
|
self.assertLogged("returned successfully 0", "returned successfully 1", all=True)
|
||||||
|
@ -409,6 +409,60 @@ class IgnoreIP(LogCaptureTestCase):
|
||||||
self.assertFalse(self.filter.inIgnoreIPList(""))
|
self.assertFalse(self.filter.inIgnoreIPList(""))
|
||||||
self.assertLogged("usage: ignorecommand IP", "returned 10", all=True)
|
self.assertLogged("usage: ignorecommand IP", "returned 10", all=True)
|
||||||
|
|
||||||
|
def testIgnoreCommandForTicket(self):
|
||||||
|
# by host of IP (2001:db8::1 and 2001:db8::ffff map to "test-host" and "test-other" in the test-suite):
|
||||||
|
self.filter.ignoreCommand = 'if [ "<ip-host>" = "test-host" ]; then exit 0; fi; exit 1'
|
||||||
|
self.pruneLog()
|
||||||
|
self.assertTrue(self.filter.inIgnoreIPList(FailTicket("2001:db8::1")))
|
||||||
|
self.assertLogged("returned successfully 0")
|
||||||
|
self.pruneLog()
|
||||||
|
self.assertFalse(self.filter.inIgnoreIPList(FailTicket("2001:db8::ffff")))
|
||||||
|
self.assertLogged("returned successfully 1")
|
||||||
|
# by user-name (ignore tester):
|
||||||
|
self.filter.ignoreCommand = 'if [ "<F-USER>" = "tester" ]; then exit 0; fi; exit 1'
|
||||||
|
self.pruneLog()
|
||||||
|
self.assertTrue(self.filter.inIgnoreIPList(FailTicket("tester", data={'user': 'tester'})))
|
||||||
|
self.assertLogged("returned successfully 0")
|
||||||
|
self.pruneLog()
|
||||||
|
self.assertFalse(self.filter.inIgnoreIPList(FailTicket("root", data={'user': 'root'})))
|
||||||
|
self.assertLogged("returned successfully 1", all=True)
|
||||||
|
|
||||||
|
def testIgnoreCache(self):
|
||||||
|
# like both test-cases above, just cached (so once per key)...
|
||||||
|
self.filter.ignoreCache = {"key":"<ip>"}
|
||||||
|
self.filter.ignoreCommand = 'if [ "<ip>" = "10.0.0.1" ]; then exit 0; fi; exit 1'
|
||||||
|
for i in xrange(5):
|
||||||
|
self.pruneLog()
|
||||||
|
self.assertTrue(self.filter.inIgnoreIPList("10.0.0.1"))
|
||||||
|
self.assertFalse(self.filter.inIgnoreIPList("10.0.0.0"))
|
||||||
|
if not i:
|
||||||
|
self.assertLogged("returned successfully 0", "returned successfully 1", all=True)
|
||||||
|
else:
|
||||||
|
self.assertNotLogged("returned successfully 0", "returned successfully 1", all=True)
|
||||||
|
# by host of IP:
|
||||||
|
self.filter.ignoreCache = {"key":"<ip-host>"}
|
||||||
|
self.filter.ignoreCommand = 'if [ "<ip-host>" = "test-host" ]; then exit 0; fi; exit 1'
|
||||||
|
for i in xrange(5):
|
||||||
|
self.pruneLog()
|
||||||
|
self.assertTrue(self.filter.inIgnoreIPList(FailTicket("2001:db8::1")))
|
||||||
|
self.assertFalse(self.filter.inIgnoreIPList(FailTicket("2001:db8::ffff")))
|
||||||
|
if not i:
|
||||||
|
self.assertLogged("returned successfully")
|
||||||
|
else:
|
||||||
|
self.assertNotLogged("returned successfully")
|
||||||
|
# by user-name:
|
||||||
|
self.filter.ignoreCache = {"key":"<F-USER>", "max-count":"10", "max-time":"1h"}
|
||||||
|
self.assertEqual(self.filter.ignoreCache, ["<F-USER>", 10, 60*60])
|
||||||
|
self.filter.ignoreCommand = 'if [ "<F-USER>" = "tester" ]; then exit 0; fi; exit 1'
|
||||||
|
for i in xrange(5):
|
||||||
|
self.pruneLog()
|
||||||
|
self.assertTrue(self.filter.inIgnoreIPList(FailTicket("tester", data={'user': 'tester'})))
|
||||||
|
self.assertFalse(self.filter.inIgnoreIPList(FailTicket("root", data={'user': 'root'})))
|
||||||
|
if not i:
|
||||||
|
self.assertLogged("returned successfully")
|
||||||
|
else:
|
||||||
|
self.assertNotLogged("returned successfully")
|
||||||
|
|
||||||
def testIgnoreCauseOK(self):
|
def testIgnoreCauseOK(self):
|
||||||
ip = "93.184.216.34"
|
ip = "93.184.216.34"
|
||||||
for ignore_source in ["dns", "ip", "command"]:
|
for ignore_source in ["dns", "ip", "command"]:
|
||||||
|
@ -472,7 +526,7 @@ class IgnoreIPDNS(LogCaptureTestCase):
|
||||||
self.assertRaises(ValueError, lambda: mod.is_googlebot(mod.process_args([cmd])))
|
self.assertRaises(ValueError, lambda: mod.is_googlebot(mod.process_args([cmd])))
|
||||||
self.assertRaises(ValueError, lambda: mod.is_googlebot(mod.process_args([cmd, "192.0"])))
|
self.assertRaises(ValueError, lambda: mod.is_googlebot(mod.process_args([cmd, "192.0"])))
|
||||||
## via command:
|
## via command:
|
||||||
self.filter.setIgnoreCommand(cmd + " <ip>")
|
self.filter.ignoreCommand = cmd + " <ip>"
|
||||||
for ip in bot_ips:
|
for ip in bot_ips:
|
||||||
self.assertTrue(self.filter.inIgnoreIPList(str(ip)), "test of googlebot ip %s failed" % ip)
|
self.assertTrue(self.filter.inIgnoreIPList(str(ip)), "test of googlebot ip %s failed" % ip)
|
||||||
self.assertLogged('-- returned successfully')
|
self.assertLogged('-- returned successfully')
|
||||||
|
@ -480,7 +534,7 @@ class IgnoreIPDNS(LogCaptureTestCase):
|
||||||
self.assertFalse(self.filter.inIgnoreIPList("192.0"))
|
self.assertFalse(self.filter.inIgnoreIPList("192.0"))
|
||||||
self.assertLogged('Argument must be a single valid IP.')
|
self.assertLogged('Argument must be a single valid IP.')
|
||||||
self.pruneLog()
|
self.pruneLog()
|
||||||
self.filter.setIgnoreCommand(cmd + " bad arguments <ip>")
|
self.filter.ignoreCommand = cmd + " bad arguments <ip>"
|
||||||
self.assertFalse(self.filter.inIgnoreIPList("192.0"))
|
self.assertFalse(self.filter.inIgnoreIPList("192.0"))
|
||||||
self.assertLogged('Please provide a single IP as an argument.')
|
self.assertLogged('Please provide a single IP as an argument.')
|
||||||
|
|
||||||
|
|
|
@ -463,7 +463,14 @@ class Transmitter(TransmitterBase):
|
||||||
(0, False))
|
(0, False))
|
||||||
|
|
||||||
def testJailIgnoreCommand(self):
|
def testJailIgnoreCommand(self):
|
||||||
self.setGetTest("ignorecommand", "bin ", jail=self.jailName)
|
self.setGetTest("ignorecommand", "bin/ignore-command <ip>", jail=self.jailName)
|
||||||
|
|
||||||
|
def testJailIgnoreCache(self):
|
||||||
|
self.setGetTest("ignorecache",
|
||||||
|
'key="<ip>",max-time=1d,max-count=9999',
|
||||||
|
["<ip>", 9999, 24*60*60],
|
||||||
|
jail=self.jailName)
|
||||||
|
self.setGetTest("ignorecache", '', None, jail=self.jailName)
|
||||||
|
|
||||||
def testJailRegex(self):
|
def testJailRegex(self):
|
||||||
self.jailAddDelRegexTest("failregex",
|
self.jailAddDelRegexTest("failregex",
|
||||||
|
|
|
@ -316,6 +316,7 @@ def initTests(opts):
|
||||||
c.set('203.0.113.%s' % i, None)
|
c.set('203.0.113.%s' % i, None)
|
||||||
c.set('2001:db8::%s' %i, 'test-host')
|
c.set('2001:db8::%s' %i, 'test-host')
|
||||||
# some legal ips used in our test cases (prevent slow dns-resolving and failures if will be changed later):
|
# some legal ips used in our test cases (prevent slow dns-resolving and failures if will be changed later):
|
||||||
|
c.set('2001:db8::ffff', 'test-other')
|
||||||
c.set('87.142.124.10', 'test-host')
|
c.set('87.142.124.10', 'test-host')
|
||||||
if unittest.F2B.no_network: # pragma: no cover
|
if unittest.F2B.no_network: # pragma: no cover
|
||||||
# precache all wrong dns to ip's used in test cases:
|
# precache all wrong dns to ip's used in test cases:
|
||||||
|
|
|
@ -233,7 +233,19 @@ list of IPs not to ban. They can include a DNS resp. CIDR mask too. The option a
|
||||||
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.
|
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
|
.br
|
||||||
IP will not be banned if command returns successfully (exit code 0).
|
IP will not be banned if command returns successfully (exit code 0).
|
||||||
Like ACTION FILES, tags like <ip> are can be included in the ignorecommand value and will be substituted before execution. Currently only <ip> is supported however more will be added later.
|
Like ACTION FILES, tags like <ip> are can be included in the ignorecommand value and will be substituted before execution.
|
||||||
|
.TP
|
||||||
|
.B ignorecache
|
||||||
|
provide cache parameters (default disabled) for ignore failure check (caching of the result from `ignoreip`, `ignoreself` and `ignorecommand`), syntax:
|
||||||
|
|
||||||
|
.RS
|
||||||
|
.nf
|
||||||
|
ignorecache = key="<F-USER>@<ip-host>", max-count=100, max-time=5m
|
||||||
|
ignorecommand = if [ "<F-USER>" = "technical" ] && [ "<ip-host>" = "my-host.example.com" ]; then exit 0; fi;
|
||||||
|
exit 1
|
||||||
|
.fi
|
||||||
|
This will cache the result of \fBignorecommand\fR (does not call it repeatedly) for 5 minutes (cache time) for maximal 100 entries (cache size), using values substituted like "user@host" as cache-keys. Set option \fBignorecache\fR to empty value disables the cache.
|
||||||
|
.RE
|
||||||
.TP
|
.TP
|
||||||
.B bantime
|
.B bantime
|
||||||
effective ban duration (in seconds or time abbreviation format).
|
effective ban duration (in seconds or time abbreviation format).
|
||||||
|
|
Loading…
Reference in New Issue