Merge pull request #401 from bes-internal/ignorecommand

ENH: New ignorecommand with path to external command for dynamic ignoreip list
pull/522/head
Daniel Black 2013-12-24 23:43:41 -08:00
commit dfb6c45297
12 changed files with 73 additions and 3 deletions

View File

@ -54,7 +54,9 @@ class FilterReader(ConfigReader):
def getOptions(self, pOpts):
opts = [["string", "ignoreregex", ""],
["string", "failregex", ""]]
["string", "failregex", ""],
["string", "ignorecommand", ""]
]
self.__opts = ConfigReader.getOptions(self, "Definition", opts, pOpts)
def convert(self):

View File

@ -80,6 +80,7 @@ class JailReader(ConfigReader):
["string", "usedns", "warn"],
["string", "failregex", None],
["string", "ignoreregex", None],
["string", "ignorecommand", None],
["string", "ignoreip", None],
["string", "filter", ""],
["string", "action", ""]]
@ -164,6 +165,8 @@ class JailReader(ConfigReader):
stream.append(["set", self.__name, "usedns", self.__opts[opt]])
elif opt == "failregex":
stream.append(["set", self.__name, "addfailregex", self.__opts[opt]])
elif opt == "ignorecommand":
stream.append(["set", self.__name, "ignorecommand", self.__opts[opt]])
elif opt == "ignoreregex":
for regex in self.__opts[opt].split('\n'):
# Do not send a command if the rule is empty.

View File

