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 sys
|
||||||
import socket
|
import socket
|
||||||
|
@ -52,94 +70,112 @@ Matches for %(ip)s for jail %(jailname)s:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class SMTPAction(ActionBase):
|
class SMTPAction(ActionBase):
|
||||||
|
"""Fail2Ban action which sends emails to inform on jail starting,
|
||||||
|
stopping and bans.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, jail, name, host="localhost", user=None, password=None,
|
self, jail, actionname, host="localhost", user=None, password=None,
|
||||||
sendername="Fail2Ban", sender="fail2ban", dest="root", matches=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
|
self.host = host
|
||||||
#TODO: self.ssl = ssl
|
#TODO: self.ssl = ssl
|
||||||
|
|
||||||
self.user = user
|
self.user = user
|
||||||
self.password =password
|
self.password =password
|
||||||
|
|
||||||
self.fromname = sendername
|
self.fromname = sendername
|
||||||
self.fromaddr = sender
|
self.fromaddr = sender
|
||||||
self.toaddr = dest
|
self.toaddr = dest
|
||||||
|
|
||||||
self.matches = matches
|
self.matches = matches
|
||||||
|
|
||||||
self.message_values = CallingMap(
|
self.message_values = CallingMap(
|
||||||
jailname = self.jail.getName(), # Doesn't change
|
jailname = self._jail.getName(), # Doesn't change
|
||||||
hostname = socket.gethostname,
|
hostname = socket.gethostname,
|
||||||
bantime = self.jail.getAction().getBanTime,
|
bantime = self._jail.getAction().getBanTime,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _sendMessage(self, subject, text):
|
def _sendMessage(self, subject, text):
|
||||||
msg = MIMEText(text)
|
msg = MIMEText(text)
|
||||||
msg['Subject'] = subject
|
msg['Subject'] = subject
|
||||||
msg['From'] = formataddr((self.fromname, self.fromaddr))
|
msg['From'] = formataddr((self.fromname, self.fromaddr))
|
||||||
msg['To'] = self.toaddr
|
msg['To'] = self.toaddr
|
||||||
msg['Date'] = formatdate()
|
msg['Date'] = formatdate()
|
||||||
|
|
||||||
smtp = smtplib.SMTP()
|
smtp = smtplib.SMTP()
|
||||||
try:
|
try:
|
||||||
self.logSys.debug("Connected to SMTP '%s', response: %i: %s",
|
self._logSys.debug("Connected to SMTP '%s', response: %i: %s",
|
||||||
self.host, *smtp.connect(self.host))
|
self.host, *smtp.connect(self.host))
|
||||||
if self.user and self.password:
|
if self.user and self.password:
|
||||||
smtp.login(self.user, self.password)
|
smtp.login(self.user, self.password)
|
||||||
failed_recipients = smtp.sendmail(
|
failed_recipients = smtp.sendmail(
|
||||||
self.fromaddr, self.toaddr, msg.as_string())
|
self.fromaddr, self.toaddr, msg.as_string())
|
||||||
except smtplib.SMTPConnectError:
|
except smtplib.SMTPConnectError:
|
||||||
self.logSys.error("Error connecting to host '%s'", self.host)
|
self._logSys.error("Error connecting to host '%s'", self.host)
|
||||||
raise
|
raise
|
||||||
except smtplib.SMTPAuthenticationError:
|
except smtplib.SMTPAuthenticationError:
|
||||||
self.logSys.error(
|
self._logSys.error(
|
||||||
"Failed to authenticate with host '%s' user '%s'",
|
"Failed to authenticate with host '%s' user '%s'",
|
||||||
self.host, self.user)
|
self.host, self.user)
|
||||||
raise
|
raise
|
||||||
except smtplib.SMTPException:
|
except smtplib.SMTPException:
|
||||||
self.logSys.error(
|
self._logSys.error(
|
||||||
"Error sending mail to host '%s' from '%s' to '%s'",
|
"Error sending mail to host '%s' from '%s' to '%s'",
|
||||||
self.host, self.fromaddr, self.toaddr)
|
self.host, self.fromaddr, self.toaddr)
|
||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
if failed_recipients:
|
if failed_recipients:
|
||||||
self.logSys.warning(
|
self._logSys.warning(
|
||||||
"Email to '%s' failed to following recipients: %r",
|
"Email to '%s' failed to following recipients: %r",
|
||||||
self.toaddr, failed_recipients)
|
self.toaddr, failed_recipients)
|
||||||
self.logSys.debug("Email '%s' successfully sent", subject)
|
self._logSys.debug("Email '%s' successfully sent", subject)
|
||||||
finally:
|
finally:
|
||||||
try:
|
try:
|
||||||
self.logSys.debug("Disconnected from '%s', response %i: %s",
|
self._logSys.debug("Disconnected from '%s', response %i: %s",
|
||||||
self.host, *smtp.quit())
|
self.host, *smtp.quit())
|
||||||
except smtplib.SMTPServerDisconnected:
|
except smtplib.SMTPServerDisconnected:
|
||||||
pass # Not connected
|
pass # Not connected
|
||||||
|
|
||||||
def execActionStart(self):
|
def start(self):
|
||||||
self._sendMessage(
|
"""Sends email to recipients informing that the jail has started.
|
||||||
"[Fail2Ban] %(jailname)s: started on %(hostname)s" %
|
"""
|
||||||
self.message_values,
|
self._sendMessage(
|
||||||
messages['start'] % self.message_values)
|
"[Fail2Ban] %(jailname)s: started on %(hostname)s" %
|
||||||
|
self.message_values,
|
||||||
|
messages['start'] % self.message_values)
|
||||||
|
|
||||||
def execActionStop(self):
|
def stop(self):
|
||||||
self._sendMessage(
|
"""Sends email to recipients informing that the jail has stopped.
|
||||||
"[Fail2Ban] %(jailname)s: stopped on %(hostname)s" %
|
"""
|
||||||
self.message_values,
|
self._sendMessage(
|
||||||
messages['stop'] % self.message_values)
|
"[Fail2Ban] %(jailname)s: stopped on %(hostname)s" %
|
||||||
|
self.message_values,
|
||||||
|
messages['stop'] % self.message_values)
|
||||||
|
|
||||||
def execActionBan(self, aInfo):
|
def ban(self, aInfo):
|
||||||
aInfo.update(self.message_values)
|
"""Sends email to recipients informing that ban has occurred and
|
||||||
message = "".join([
|
has associated information about the ban.
|
||||||
messages['ban']['head'],
|
"""
|
||||||
messages['ban'].get(self.matches, ""),
|
aInfo.update(self.message_values)
|
||||||
messages['ban']['tail']
|
message = "".join([
|
||||||
])
|
messages['ban']['head'],
|
||||||
self._sendMessage(
|
messages['ban'].get(self.matches, ""),
|
||||||
"[Fail2Ban] %(jailname)s: banned %(ip)s from %(hostname)s" %
|
messages['ban']['tail']
|
||||||
aInfo,
|
])
|
||||||
message % aInfo)
|
self._sendMessage(
|
||||||
|
"[Fail2Ban] %(jailname)s: banned %(ip)s from %(hostname)s" %
|
||||||
|
aInfo,
|
||||||
|
message % aInfo)
|
||||||
|
|
||||||
Action = SMTPAction
|
Action = SMTPAction
|
||||||
|
|
|
@ -59,22 +59,20 @@ class ActionReader(DefinitionInitConfigReader):
|
||||||
head = ["set", self._jailName]
|
head = ["set", self._jailName]
|
||||||
stream = list()
|
stream = list()
|
||||||
stream.append(head + ["addaction", self._name])
|
stream.append(head + ["addaction", self._name])
|
||||||
|
head.extend(["action", self._name])
|
||||||
for opt in self._opts:
|
for opt in self._opts:
|
||||||
if opt == "actionstart":
|
if opt == "actionstart":
|
||||||
stream.append(head + ["actionstart", self._name, self._opts[opt]])
|
stream.append(head + ["actionstart", self._opts[opt]])
|
||||||
elif opt == "actionstop":
|
elif opt == "actionstop":
|
||||||
stream.append(head + ["actionstop", self._name, self._opts[opt]])
|
stream.append(head + ["actionstop", self._opts[opt]])
|
||||||
elif opt == "actioncheck":
|
elif opt == "actioncheck":
|
||||||
stream.append(head + ["actioncheck", self._name, self._opts[opt]])
|
stream.append(head + ["actioncheck", self._opts[opt]])
|
||||||
elif opt == "actionban":
|
elif opt == "actionban":
|
||||||
stream.append(head + ["actionban", self._name, self._opts[opt]])
|
stream.append(head + ["actionban", self._opts[opt]])
|
||||||
elif opt == "actionunban":
|
elif opt == "actionunban":
|
||||||
stream.append(head + ["actionunban", self._name, self._opts[opt]])
|
stream.append(head + ["actionunban", self._opts[opt]])
|
||||||
if self._initOpts:
|
if self._initOpts:
|
||||||
if "timeout" in self._initOpts:
|
|
||||||
stream.append(head + ["timeout", self._name, self._opts["timeout"]])
|
|
||||||
# cInfo
|
|
||||||
for p in self._initOpts:
|
for p in self._initOpts:
|
||||||
stream.append(head + ["setcinfo", self._name, p, self._initOpts[p]])
|
stream.append(head + [p, self._initOpts[p]])
|
||||||
|
|
||||||
return stream
|
return stream
|
||||||
|
|
|
@ -165,7 +165,23 @@ class Beautifier:
|
||||||
msg = "No actions for jail %s" % inC[1]
|
msg = "No actions for jail %s" % inC[1]
|
||||||
else:
|
else:
|
||||||
msg = "The jail %s has the following actions:\n" % inC[1]
|
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:
|
except Exception:
|
||||||
logSys.warning("Beautifier error. Please report the error")
|
logSys.warning("Beautifier error. Please report the error")
|
||||||
logSys.error("Beautify " + `response` + " with " + `self.__inputCmd` +
|
logSys.error("Beautify " + `response` + " with " + `self.__inputCmd` +
|
||||||
|
|
|
@ -76,16 +76,18 @@ protocol = [
|
||||||
["set <JAIL> unbanip <IP>", "manually Unban <IP> in <JAIL>"],
|
["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> 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> 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> 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 <NAME> from <JAIL>"],
|
["set <JAIL> delaction <ACT>", "removes the action <ACT> from <JAIL>"],
|
||||||
["set <JAIL> setcinfo <ACT> <KEY> <VALUE>", "sets <VALUE> for <KEY> of the action <NAME> for <JAIL>"],
|
["", "COMMAND ACTION CONFIGURATION", ""],
|
||||||
["set <JAIL> delcinfo <ACT> <KEY>", "removes <KEY> for the action <NAME> for <JAIL>"],
|
["set <JAIL> action <ACT> actionstart <CMD>", "sets the start command <CMD> of the action <ACT> for <JAIL>"],
|
||||||
["set <JAIL> timeout <ACT> <TIMEOUT>", "sets <TIMEOUT> as the command timeout in seconds for the action <ACT> for <JAIL>"],
|
["set <JAIL> action <ACT> actionstop <CMD>", "sets the stop command <CMD> of the action <ACT> for <JAIL>"],
|
||||||
["set <JAIL> actionstart <ACT> <CMD>", "sets the start 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> actionstop <ACT> <CMD>", "sets the stop 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> actioncheck <ACT> <CMD>", "sets the check 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> actionban <ACT> <CMD>", "sets the ban 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>"],
|
||||||
["set <JAIL> actionunban <ACT> <CMD>", "sets the unban command <CMD> of 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", ""],
|
['', "JAIL INFORMATION", ""],
|
||||||
["get <JAIL> logpath", "gets the list of the monitored files for <JAIL>"],
|
["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>"],
|
["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> 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> addaction", "gets the last action which has been added for <JAIL>"],
|
||||||
["get <JAIL> actions", "gets a list of actions 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>"],
|
["", "COMMAND ACTION INFORMATION",""],
|
||||||
["get <JAIL> actionstop <ACT>", "gets the stop command for the action <ACT> for <JAIL>"],
|
["get <JAIL> action <ACT> actionstart", "gets the start command for the action <ACT> for <JAIL>"],
|
||||||
["get <JAIL> actioncheck <ACT>", "gets the check command for the action <ACT> for <JAIL>"],
|
["get <JAIL> action <ACT> actionstop", "gets the stop command for the action <ACT> for <JAIL>"],
|
||||||
["get <JAIL> actionban <ACT>", "gets the ban command for the action <ACT> for <JAIL>"],
|
["get <JAIL> action <ACT> actioncheck", "gets the check command for the action <ACT> for <JAIL>"],
|
||||||
["get <JAIL> actionunban <ACT>", "gets the unban command for the action <ACT> for <JAIL>"],
|
["get <JAIL> action <ACT> actionban", "gets the ban 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> action <ACT> actionunban", "gets the unban command for the action <ACT> for <JAIL>"],
|
||||||
["get <JAIL> timeout <ACT>", "gets the command timeout in seconds 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
|
print
|
||||||
firstHeading = True
|
firstHeading = True
|
||||||
first = True
|
first = True
|
||||||
if len(m[0]) > MARGIN+INDENT:
|
if len(m[0]) >= MARGIN:
|
||||||
m[1] = ' ' * WIDTH + m[1]
|
m[1] = ' ' * WIDTH + m[1]
|
||||||
for n in textwrap.wrap(m[1], WIDTH, drop_whitespace=False):
|
for n in textwrap.wrap(m[1], WIDTH, drop_whitespace=False):
|
||||||
if first:
|
if first:
|
||||||
line = ' ' * INDENT + m[0] + ' ' * (MARGIN - len(m[0])) + n
|
line = ' ' * INDENT + m[0] + ' ' * (MARGIN - len(m[0])) + n.strip()
|
||||||
first = False
|
first = False
|
||||||
else:
|
else:
|
||||||
line = ' ' * (INDENT + MARGIN) + n
|
line = ' ' * (INDENT + MARGIN) + n.strip()
|
||||||
print line.rstrip()
|
print line
|
||||||
|
|
||||||
##
|
##
|
||||||
# Prints the protocol in a "mediawiki" format.
|
# 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"))
|
for name, num in signal.__dict__.iteritems() if name.startswith("SIG"))
|
||||||
|
|
||||||
class CallingMap(MutableMapping):
|
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):
|
def __init__(self, *args, **kwargs):
|
||||||
self.data = dict(*args, **kwargs)
|
self.data = dict(*args, **kwargs)
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
|
@ -66,262 +74,203 @@ class CallingMap(MutableMapping):
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(self.data)
|
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):
|
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
|
__metaclass__ = ABCMeta
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def __subclasshook__(cls, C):
|
def __subclasshook__(cls, C):
|
||||||
required = (
|
required = (
|
||||||
"getName",
|
"start",
|
||||||
"execActionStart",
|
"stop",
|
||||||
"execActionStop",
|
"ban",
|
||||||
"execActionBan",
|
"unban",
|
||||||
"execActionUnban",
|
|
||||||
)
|
)
|
||||||
for method in required:
|
for method in required:
|
||||||
if not callable(getattr(C, method, None)):
|
if not callable(getattr(C, method, None)):
|
||||||
return False
|
return False
|
||||||
return True
|
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._jail = jail
|
||||||
self._name = name
|
self._actionname = actionname
|
||||||
self._logSys = logging.getLogger(
|
self._logSys = logging.getLogger(
|
||||||
'%s.%s' % (__name__, self.__class__.__name__))
|
'%s.%s' % (__name__, self.__class__.__name__))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def jail(self):
|
def actionname(self):
|
||||||
return self._jail
|
"""The name of the action, which should not change in the
|
||||||
|
lifetime of the action."""
|
||||||
|
return self._actionname
|
||||||
|
|
||||||
@property
|
def start(self):
|
||||||
def logSys(self):
|
"""Executed when the jail/action starts."""
|
||||||
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):
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def execActionBan(self, aInfo):
|
def stop(self):
|
||||||
|
"""Executed when the jail/action stops or action is deleted.
|
||||||
|
"""
|
||||||
pass
|
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
|
pass
|
||||||
|
|
||||||
def execActionStop(self):
|
def unban(self, aInfo):
|
||||||
|
"""Executed when a ban expires. `aInfo` as per execActionBan.
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class CommandAction(ActionBase):
|
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):
|
def __init__(self, jail, actionname):
|
||||||
super(CommandAction, self).__init__(None, name)
|
super(CommandAction, self).__init__(jail, actionname)
|
||||||
self.__timeout = 60
|
self.timeout = 60
|
||||||
self.__cInfo = dict()
|
|
||||||
## Command executed in order to initialize the system.
|
## Command executed in order to initialize the system.
|
||||||
self.__actionStart = ''
|
self.actionstart = ''
|
||||||
## Command executed when an IP address gets banned.
|
## Command executed when an IP address gets banned.
|
||||||
self.__actionBan = ''
|
self.actionban = ''
|
||||||
## Command executed when an IP address gets removed.
|
## Command executed when an IP address gets removed.
|
||||||
self.__actionUnban = ''
|
self.actionunban = ''
|
||||||
## Command executed in order to check requirements.
|
## Command executed in order to check requirements.
|
||||||
self.__actionCheck = ''
|
self.actioncheck = ''
|
||||||
## Command executed in order to stop the system.
|
## Command executed in order to stop the system.
|
||||||
self.__actionStop = ''
|
self.actionstop = ''
|
||||||
logSys.debug("Created Action")
|
self._logSys.debug("Created %s" % self.__class__)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def __subclasshook__(cls, C):
|
def __subclasshook__(cls, C):
|
||||||
return NotImplemented # Standard checks
|
return NotImplemented # Standard checks
|
||||||
|
|
||||||
##
|
@property
|
||||||
# Sets the timeout period for commands.
|
def timeout(self):
|
||||||
#
|
"""Timeout period in seconds for execution of commands
|
||||||
# @param timeout timeout period in seconds
|
"""
|
||||||
|
return self._timeout
|
||||||
def setTimeout(self, timeout):
|
@timeout.setter
|
||||||
self.__timeout = int(timeout)
|
def timeout(self, timeout):
|
||||||
logSys.debug("Set action %s timeout = %i" % (self.getName(), timeout))
|
self._timeout = int(timeout)
|
||||||
|
self._logSys.debug("Set action %s timeout = %i" %
|
||||||
##
|
(self.actionname, self.timeout))
|
||||||
# Returns the action timeout period for commands.
|
|
||||||
#
|
@property
|
||||||
# @return the timeout period in seconds
|
def _properties(self):
|
||||||
|
return dict(
|
||||||
def getTimeout(self):
|
(key, getattr(self, key))
|
||||||
return self.__timeout
|
for key in dir(self)
|
||||||
|
if not key.startswith("_") and not callable(getattr(self, key)))
|
||||||
##
|
|
||||||
# Sets a "CInfo".
|
@property
|
||||||
#
|
def actionstart(self):
|
||||||
# CInfo are statically defined properties. They can be definied by
|
"""The command executed on start of the jail/action.
|
||||||
# the user and are used to set e-mail addresses, port, host or
|
"""
|
||||||
# anything that should not change during the life of the server.
|
return self._actionstart
|
||||||
#
|
@actionstart.setter
|
||||||
# @param key the property name
|
def actionstart(self, value):
|
||||||
# @param value the property value
|
self._actionstart = value
|
||||||
|
self._logSys.debug("Set actionstart = %s" % value)
|
||||||
def setCInfo(self, key, value):
|
|
||||||
self.__cInfo[key] = value
|
def start(self):
|
||||||
|
"""Executes the "actionstart" command.
|
||||||
##
|
Replace the tags in the action command with actions properties
|
||||||
# Returns a "CInfo".
|
and executes the resulting command.
|
||||||
#
|
"""
|
||||||
# @param key the property name
|
if (self._properties and
|
||||||
|
not self.substituteRecursiveTags(self._properties)):
|
||||||
def getCInfo(self, key):
|
self._logSys.error(
|
||||||
return self.__cInfo[key]
|
"properties contain self referencing definitions "
|
||||||
|
"and cannot be resolved")
|
||||||
##
|
raise RuntimeError("Error starting action")
|
||||||
# Removes a "CInfo".
|
startCmd = self.replaceTag(self.actionstart, self._properties)
|
||||||
#
|
if not self.executeCmd(startCmd, self.timeout):
|
||||||
# @param key the property name
|
raise RuntimeError("Error starting action")
|
||||||
|
|
||||||
def delCInfo(self, key):
|
@property
|
||||||
del self.__cInfo[key]
|
def actionban(self):
|
||||||
|
"""The command used when a ban occurs.
|
||||||
##
|
"""
|
||||||
# Set the "start" command.
|
return self._actionban
|
||||||
#
|
@actionban.setter
|
||||||
# @param value the command
|
def actionban(self, value):
|
||||||
|
self._actionban = value
|
||||||
def setActionStart(self, value):
|
self._logSys.debug("Set actionban = %s" % value)
|
||||||
self.__actionStart = value
|
|
||||||
logSys.debug("Set actionStart = %s" % value)
|
def ban(self, aInfo):
|
||||||
|
"""Executes the "actionban" command.
|
||||||
##
|
Replace the tags in the action command with actions properties
|
||||||
# Get the "start" command.
|
and ban information, and executes the resulting command.
|
||||||
#
|
"""
|
||||||
# @return the command
|
if not self._processCmd(self.actionban, aInfo):
|
||||||
|
raise RuntimeError("Error banning %(ip)s" % aInfo)
|
||||||
def getActionStart(self):
|
|
||||||
return self.__actionStart
|
@property
|
||||||
|
def actionunban(self):
|
||||||
##
|
"""The command used when an unban occurs.
|
||||||
# Executes the action "start" command.
|
"""
|
||||||
#
|
return self._actionunban
|
||||||
# Replaces the tags in the action command with value of "cInfo"
|
@actionunban.setter
|
||||||
# and executes the resulting command.
|
def actionunban(self, value):
|
||||||
#
|
self._actionunban = value
|
||||||
# @return True if the command succeeded
|
self._logSys.debug("Set actionunban = %s" % value)
|
||||||
|
|
||||||
def execActionStart(self):
|
def unban(self, aInfo):
|
||||||
if self.__cInfo:
|
"""Executes the "actionunban" command.
|
||||||
if not self.substituteRecursiveTags(self.__cInfo):
|
Replace the tags in the action command with actions properties
|
||||||
logSys.error("Cinfo/definitions contain self referencing definitions and cannot be resolved")
|
and ban information, and executes the resulting command.
|
||||||
return False
|
"""
|
||||||
startCmd = self.replaceTag(self.__actionStart, self.__cInfo)
|
if not self._processCmd(self.actionunban, aInfo):
|
||||||
return self.executeCmd(startCmd, self.__timeout)
|
raise RuntimeError("Error unbanning %(ip)s" % aInfo)
|
||||||
|
|
||||||
##
|
@property
|
||||||
# Set the "ban" command.
|
def actioncheck(self):
|
||||||
#
|
"""The command used to check correct environment in place for
|
||||||
# @param value the command
|
ban action to take place.
|
||||||
|
"""
|
||||||
def setActionBan(self, value):
|
return self._actioncheck
|
||||||
self.__actionBan = value
|
@actioncheck.setter
|
||||||
logSys.debug("Set actionBan = %s" % value)
|
def actioncheck(self, value):
|
||||||
|
self._actioncheck = value
|
||||||
##
|
self._logSys.debug("Set actioncheck = %s" % value)
|
||||||
# Get the "ban" command.
|
|
||||||
#
|
@property
|
||||||
# @return the command
|
def actionstop(self):
|
||||||
|
"""The command executed when the jail/actions stops.
|
||||||
def getActionBan(self):
|
"""
|
||||||
return self.__actionBan
|
return self._actionstop
|
||||||
|
@actionstop.setter
|
||||||
##
|
def actionstop(self, value):
|
||||||
# Executes the action "ban" command.
|
self._actionstop = value
|
||||||
#
|
self._logSys.debug("Set actionstop = %s" % value)
|
||||||
# @return True if the command succeeded
|
|
||||||
|
def stop(self):
|
||||||
def execActionBan(self, aInfo):
|
"""Executes the "actionstop" command.
|
||||||
return self.__processCmd(self.__actionBan, aInfo)
|
Replace the tags in the action command with actions properties
|
||||||
|
and executes the resulting command.
|
||||||
##
|
"""
|
||||||
# Set the "unban" command.
|
stopCmd = self.replaceTag(self.actionstop, self._properties)
|
||||||
#
|
if not self.executeCmd(stopCmd, self.timeout):
|
||||||
# @param value the command
|
raise RuntimeError("Error stopping action")
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Sort out tag definitions within other tags
|
# Sort out tag definitions within other tags
|
||||||
|
@ -397,21 +346,21 @@ class CommandAction(ActionBase):
|
||||||
# @param aInfo Dynamic properties
|
# @param aInfo Dynamic properties
|
||||||
# @return True if the command succeeded
|
# @return True if the command succeeded
|
||||||
|
|
||||||
def __processCmd(self, cmd, aInfo = None):
|
def _processCmd(self, cmd, aInfo = None):
|
||||||
""" Executes an OS command.
|
""" Executes an OS command.
|
||||||
"""
|
"""
|
||||||
if cmd == "":
|
if cmd == "":
|
||||||
logSys.debug("Nothing to do")
|
self._logSys.debug("Nothing to do")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
checkCmd = self.replaceTag(self.__actionCheck, self.__cInfo)
|
checkCmd = self.replaceTag(self.actioncheck, self._properties)
|
||||||
if not self.executeCmd(checkCmd, self.__timeout):
|
if not self.executeCmd(checkCmd, self.timeout):
|
||||||
logSys.error("Invariant check failed. Trying to restore a sane" +
|
self._logSys.error(
|
||||||
" environment")
|
"Invariant check failed. Trying to restore a sane environment")
|
||||||
self.execActionStop()
|
self.stop()
|
||||||
self.execActionStart()
|
self.start()
|
||||||
if not self.executeCmd(checkCmd, self.__timeout):
|
if not self.executeCmd(checkCmd, self.timeout):
|
||||||
logSys.fatal("Unable to restore environment")
|
self._logSys.fatal("Unable to restore environment")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Replace tags
|
# Replace tags
|
||||||
|
@ -421,9 +370,9 @@ class CommandAction(ActionBase):
|
||||||
realCmd = cmd
|
realCmd = cmd
|
||||||
|
|
||||||
# Replace static fields
|
# 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.
|
# Executes a command.
|
||||||
|
@ -495,5 +444,6 @@ class CommandAction(ActionBase):
|
||||||
if msg:
|
if msg:
|
||||||
logSys.info("HINT on %i: %s"
|
logSys.info("HINT on %i: %s"
|
||||||
% (retcode, msg % locals()))
|
% (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):
|
def addAction(self, name, pythonModule=None, initOpts=None):
|
||||||
# Check is action name already exists
|
# 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)
|
raise ValueError("Action %s already exists" % name)
|
||||||
if pythonModule is None:
|
if pythonModule is None:
|
||||||
action = CommandAction(name)
|
action = CommandAction(self.jail, name)
|
||||||
else:
|
else:
|
||||||
pythonModuleName = os.path.basename(pythonModule.strip(".py"))
|
pythonModuleName = os.path.basename(pythonModule.strip(".py"))
|
||||||
customActionModule = imp.load_source(
|
customActionModule = imp.load_source(
|
||||||
|
@ -91,7 +91,7 @@ class Actions(JailThread):
|
||||||
|
|
||||||
def delAction(self, name):
|
def delAction(self, name):
|
||||||
for action in self.__actions:
|
for action in self.__actions:
|
||||||
if action.getName() == name:
|
if action.actionname == name:
|
||||||
self.__actions.remove(action)
|
self.__actions.remove(action)
|
||||||
return
|
return
|
||||||
raise KeyError("Invalid Action name: %s" % name)
|
raise KeyError("Invalid Action name: %s" % name)
|
||||||
|
@ -106,7 +106,7 @@ class Actions(JailThread):
|
||||||
|
|
||||||
def getAction(self, name):
|
def getAction(self, name):
|
||||||
for action in self.__actions:
|
for action in self.__actions:
|
||||||
if action.getName() == name:
|
if action.actionname == name:
|
||||||
return action
|
return action
|
||||||
raise KeyError("Invalid Action name")
|
raise KeyError("Invalid Action name")
|
||||||
|
|
||||||
|
@ -116,9 +116,7 @@ class Actions(JailThread):
|
||||||
# @return The last defined action.
|
# @return The last defined action.
|
||||||
|
|
||||||
def getLastAction(self):
|
def getLastAction(self):
|
||||||
action = self.__actions.pop()
|
return self.__actions[-1]
|
||||||
self.__actions.append(action)
|
|
||||||
return action
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Returns the list of actions
|
# Returns the list of actions
|
||||||
|
@ -169,10 +167,10 @@ class Actions(JailThread):
|
||||||
self.setActive(True)
|
self.setActive(True)
|
||||||
for action in self.__actions:
|
for action in self.__actions:
|
||||||
try:
|
try:
|
||||||
action.execActionStart()
|
action.start()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logSys.error("Failed to start jail '%s' action '%s': %s",
|
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():
|
while self._isActive():
|
||||||
if not self.getIdle():
|
if not self.getIdle():
|
||||||
#logSys.debug(self.jail.getName() + ": action")
|
#logSys.debug(self.jail.getName() + ": action")
|
||||||
|
@ -185,10 +183,10 @@ class Actions(JailThread):
|
||||||
self.__flushBan()
|
self.__flushBan()
|
||||||
for action in self.__actions:
|
for action in self.__actions:
|
||||||
try:
|
try:
|
||||||
action.execActionStop()
|
action.stop()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logSys.error("Failed to stop jail '%s' action '%s': %s",
|
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")
|
logSys.debug(self.jail.getName() + ": action terminated")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -225,11 +223,11 @@ class Actions(JailThread):
|
||||||
logSys.warning("[%s] Ban %s" % (self.jail.getName(), aInfo["ip"]))
|
logSys.warning("[%s] Ban %s" % (self.jail.getName(), aInfo["ip"]))
|
||||||
for action in self.__actions:
|
for action in self.__actions:
|
||||||
try:
|
try:
|
||||||
action.execActionBan(aInfo)
|
action.ban(aInfo)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logSys.error(
|
logSys.error(
|
||||||
"Failed to execute ban jail '%s' action '%s': %s",
|
"Failed to execute ban jail '%s' action '%s': %s",
|
||||||
self.jail.getName(), action.getName(), e)
|
self.jail.getName(), action.actionname, e)
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
logSys.info("[%s] %s already banned" % (self.jail.getName(),
|
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"]))
|
logSys.warning("[%s] Unban %s" % (self.jail.getName(), aInfo["ip"]))
|
||||||
for action in self.__actions:
|
for action in self.__actions:
|
||||||
try:
|
try:
|
||||||
action.execActionUnban(aInfo)
|
action.unban(aInfo)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logSys.error(
|
logSys.error(
|
||||||
"Failed to execute unban jail '%s' action '%s': %s",
|
"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):
|
def delAction(self, name, value):
|
||||||
self.__jails.getAction(name).delAction(value)
|
self.__jails.getAction(name).delAction(value)
|
||||||
|
|
||||||
def setCInfo(self, name, actionName, key, value):
|
def getAction(self, name, value):
|
||||||
action = self.__jails.getAction(name).getAction(actionName)
|
return self.__jails.getAction(name).getAction(value)
|
||||||
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 setBanTime(self, name, value):
|
def setBanTime(self, name, value):
|
||||||
self.__jails.getAction(name).setBanTime(value)
|
self.__jails.getAction(name).setBanTime(value)
|
||||||
|
@ -324,90 +306,6 @@ class Server:
|
||||||
def getBanTime(self, name):
|
def getBanTime(self, name):
|
||||||
return self.__jails.getAction(name).getBanTime()
|
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
|
# Status
|
||||||
def status(self):
|
def status(self):
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -233,52 +233,22 @@ class Transmitter:
|
||||||
if len(command) > 3:
|
if len(command) > 3:
|
||||||
args.extend([command[3], json.loads(command[4])])
|
args.extend([command[3], json.loads(command[4])])
|
||||||
self.__server.addAction(name, *args)
|
self.__server.addAction(name, *args)
|
||||||
return self.__server.getLastAction(name).getName()
|
return self.__server.getLastAction(name).actionname
|
||||||
elif command[1] == "delaction":
|
elif command[1] == "delaction":
|
||||||
value = command[2]
|
value = command[2]
|
||||||
self.__server.delAction(name, value)
|
self.__server.delAction(name, value)
|
||||||
return None
|
return None
|
||||||
elif command[1] == "setcinfo":
|
elif command[1] == "action":
|
||||||
act = command[2]
|
actionname = command[2]
|
||||||
key = command[3]
|
actionkey = command[3]
|
||||||
value = " ".join(command[4:])
|
action = self.__server.getAction(name, actionname)
|
||||||
self.__server.setCInfo(name, act, key, value)
|
if callable(getattr(action, actionkey, None)):
|
||||||
return self.__server.getCInfo(name, act, key)
|
actionvalue = json.loads(command[4]) if len(command)>4 else {}
|
||||||
elif command[1] == "delcinfo":
|
return getattr(action, actionkey)(**actionvalue)
|
||||||
act = command[2]
|
else:
|
||||||
key = command[3]
|
actionvalue = command[4]
|
||||||
self.__server.delCInfo(name, act, key)
|
setattr(action, actionkey, actionvalue)
|
||||||
return None
|
return getattr(action, actionkey)
|
||||||
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)
|
|
||||||
raise Exception("Invalid command (no set action or not yet implemented)")
|
raise Exception("Invalid command (no set action or not yet implemented)")
|
||||||
|
|
||||||
def __commandGet(self, command):
|
def __commandGet(self, command):
|
||||||
|
@ -330,31 +300,28 @@ class Transmitter:
|
||||||
elif command[1] == "bantime":
|
elif command[1] == "bantime":
|
||||||
return self.__server.getBanTime(name)
|
return self.__server.getBanTime(name)
|
||||||
elif command[1] == "actions":
|
elif command[1] == "actions":
|
||||||
return self.__server.getActions(name)
|
return [action.actionname
|
||||||
|
for action in self.__server.getActions(name)]
|
||||||
elif command[1] == "addaction":
|
elif command[1] == "addaction":
|
||||||
return self.__server.getLastAction(name).getName()
|
return self.__server.getLastAction(name).actionname
|
||||||
elif command[1] == "actionstart":
|
elif command[1] == "action":
|
||||||
act = command[2]
|
actionname = command[2]
|
||||||
return self.__server.getActionStart(name, act)
|
actionvalue = command[3]
|
||||||
elif command[1] == "actionstop":
|
action = self.__server.getAction(name, actionname)
|
||||||
act = command[2]
|
return getattr(action, actionvalue)
|
||||||
return self.__server.getActionStop(name, act)
|
elif command[1] == "actionproperties":
|
||||||
elif command[1] == "actioncheck":
|
actionname = command[2]
|
||||||
act = command[2]
|
action = self.__server.getAction(name, actionname)
|
||||||
return self.__server.getActionCheck(name, act)
|
return [
|
||||||
elif command[1] == "actionban":
|
key for key in dir(action)
|
||||||
act = command[2]
|
if not key.startswith("_") and
|
||||||
return self.__server.getActionBan(name, act)
|
not callable(getattr(action, key))]
|
||||||
elif command[1] == "actionunban":
|
elif command[1] == "actionmethods":
|
||||||
act = command[2]
|
actionname = command[2]
|
||||||
return self.__server.getActionUnban(name, act)
|
action = self.__server.getAction(name, actionname)
|
||||||
elif command[1] == "cinfo":
|
return [
|
||||||
act = command[2]
|
key for key in dir(action)
|
||||||
key = command[3]
|
if not key.startswith("_") and callable(getattr(action, key))]
|
||||||
return self.__server.getCInfo(name, act, key)
|
|
||||||
elif command[1] == "timeout":
|
|
||||||
act = command[2]
|
|
||||||
return self.__server.getActionTimeout(name, act)
|
|
||||||
raise Exception("Invalid command (no get action or not yet implemented)")
|
raise Exception("Invalid command (no get action or not yet implemented)")
|
||||||
|
|
||||||
def status(self, command):
|
def status(self, command):
|
||||||
|
|
|
@ -49,11 +49,11 @@ class ExecuteActions(LogCaptureTestCase):
|
||||||
def defaultActions(self):
|
def defaultActions(self):
|
||||||
self.__actions.addAction('ip')
|
self.__actions.addAction('ip')
|
||||||
self.__ip = self.__actions.getAction('ip')
|
self.__ip = self.__actions.getAction('ip')
|
||||||
self.__ip.setActionStart('echo ip start 64 >> "%s"' % self.__tmpfilename )
|
self.__ip.actionstart = 'echo ip start 64 >> "%s"' % self.__tmpfilename
|
||||||
self.__ip.setActionBan('echo ip ban <ip> >> "%s"' % self.__tmpfilename )
|
self.__ip.actionban = 'echo ip ban <ip> >> "%s"' % self.__tmpfilename
|
||||||
self.__ip.setActionUnban('echo ip unban <ip> >> "%s"' % self.__tmpfilename )
|
self.__ip.actionunban = 'echo ip unban <ip> >> "%s"' % self.__tmpfilename
|
||||||
self.__ip.setActionCheck('echo ip check <ip> >> "%s"' % self.__tmpfilename )
|
self.__ip.actioncheck = 'echo ip check <ip> >> "%s"' % self.__tmpfilename
|
||||||
self.__ip.setActionStop('echo ip stop >> "%s"' % self.__tmpfilename )
|
self.__ip.actionstop = 'echo ip stop >> "%s"' % self.__tmpfilename
|
||||||
|
|
||||||
def testActionsManipulation(self):
|
def testActionsManipulation(self):
|
||||||
self.__actions.addAction('test')
|
self.__actions.addAction('test')
|
||||||
|
|
|
@ -31,17 +31,17 @@ from fail2ban.server.action import CommandAction, CallingMap
|
||||||
|
|
||||||
from fail2ban.tests.utils import LogCaptureTestCase
|
from fail2ban.tests.utils import LogCaptureTestCase
|
||||||
|
|
||||||
class ExecuteAction(LogCaptureTestCase):
|
class CommandActionTest(LogCaptureTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""Call before every test case."""
|
"""Call before every test case."""
|
||||||
self.__action = CommandAction("Test")
|
self.__action = CommandAction(None, "Test")
|
||||||
LogCaptureTestCase.setUp(self)
|
LogCaptureTestCase.setUp(self)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
"""Call after every test case."""
|
"""Call after every test case."""
|
||||||
LogCaptureTestCase.tearDown(self)
|
LogCaptureTestCase.tearDown(self)
|
||||||
self.__action.execActionStop()
|
self.__action.stop()
|
||||||
|
|
||||||
def testSubstituteRecursiveTags(self):
|
def testSubstituteRecursiveTags(self):
|
||||||
aInfo = {
|
aInfo = {
|
||||||
|
@ -105,62 +105,61 @@ class ExecuteAction(LogCaptureTestCase):
|
||||||
CallingMap(callme=lambda: int("a"))), "abc")
|
CallingMap(callme=lambda: int("a"))), "abc")
|
||||||
|
|
||||||
def testExecuteActionBan(self):
|
def testExecuteActionBan(self):
|
||||||
self.__action.setActionStart("touch /tmp/fail2ban.test")
|
self.__action.actionstart = "touch /tmp/fail2ban.test"
|
||||||
self.assertEqual(self.__action.getActionStart(), "touch /tmp/fail2ban.test")
|
self.assertEqual(self.__action.actionstart, "touch /tmp/fail2ban.test")
|
||||||
self.__action.setActionStop("rm -f /tmp/fail2ban.test")
|
self.__action.actionstop = "rm -f /tmp/fail2ban.test"
|
||||||
self.assertEqual(self.__action.getActionStop(), 'rm -f /tmp/fail2ban.test')
|
self.assertEqual(self.__action.actionstop, 'rm -f /tmp/fail2ban.test')
|
||||||
self.__action.setActionBan("echo -n")
|
self.__action.actionban = "echo -n"
|
||||||
self.assertEqual(self.__action.getActionBan(), 'echo -n')
|
self.assertEqual(self.__action.actionban, 'echo -n')
|
||||||
self.__action.setActionCheck("[ -e /tmp/fail2ban.test ]")
|
self.__action.actioncheck = "[ -e /tmp/fail2ban.test ]"
|
||||||
self.assertEqual(self.__action.getActionCheck(), '[ -e /tmp/fail2ban.test ]')
|
self.assertEqual(self.__action.actioncheck, '[ -e /tmp/fail2ban.test ]')
|
||||||
self.__action.setActionUnban("true")
|
self.__action.actionunban = "true"
|
||||||
self.assertEqual(self.__action.getActionUnban(), 'true')
|
self.assertEqual(self.__action.actionunban, 'true')
|
||||||
|
|
||||||
self.assertFalse(self._is_logged('returned'))
|
self.assertFalse(self._is_logged('returned'))
|
||||||
# no action was actually executed yet
|
# 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('Invariant check failed'))
|
||||||
self.assertTrue(self._is_logged('returned successfully'))
|
self.assertTrue(self._is_logged('returned successfully'))
|
||||||
|
|
||||||
def testExecuteActionEmptyUnban(self):
|
def testExecuteActionEmptyUnban(self):
|
||||||
self.__action.setActionUnban("")
|
self.__action.actionunban = ""
|
||||||
self.assertTrue(self.__action.execActionUnban(None))
|
self.__action.unban({})
|
||||||
self.assertTrue(self._is_logged('Nothing to do'))
|
self.assertTrue(self._is_logged('Nothing to do'))
|
||||||
|
|
||||||
def testExecuteActionStartCtags(self):
|
def testExecuteActionStartCtags(self):
|
||||||
self.__action.setCInfo("HOST","192.0.2.0")
|
self.__action.HOST = "192.0.2.0"
|
||||||
self.__action.setActionStart("touch /tmp/fail2ban.test.<HOST>")
|
self.__action.actionstart = "touch /tmp/fail2ban.test.<HOST>"
|
||||||
self.__action.setActionStop("rm -f /tmp/fail2ban.test.<HOST>")
|
self.__action.actionstop = "rm -f /tmp/fail2ban.test.<HOST>"
|
||||||
self.__action.setActionCheck("[ -e /tmp/fail2ban.test.192.0.2.0 ]")
|
self.__action.actioncheck = "[ -e /tmp/fail2ban.test.192.0.2.0 ]"
|
||||||
self.assertTrue(self.__action.execActionStart())
|
self.__action.start()
|
||||||
|
|
||||||
def testExecuteActionCheckRestoreEnvironment(self):
|
def testExecuteActionCheckRestoreEnvironment(self):
|
||||||
self.__action.setActionStart("")
|
self.__action.actionstart = ""
|
||||||
self.__action.setActionStop("rm -f /tmp/fail2ban.test")
|
self.__action.actionstop = "rm -f /tmp/fail2ban.test"
|
||||||
self.__action.setActionBan("rm /tmp/fail2ban.test")
|
self.__action.actionban = "rm /tmp/fail2ban.test"
|
||||||
self.__action.setActionCheck("[ -e /tmp/fail2ban.test ]")
|
self.__action.actioncheck = "[ -e /tmp/fail2ban.test ]"
|
||||||
self.assertFalse(self.__action.execActionBan(None))
|
self.assertRaises(RuntimeError, self.__action.ban, {'ip': None})
|
||||||
self.assertTrue(self._is_logged('Unable to restore environment'))
|
self.assertTrue(self._is_logged('Unable to restore environment'))
|
||||||
|
|
||||||
def testExecuteActionChangeCtags(self):
|
def testExecuteActionChangeCtags(self):
|
||||||
self.__action.setCInfo("ROST","192.0.2.0")
|
self.assertRaises(AttributeError, getattr, self.__action, "ROST")
|
||||||
self.assertEqual(self.__action.getCInfo("ROST"),"192.0.2.0")
|
self.__action.ROST = "192.0.2.0"
|
||||||
self.__action.delCInfo("ROST")
|
self.assertEqual(self.__action.ROST,"192.0.2.0")
|
||||||
self.assertRaises(KeyError, self.__action.getCInfo, "ROST")
|
|
||||||
|
|
||||||
def testExecuteActionUnbanAinfo(self):
|
def testExecuteActionUnbanAinfo(self):
|
||||||
aInfo = {
|
aInfo = {
|
||||||
'ABC': "123",
|
'ABC': "123",
|
||||||
}
|
}
|
||||||
self.__action.setActionBan("touch /tmp/fail2ban.test.123")
|
self.__action.actionban = "touch /tmp/fail2ban.test.123"
|
||||||
self.__action.setActionUnban("rm /tmp/fail2ban.test.<ABC>")
|
self.__action.actionunban = "rm /tmp/fail2ban.test.<ABC>"
|
||||||
self.assertTrue(self.__action.execActionBan(None))
|
self.__action.ban(aInfo)
|
||||||
self.assertTrue(self.__action.execActionUnban(aInfo))
|
self.__action.unban(aInfo)
|
||||||
|
|
||||||
def testExecuteActionStartEmpty(self):
|
def testExecuteActionStartEmpty(self):
|
||||||
self.__action.setActionStart("")
|
self.__action.actionstart = ""
|
||||||
self.assertTrue(self.__action.execActionStart())
|
self.__action.start()
|
||||||
self.assertTrue(self._is_logged('Nothing to do'))
|
self.assertTrue(self._is_logged('Nothing to do'))
|
||||||
|
|
||||||
def testExecuteIncorrectCmd(self):
|
def testExecuteIncorrectCmd(self):
|
||||||
|
@ -169,7 +168,9 @@ class ExecuteAction(LogCaptureTestCase):
|
||||||
|
|
||||||
def testExecuteTimeout(self):
|
def testExecuteTimeout(self):
|
||||||
stime = time.time()
|
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.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 -- timed out after 2 seconds'))
|
||||||
self.assertTrue(self._is_logged('sleep 60 -- killed with SIGTERM'))
|
self.assertTrue(self._is_logged('sleep 60 -- killed with SIGTERM'))
|
||||||
|
|
|
@ -353,13 +353,18 @@ class JailsReaderTest(LogCaptureTestCase):
|
||||||
['set', 'brokenaction', 'addaction', 'brokenaction'],
|
['set', 'brokenaction', 'addaction', 'brokenaction'],
|
||||||
['set',
|
['set',
|
||||||
'brokenaction',
|
'brokenaction',
|
||||||
'actionban',
|
'action',
|
||||||
'brokenaction',
|
'brokenaction',
|
||||||
|
'actionban',
|
||||||
'hit with big stick <ip>'],
|
'hit with big stick <ip>'],
|
||||||
['set', 'brokenaction', 'actionstop', 'brokenaction', ''],
|
['set', 'brokenaction', 'action', 'brokenaction',
|
||||||
['set', 'brokenaction', 'actionstart', 'brokenaction', ''],
|
'actionstop', ''],
|
||||||
['set', 'brokenaction', 'actionunban', 'brokenaction', ''],
|
['set', 'brokenaction', 'action', 'brokenaction',
|
||||||
['set', 'brokenaction', 'actioncheck', 'brokenaction', ''],
|
'actionstart', ''],
|
||||||
|
['set', 'brokenaction', 'action', 'brokenaction',
|
||||||
|
'actionunban', ''],
|
||||||
|
['set', 'brokenaction', 'action', 'brokenaction',
|
||||||
|
'actioncheck', ''],
|
||||||
['add', 'parse_to_end_of_jail.conf', 'auto'],
|
['add', 'parse_to_end_of_jail.conf', 'auto'],
|
||||||
['set', 'parse_to_end_of_jail.conf', 'usedns', 'warn'],
|
['set', 'parse_to_end_of_jail.conf', 'usedns', 'warn'],
|
||||||
['set', 'parse_to_end_of_jail.conf', 'maxretry', 3],
|
['set', 'parse_to_end_of_jail.conf', 'maxretry', 3],
|
||||||
|
@ -486,7 +491,7 @@ class JailsReaderTest(LogCaptureTestCase):
|
||||||
self.assertTrue('blocktype' in action._initOpts)
|
self.assertTrue('blocktype' in action._initOpts)
|
||||||
# Verify that we have a call to set it up
|
# Verify that we have a call to set it up
|
||||||
blocktype_present = False
|
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:
|
for command in commands:
|
||||||
if (len(command) > 5 and
|
if (len(command) > 5 and
|
||||||
command[:5] == target_command):
|
command[:5] == target_command):
|
||||||
|
|
|
@ -3,20 +3,26 @@ from fail2ban.server.action import ActionBase
|
||||||
|
|
||||||
class TestAction(ActionBase):
|
class TestAction(ActionBase):
|
||||||
|
|
||||||
def __init__(self, jail, name, opt1, opt2=None):
|
def __init__(self, jail, actionname, opt1, opt2=None):
|
||||||
super(TestAction, self).__init__(jail, name)
|
super(TestAction, self).__init__(jail, actionname)
|
||||||
self.logSys.debug("%s initialised" % self.__class__.__name__)
|
self._logSys.debug("%s initialised" % self.__class__.__name__)
|
||||||
|
self.opt1 = opt1
|
||||||
|
self.opt2 = opt2
|
||||||
|
self._opt3 = "Hello"
|
||||||
|
|
||||||
def execActionStart(self):
|
def start(self):
|
||||||
self.logSys.debug("%s action start" % self.__class__.__name__)
|
self._logSys.debug("%s action start" % self.__class__.__name__)
|
||||||
|
|
||||||
def execActionStop(self):
|
def stop(self):
|
||||||
self.logSys.debug("%s action stop" % self.__class__.__name__)
|
self._logSys.debug("%s action stop" % self.__class__.__name__)
|
||||||
|
|
||||||
def execActionBan(self, aInfo):
|
def ban(self, aInfo):
|
||||||
self.logSys.debug("%s action ban" % self.__class__.__name__)
|
self._logSys.debug("%s action ban" % self.__class__.__name__)
|
||||||
|
|
||||||
def execActionUnban(self, aInfo):
|
def unban(self, aInfo):
|
||||||
self.logSys.debug("%s action unban" % self.__class__.__name__)
|
self._logSys.debug("%s action unban" % self.__class__.__name__)
|
||||||
|
|
||||||
|
def testmethod(self, text):
|
||||||
|
return "%s %s %s" % (self._opt3, text, self.opt1)
|
||||||
|
|
||||||
Action = TestAction
|
Action = TestAction
|
||||||
|
|
|
@ -523,40 +523,40 @@ class Transmitter(TransmitterBase):
|
||||||
(0, action))
|
(0, action))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.transm.proceed(
|
self.transm.proceed(
|
||||||
["get", self.jailName, "actions"])[1][0].getName(),
|
["get", self.jailName, "actions"])[1][0],
|
||||||
action)
|
action)
|
||||||
for cmd, value in zip(cmdList, cmdValueList):
|
for cmd, value in zip(cmdList, cmdValueList):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.transm.proceed(
|
self.transm.proceed(
|
||||||
["set", self.jailName, cmd, action, value]),
|
["set", self.jailName, "action", action, cmd, value]),
|
||||||
(0, value))
|
(0, value))
|
||||||
for cmd, value in zip(cmdList, cmdValueList):
|
for cmd, value in zip(cmdList, cmdValueList):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.transm.proceed(["get", self.jailName, cmd, action]),
|
self.transm.proceed(["get", self.jailName, "action", action, cmd]),
|
||||||
(0, value))
|
(0, value))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.transm.proceed(
|
self.transm.proceed(
|
||||||
["set", self.jailName, "setcinfo", action, "KEY", "VALUE"]),
|
["set", self.jailName, "action", action, "KEY", "VALUE"]),
|
||||||
(0, "VALUE"))
|
(0, "VALUE"))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.transm.proceed(
|
self.transm.proceed(
|
||||||
["get", self.jailName, "cinfo", action, "KEY"]),
|
["get", self.jailName, "action", action, "KEY"]),
|
||||||
(0, "VALUE"))
|
(0, "VALUE"))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.transm.proceed(
|
self.transm.proceed(
|
||||||
["get", self.jailName, "cinfo", action, "InvalidKey"])[0],
|
["get", self.jailName, "action", action, "InvalidKey"])[0],
|
||||||
1)
|
1)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.transm.proceed(
|
self.transm.proceed(
|
||||||
["set", self.jailName, "delcinfo", action, "KEY"]),
|
["get", self.jailName, "action", action, "actionname"]),
|
||||||
(0, None))
|
(0, action))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.transm.proceed(
|
self.transm.proceed(
|
||||||
["set", self.jailName, "timeout", action, "10"]),
|
["set", self.jailName, "action", action, "timeout", "10"]),
|
||||||
(0, 10))
|
(0, 10))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.transm.proceed(
|
self.transm.proceed(
|
||||||
["get", self.jailName, "timeout", action]),
|
["get", self.jailName, "action", action, "timeout"]),
|
||||||
(0, 10))
|
(0, 10))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.transm.proceed(["set", self.jailName, "delaction", action]),
|
self.transm.proceed(["set", self.jailName, "delaction", action]),
|
||||||
|
@ -564,23 +564,42 @@ class Transmitter(TransmitterBase):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.transm.proceed(
|
self.transm.proceed(
|
||||||
["set", self.jailName, "delaction", "Doesn't exist"])[0],1)
|
["set", self.jailName, "delaction", "Doesn't exist"])[0],1)
|
||||||
|
|
||||||
|
def testPythonActionMethodsAndProperties(self):
|
||||||
|
action = "TestCaseAction"
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.transm.proceed(["set", self.jailName, "addaction", action,
|
self.transm.proceed(["set", self.jailName, "addaction", action,
|
||||||
os.path.join(TEST_FILES_DIR, "action.d", "action.py"),
|
os.path.join(TEST_FILES_DIR, "action.d", "action.py"),
|
||||||
'{"opt1": "value"}']),
|
'{"opt1": "value"}']),
|
||||||
(0, action))
|
(0, action))
|
||||||
for cmd, value in zip(cmdList, cmdValueList):
|
self.assertEqual(
|
||||||
self.assertTrue(
|
sorted(self.transm.proceed(["get", self.jailName,
|
||||||
isinstance(self.transm.proceed(
|
"actionproperties", action])),
|
||||||
["set", self.jailName, cmd, action, value])[1],
|
[0, ['actionname', 'opt1', 'opt2']])
|
||||||
TypeError),
|
self.assertEqual(
|
||||||
"set %s for python action did not raise TypeError" % cmd)
|
self.transm.proceed(["get", self.jailName, "action", action,
|
||||||
for cmd, value in zip(cmdList, cmdValueList):
|
"opt1"]),
|
||||||
self.assertTrue(
|
(0, 'value'))
|
||||||
isinstance(self.transm.proceed(
|
self.assertEqual(
|
||||||
["get", self.jailName, cmd, action])[1],
|
self.transm.proceed(["get", self.jailName, "action", action,
|
||||||
TypeError),
|
"opt2"]),
|
||||||
"get %s for python action did not raise TypeError" % cmd)
|
(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):
|
def testNOK(self):
|
||||||
self.assertEqual(self.transm.proceed(["INVALID", "COMMAND"])[0],1)
|
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.Transmitter))
|
||||||
tests.addTest(unittest.makeSuite(servertestcase.JailTests))
|
tests.addTest(unittest.makeSuite(servertestcase.JailTests))
|
||||||
tests.addTest(unittest.makeSuite(servertestcase.RegexTests))
|
tests.addTest(unittest.makeSuite(servertestcase.RegexTests))
|
||||||
tests.addTest(unittest.makeSuite(actiontestcase.ExecuteAction))
|
tests.addTest(unittest.makeSuite(actiontestcase.CommandActionTest))
|
||||||
tests.addTest(unittest.makeSuite(actionstestcase.ExecuteActions))
|
tests.addTest(unittest.makeSuite(actionstestcase.ExecuteActions))
|
||||||
# FailManager
|
# FailManager
|
||||||
tests.addTest(unittest.makeSuite(failmanagertestcase.AddFailure))
|
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
|
will try to use the following backends, in order: pyinotify, gamin, polling
|
||||||
.PP
|
.PP
|
||||||
.SS Actions
|
.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
|
.PP
|
||||||
.nf
|
.nf
|
||||||
[ssh-iptables-ipset]
|
[ssh-iptables-ipset]
|
||||||
|
@ -163,7 +163,7 @@ two commands to be executed.
|
||||||
actionban = iptables -I fail2ban-<name> --source <ip> -j DROP
|
actionban = iptables -I fail2ban-<name> --source <ip> -j DROP
|
||||||
echo ip=<ip>, match=<match>, time=<time> >> /var/log/fail2ban.log
|
echo ip=<ip>, match=<match>, time=<time> >> /var/log/fail2ban.log
|
||||||
.TP
|
.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"
|
.SS "Action Tags"
|
||||||
The following tags are substituted in the actionban, actionunban and actioncheck (when called before actionban/actionunban) commands.
|
The following tags are substituted in the actionban, actionunban and actioncheck (when called before actionban/actionunban) commands.
|
||||||
|
|
3
setup.py
3
setup.py
|
@ -120,7 +120,8 @@ setup(
|
||||||
glob("config/filter.d/*.conf")
|
glob("config/filter.d/*.conf")
|
||||||
),
|
),
|
||||||
('/etc/fail2ban/action.d',
|
('/etc/fail2ban/action.d',
|
||||||
glob("config/action.d/*.*")
|
glob("config/action.d/*.conf") +
|
||||||
|
glob("config/action.d/*.py")
|
||||||
),
|
),
|
||||||
('/etc/fail2ban/fail2ban.d',
|
('/etc/fail2ban/fail2ban.d',
|
||||||
''
|
''
|
||||||
|
|
Loading…
Reference in New Issue