mirror of https://github.com/fail2ban/fail2ban
RF: Refactor actions further, include removing server proxy interface
This allows direct setting of action properties and calling of methods from the fail2ban-client if so required.pull/549/head
parent
414c5e1146
commit
80d6f74ee8
|
@ -1,3 +1,21 @@
|
|||
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
|
||||
# vi: set ft=python sts=4 ts=4 sw=4 noet :
|
||||
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Fail2Ban; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
import sys
|
||||
import socket
|
||||
|
@ -52,94 +70,112 @@ Matches for %(ip)s for jail %(jailname)s:
|
|||
"""
|
||||
|
||||
class SMTPAction(ActionBase):
|
||||
"""Fail2Ban action which sends emails to inform on jail starting,
|
||||
stopping and bans.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, jail, name, host="localhost", user=None, password=None,
|
||||
sendername="Fail2Ban", sender="fail2ban", dest="root", matches=None):
|
||||
def __init__(
|
||||
self, jail, actionname, host="localhost", user=None, password=None,
|
||||
sendername="Fail2Ban", sender="fail2ban", dest="root", matches=None):
|
||||
"""SMTPAction is initiliased with a Fail2Ban `jail` instance, and
|
||||
an `actionname`. `host` is the SMTP host, which can include port
|
||||
number in "host:port" format. `user` and `password` can be specified
|
||||
for SMTP authentication. `sendername` and `sender` is the email
|
||||
address and readable name. `dest` is the email address of intended
|
||||
recipient(s) in comma delimited format. `matches` can be one of
|
||||
`matches`, `ipmatches` and `ipjailmatches` (see man jail.conf.5).
|
||||
"""
|
||||
|
||||
super(SMTPAction, self).__init__(jail, name)
|
||||
super(SMTPAction, self).__init__(jail, actionname)
|
||||
|
||||
self.host = host
|
||||
#TODO: self.ssl = ssl
|
||||
self.host = host
|
||||
#TODO: self.ssl = ssl
|
||||
|
||||
self.user = user
|
||||
self.password =password
|
||||
self.user = user
|
||||
self.password =password
|
||||
|
||||
self.fromname = sendername
|
||||
self.fromaddr = sender
|
||||
self.toaddr = dest
|
||||
self.fromname = sendername
|
||||
self.fromaddr = sender
|
||||
self.toaddr = dest
|
||||
|
||||
self.matches = matches
|
||||
self.matches = matches
|
||||
|
||||
self.message_values = CallingMap(
|
||||
jailname = self.jail.getName(), # Doesn't change
|
||||
hostname = socket.gethostname,
|
||||
bantime = self.jail.getAction().getBanTime,
|
||||
)
|
||||
self.message_values = CallingMap(
|
||||
jailname = self._jail.getName(), # Doesn't change
|
||||
hostname = socket.gethostname,
|
||||
bantime = self._jail.getAction().getBanTime,
|
||||
)
|
||||
|
||||
def _sendMessage(self, subject, text):
|
||||
msg = MIMEText(text)
|
||||
msg['Subject'] = subject
|
||||
msg['From'] = formataddr((self.fromname, self.fromaddr))
|
||||
msg['To'] = self.toaddr
|
||||
msg['Date'] = formatdate()
|
||||
def _sendMessage(self, subject, text):
|
||||
msg = MIMEText(text)
|
||||
msg['Subject'] = subject
|
||||
msg['From'] = formataddr((self.fromname, self.fromaddr))
|
||||
msg['To'] = self.toaddr
|
||||
msg['Date'] = formatdate()
|
||||
|
||||
smtp = smtplib.SMTP()
|
||||
try:
|
||||
self.logSys.debug("Connected to SMTP '%s', response: %i: %s",
|
||||
self.host, *smtp.connect(self.host))
|
||||
if self.user and self.password:
|
||||
smtp.login(self.user, self.password)
|
||||
failed_recipients = smtp.sendmail(
|
||||
self.fromaddr, self.toaddr, msg.as_string())
|
||||
except smtplib.SMTPConnectError:
|
||||
self.logSys.error("Error connecting to host '%s'", self.host)
|
||||
raise
|
||||
except smtplib.SMTPAuthenticationError:
|
||||
self.logSys.error(
|
||||
"Failed to authenticate with host '%s' user '%s'",
|
||||
self.host, self.user)
|
||||
raise
|
||||
except smtplib.SMTPException:
|
||||
self.logSys.error(
|
||||
"Error sending mail to host '%s' from '%s' to '%s'",
|
||||
self.host, self.fromaddr, self.toaddr)
|
||||
raise
|
||||
else:
|
||||
if failed_recipients:
|
||||
self.logSys.warning(
|
||||
"Email to '%s' failed to following recipients: %r",
|
||||
self.toaddr, failed_recipients)
|
||||
self.logSys.debug("Email '%s' successfully sent", subject)
|
||||
finally:
|
||||
try:
|
||||
self.logSys.debug("Disconnected from '%s', response %i: %s",
|
||||
self.host, *smtp.quit())
|
||||
except smtplib.SMTPServerDisconnected:
|
||||
pass # Not connected
|
||||
smtp = smtplib.SMTP()
|
||||
try:
|
||||
self._logSys.debug("Connected to SMTP '%s', response: %i: %s",
|
||||
self.host, *smtp.connect(self.host))
|
||||
if self.user and self.password:
|
||||
smtp.login(self.user, self.password)
|
||||
failed_recipients = smtp.sendmail(
|
||||
self.fromaddr, self.toaddr, msg.as_string())
|
||||
except smtplib.SMTPConnectError:
|
||||
self._logSys.error("Error connecting to host '%s'", self.host)
|
||||
raise
|
||||
except smtplib.SMTPAuthenticationError:
|
||||
self._logSys.error(
|
||||
"Failed to authenticate with host '%s' user '%s'",
|
||||
self.host, self.user)
|
||||
raise
|
||||
except smtplib.SMTPException:
|
||||
self._logSys.error(
|
||||
"Error sending mail to host '%s' from '%s' to '%s'",
|
||||
self.host, self.fromaddr, self.toaddr)
|
||||
raise
|
||||
else:
|
||||
if failed_recipients:
|
||||
self._logSys.warning(
|
||||
"Email to '%s' failed to following recipients: %r",
|
||||
self.toaddr, failed_recipients)
|
||||
self._logSys.debug("Email '%s' successfully sent", subject)
|
||||
finally:
|
||||
try:
|
||||
self._logSys.debug("Disconnected from '%s', response %i: %s",
|
||||
self.host, *smtp.quit())
|
||||
except smtplib.SMTPServerDisconnected:
|
||||
pass # Not connected
|
||||
|
||||
def execActionStart(self):
|
||||
self._sendMessage(
|
||||
"[Fail2Ban] %(jailname)s: started on %(hostname)s" %
|
||||
self.message_values,
|
||||
messages['start'] % self.message_values)
|
||||
def start(self):
|
||||
"""Sends email to recipients informing that the jail has started.
|
||||
"""
|
||||
self._sendMessage(
|
||||
"[Fail2Ban] %(jailname)s: started on %(hostname)s" %
|
||||
self.message_values,
|
||||
messages['start'] % self.message_values)
|
||||
|
||||
def execActionStop(self):
|
||||
self._sendMessage(
|
||||
"[Fail2Ban] %(jailname)s: stopped on %(hostname)s" %
|
||||
self.message_values,
|
||||
messages['stop'] % self.message_values)
|
||||
def stop(self):
|
||||
"""Sends email to recipients informing that the jail has stopped.
|
||||
"""
|
||||
self._sendMessage(
|
||||
"[Fail2Ban] %(jailname)s: stopped on %(hostname)s" %
|
||||
self.message_values,
|
||||
messages['stop'] % self.message_values)
|
||||
|
||||
def execActionBan(self, aInfo):
|
||||
aInfo.update(self.message_values)
|
||||
message = "".join([
|
||||
messages['ban']['head'],
|
||||
messages['ban'].get(self.matches, ""),
|
||||
messages['ban']['tail']
|
||||
])
|
||||
self._sendMessage(
|
||||
"[Fail2Ban] %(jailname)s: banned %(ip)s from %(hostname)s" %
|
||||
aInfo,
|
||||
message % aInfo)
|
||||
def ban(self, aInfo):
|
||||
"""Sends email to recipients informing that ban has occurred and
|
||||
has associated information about the ban.
|
||||
"""
|
||||
aInfo.update(self.message_values)
|
||||
message = "".join([
|
||||
messages['ban']['head'],
|
||||
messages['ban'].get(self.matches, ""),
|
||||
messages['ban']['tail']
|
||||
])
|
||||
self._sendMessage(
|
||||
"[Fail2Ban] %(jailname)s: banned %(ip)s from %(hostname)s" %
|
||||
aInfo,
|
||||
message % aInfo)
|
||||
|
||||
Action = SMTPAction
|
||||
|
|
|
@ -59,22 +59,20 @@ class ActionReader(DefinitionInitConfigReader):
|
|||
head = ["set", self._jailName]
|
||||
stream = list()
|
||||
stream.append(head + ["addaction", self._name])
|
||||
head.extend(["action", self._name])
|
||||
for opt in self._opts:
|
||||
if opt == "actionstart":
|
||||
stream.append(head + ["actionstart", self._name, self._opts[opt]])
|
||||
stream.append(head + ["actionstart", self._opts[opt]])
|
||||
elif opt == "actionstop":
|
||||
stream.append(head + ["actionstop", self._name, self._opts[opt]])
|
||||
stream.append(head + ["actionstop", self._opts[opt]])
|
||||
elif opt == "actioncheck":
|
||||
stream.append(head + ["actioncheck", self._name, self._opts[opt]])
|
||||
stream.append(head + ["actioncheck", self._opts[opt]])
|
||||
elif opt == "actionban":
|
||||
stream.append(head + ["actionban", self._name, self._opts[opt]])
|
||||
stream.append(head + ["actionban", self._opts[opt]])
|
||||
elif opt == "actionunban":
|
||||
stream.append(head + ["actionunban", self._name, self._opts[opt]])
|
||||
stream.append(head + ["actionunban", self._opts[opt]])
|
||||
if self._initOpts:
|
||||
if "timeout" in self._initOpts:
|
||||
stream.append(head + ["timeout", self._name, self._opts["timeout"]])
|
||||
# cInfo
|
||||
for p in self._initOpts:
|
||||
stream.append(head + ["setcinfo", self._name, p, self._initOpts[p]])
|
||||
stream.append(head + [p, self._initOpts[p]])
|
||||
|
||||
return stream
|
||||
|
|
|
@ -165,7 +165,23 @@ class Beautifier:
|
|||
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)
|
||||
msg += ", ".join(response)
|
||||
elif inC[2] == "actionproperties":
|
||||
if len(response) == 0:
|
||||
msg = "No properties for jail %s action %s" % (
|
||||
inC[1], inC[3])
|
||||
else:
|
||||
msg = "The jail %s action %s has the following " \
|
||||
"properties:\n" % (inC[1], inC[3])
|
||||
msg += ", ".join(response)
|
||||
elif inC[2] == "actionmethods":
|
||||
if len(response) == 0:
|
||||
msg = "No methods for jail %s action %s" % (
|
||||
inC[1], inC[3])
|
||||
else:
|
||||
msg = "The jail %s action %s has the following " \
|
||||
"methods:\n" % (inC[1], inC[3])
|
||||
msg += ", ".join(response)
|
||||
except Exception:
|
||||
logSys.warning("Beautifier error. Please report the error")
|
||||
logSys.error("Beautify " + `response` + " with " + `self.__inputCmd` +
|
||||
|
|
|
@ -76,16 +76,18 @@ protocol = [
|
|||
["set <JAIL> unbanip <IP>", "manually Unban <IP> in <JAIL>"],
|
||||
["set <JAIL> maxretry <RETRY>", "sets the number of failures <RETRY> before banning the host for <JAIL>"],
|
||||
["set <JAIL> maxlines <LINES>", "sets the number of <LINES> to buffer for regex search for <JAIL>"],
|
||||
["set <JAIL> addaction <ACT> [<PYTHONFILE> <JSONOPTS>]", "adds a new action named <NAME> for <JAIL>. Optionally for a python based action, a <PYTHONFILE> and <JSONOPTS> can be specified"],
|
||||
["set <JAIL> delaction <ACT>", "removes the action <NAME> from <JAIL>"],
|
||||
["set <JAIL> setcinfo <ACT> <KEY> <VALUE>", "sets <VALUE> for <KEY> of the action <NAME> for <JAIL>"],
|
||||
["set <JAIL> delcinfo <ACT> <KEY>", "removes <KEY> for the action <NAME> for <JAIL>"],
|
||||
["set <JAIL> timeout <ACT> <TIMEOUT>", "sets <TIMEOUT> as the command timeout in seconds for the action <ACT> for <JAIL>"],
|
||||
["set <JAIL> actionstart <ACT> <CMD>", "sets the start command <CMD> of the action <ACT> for <JAIL>"],
|
||||
["set <JAIL> actionstop <ACT> <CMD>", "sets the stop command <CMD> of the action <ACT> for <JAIL>"],
|
||||
["set <JAIL> actioncheck <ACT> <CMD>", "sets the check command <CMD> of the action <ACT> for <JAIL>"],
|
||||
["set <JAIL> actionban <ACT> <CMD>", "sets the ban command <CMD> of the action <ACT> for <JAIL>"],
|
||||
["set <JAIL> actionunban <ACT> <CMD>", "sets the unban command <CMD> of the action <ACT> for <JAIL>"],
|
||||
["set <JAIL> addaction <ACT>[ <PYTHONFILE> <JSONKWARGS>]", "adds a new action named <NAME> for <JAIL>. Optionally for a Python based action, a <PYTHONFILE> and <JSONKWARGS> can be specified, else will be a Command Action"],
|
||||
["set <JAIL> delaction <ACT>", "removes the action <ACT> from <JAIL>"],
|
||||
["", "COMMAND ACTION CONFIGURATION", ""],
|
||||
["set <JAIL> action <ACT> actionstart <CMD>", "sets the start command <CMD> of the action <ACT> for <JAIL>"],
|
||||
["set <JAIL> action <ACT> actionstop <CMD>", "sets the stop command <CMD> of the action <ACT> for <JAIL>"],
|
||||
["set <JAIL> action <ACT> actioncheck <CMD>", "sets the check command <CMD> of the action <ACT> for <JAIL>"],
|
||||
["set <JAIL> action <ACT> actionban <CMD>", "sets the ban command <CMD> of the action <ACT> for <JAIL>"],
|
||||
["set <JAIL> action <ACT> actionunban <CMD>", "sets the unban command <CMD> of the action <ACT> for <JAIL>"],
|
||||
["set <JAIL> action <ACT> timeout <TIMEOUT>", "sets <TIMEOUT> as the command timeout in seconds for the action <ACT> for <JAIL>"],
|
||||
["", "GENERAL ACTION CONFIGURATION", ""],
|
||||
["set <JAIL> action <ACT> <PROPERTY> <VALUE>", "sets the <VALUE> of <PROPERTY> for the action <ACT> for <JAIL>"],
|
||||
["set <JAIL> action <ACT> <METHOD>[ <JSONKWARGS>]", "calls the <METHOD> with <JSONKWARGS> for the action <ACT> for <JAIL>"],
|
||||
['', "JAIL INFORMATION", ""],
|
||||
["get <JAIL> logpath", "gets the list of the monitored files for <JAIL>"],
|
||||
["get <JAIL> logencoding <ENCODING>", "gets the <ENCODING> of the log files for <JAIL>"],
|
||||
|
@ -102,13 +104,17 @@ protocol = [
|
|||
["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>"],
|
||||
["get <JAIL> actionban <ACT>", "gets the ban command for the action <ACT> for <JAIL>"],
|
||||
["get <JAIL> actionunban <ACT>", "gets the unban command for the action <ACT> for <JAIL>"],
|
||||
["get <JAIL> cinfo <ACT> <KEY>", "gets the value for <KEY> for the action <ACT> for <JAIL>"],
|
||||
["get <JAIL> timeout <ACT>", "gets the command timeout in seconds for the action <ACT> for <JAIL>"],
|
||||
["", "COMMAND ACTION INFORMATION",""],
|
||||
["get <JAIL> action <ACT> actionstart", "gets the start command for the action <ACT> for <JAIL>"],
|
||||
["get <JAIL> action <ACT> actionstop", "gets the stop command for the action <ACT> for <JAIL>"],
|
||||
["get <JAIL> action <ACT> actioncheck", "gets the check command for the action <ACT> for <JAIL>"],
|
||||
["get <JAIL> action <ACT> actionban", "gets the ban command for the action <ACT> for <JAIL>"],
|
||||
["get <JAIL> action <ACT> actionunban", "gets the unban command for the action <ACT> for <JAIL>"],
|
||||
["get <JAIL> action <ACT> timeout", "gets the command timeout in seconds for the action <ACT> for <JAIL>"],
|
||||
["", "GENERAL ACTION INFORMATION", ""],
|
||||
["get <JAIL> actionproperties <ACT>", "gets a list of properties for the action <ACT> for <JAIL>"],
|
||||
["get <JAIL> actionmethods <ACT>", "gets a list of methods for the action <ACT> for <JAIL>"],
|
||||
["get <JAIL> action <ACT> <PROPERTY>", "gets the value of <PROPERTY> for the action <ACT> for <JAIL>"],
|
||||
]
|
||||
|
||||
##
|
||||
|
@ -125,15 +131,15 @@ def printFormatted():
|
|||
print
|
||||
firstHeading = True
|
||||
first = True
|
||||
if len(m[0]) > MARGIN+INDENT:
|
||||
if len(m[0]) >= MARGIN:
|
||||
m[1] = ' ' * WIDTH + m[1]
|
||||
for n in textwrap.wrap(m[1], WIDTH, drop_whitespace=False):
|
||||
if first:
|
||||
line = ' ' * INDENT + m[0] + ' ' * (MARGIN - len(m[0])) + n
|
||||
line = ' ' * INDENT + m[0] + ' ' * (MARGIN - len(m[0])) + n.strip()
|
||||
first = False
|
||||
else:
|
||||
line = ' ' * (INDENT + MARGIN) + n
|
||||
print line.rstrip()
|
||||
line = ' ' * (INDENT + MARGIN) + n.strip()
|
||||
print line
|
||||
|
||||
##
|
||||
# Prints the protocol in a "mediawiki" format.
|
||||
|
|
|
@ -49,6 +49,14 @@ signame = dict((num, name)
|
|||
for name, num in signal.__dict__.iteritems() if name.startswith("SIG"))
|
||||
|
||||
class CallingMap(MutableMapping):
|
||||
"""Calling Map behaves similar to a standard python dictionary,
|
||||
with the exception that any values which are callable, are called
|
||||
and the result of the callable is returned.
|
||||
No error handling is in place, such that any errors raised in the
|
||||
callable will raised as usual.
|
||||
Actual dictionary is stored in property `data`, and can be accessed
|
||||
to obtain original callable values.
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.data = dict(*args, **kwargs)
|
||||
def __getitem__(self, key):
|
||||
|
@ -66,262 +74,203 @@ class CallingMap(MutableMapping):
|
|||
def __len__(self):
|
||||
return len(self.data)
|
||||
|
||||
##
|
||||
# Execute commands.
|
||||
#
|
||||
# This class reads the failures from the Jail queue and decide if an
|
||||
# action has to be taken. A BanManager take care of the banned IP
|
||||
# addresses.
|
||||
|
||||
class ActionBase(object):
|
||||
"""Action Base is a base definition of what methods need to be in
|
||||
place to create a python based action for fail2ban. This class can
|
||||
be inherited from to ease implementation, but is not required as
|
||||
long as the following required methods/properties are implemented:
|
||||
- __init__(jail, actionname)
|
||||
- start()
|
||||
- stop()
|
||||
- ban(aInfo)
|
||||
- unban(aInfo)
|
||||
- actionname
|
||||
"""
|
||||
__metaclass__ = ABCMeta
|
||||
|
||||
@classmethod
|
||||
def __subclasshook__(cls, C):
|
||||
required = (
|
||||
"getName",
|
||||
"execActionStart",
|
||||
"execActionStop",
|
||||
"execActionBan",
|
||||
"execActionUnban",
|
||||
"start",
|
||||
"stop",
|
||||
"ban",
|
||||
"unban",
|
||||
)
|
||||
for method in required:
|
||||
if not callable(getattr(C, method, None)):
|
||||
return False
|
||||
return True
|
||||
|
||||
def __init__(self, jail, name):
|
||||
def __init__(self, jail, actionname):
|
||||
"""Should initialise the action class with `jail` being the Jail
|
||||
object the action belongs to, `actionname` being the name assigned
|
||||
to the action, and `kwargs` being all other args that have been
|
||||
specified with jail.conf or on the fail2ban-client.
|
||||
"""
|
||||
self._jail = jail
|
||||
self._name = name
|
||||
self._actionname = actionname
|
||||
self._logSys = logging.getLogger(
|
||||
'%s.%s' % (__name__, self.__class__.__name__))
|
||||
|
||||
@property
|
||||
def jail(self):
|
||||
return self._jail
|
||||
def actionname(self):
|
||||
"""The name of the action, which should not change in the
|
||||
lifetime of the action."""
|
||||
return self._actionname
|
||||
|
||||
@property
|
||||
def logSys(self):
|
||||
return self._logSys
|
||||
|
||||
##
|
||||
# Returns the action name.
|
||||
#
|
||||
# @return the name of the action
|
||||
|
||||
def getName(self):
|
||||
return self._name
|
||||
|
||||
name = property(getName)
|
||||
|
||||
def execActionStart(self):
|
||||
def start(self):
|
||||
"""Executed when the jail/action starts."""
|
||||
pass
|
||||
|
||||
def execActionBan(self, aInfo):
|
||||
def stop(self):
|
||||
"""Executed when the jail/action stops or action is deleted.
|
||||
"""
|
||||
pass
|
||||
|
||||
def execActionUnban(self, aInfo):
|
||||
def ban(self, aInfo):
|
||||
"""Executed when a ban occurs. `aInfo` is a dictionary which
|
||||
includes information in relation to the ban.
|
||||
"""
|
||||
pass
|
||||
|
||||
def execActionStop(self):
|
||||
def unban(self, aInfo):
|
||||
"""Executed when a ban expires. `aInfo` as per execActionBan.
|
||||
"""
|
||||
pass
|
||||
|
||||
class CommandAction(ActionBase):
|
||||
"""A Fail2Ban action which executes commands with Python's
|
||||
subprocess module. This is the default type of action which
|
||||
Fail2Ban uses.
|
||||
"""
|
||||
|
||||
def __init__(self, name):
|
||||
super(CommandAction, self).__init__(None, name)
|
||||
self.__timeout = 60
|
||||
self.__cInfo = dict()
|
||||
def __init__(self, jail, actionname):
|
||||
super(CommandAction, self).__init__(jail, actionname)
|
||||
self.timeout = 60
|
||||
## Command executed in order to initialize the system.
|
||||
self.__actionStart = ''
|
||||
self.actionstart = ''
|
||||
## Command executed when an IP address gets banned.
|
||||
self.__actionBan = ''
|
||||
self.actionban = ''
|
||||
## Command executed when an IP address gets removed.
|
||||
self.__actionUnban = ''
|
||||
self.actionunban = ''
|
||||
## Command executed in order to check requirements.
|
||||
self.__actionCheck = ''
|
||||
self.actioncheck = ''
|
||||
## Command executed in order to stop the system.
|
||||
self.__actionStop = ''
|
||||
logSys.debug("Created Action")
|
||||
self.actionstop = ''
|
||||
self._logSys.debug("Created %s" % self.__class__)
|
||||
|
||||
@classmethod
|
||||
def __subclasshook__(cls, C):
|
||||
return NotImplemented # Standard checks
|
||||
|
||||
##
|
||||
# Sets the timeout period for commands.
|
||||
#
|
||||
# @param timeout timeout period in seconds
|
||||
|
||||
def setTimeout(self, timeout):
|
||||
self.__timeout = int(timeout)
|
||||
logSys.debug("Set action %s timeout = %i" % (self.getName(), timeout))
|
||||
|
||||
##
|
||||
# Returns the action timeout period for commands.
|
||||
#
|
||||
# @return the timeout period in seconds
|
||||
|
||||
def getTimeout(self):
|
||||
return self.__timeout
|
||||
|
||||
##
|
||||
# Sets a "CInfo".
|
||||
#
|
||||
# CInfo are statically defined properties. They can be definied by
|
||||
# the user and are used to set e-mail addresses, port, host or
|
||||
# anything that should not change during the life of the server.
|
||||
#
|
||||
# @param key the property name
|
||||
# @param value the property value
|
||||
|
||||
def setCInfo(self, key, value):
|
||||
self.__cInfo[key] = value
|
||||
|
||||
##
|
||||
# Returns a "CInfo".
|
||||
#
|
||||
# @param key the property name
|
||||
|
||||
def getCInfo(self, key):
|
||||
return self.__cInfo[key]
|
||||
|
||||
##
|
||||
# Removes a "CInfo".
|
||||
#
|
||||
# @param key the property name
|
||||
|
||||
def delCInfo(self, key):
|
||||
del self.__cInfo[key]
|
||||
|
||||
##
|
||||
# Set the "start" command.
|
||||
#
|
||||
# @param value the command
|
||||
|
||||
def setActionStart(self, value):
|
||||
self.__actionStart = value
|
||||
logSys.debug("Set actionStart = %s" % value)
|
||||
|
||||
##
|
||||
# Get the "start" command.
|
||||
#
|
||||
# @return the command
|
||||
|
||||
def getActionStart(self):
|
||||
return self.__actionStart
|
||||
|
||||
##
|
||||
# Executes the action "start" command.
|
||||
#
|
||||
# Replaces the tags in the action command with value of "cInfo"
|
||||
# and executes the resulting command.
|
||||
#
|
||||
# @return True if the command succeeded
|
||||
|
||||
def execActionStart(self):
|
||||
if self.__cInfo:
|
||||
if not self.substituteRecursiveTags(self.__cInfo):
|
||||
logSys.error("Cinfo/definitions contain self referencing definitions and cannot be resolved")
|
||||
return False
|
||||
startCmd = self.replaceTag(self.__actionStart, self.__cInfo)
|
||||
return self.executeCmd(startCmd, self.__timeout)
|
||||
|
||||
##
|
||||
# Set the "ban" command.
|
||||
#
|
||||
# @param value the command
|
||||
|
||||
def setActionBan(self, value):
|
||||
self.__actionBan = value
|
||||
logSys.debug("Set actionBan = %s" % value)
|
||||
|
||||
##
|
||||
# Get the "ban" command.
|
||||
#
|
||||
# @return the command
|
||||
|
||||
def getActionBan(self):
|
||||
return self.__actionBan
|
||||
|
||||
##
|
||||
# Executes the action "ban" command.
|
||||
#
|
||||
# @return True if the command succeeded
|
||||
|
||||
def execActionBan(self, aInfo):
|
||||
return self.__processCmd(self.__actionBan, aInfo)
|
||||
|
||||
##
|
||||
# Set the "unban" command.
|
||||
#
|
||||
# @param value the command
|
||||
|
||||
def setActionUnban(self, value):
|
||||
self.__actionUnban = value
|
||||
logSys.debug("Set actionUnban = %s" % value)
|
||||
|
||||
##
|
||||
# Get the "unban" command.
|
||||
#
|
||||
# @return the command
|
||||
|
||||
def getActionUnban(self):
|
||||
return self.__actionUnban
|
||||
|
||||
##
|
||||
# Executes the action "unban" command.
|
||||
#
|
||||
# @return True if the command succeeded
|
||||
|
||||
def execActionUnban(self, aInfo):
|
||||
return self.__processCmd(self.__actionUnban, aInfo)
|
||||
|
||||
##
|
||||
# Set the "check" command.
|
||||
#
|
||||
# @param value the command
|
||||
|
||||
def setActionCheck(self, value):
|
||||
self.__actionCheck = value
|
||||
logSys.debug("Set actionCheck = %s" % value)
|
||||
|
||||
##
|
||||
# Get the "check" command.
|
||||
#
|
||||
# @return the command
|
||||
|
||||
def getActionCheck(self):
|
||||
return self.__actionCheck
|
||||
|
||||
##
|
||||
# Set the "stop" command.
|
||||
#
|
||||
# @param value the command
|
||||
|
||||
def setActionStop(self, value):
|
||||
self.__actionStop = value
|
||||
logSys.debug("Set actionStop = %s" % value)
|
||||
|
||||
##
|
||||
# Get the "stop" command.
|
||||
#
|
||||
# @return the command
|
||||
|
||||
def getActionStop(self):
|
||||
return self.__actionStop
|
||||
|
||||
##
|
||||
# Executes the action "stop" command.
|
||||
#
|
||||
# Replaces the tags in the action command with value of "cInfo"
|
||||
# and executes the resulting command.
|
||||
#
|
||||
# @return True if the command succeeded
|
||||
|
||||
def execActionStop(self):
|
||||
stopCmd = self.replaceTag(self.__actionStop, self.__cInfo)
|
||||
return self.executeCmd(stopCmd, self.__timeout)
|
||||
|
||||
@property
|
||||
def timeout(self):
|
||||
"""Timeout period in seconds for execution of commands
|
||||
"""
|
||||
return self._timeout
|
||||
@timeout.setter
|
||||
def timeout(self, timeout):
|
||||
self._timeout = int(timeout)
|
||||
self._logSys.debug("Set action %s timeout = %i" %
|
||||
(self.actionname, self.timeout))
|
||||
|
||||
@property
|
||||
def _properties(self):
|
||||
return dict(
|
||||
(key, getattr(self, key))
|
||||
for key in dir(self)
|
||||
if not key.startswith("_") and not callable(getattr(self, key)))
|
||||
|
||||
@property
|
||||
def actionstart(self):
|
||||
"""The command executed on start of the jail/action.
|
||||
"""
|
||||
return self._actionstart
|
||||
@actionstart.setter
|
||||
def actionstart(self, value):
|
||||
self._actionstart = value
|
||||
self._logSys.debug("Set actionstart = %s" % value)
|
||||
|
||||
def start(self):
|
||||
"""Executes the "actionstart" command.
|
||||
Replace the tags in the action command with actions properties
|
||||
and executes the resulting command.
|
||||
"""
|
||||
if (self._properties and
|
||||
not self.substituteRecursiveTags(self._properties)):
|
||||
self._logSys.error(
|
||||
"properties contain self referencing definitions "
|
||||
"and cannot be resolved")
|
||||
raise RuntimeError("Error starting action")
|
||||
startCmd = self.replaceTag(self.actionstart, self._properties)
|
||||
if not self.executeCmd(startCmd, self.timeout):
|
||||
raise RuntimeError("Error starting action")
|
||||
|
||||
@property
|
||||
def actionban(self):
|
||||
"""The command used when a ban occurs.
|
||||
"""
|
||||
return self._actionban
|
||||
@actionban.setter
|
||||
def actionban(self, value):
|
||||
self._actionban = value
|
||||
self._logSys.debug("Set actionban = %s" % value)
|
||||
|
||||
def ban(self, aInfo):
|
||||
"""Executes the "actionban" command.
|
||||
Replace the tags in the action command with actions properties
|
||||
and ban information, and executes the resulting command.
|
||||
"""
|
||||
if not self._processCmd(self.actionban, aInfo):
|
||||
raise RuntimeError("Error banning %(ip)s" % aInfo)
|
||||
|
||||
@property
|
||||
def actionunban(self):
|
||||
"""The command used when an unban occurs.
|
||||
"""
|
||||
return self._actionunban
|
||||
@actionunban.setter
|
||||
def actionunban(self, value):
|
||||
self._actionunban = value
|
||||
self._logSys.debug("Set actionunban = %s" % value)
|
||||
|
||||
def unban(self, aInfo):
|
||||
"""Executes the "actionunban" command.
|
||||
Replace the tags in the action command with actions properties
|
||||
and ban information, and executes the resulting command.
|
||||
"""
|
||||
if not self._processCmd(self.actionunban, aInfo):
|
||||
raise RuntimeError("Error unbanning %(ip)s" % aInfo)
|
||||
|
||||
@property
|
||||
def actioncheck(self):
|
||||
"""The command used to check correct environment in place for
|
||||
ban action to take place.
|
||||
"""
|
||||
return self._actioncheck
|
||||
@actioncheck.setter
|
||||
def actioncheck(self, value):
|
||||
self._actioncheck = value
|
||||
self._logSys.debug("Set actioncheck = %s" % value)
|
||||
|
||||
@property
|
||||
def actionstop(self):
|
||||
"""The command executed when the jail/actions stops.
|
||||
"""
|
||||
return self._actionstop
|
||||
@actionstop.setter
|
||||
def actionstop(self, value):
|
||||
self._actionstop = value
|
||||
self._logSys.debug("Set actionstop = %s" % value)
|
||||
|
||||
def stop(self):
|
||||
"""Executes the "actionstop" command.
|
||||
Replace the tags in the action command with actions properties
|
||||
and executes the resulting command.
|
||||
"""
|
||||
stopCmd = self.replaceTag(self.actionstop, self._properties)
|
||||
if not self.executeCmd(stopCmd, self.timeout):
|
||||
raise RuntimeError("Error stopping action")
|
||||
|
||||
##
|
||||
# Sort out tag definitions within other tags
|
||||
|
@ -397,21 +346,21 @@ class CommandAction(ActionBase):
|
|||
# @param aInfo Dynamic properties
|
||||
# @return True if the command succeeded
|
||||
|
||||
def __processCmd(self, cmd, aInfo = None):
|
||||
def _processCmd(self, cmd, aInfo = None):
|
||||
""" Executes an OS command.
|
||||
"""
|
||||
if cmd == "":
|
||||
logSys.debug("Nothing to do")
|
||||
self._logSys.debug("Nothing to do")
|
||||
return True
|
||||
|
||||
checkCmd = self.replaceTag(self.__actionCheck, self.__cInfo)
|
||||
if not self.executeCmd(checkCmd, self.__timeout):
|
||||
logSys.error("Invariant check failed. Trying to restore a sane" +
|
||||
" environment")
|
||||
self.execActionStop()
|
||||
self.execActionStart()
|
||||
if not self.executeCmd(checkCmd, self.__timeout):
|
||||
logSys.fatal("Unable to restore environment")
|
||||
checkCmd = self.replaceTag(self.actioncheck, self._properties)
|
||||
if not self.executeCmd(checkCmd, self.timeout):
|
||||
self._logSys.error(
|
||||
"Invariant check failed. Trying to restore a sane environment")
|
||||
self.stop()
|
||||
self.start()
|
||||
if not self.executeCmd(checkCmd, self.timeout):
|
||||
self._logSys.fatal("Unable to restore environment")
|
||||
return False
|
||||
|
||||
# Replace tags
|
||||
|
@ -421,9 +370,9 @@ class CommandAction(ActionBase):
|
|||
realCmd = cmd
|
||||
|
||||
# Replace static fields
|
||||
realCmd = self.replaceTag(realCmd, self.__cInfo)
|
||||
realCmd = self.replaceTag(realCmd, self._properties)
|
||||
|
||||
return self.executeCmd(realCmd, self.__timeout)
|
||||
return self.executeCmd(realCmd, self.timeout)
|
||||
|
||||
##
|
||||
# Executes a command.
|
||||
|
@ -495,5 +444,6 @@ class CommandAction(ActionBase):
|
|||
if msg:
|
||||
logSys.info("HINT on %i: %s"
|
||||
% (retcode, msg % locals()))
|
||||
return False
|
||||
return False
|
||||
raise RuntimeError("Command execution failed: %s" % realCmd)
|
||||
|
||||
|
|
|
@ -66,10 +66,10 @@ class Actions(JailThread):
|
|||
|
||||
def addAction(self, name, pythonModule=None, initOpts=None):
|
||||
# Check is action name already exists
|
||||
if name in [action.getName() for action in self.__actions]:
|
||||
if name in [action.actionname for action in self.__actions]:
|
||||
raise ValueError("Action %s already exists" % name)
|
||||
if pythonModule is None:
|
||||
action = CommandAction(name)
|
||||
action = CommandAction(self.jail, name)
|
||||
else:
|
||||
pythonModuleName = os.path.basename(pythonModule.strip(".py"))
|
||||
customActionModule = imp.load_source(
|
||||
|
@ -91,7 +91,7 @@ class Actions(JailThread):
|
|||
|
||||
def delAction(self, name):
|
||||
for action in self.__actions:
|
||||
if action.getName() == name:
|
||||
if action.actionname == name:
|
||||
self.__actions.remove(action)
|
||||
return
|
||||
raise KeyError("Invalid Action name: %s" % name)
|
||||
|
@ -106,7 +106,7 @@ class Actions(JailThread):
|
|||
|
||||
def getAction(self, name):
|
||||
for action in self.__actions:
|
||||
if action.getName() == name:
|
||||
if action.actionname == name:
|
||||
return action
|
||||
raise KeyError("Invalid Action name")
|
||||
|
||||
|
@ -116,9 +116,7 @@ class Actions(JailThread):
|
|||
# @return The last defined action.
|
||||
|
||||
def getLastAction(self):
|
||||
action = self.__actions.pop()
|
||||
self.__actions.append(action)
|
||||
return action
|
||||
return self.__actions[-1]
|
||||
|
||||
##
|
||||
# Returns the list of actions
|
||||
|
@ -169,10 +167,10 @@ class Actions(JailThread):
|
|||
self.setActive(True)
|
||||
for action in self.__actions:
|
||||
try:
|
||||
action.execActionStart()
|
||||
action.start()
|
||||
except Exception as e:
|
||||
logSys.error("Failed to start jail '%s' action '%s': %s",
|
||||
self.jail.getName(), action.getName(), e)
|
||||
self.jail.getName(), action.actionname, e)
|
||||
while self._isActive():
|
||||
if not self.getIdle():
|
||||
#logSys.debug(self.jail.getName() + ": action")
|
||||
|
@ -185,10 +183,10 @@ class Actions(JailThread):
|
|||
self.__flushBan()
|
||||
for action in self.__actions:
|
||||
try:
|
||||
action.execActionStop()
|
||||
action.stop()
|
||||
except Exception as e:
|
||||
logSys.error("Failed to stop jail '%s' action '%s': %s",
|
||||
self.jail.getName(), action.getName(), e)
|
||||
self.jail.getName(), action.actionname, e)
|
||||
logSys.debug(self.jail.getName() + ": action terminated")
|
||||
return True
|
||||
|
||||
|
@ -225,11 +223,11 @@ class Actions(JailThread):
|
|||
logSys.warning("[%s] Ban %s" % (self.jail.getName(), aInfo["ip"]))
|
||||
for action in self.__actions:
|
||||
try:
|
||||
action.execActionBan(aInfo)
|
||||
action.ban(aInfo)
|
||||
except Exception as e:
|
||||
logSys.error(
|
||||
"Failed to execute ban jail '%s' action '%s': %s",
|
||||
self.jail.getName(), action.getName(), e)
|
||||
self.jail.getName(), action.actionname, e)
|
||||
return True
|
||||
else:
|
||||
logSys.info("[%s] %s already banned" % (self.jail.getName(),
|
||||
|
@ -270,11 +268,11 @@ class Actions(JailThread):
|
|||
logSys.warning("[%s] Unban %s" % (self.jail.getName(), aInfo["ip"]))
|
||||
for action in self.__actions:
|
||||
try:
|
||||
action.execActionUnban(aInfo)
|
||||
action.unban(aInfo)
|
||||
except Exception as e:
|
||||
logSys.error(
|
||||
"Failed to execute unban jail '%s' action '%s': %s",
|
||||
self.jail.getName(), action.getName(), e)
|
||||
self.jail.getName(), action.actionname, e)
|
||||
|
||||
|
||||
##
|
||||
|
|
|
@ -291,26 +291,8 @@ class Server:
|
|||
def delAction(self, name, value):
|
||||
self.__jails.getAction(name).delAction(value)
|
||||
|
||||
def setCInfo(self, name, actionName, key, value):
|
||||
action = self.__jails.getAction(name).getAction(actionName)
|
||||
if isinstance(action, CommandAction):
|
||||
action.setCInfo(key, value)
|
||||
else:
|
||||
raise TypeError("%s is not a CommandAction" % actionName)
|
||||
|
||||
def getCInfo(self, name, actionName, key):
|
||||
action = self.__jails.getAction(name).getAction(actionName)
|
||||
if isinstance(action, CommandAction):
|
||||
return action.getCInfo(key)
|
||||
else:
|
||||
raise TypeError("%s is not a CommandAction" % actionName)
|
||||
|
||||
def delCInfo(self, name, actionName, key):
|
||||
action = self.__jails.getAction(name).getAction(actionName)
|
||||
if isinstance(action, CommandAction):
|
||||
action.delCInfo(key)
|
||||
else:
|
||||
raise TypeError("%s is not a CommandAction" % actionName)
|
||||
def getAction(self, name, value):
|
||||
return self.__jails.getAction(name).getAction(value)
|
||||
|
||||
def setBanTime(self, name, value):
|
||||
self.__jails.getAction(name).setBanTime(value)
|
||||
|
@ -324,90 +306,6 @@ class Server:
|
|||
def getBanTime(self, name):
|
||||
return self.__jails.getAction(name).getBanTime()
|
||||
|
||||
def setActionStart(self, name, actionName, value):
|
||||
action = self.__jails.getAction(name).getAction(actionName)
|
||||
if isinstance(action, CommandAction):
|
||||
action.setActionStart(value)
|
||||
else:
|
||||
raise TypeError("%s is not a CommandAction" % actionName)
|
||||
|
||||
def getActionStart(self, name, actionName):
|
||||
action = self.__jails.getAction(name).getAction(actionName)
|
||||
if isinstance(action, CommandAction):
|
||||
return action.getActionStart()
|
||||
else:
|
||||
raise TypeError("%s is not a CommandAction" % actionName)
|
||||
|
||||
def setActionStop(self, name, actionName, value):
|
||||
action = self.__jails.getAction(name).getAction(actionName)
|
||||
if isinstance(action, CommandAction):
|
||||
action.setActionStop(value)
|
||||
else:
|
||||
raise TypeError("%s is not a CommandAction" % actionName)
|
||||
|
||||
def getActionStop(self, name, actionName):
|
||||
action = self.__jails.getAction(name).getAction(actionName)
|
||||
if isinstance(action, CommandAction):
|
||||
return action.getActionStop()
|
||||
else:
|
||||
raise TypeError("%s is not a CommandAction" % actionName)
|
||||
|
||||
def setActionCheck(self, name, actionName, value):
|
||||
action = self.__jails.getAction(name).getAction(actionName)
|
||||
if isinstance(action, CommandAction):
|
||||
action.setActionCheck(value)
|
||||
else:
|
||||
raise TypeError("%s is not a CommandAction" % actionName)
|
||||
|
||||
def getActionCheck(self, name, actionName):
|
||||
action = self.__jails.getAction(name).getAction(actionName)
|
||||
if isinstance(action, CommandAction):
|
||||
return action.getActionCheck()
|
||||
else:
|
||||
raise TypeError("%s is not a CommandAction" % actionName)
|
||||
|
||||
def setActionBan(self, name, actionName, value):
|
||||
action = self.__jails.getAction(name).getAction(actionName)
|
||||
if isinstance(action, CommandAction):
|
||||
action.setActionBan(value)
|
||||
else:
|
||||
raise TypeError("%s is not a CommandAction" % actionName)
|
||||
|
||||
def getActionBan(self, name, actionName):
|
||||
action = self.__jails.getAction(name).getAction(actionName)
|
||||
if isinstance(action, CommandAction):
|
||||
return action.getActionBan()
|
||||
else:
|
||||
raise TypeError("%s is not a CommandAction" % actionName)
|
||||
|
||||
def setActionUnban(self, name, actionName, value):
|
||||
action = self.__jails.getAction(name).getAction(actionName)
|
||||
if isinstance(action, CommandAction):
|
||||
action.setActionUnban(value)
|
||||
else:
|
||||
raise TypeError("%s is not a CommandAction" % actionName)
|
||||
|
||||
def getActionUnban(self, name, actionName):
|
||||
action = self.__jails.getAction(name).getAction(actionName)
|
||||
if isinstance(action, CommandAction):
|
||||
return action.getActionUnban()
|
||||
else:
|
||||
raise TypeError("%s is not a CommandAction" % actionName)
|
||||
|
||||
def setActionTimeout(self, name, actionName, value):
|
||||
action = self.__jails.getAction(name).getAction(actionName)
|
||||
if isinstance(action, CommandAction):
|
||||
action.setTimeout(value)
|
||||
else:
|
||||
raise TypeError("%s is not a CommandAction" % actionName)
|
||||
|
||||
def getActionTimeout(self, name, actionName):
|
||||
action = self.__jails.getAction(name).getAction(actionName)
|
||||
if isinstance(action, CommandAction):
|
||||
return action.getTimeout()
|
||||
else:
|
||||
raise TypeError("%s is not a CommandAction" % actionName)
|
||||
|
||||
# Status
|
||||
def status(self):
|
||||
try:
|
||||
|
|
|
@ -233,52 +233,22 @@ class Transmitter:
|
|||
if len(command) > 3:
|
||||
args.extend([command[3], json.loads(command[4])])
|
||||
self.__server.addAction(name, *args)
|
||||
return self.__server.getLastAction(name).getName()
|
||||
return self.__server.getLastAction(name).actionname
|
||||
elif command[1] == "delaction":
|
||||
value = command[2]
|
||||
self.__server.delAction(name, value)
|
||||
return None
|
||||
elif command[1] == "setcinfo":
|
||||
act = command[2]
|
||||
key = command[3]
|
||||
value = " ".join(command[4:])
|
||||
self.__server.setCInfo(name, act, key, value)
|
||||
return self.__server.getCInfo(name, act, key)
|
||||
elif command[1] == "delcinfo":
|
||||
act = command[2]
|
||||
key = command[3]
|
||||
self.__server.delCInfo(name, act, key)
|
||||
return None
|
||||
elif command[1] == "actionstart":
|
||||
act = command[2]
|
||||
value = " ".join(command[3:])
|
||||
self.__server.setActionStart(name, act, value)
|
||||
return self.__server.getActionStart(name, act)
|
||||
elif command[1] == "actionstop":
|
||||
act = command[2]
|
||||
value = " ".join(command[3:])
|
||||
self.__server.setActionStop(name, act, value)
|
||||
return self.__server.getActionStop(name, act)
|
||||
elif command[1] == "actioncheck":
|
||||
act = command[2]
|
||||
value = " ".join(command[3:])
|
||||
self.__server.setActionCheck(name, act, value)
|
||||
return self.__server.getActionCheck(name, act)
|
||||
elif command[1] == "actionban":
|
||||
act = command[2]
|
||||
value = " ".join(command[3:])
|
||||
self.__server.setActionBan(name, act, value)
|
||||
return self.__server.getActionBan(name, act)
|
||||
elif command[1] == "actionunban":
|
||||
act = command[2]
|
||||
value = " ".join(command[3:])
|
||||
self.__server.setActionUnban(name, act, value)
|
||||
return self.__server.getActionUnban(name, act)
|
||||
elif command[1] == "timeout":
|
||||
act = command[2]
|
||||
value = int(command[3])
|
||||
self.__server.setActionTimeout(name, act, value)
|
||||
return self.__server.getActionTimeout(name, act)
|
||||
elif command[1] == "action":
|
||||
actionname = command[2]
|
||||
actionkey = command[3]
|
||||
action = self.__server.getAction(name, actionname)
|
||||
if callable(getattr(action, actionkey, None)):
|
||||
actionvalue = json.loads(command[4]) if len(command)>4 else {}
|
||||
return getattr(action, actionkey)(**actionvalue)
|
||||
else:
|
||||
actionvalue = command[4]
|
||||
setattr(action, actionkey, actionvalue)
|
||||
return getattr(action, actionkey)
|
||||
raise Exception("Invalid command (no set action or not yet implemented)")
|
||||
|
||||
def __commandGet(self, command):
|
||||
|
@ -330,31 +300,28 @@ class Transmitter:
|
|||
elif command[1] == "bantime":
|
||||
return self.__server.getBanTime(name)
|
||||
elif command[1] == "actions":
|
||||
return self.__server.getActions(name)
|
||||
return [action.actionname
|
||||
for action in self.__server.getActions(name)]
|
||||
elif command[1] == "addaction":
|
||||
return self.__server.getLastAction(name).getName()
|
||||
elif command[1] == "actionstart":
|
||||
act = command[2]
|
||||
return self.__server.getActionStart(name, act)
|
||||
elif command[1] == "actionstop":
|
||||
act = command[2]
|
||||
return self.__server.getActionStop(name, act)
|
||||
elif command[1] == "actioncheck":
|
||||
act = command[2]
|
||||
return self.__server.getActionCheck(name, act)
|
||||
elif command[1] == "actionban":
|
||||
act = command[2]
|
||||
return self.__server.getActionBan(name, act)
|
||||
elif command[1] == "actionunban":
|
||||
act = command[2]
|
||||
return self.__server.getActionUnban(name, act)
|
||||
elif command[1] == "cinfo":
|
||||
act = command[2]
|
||||
key = command[3]
|
||||
return self.__server.getCInfo(name, act, key)
|
||||
elif command[1] == "timeout":
|
||||
act = command[2]
|
||||
return self.__server.getActionTimeout(name, act)
|
||||
return self.__server.getLastAction(name).actionname
|
||||
elif command[1] == "action":
|
||||
actionname = command[2]
|
||||
actionvalue = command[3]
|
||||
action = self.__server.getAction(name, actionname)
|
||||
return getattr(action, actionvalue)
|
||||
elif command[1] == "actionproperties":
|
||||
actionname = command[2]
|
||||
action = self.__server.getAction(name, actionname)
|
||||
return [
|
||||
key for key in dir(action)
|
||||
if not key.startswith("_") and
|
||||
not callable(getattr(action, key))]
|
||||
elif command[1] == "actionmethods":
|
||||
actionname = command[2]
|
||||
action = self.__server.getAction(name, actionname)
|
||||
return [
|
||||
key for key in dir(action)
|
||||
if not key.startswith("_") and callable(getattr(action, key))]
|
||||
raise Exception("Invalid command (no get action or not yet implemented)")
|
||||
|
||||
def status(self, command):
|
||||
|
|
|
@ -49,11 +49,11 @@ class ExecuteActions(LogCaptureTestCase):
|
|||
def defaultActions(self):
|
||||
self.__actions.addAction('ip')
|
||||
self.__ip = self.__actions.getAction('ip')
|
||||
self.__ip.setActionStart('echo ip start 64 >> "%s"' % self.__tmpfilename )
|
||||
self.__ip.setActionBan('echo ip ban <ip> >> "%s"' % self.__tmpfilename )
|
||||
self.__ip.setActionUnban('echo ip unban <ip> >> "%s"' % self.__tmpfilename )
|
||||
self.__ip.setActionCheck('echo ip check <ip> >> "%s"' % self.__tmpfilename )
|
||||
self.__ip.setActionStop('echo ip stop >> "%s"' % self.__tmpfilename )
|
||||
self.__ip.actionstart = 'echo ip start 64 >> "%s"' % self.__tmpfilename
|
||||
self.__ip.actionban = 'echo ip ban <ip> >> "%s"' % self.__tmpfilename
|
||||
self.__ip.actionunban = 'echo ip unban <ip> >> "%s"' % self.__tmpfilename
|
||||
self.__ip.actioncheck = 'echo ip check <ip> >> "%s"' % self.__tmpfilename
|
||||
self.__ip.actionstop = 'echo ip stop >> "%s"' % self.__tmpfilename
|
||||
|
||||
def testActionsManipulation(self):
|
||||
self.__actions.addAction('test')
|
||||
|
|
|
@ -31,17 +31,17 @@ from fail2ban.server.action import CommandAction, CallingMap
|
|||
|
||||
from fail2ban.tests.utils import LogCaptureTestCase
|
||||
|
||||
class ExecuteAction(LogCaptureTestCase):
|
||||
class CommandActionTest(LogCaptureTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Call before every test case."""
|
||||
self.__action = CommandAction("Test")
|
||||
self.__action = CommandAction(None, "Test")
|
||||
LogCaptureTestCase.setUp(self)
|
||||
|
||||
def tearDown(self):
|
||||
"""Call after every test case."""
|
||||
LogCaptureTestCase.tearDown(self)
|
||||
self.__action.execActionStop()
|
||||
self.__action.stop()
|
||||
|
||||
def testSubstituteRecursiveTags(self):
|
||||
aInfo = {
|
||||
|
@ -105,62 +105,61 @@ class ExecuteAction(LogCaptureTestCase):
|
|||
CallingMap(callme=lambda: int("a"))), "abc")
|
||||
|
||||
def testExecuteActionBan(self):
|
||||
self.__action.setActionStart("touch /tmp/fail2ban.test")
|
||||
self.assertEqual(self.__action.getActionStart(), "touch /tmp/fail2ban.test")
|
||||
self.__action.setActionStop("rm -f /tmp/fail2ban.test")
|
||||
self.assertEqual(self.__action.getActionStop(), 'rm -f /tmp/fail2ban.test')
|
||||
self.__action.setActionBan("echo -n")
|
||||
self.assertEqual(self.__action.getActionBan(), 'echo -n')
|
||||
self.__action.setActionCheck("[ -e /tmp/fail2ban.test ]")
|
||||
self.assertEqual(self.__action.getActionCheck(), '[ -e /tmp/fail2ban.test ]')
|
||||
self.__action.setActionUnban("true")
|
||||
self.assertEqual(self.__action.getActionUnban(), 'true')
|
||||
self.__action.actionstart = "touch /tmp/fail2ban.test"
|
||||
self.assertEqual(self.__action.actionstart, "touch /tmp/fail2ban.test")
|
||||
self.__action.actionstop = "rm -f /tmp/fail2ban.test"
|
||||
self.assertEqual(self.__action.actionstop, 'rm -f /tmp/fail2ban.test')
|
||||
self.__action.actionban = "echo -n"
|
||||
self.assertEqual(self.__action.actionban, 'echo -n')
|
||||
self.__action.actioncheck = "[ -e /tmp/fail2ban.test ]"
|
||||
self.assertEqual(self.__action.actioncheck, '[ -e /tmp/fail2ban.test ]')
|
||||
self.__action.actionunban = "true"
|
||||
self.assertEqual(self.__action.actionunban, 'true')
|
||||
|
||||
self.assertFalse(self._is_logged('returned'))
|
||||
# no action was actually executed yet
|
||||
|
||||
self.assertTrue(self.__action.execActionBan(None))
|
||||
self.__action.ban({'ip': None})
|
||||
self.assertTrue(self._is_logged('Invariant check failed'))
|
||||
self.assertTrue(self._is_logged('returned successfully'))
|
||||
|
||||
def testExecuteActionEmptyUnban(self):
|
||||
self.__action.setActionUnban("")
|
||||
self.assertTrue(self.__action.execActionUnban(None))
|
||||
self.__action.actionunban = ""
|
||||
self.__action.unban({})
|
||||
self.assertTrue(self._is_logged('Nothing to do'))
|
||||
|
||||
def testExecuteActionStartCtags(self):
|
||||
self.__action.setCInfo("HOST","192.0.2.0")
|
||||
self.__action.setActionStart("touch /tmp/fail2ban.test.<HOST>")
|
||||
self.__action.setActionStop("rm -f /tmp/fail2ban.test.<HOST>")
|
||||
self.__action.setActionCheck("[ -e /tmp/fail2ban.test.192.0.2.0 ]")
|
||||
self.assertTrue(self.__action.execActionStart())
|
||||
self.__action.HOST = "192.0.2.0"
|
||||
self.__action.actionstart = "touch /tmp/fail2ban.test.<HOST>"
|
||||
self.__action.actionstop = "rm -f /tmp/fail2ban.test.<HOST>"
|
||||
self.__action.actioncheck = "[ -e /tmp/fail2ban.test.192.0.2.0 ]"
|
||||
self.__action.start()
|
||||
|
||||
def testExecuteActionCheckRestoreEnvironment(self):
|
||||
self.__action.setActionStart("")
|
||||
self.__action.setActionStop("rm -f /tmp/fail2ban.test")
|
||||
self.__action.setActionBan("rm /tmp/fail2ban.test")
|
||||
self.__action.setActionCheck("[ -e /tmp/fail2ban.test ]")
|
||||
self.assertFalse(self.__action.execActionBan(None))
|
||||
self.__action.actionstart = ""
|
||||
self.__action.actionstop = "rm -f /tmp/fail2ban.test"
|
||||
self.__action.actionban = "rm /tmp/fail2ban.test"
|
||||
self.__action.actioncheck = "[ -e /tmp/fail2ban.test ]"
|
||||
self.assertRaises(RuntimeError, self.__action.ban, {'ip': None})
|
||||
self.assertTrue(self._is_logged('Unable to restore environment'))
|
||||
|
||||
def testExecuteActionChangeCtags(self):
|
||||
self.__action.setCInfo("ROST","192.0.2.0")
|
||||
self.assertEqual(self.__action.getCInfo("ROST"),"192.0.2.0")
|
||||
self.__action.delCInfo("ROST")
|
||||
self.assertRaises(KeyError, self.__action.getCInfo, "ROST")
|
||||
self.assertRaises(AttributeError, getattr, self.__action, "ROST")
|
||||
self.__action.ROST = "192.0.2.0"
|
||||
self.assertEqual(self.__action.ROST,"192.0.2.0")
|
||||
|
||||
def testExecuteActionUnbanAinfo(self):
|
||||
aInfo = {
|
||||
'ABC': "123",
|
||||
}
|
||||
self.__action.setActionBan("touch /tmp/fail2ban.test.123")
|
||||
self.__action.setActionUnban("rm /tmp/fail2ban.test.<ABC>")
|
||||
self.assertTrue(self.__action.execActionBan(None))
|
||||
self.assertTrue(self.__action.execActionUnban(aInfo))
|
||||
self.__action.actionban = "touch /tmp/fail2ban.test.123"
|
||||
self.__action.actionunban = "rm /tmp/fail2ban.test.<ABC>"
|
||||
self.__action.ban(aInfo)
|
||||
self.__action.unban(aInfo)
|
||||
|
||||
def testExecuteActionStartEmpty(self):
|
||||
self.__action.setActionStart("")
|
||||
self.assertTrue(self.__action.execActionStart())
|
||||
self.__action.actionstart = ""
|
||||
self.__action.start()
|
||||
self.assertTrue(self._is_logged('Nothing to do'))
|
||||
|
||||
def testExecuteIncorrectCmd(self):
|
||||
|
@ -169,7 +168,9 @@ class ExecuteAction(LogCaptureTestCase):
|
|||
|
||||
def testExecuteTimeout(self):
|
||||
stime = time.time()
|
||||
CommandAction.executeCmd('sleep 60', timeout=2) # Should take a minute
|
||||
# Should take a minute
|
||||
self.assertRaises(
|
||||
RuntimeError, CommandAction.executeCmd, 'sleep 60', timeout=2)
|
||||
self.assertAlmostEqual(time.time() - stime, 2, places=0)
|
||||
self.assertTrue(self._is_logged('sleep 60 -- timed out after 2 seconds'))
|
||||
self.assertTrue(self._is_logged('sleep 60 -- killed with SIGTERM'))
|
||||
|
|
|
@ -353,13 +353,18 @@ class JailsReaderTest(LogCaptureTestCase):
|
|||
['set', 'brokenaction', 'addaction', 'brokenaction'],
|
||||
['set',
|
||||
'brokenaction',
|
||||
'actionban',
|
||||
'action',
|
||||
'brokenaction',
|
||||
'actionban',
|
||||
'hit with big stick <ip>'],
|
||||
['set', 'brokenaction', 'actionstop', 'brokenaction', ''],
|
||||
['set', 'brokenaction', 'actionstart', 'brokenaction', ''],
|
||||
['set', 'brokenaction', 'actionunban', 'brokenaction', ''],
|
||||
['set', 'brokenaction', 'actioncheck', 'brokenaction', ''],
|
||||
['set', 'brokenaction', 'action', 'brokenaction',
|
||||
'actionstop', ''],
|
||||
['set', 'brokenaction', 'action', 'brokenaction',
|
||||
'actionstart', ''],
|
||||
['set', 'brokenaction', 'action', 'brokenaction',
|
||||
'actionunban', ''],
|
||||
['set', 'brokenaction', 'action', 'brokenaction',
|
||||
'actioncheck', ''],
|
||||
['add', 'parse_to_end_of_jail.conf', 'auto'],
|
||||
['set', 'parse_to_end_of_jail.conf', 'usedns', 'warn'],
|
||||
['set', 'parse_to_end_of_jail.conf', 'maxretry', 3],
|
||||
|
@ -486,7 +491,7 @@ class JailsReaderTest(LogCaptureTestCase):
|
|||
self.assertTrue('blocktype' in action._initOpts)
|
||||
# Verify that we have a call to set it up
|
||||
blocktype_present = False
|
||||
target_command = [ 'set', jail_name, 'setcinfo', action_name, 'blocktype' ]
|
||||
target_command = ['set', jail_name, 'action', action_name, 'blocktype']
|
||||
for command in commands:
|
||||
if (len(command) > 5 and
|
||||
command[:5] == target_command):
|
||||
|
|
|
@ -3,20 +3,26 @@ from fail2ban.server.action import ActionBase
|
|||
|
||||
class TestAction(ActionBase):
|
||||
|
||||
def __init__(self, jail, name, opt1, opt2=None):
|
||||
super(TestAction, self).__init__(jail, name)
|
||||
self.logSys.debug("%s initialised" % self.__class__.__name__)
|
||||
def __init__(self, jail, actionname, opt1, opt2=None):
|
||||
super(TestAction, self).__init__(jail, actionname)
|
||||
self._logSys.debug("%s initialised" % self.__class__.__name__)
|
||||
self.opt1 = opt1
|
||||
self.opt2 = opt2
|
||||
self._opt3 = "Hello"
|
||||
|
||||
def execActionStart(self):
|
||||
self.logSys.debug("%s action start" % self.__class__.__name__)
|
||||
def start(self):
|
||||
self._logSys.debug("%s action start" % self.__class__.__name__)
|
||||
|
||||
def execActionStop(self):
|
||||
self.logSys.debug("%s action stop" % self.__class__.__name__)
|
||||
def stop(self):
|
||||
self._logSys.debug("%s action stop" % self.__class__.__name__)
|
||||
|
||||
def execActionBan(self, aInfo):
|
||||
self.logSys.debug("%s action ban" % self.__class__.__name__)
|
||||
def ban(self, aInfo):
|
||||
self._logSys.debug("%s action ban" % self.__class__.__name__)
|
||||
|
||||
def execActionUnban(self, aInfo):
|
||||
self.logSys.debug("%s action unban" % self.__class__.__name__)
|
||||
def unban(self, aInfo):
|
||||
self._logSys.debug("%s action unban" % self.__class__.__name__)
|
||||
|
||||
def testmethod(self, text):
|
||||
return "%s %s %s" % (self._opt3, text, self.opt1)
|
||||
|
||||
Action = TestAction
|
||||
|
|
|
@ -523,40 +523,40 @@ class Transmitter(TransmitterBase):
|
|||
(0, action))
|
||||
self.assertEqual(
|
||||
self.transm.proceed(
|
||||
["get", self.jailName, "actions"])[1][0].getName(),
|
||||
["get", self.jailName, "actions"])[1][0],
|
||||
action)
|
||||
for cmd, value in zip(cmdList, cmdValueList):
|
||||
self.assertEqual(
|
||||
self.transm.proceed(
|
||||
["set", self.jailName, cmd, action, value]),
|
||||
["set", self.jailName, "action", action, cmd, value]),
|
||||
(0, value))
|
||||
for cmd, value in zip(cmdList, cmdValueList):
|
||||
self.assertEqual(
|
||||
self.transm.proceed(["get", self.jailName, cmd, action]),
|
||||
self.transm.proceed(["get", self.jailName, "action", action, cmd]),
|
||||
(0, value))
|
||||
self.assertEqual(
|
||||
self.transm.proceed(
|
||||
["set", self.jailName, "setcinfo", action, "KEY", "VALUE"]),
|
||||
["set", self.jailName, "action", action, "KEY", "VALUE"]),
|
||||
(0, "VALUE"))
|
||||
self.assertEqual(
|
||||
self.transm.proceed(
|
||||
["get", self.jailName, "cinfo", action, "KEY"]),
|
||||
["get", self.jailName, "action", action, "KEY"]),
|
||||
(0, "VALUE"))
|
||||
self.assertEqual(
|
||||
self.transm.proceed(
|
||||
["get", self.jailName, "cinfo", action, "InvalidKey"])[0],
|
||||
["get", self.jailName, "action", action, "InvalidKey"])[0],
|
||||
1)
|
||||
self.assertEqual(
|
||||
self.transm.proceed(
|
||||
["set", self.jailName, "delcinfo", action, "KEY"]),
|
||||
(0, None))
|
||||
["get", self.jailName, "action", action, "actionname"]),
|
||||
(0, action))
|
||||
self.assertEqual(
|
||||
self.transm.proceed(
|
||||
["set", self.jailName, "timeout", action, "10"]),
|
||||
["set", self.jailName, "action", action, "timeout", "10"]),
|
||||
(0, 10))
|
||||
self.assertEqual(
|
||||
self.transm.proceed(
|
||||
["get", self.jailName, "timeout", action]),
|
||||
["get", self.jailName, "action", action, "timeout"]),
|
||||
(0, 10))
|
||||
self.assertEqual(
|
||||
self.transm.proceed(["set", self.jailName, "delaction", action]),
|
||||
|
@ -564,23 +564,42 @@ class Transmitter(TransmitterBase):
|
|||
self.assertEqual(
|
||||
self.transm.proceed(
|
||||
["set", self.jailName, "delaction", "Doesn't exist"])[0],1)
|
||||
|
||||
def testPythonActionMethodsAndProperties(self):
|
||||
action = "TestCaseAction"
|
||||
self.assertEqual(
|
||||
self.transm.proceed(["set", self.jailName, "addaction", action,
|
||||
os.path.join(TEST_FILES_DIR, "action.d", "action.py"),
|
||||
'{"opt1": "value"}']),
|
||||
(0, action))
|
||||
for cmd, value in zip(cmdList, cmdValueList):
|
||||
self.assertTrue(
|
||||
isinstance(self.transm.proceed(
|
||||
["set", self.jailName, cmd, action, value])[1],
|
||||
TypeError),
|
||||
"set %s for python action did not raise TypeError" % cmd)
|
||||
for cmd, value in zip(cmdList, cmdValueList):
|
||||
self.assertTrue(
|
||||
isinstance(self.transm.proceed(
|
||||
["get", self.jailName, cmd, action])[1],
|
||||
TypeError),
|
||||
"get %s for python action did not raise TypeError" % cmd)
|
||||
self.assertEqual(
|
||||
sorted(self.transm.proceed(["get", self.jailName,
|
||||
"actionproperties", action])),
|
||||
[0, ['actionname', 'opt1', 'opt2']])
|
||||
self.assertEqual(
|
||||
self.transm.proceed(["get", self.jailName, "action", action,
|
||||
"opt1"]),
|
||||
(0, 'value'))
|
||||
self.assertEqual(
|
||||
self.transm.proceed(["get", self.jailName, "action", action,
|
||||
"opt2"]),
|
||||
(0, None))
|
||||
self.assertEqual(
|
||||
sorted(self.transm.proceed(["get", self.jailName, "actionmethods",
|
||||
action])),
|
||||
[0, ['ban', 'start', 'stop', 'testmethod', 'unban']])
|
||||
self.assertEqual(
|
||||
self.transm.proceed(["set", self.jailName, "action", action,
|
||||
"testmethod", '{"text": "world!"}']),
|
||||
(0, 'Hello world! value'))
|
||||
self.assertEqual(
|
||||
self.transm.proceed(["set", self.jailName, "action", action,
|
||||
"opt1", "another value"]),
|
||||
(0, 'another value'))
|
||||
self.assertEqual(
|
||||
self.transm.proceed(["set", self.jailName, "action", action,
|
||||
"testmethod", '{"text": "world!"}']),
|
||||
(0, 'Hello world! another value'))
|
||||
|
||||
def testNOK(self):
|
||||
self.assertEqual(self.transm.proceed(["INVALID", "COMMAND"])[0],1)
|
||||
|
|
|
@ -172,7 +172,7 @@ def gatherTests(regexps=None, no_network=False):
|
|||
tests.addTest(unittest.makeSuite(servertestcase.Transmitter))
|
||||
tests.addTest(unittest.makeSuite(servertestcase.JailTests))
|
||||
tests.addTest(unittest.makeSuite(servertestcase.RegexTests))
|
||||
tests.addTest(unittest.makeSuite(actiontestcase.ExecuteAction))
|
||||
tests.addTest(unittest.makeSuite(actiontestcase.CommandActionTest))
|
||||
tests.addTest(unittest.makeSuite(actionstestcase.ExecuteActions))
|
||||
# FailManager
|
||||
tests.addTest(unittest.makeSuite(failmanagertestcase.AddFailure))
|
||||
|
|
|
@ -113,7 +113,7 @@ uses systemd python library to access the systemd journal. Specifying \fBlogpath
|
|||
will try to use the following backends, in order: pyinotify, gamin, polling
|
||||
.PP
|
||||
.SS Actions
|
||||
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, and in the case of python actions, the ".py" file extension is stripped. Where multiple of the same action are to be used, the \fBactname\fR option can be assigned to the action to avoid duplication e.g.:
|
||||
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, and in the case of Python actions, the ".py" file extension is stripped. Where multiple of the same action are to be used, the \fBactname\fR option can be assigned to the action to avoid duplication e.g.:
|
||||
.PP
|
||||
.nf
|
||||
[ssh-iptables-ipset]
|
||||
|
@ -163,7 +163,7 @@ two commands to be executed.
|
|||
actionban = iptables -I fail2ban-<name> --source <ip> -j DROP
|
||||
echo ip=<ip>, match=<match>, time=<time> >> /var/log/fail2ban.log
|
||||
.TP
|
||||
Python based actions can also be used, where the file name must be \fI[actionname].py\fR. The python file must contain a variable \fIAction\fR which points to python class. This class must implement a minimum interface as described by \fIfail2ban.server.action.ActionBase\fR, which can be inherited from to ease implementation.
|
||||
Python based actions can also be used, where the file name must be \fI[actionname].py\fR. The Python file must contain a variable \fIAction\fR which points to Python class. This class must implement a minimum interface as described by \fIfail2ban.server.action.ActionBase\fR, which can be inherited from to ease implementation.
|
||||
|
||||
.SS "Action Tags"
|
||||
The following tags are substituted in the actionban, actionunban and actioncheck (when called before actionban/actionunban) commands.
|
||||
|
|
Loading…
Reference in New Issue