@ -57,6 +57,7 @@ protocol = [
["set <JAIL> dellogpath <FILE>", "removes <FILE> from the monitoring list of <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> 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> 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>"],
@ -77,6 +78,7 @@ protocol = [
['', "JAIL INFORMATION", ""],
["get <JAIL> logpath", "gets the list of the monitored files for <JAIL>"],
["get <JAIL> ignoreip", "gets the list of ignored IP addresses for <JAIL>"],
["get <JAIL> ignorecommand", "gets ignorecommand of <JAIL>"],
["get <JAIL> failregex", "gets the list of regular expressions which matches the failures for <JAIL>"],
["get <JAIL> ignoreregex", "gets the list of regular expressions which matches patterns to ignore for <JAIL>"],
["get <JAIL> findtime", "gets the time for which the filter will look back for failures for <JAIL>"],

View File

@ -31,6 +31,11 @@
# defined using space separator.
ignoreip = 127.0.0.1/8
# External command that will take an tagged arguments to ignore, e.g. <ip>,
# and return true if the IP is to be ignored. False otherwise.
#
# ignorecommand = /path/to/command <ip>
# "bantime" is the number of seconds that a host is banned.
bantime = 600

View File

@ -128,6 +128,9 @@ for <JAIL>
removes the regular expression at
<INDEX> for failregex
.TP
\fBset <JAIL> ignorecommand <VALUE>\fR
sets ignorecommand of <JAIL>
.TP
\fBset <JAIL> addignoreregex <REGEX>\fR
adds the regular expression
<REGEX> which should match pattern
@ -206,6 +209,9 @@ files for <JAIL>
gets the list of ignored IP
addresses for <JAIL>
.TP
\fBget <JAIL> ignorecommand\fR
gets ignorecommand of <JAIL>
.TP
\fBget <JAIL> failregex\fR
gets the list of regular
expressions which matches the

View File

@ -70,6 +70,11 @@ The following options are applicable to all jails. Their meaning is described in
\fBaction\fR
.TP
\fBignoreip\fR
A space separated list of IPs not to ban.
.TP
\fBignorecommand\fR
A command that is executed to determine if the current ban's actionban is to be executed. This command will return true if the current ban should be ignored. A false return value will result in the ban's actionban executed.
Like ACTION FILES, tags like <ip> are can be included in the ignore command value and will be substitued before execution. Currently only <ip> is supported however more will be added later.
.TP
\fBbantime\fR
.TP

View File

@ -30,8 +30,9 @@ from jailthread import JailThread
from datedetector import DateDetector
from mytime import MyTime
from failregex import FailRegex, Regex, RegexException
from action import Action
import logging, re, os, fcntl, time
import logging, re, os, fcntl, time, shlex, subprocess
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.filter")
@ -67,6 +68,8 @@ class Filter(JailThread):
self.__findTime = 6000
## The ignore IP list.
self.__ignoreIpList = []
## External command
self.__ignoreCommand = False
self.dateDetector = DateDetector()
self.dateDetector.addDefaultTemplate()
@ -212,6 +215,20 @@ class Filter(JailThread):
def run(self): # pragma: no cover
raise Exception("run() is abstract")
##
# Set external command, for ignoredips
#
def setIgnoreCommand(self, command):
self.__ignoreCommand = command
##
# Get external command, for ignoredips
#
def getIgnoreCommand(self):
return self.__ignoreCommand
##
# Ban an IP - http://blogs.buanzo.com.ar/2009/04/fail2ban-patch-ban-ip-address-manually.html
# Arturo 'Buanzo' Busleiman <buanzo@buanzo.com.ar>
@ -284,6 +301,12 @@ class Filter(JailThread):
continue
if a == b:
return True
if self.__ignoreCommand:
command = Action.replaceTag(self.__ignoreCommand, { 'ip': ip } )
logSys.debug('ignore command: ' + command)
return Action.executeCmd(command)
return False

View File

@ -184,6 +184,12 @@ class Server:
def getFindTime(self, name):
return self.__jails.getFilter(name).getFindTime()
def setIgnoreCommand(self, name, value):
self.__jails.getFilter(name).setIgnoreCommand(value)
def getIgnoreCommand(self, name):
return self.__jails.getFilter(name).getIgnoreCommand()
def addFailRegex(self, name, value):
self.__jails.getFilter(name).addFailRegex(value)

View File

@ -142,6 +142,10 @@ class Transmitter:
value = command[2]
self.__server.delLogPath(name, value)
return self.__server.getLogPath(name)
elif command[1] == "ignorecommand":
value = command[2]
self.__server.setIgnoreCommand(name, value)
return self.__server.getIgnoreCommand(name)
elif command[1] == "addfailregex":
value = command[2]
self.__server.addFailRegex(name, value)
@ -239,6 +243,8 @@ class Transmitter:
return self.__server.getLogPath(name)
elif command[1] == "ignoreip":
return self.__server.getIgnoreIP(name)
elif command[1] == "ignorecommand":
return self.__server.getIgnoreCommand(name)
elif command[1] == "failregex":
return self.__server.getFailRegex(name)
elif command[1] == "ignoreregex":

View File

@ -0,0 +1,5 @@
#!/usr/bin/python
import sys
if sys.argv[1] == "10.0.0.1":
exit(0)
exit(1)

View File

@ -171,7 +171,6 @@ class IgnoreIP(LogCaptureTestCase):
ipList = "127.0.0.1", "192.168.0.1", "255.255.255.255", "99.99.99.99"
for ip in ipList:
self.filter.addIgnoreIP(ip)
self.assertTrue(self.filter.inIgnoreIPList(ip))
def testIgnoreIPNOK(self):
@ -201,6 +200,11 @@ class IgnoreIP(LogCaptureTestCase):
self.assertFalse(self._is_logged('Ignore 192.168.1.32'))
self.assertTrue(self._is_logged('Requested to manually ban an ignored IP 192.168.1.32. User knows best. Proceeding to ban it.'))
def testIgnoreCommand(self):
self.filter.setIgnoreCommand("testcases/files/ignorecommand.py <ip>")
self.assertTrue(self.filter.inIgnoreIPList("10.0.0.1"))
self.assertFalse(self.filter.inIgnoreIPList("10.0.0.0"))
class IgnoreIPDNS(IgnoreIP):

View File

@ -337,6 +337,9 @@ class Transmitter(TransmitterBase):
self.transm.proceed(["set", self.jailName, "delignoreip", value]),
(0, [value]))
def testJailIgnoreCommand(self):
self.setGetTest("ignorecommand", "bin ", jail=self.jailName)
def testJailRegex(self):
self.jailAddDelRegexTest("failregex",
[