mirror of https://github.com/fail2ban/fail2ban
Merge pull request #189 from kwirk/multiaction
Allow creation of multiple of the same action for a filter -- use actname option. Close #37pull/214/merge
commit
e019ab784c
|
@ -457,23 +457,15 @@ ignoreip = 168.192.0.1
|
|||
# Miscelaneous
|
||||
#
|
||||
|
||||
# Multiple jails, 1 per protocol, are necessary ATM:
|
||||
# see https://github.com/fail2ban/fail2ban/issues/37
|
||||
[asterisk-tcp]
|
||||
[asterisk]
|
||||
|
||||
filter = asterisk
|
||||
port = 5060,5061
|
||||
protocol = tcp
|
||||
logpath = /var/log/asterisk/messages
|
||||
maxretry = 10
|
||||
|
||||
[asterisk-udp]
|
||||
|
||||
filter = asterisk
|
||||
port = 5060,5061
|
||||
protocol = udp
|
||||
logpath = /var/log/asterisk/messages
|
||||
maxretry = 10
|
||||
# Astrix requires both tcp and udp
|
||||
action = %(banaction)s[name=%(__name__)s-tcp, port="%(port)s", protocol="tcp", chain="%(chain)s", actname=%(banaction)s-tcp]
|
||||
%(banaction)s[name=%(__name__)s-udp, port="%(port)s", protocol="udp", chain="%(chain)s", actname=%(banaction)s-udp]
|
||||
%(mta)s-whois[name=%(__name__)s, dest="%(destemail)s"]
|
||||
|
||||
# To log wrong MySQL access attempts add to /etc/my.cnf:
|
||||
# log-error=/var/log/mysqld.log
|
||||
|
|
|
@ -43,27 +43,38 @@ class ActionReader(DefinitionInitConfigReader):
|
|||
["string", "actionunban", ""],
|
||||
]
|
||||
|
||||
def __init__(self, file_, jailName, initOpts, **kwargs):
|
||||
self._name = initOpts.get("actname", file_)
|
||||
DefinitionInitConfigReader.__init__(
|
||||
self, file_, jailName, initOpts, **kwargs)
|
||||
|
||||
def setName(self, name):
|
||||
self._name = name
|
||||
|
||||
def getName(self):
|
||||
return self._name
|
||||
|
||||
def read(self):
|
||||
return ConfigReader.read(self, os.path.join("action.d", self._file))
|
||||
|
||||
def convert(self):
|
||||
head = ["set", self._name]
|
||||
head = ["set", self._jailName]
|
||||
stream = list()
|
||||
stream.append(head + ["addaction", self._file])
|
||||
stream.append(head + ["addaction", self._name])
|
||||
for opt in self._opts:
|
||||
if opt == "actionstart":
|
||||
stream.append(head + ["actionstart", self._file, self._opts[opt]])
|
||||
stream.append(head + ["actionstart", self._name, self._opts[opt]])
|
||||
elif opt == "actionstop":
|
||||
stream.append(head + ["actionstop", self._file, self._opts[opt]])
|
||||
stream.append(head + ["actionstop", self._name, self._opts[opt]])
|
||||
elif opt == "actioncheck":
|
||||
stream.append(head + ["actioncheck", self._file, self._opts[opt]])
|
||||
stream.append(head + ["actioncheck", self._name, self._opts[opt]])
|
||||
elif opt == "actionban":
|
||||
stream.append(head + ["actionban", self._file, self._opts[opt]])
|
||||
stream.append(head + ["actionban", self._name, self._opts[opt]])
|
||||
elif opt == "actionunban":
|
||||
stream.append(head + ["actionunban", self._file, self._opts[opt]])
|
||||
stream.append(head + ["actionunban", self._name, self._opts[opt]])
|
||||
# cInfo
|
||||
if self._initOpts:
|
||||
for p in self._initOpts:
|
||||
stream.append(head + ["setcinfo", self._file, p, self._initOpts[p]])
|
||||
stream.append(head + ["setcinfo", self._name, p, self._initOpts[p]])
|
||||
|
||||
return stream
|
||||
|
|
|
@ -132,6 +132,12 @@ class Beautifier:
|
|||
msg = msg + "|- [" + str(c) + "]: " + ip + "\n"
|
||||
c += 1
|
||||
msg = msg + "`- [" + str(c) + "]: " + response[len(response)-1]
|
||||
elif inC[2] == "actions":
|
||||
if len(response) == 0:
|
||||
msg = "No actions for jail %s" % inC[1]
|
||||
else:
|
||||
msg = "The jail %s has the following actions:\n" % inC[1]
|
||||
msg += ", ".join(action.getName() for action in response)
|
||||
except Exception:
|
||||
logSys.warning("Beautifier error. Please report the error")
|
||||
logSys.error("Beautify " + `response` + " with " + `self.__inputCmd` +
|
||||
|
|
|
@ -145,7 +145,7 @@ class DefinitionInitConfigReader(ConfigReader):
|
|||
def __init__(self, file_, jailName, initOpts, **kwargs):
|
||||
ConfigReader.__init__(self, **kwargs)
|
||||
self._file = file_
|
||||
self._name = jailName
|
||||
self._jailName = jailName
|
||||
self._initOpts = initOpts
|
||||
|
||||
def setFile(self, fileName):
|
||||
|
@ -154,11 +154,11 @@ class DefinitionInitConfigReader(ConfigReader):
|
|||
def getFile(self):
|
||||
return self.__file
|
||||
|
||||
def setName(self, name):
|
||||
self._name = name
|
||||
def setJailName(self, jailName):
|
||||
self._jailName = jailName
|
||||
|
||||
def getName(self):
|
||||
return self._name
|
||||
def getJailName(self):
|
||||
return self._jailName
|
||||
|
||||
def read(self):
|
||||
return ConfigReader.read(self, self._file)
|
||||
|
|
|
@ -50,14 +50,14 @@ class FilterReader(DefinitionInitConfigReader):
|
|||
for regex in self._opts[opt].split('\n'):
|
||||
# Do not send a command if the rule is empty.
|
||||
if regex != '':
|
||||
stream.append(["set", self._name, "addfailregex", regex])
|
||||
stream.append(["set", self._jailName, "addfailregex", regex])
|
||||
elif opt == "ignoreregex":
|
||||
for regex in self._opts[opt].split('\n'):
|
||||
# Do not send a command if the rule is empty.
|
||||
if regex != '':
|
||||
stream.append(["set", self._name, "addignoreregex", regex])
|
||||
stream.append(["set", self._jailName, "addignoreregex", regex])
|
||||
if self._initOpts:
|
||||
if 'maxlines' in self._initOpts:
|
||||
stream.append(["set", self._name, "maxlines", self._initOpts["maxlines"]])
|
||||
stream.append(["set", self._jailName, "maxlines", self._initOpts["maxlines"]])
|
||||
return stream
|
||||
|
||||
|
|
|
@ -90,6 +90,7 @@ protocol = [
|
|||
["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> addaction", "gets the last action which has been added for <JAIL>"],
|
||||
["get <JAIL> actions", "gets a list of actions for <JAIL>"],
|
||||
["get <JAIL> actionstart <ACT>", "gets the start command for the action <ACT> for <JAIL>"],
|
||||
["get <JAIL> actionstop <ACT>", "gets the stop command for the action <ACT> for <JAIL>"],
|
||||
["get <JAIL> actioncheck <ACT>", "gets the check command for the action <ACT> for <JAIL>"],
|
||||
|
|
|
@ -65,6 +65,9 @@ class Actions(JailThread):
|
|||
# @param name The action name
|
||||
|
||||
def addAction(self, name):
|
||||
# Check is action name already exists
|
||||
if name in [action.getName() for action in self.__actions]:
|
||||
raise ValueError("Action %s already exists" % name)
|
||||
action = Action(name)
|
||||
self.__actions.append(action)
|
||||
|
||||
|
@ -104,6 +107,14 @@ class Actions(JailThread):
|
|||
self.__actions.append(action)
|
||||
return action
|
||||
|
||||
##
|
||||
# Returns the list of actions
|
||||
#
|
||||
# @return list of actions
|
||||
|
||||
def getActions(self):
|
||||
return self.__actions
|
||||
|
||||
##
|
||||
# Set the ban time.
|
||||
#
|
||||
|
|
|
@ -236,6 +236,9 @@ class Server:
|
|||
def getLastAction(self, name):
|
||||
return self.__jails.getAction(name).getLastAction()
|
||||
|
||||
def getActions(self, name):
|
||||
return self.__jails.getAction(name).getActions()
|
||||
|
||||
def delAction(self, name, value):
|
||||
self.__jails.getAction(name).delAction(value)
|
||||
|
||||
|
|
|
@ -265,6 +265,8 @@ class Transmitter:
|
|||
# Action
|
||||
elif command[1] == "bantime":
|
||||
return self.__server.getBanTime(name)
|
||||
elif command[1] == "actions":
|
||||
return self.__server.getActions(name)
|
||||
elif command[1] == "addaction":
|
||||
return self.__server.getLastAction(name).getName()
|
||||
elif command[1] == "actionstart":
|
||||
|
|
|
@ -302,3 +302,28 @@ class JailsReaderTest(unittest.TestCase):
|
|||
configurator._Configurator__jails.setBaseDir('/tmp')
|
||||
self.assertEqual(configurator._Configurator__jails.getBaseDir(), '/tmp')
|
||||
self.assertEqual(configurator.getBaseDir(), CONFIG_DIR)
|
||||
|
||||
def testMultipleSameAction(self):
|
||||
basedir = tempfile.mkdtemp("fail2ban_conf")
|
||||
os.mkdir(os.path.join(basedir, "filter.d"))
|
||||
os.mkdir(os.path.join(basedir, "action.d"))
|
||||
open(os.path.join(basedir, "action.d", "testaction1.conf"), 'w').close()
|
||||
open(os.path.join(basedir, "filter.d", "testfilter1.conf"), 'w').close()
|
||||
jailfd = open(os.path.join(basedir, "jail.conf"), 'w')
|
||||
jailfd.write("""
|
||||
[testjail1]
|
||||
action = testaction1[actname=test1]
|
||||
testaction1[actname=test2]
|
||||
filter = testfilter1
|
||||
""")
|
||||
jailfd.close()
|
||||
jails = JailsReader(basedir=basedir)
|
||||
self.assertTrue(jails.read())
|
||||
self.assertTrue(jails.getOptions())
|
||||
comm_commands = jails.convert()
|
||||
|
||||
action_names = [comm[-1] for comm in comm_commands if comm[:3] == ['set', 'testjail1', 'addaction']]
|
||||
|
||||
self.assertNotEqual(len(set(action_names)), 1)
|
||||
|
||||
shutil.rmtree(basedir)
|
||||
|
|
|
@ -440,8 +440,12 @@ class Transmitter(TransmitterBase):
|
|||
self.transm.proceed(["set", self.jailName, "addaction", action]),
|
||||
(0, action))
|
||||
self.assertEqual(
|
||||
self.transm.proceed(["get", self.jailName, "addaction", action]),
|
||||
self.transm.proceed(["get", self.jailName, "addaction"]),
|
||||
(0, action))
|
||||
self.assertEqual(
|
||||
self.transm.proceed(
|
||||
["get", self.jailName, "actions"])[1][0].getName(),
|
||||
action)
|
||||
for cmd, value in zip(cmdList, cmdValueList):
|
||||
self.assertEqual(
|
||||
self.transm.proceed(
|
||||
|
|
|
@ -59,7 +59,15 @@ The following options are applicable to all jails. Their meaning is described in
|
|||
\fBbackend\fR
|
||||
.TP
|
||||
\fBusedns\fR
|
||||
|
||||
.PP
|
||||
Each jail can be configured with only a single filter, but may have multiple actions. By default, the name of a action is the action filename. In the case where multiple of the same action are to be used, the \fBactname\fR option can be assigned to the action to avoid duplicatione.g.:
|
||||
.PP
|
||||
.nf
|
||||
[ssh-iptables-ipset]
|
||||
enabled = true
|
||||
action = sendmail[name=ssh, dest=john@example.com, actname=mail-john]
|
||||
sendmail[name=ssh, dest=paul@example.com, actname=mail-paul]
|
||||
.fi
|
||||
|
||||
.SH "ACTION FILES"
|
||||
Action files specify which commands are executed to ban and unban an IP address. They are located under \fI/etc/fail2ban/action.d\fR.
|
||||
|
|
Loading…
Reference in New Issue