New ignorecommand that is added to the ignoreip list from output of an external program

ignorecommand update man and fix protocol help

ENH: run ignore command only after internal list has been examined. Change interface on ignorecommand to take IP as environment variable and return true if it is to be banned

ENH: ignore IP command to take tagged command

DOC: man pages for ingorecommand

TST: add test cases for ignorecommand
pull/401/head
bes.internal 2013-10-21 16:15:13 +03:00
parent a8ea347fe3
commit ebd89ec077
11 changed files with 70 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):