mirror of https://github.com/fail2ban/fail2ban
commit
ab3ded2205
|
@ -0,0 +1,225 @@
|
||||||
|
# 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
|
||||||
|
import smtplib
|
||||||
|
from email.mime.text import MIMEText
|
||||||
|
from email.utils import formatdate, formataddr
|
||||||
|
|
||||||
|
from fail2ban.server.actions import ActionBase, CallingMap
|
||||||
|
|
||||||
|
messages = {}
|
||||||
|
messages['start'] = \
|
||||||
|
"""Hi,
|
||||||
|
|
||||||
|
The jail %(jailname)s has been started successfully.
|
||||||
|
|
||||||
|
Regards,
|
||||||
|
Fail2Ban"""
|
||||||
|
|
||||||
|
messages['stop'] = \
|
||||||
|
"""Hi,
|
||||||
|
|
||||||
|
The jail %(jailname)s has been stopped.
|
||||||
|
|
||||||
|
Regards,
|
||||||
|
Fail2Ban"""
|
||||||
|
|
||||||
|
messages['ban'] = {}
|
||||||
|
messages['ban']['head'] = \
|
||||||
|
"""Hi,
|
||||||
|
|
||||||
|
The IP %(ip)s has just been banned for %(bantime)s seconds
|
||||||
|
by Fail2Ban after %(failures)i attempts against %(jailname)s.
|
||||||
|
"""
|
||||||
|
messages['ban']['tail'] = \
|
||||||
|
"""
|
||||||
|
Regards,
|
||||||
|
Fail2Ban"""
|
||||||
|
messages['ban']['matches'] = \
|
||||||
|
"""
|
||||||
|
Matches for this ban:
|
||||||
|
%(matches)s
|
||||||
|
"""
|
||||||
|
messages['ban']['ipmatches'] = \
|
||||||
|
"""
|
||||||
|
Matches for %(ip)s:
|
||||||
|
%(ipmatches)s
|
||||||
|
"""
|
||||||
|
messages['ban']['ipjailmatches'] = \
|
||||||
|
"""
|
||||||
|
Matches for %(ip)s for jail %(jailname)s:
|
||||||
|
%(ipjailmatches)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):
|
||||||
|
"""Initialise action.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
jail : Jail
|
||||||
|
The jail which the action belongs to.
|
||||||
|
name : str
|
||||||
|
Named assigned to the action.
|
||||||
|
host : str, optional
|
||||||
|
SMTP host, of host:port format. Default host "localhost" and
|
||||||
|
port "25"
|
||||||
|
user : str, optional
|
||||||
|
Username used for authentication with SMTP server.
|
||||||
|
password : str, optional
|
||||||
|
Password used for authentication with SMTP server.
|
||||||
|
sendername : str, optional
|
||||||
|
Name to use for from address in email. Default "Fail2Ban".
|
||||||
|
sender : str, optional
|
||||||
|
Email address to use for from address in email.
|
||||||
|
Default "fail2ban".
|
||||||
|
dest : str, optional
|
||||||
|
Email addresses of intended recipient(s) in comma delimited
|
||||||
|
format. Default "root".
|
||||||
|
matches : str, optional
|
||||||
|
Type of matches to be included from ban in email. Can be one
|
||||||
|
of "matches", "ipmatches" or "ipjailmatches". Default None
|
||||||
|
(see man jail.conf.5).
|
||||||
|
"""
|
||||||
|
|
||||||
|
super(SMTPAction, self).__init__(jail, name)
|
||||||
|
|
||||||
|
self.host = host
|
||||||
|
#TODO: self.ssl = ssl
|
||||||
|
|
||||||
|
self.user = user
|
||||||
|
self.password =password
|
||||||
|
|
||||||
|
self.fromname = sendername
|
||||||
|
self.fromaddr = sender
|
||||||
|
self.toaddr = dest
|
||||||
|
|
||||||
|
self.matches = matches
|
||||||
|
|
||||||
|
self.message_values = CallingMap(
|
||||||
|
jailname = self._jail.getName(), # Doesn't change
|
||||||
|
hostname = socket.gethostname,
|
||||||
|
bantime = self._jail.actions.getBanTime,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _sendMessage(self, subject, text):
|
||||||
|
"""Sends message based on arguments and instance's properties.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
subject : str
|
||||||
|
Subject of the email.
|
||||||
|
text : str
|
||||||
|
Body of the email.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
SMTPConnectionError
|
||||||
|
Error on connecting to host.
|
||||||
|
SMTPAuthenticationError
|
||||||
|
Error authenticating with SMTP server.
|
||||||
|
SMTPException
|
||||||
|
See Python `smtplib` for full list of other possible
|
||||||
|
exceptions.
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
|
||||||
|
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 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 ban(self, aInfo):
|
||||||
|
"""Sends email to recipients informing that ban has occurred.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
aInfo : dict
|
||||||
|
Dictionary which includes information in relation to
|
||||||
|
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
|
|
@ -25,7 +25,8 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import logging, os
|
import logging, os
|
||||||
from configreader import ConfigReader, DefinitionInitConfigReader
|
|
||||||
|
from .configreader import ConfigReader, DefinitionInitConfigReader
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = logging.getLogger(__name__)
|
||||||
|
@ -58,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
|
||||||
|
|
|
@ -23,7 +23,7 @@ __license__ = "GPL"
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from fail2ban.exceptions import UnknownJailException, DuplicateJailException
|
from ..exceptions import UnknownJailException, DuplicateJailException
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = logging.getLogger(__name__)
|
||||||
|
@ -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` +
|
||||||
|
|
|
@ -25,9 +25,10 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import glob, logging, os
|
import glob, logging, os
|
||||||
from configparserinc import SafeConfigParserWithIncludes
|
|
||||||
from ConfigParser import NoOptionError, NoSectionError
|
from ConfigParser import NoOptionError, NoSectionError
|
||||||
|
|
||||||
|
from .configparserinc import SafeConfigParserWithIncludes
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
|
@ -25,9 +25,10 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from configreader import ConfigReader
|
|
||||||
from fail2banreader import Fail2banReader
|
from .configreader import ConfigReader
|
||||||
from jailsreader import JailsReader
|
from .fail2banreader import Fail2banReader
|
||||||
|
from .jailsreader import JailsReader
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = logging.getLogger(__name__)
|
||||||
|
|
|
@ -25,7 +25,8 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from configreader import ConfigReader
|
|
||||||
|
from .configreader import ConfigReader
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = logging.getLogger(__name__)
|
||||||
|
|
|
@ -25,7 +25,8 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import logging, os, shlex
|
import logging, os, shlex
|
||||||
from configreader import ConfigReader, DefinitionInitConfigReader
|
|
||||||
|
from .configreader import ConfigReader, DefinitionInitConfigReader
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = logging.getLogger(__name__)
|
||||||
|
|
|
@ -25,10 +25,11 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import logging, re, glob, os.path
|
import logging, re, glob, os.path
|
||||||
|
import json
|
||||||
|
|
||||||
from configreader import ConfigReader
|
from .configreader import ConfigReader
|
||||||
from filterreader import FilterReader
|
from .filterreader import FilterReader
|
||||||
from actionreader import ActionReader
|
from .actionreader import ActionReader
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = logging.getLogger(__name__)
|
||||||
|
@ -120,14 +121,26 @@ class JailReader(ConfigReader):
|
||||||
if not act: # skip empty actions
|
if not act: # skip empty actions
|
||||||
continue
|
continue
|
||||||
actName, actOpt = JailReader.extractOptions(act)
|
actName, actOpt = JailReader.extractOptions(act)
|
||||||
action = ActionReader(
|
if actName.endswith(".py"):
|
||||||
actName, self.__name, actOpt, basedir=self.getBaseDir())
|
self.__actions.append([
|
||||||
ret = action.read()
|
"set",
|
||||||
if ret:
|
self.__name,
|
||||||
action.getOptions(self.__opts)
|
"addaction",
|
||||||
self.__actions.append(action)
|
actOpt.pop("actname", os.path.splitext(actName)[0]),
|
||||||
|
os.path.join(
|
||||||
|
self.getBaseDir(), "action.d", actName),
|
||||||
|
json.dumps(actOpt),
|
||||||
|
])
|
||||||
else:
|
else:
|
||||||
raise AttributeError("Unable to read action")
|
action = ActionReader(
|
||||||
|
actName, self.__name, actOpt,
|
||||||
|
basedir=self.getBaseDir())
|
||||||
|
ret = action.read()
|
||||||
|
if ret:
|
||||||
|
action.getOptions(self.__opts)
|
||||||
|
self.__actions.append(action)
|
||||||
|
else:
|
||||||
|
raise AttributeError("Unable to read action")
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
logSys.error("Error in action definition " + act)
|
logSys.error("Error in action definition " + act)
|
||||||
logSys.debug("Caught exception: %s" % (e,))
|
logSys.debug("Caught exception: %s" % (e,))
|
||||||
|
@ -193,7 +206,10 @@ class JailReader(ConfigReader):
|
||||||
if self.__filter:
|
if self.__filter:
|
||||||
stream.extend(self.__filter.convert())
|
stream.extend(self.__filter.convert())
|
||||||
for action in self.__actions:
|
for action in self.__actions:
|
||||||
stream.extend(action.convert())
|
if isinstance(action, ConfigReader):
|
||||||
|
stream.extend(action.convert())
|
||||||
|
else:
|
||||||
|
stream.append(action)
|
||||||
stream.insert(0, ["add", self.__name, backend])
|
stream.insert(0, ["add", self.__name, backend])
|
||||||
return stream
|
return stream
|
||||||
|
|
||||||
|
|
|
@ -25,8 +25,9 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from configreader import ConfigReader
|
|
||||||
from jailreader import JailReader
|
from .configreader import ConfigReader
|
||||||
|
from .jailreader import JailReader
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = logging.getLogger(__name__)
|
||||||
|
|
|
@ -29,7 +29,7 @@ __license__ = "GPL"
|
||||||
class DuplicateJailException(Exception):
|
class DuplicateJailException(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class UnknownJailException(Exception):
|
class UnknownJailException(KeyError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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>", "adds a new action named <NAME> 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 <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>"],
|
||||||
|
@ -100,15 +102,18 @@ protocol = [
|
||||||
["get <JAIL> usedns", "gets the usedns setting for <JAIL>"],
|
["get <JAIL> usedns", "gets the usedns setting for <JAIL>"],
|
||||||
["get <JAIL> maxretry", "gets the number of failures allowed for <JAIL>"],
|
["get <JAIL> maxretry", "gets the number of failures allowed for <JAIL>"],
|
||||||
["get <JAIL> maxlines", "gets the number of lines to buffer for <JAIL>"],
|
["get <JAIL> 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> 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,12 +130,14 @@ def printFormatted():
|
||||||
print
|
print
|
||||||
firstHeading = True
|
firstHeading = True
|
||||||
first = True
|
first = True
|
||||||
for n in textwrap.wrap(m[1], WIDTH):
|
if len(m[0]) >= MARGIN:
|
||||||
|
m[1] = ' ' * WIDTH + m[1]
|
||||||
|
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
|
print line
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|
|
@ -23,6 +23,8 @@ __license__ = "GPL"
|
||||||
|
|
||||||
import logging, os, subprocess, time, signal, tempfile
|
import logging, os, subprocess, time, signal, tempfile
|
||||||
import threading, re
|
import threading, re
|
||||||
|
from abc import ABCMeta
|
||||||
|
from collections import MutableMapping
|
||||||
#from subprocess import call
|
#from subprocess import call
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
|
@ -34,7 +36,7 @@ _cmd_lock = threading.Lock()
|
||||||
# Some hints on common abnormal exit codes
|
# Some hints on common abnormal exit codes
|
||||||
_RETCODE_HINTS = {
|
_RETCODE_HINTS = {
|
||||||
127: '"Command not found". Make sure that all commands in %(realCmd)r '
|
127: '"Command not found". Make sure that all commands in %(realCmd)r '
|
||||||
'are in the PATH of fail2ban-server process '
|
'are in the PATH of fail2ban-server process '
|
||||||
'(grep -a PATH= /proc/`pidof -x fail2ban-server`/environ). '
|
'(grep -a PATH= /proc/`pidof -x fail2ban-server`/environ). '
|
||||||
'You may want to start '
|
'You may want to start '
|
||||||
'"fail2ban-server -f" separately, initiate it with '
|
'"fail2ban-server -f" separately, initiate it with '
|
||||||
|
@ -46,232 +48,326 @@ _RETCODE_HINTS = {
|
||||||
signame = dict((num, name)
|
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):
|
||||||
# Execute commands.
|
"""A Mapping type which returns the result of callable values.
|
||||||
#
|
|
||||||
# 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 Action:
|
`CallingMap` behaves similar to a standard python dictionary,
|
||||||
|
with the exception that any values which are callable, are called
|
||||||
def __init__(self, name):
|
and the result is returned as the value.
|
||||||
self.__name = name
|
No error handling is in place, such that any errors raised in the
|
||||||
self.__timeout = 60
|
callable will raised as usual.
|
||||||
self.__cInfo = dict()
|
Actual dictionary is stored in property `data`, and can be accessed
|
||||||
## Command executed in order to initialize the system.
|
to obtain original callable values.
|
||||||
self.__actionStart = ''
|
|
||||||
## Command executed when an IP address gets banned.
|
Attributes
|
||||||
self.__actionBan = ''
|
----------
|
||||||
## Command executed when an IP address gets removed.
|
data : dict
|
||||||
self.__actionUnban = ''
|
The dictionary data which can be accessed to obtain items
|
||||||
## Command executed in order to check requirements.
|
without callable values being called.
|
||||||
self.__actionCheck = ''
|
|
||||||
## Command executed in order to stop the system.
|
"""
|
||||||
self.__actionStop = ''
|
|
||||||
logSys.debug("Created Action")
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.data = dict(*args, **kwargs)
|
||||||
##
|
|
||||||
# Sets the action name.
|
def __getitem__(self, key):
|
||||||
#
|
value = self.data[key]
|
||||||
# @param name the name of the action
|
if callable(value):
|
||||||
|
return value()
|
||||||
def setName(self, name):
|
else:
|
||||||
self.__name = name
|
return value
|
||||||
|
|
||||||
##
|
def __setitem__(self, key, value):
|
||||||
# Returns the action name.
|
self.data[key] = value
|
||||||
#
|
|
||||||
# @return the name of the action
|
def __delitem__(self, key):
|
||||||
|
del self.data[key]
|
||||||
def getName(self):
|
|
||||||
return self.__name
|
def __iter__(self):
|
||||||
|
return iter(self.data)
|
||||||
##
|
|
||||||
# Sets the timeout period for commands.
|
def __len__(self):
|
||||||
#
|
return len(self.data)
|
||||||
# @param timeout timeout period in seconds
|
|
||||||
|
class ActionBase(object):
|
||||||
def setTimeout(self, timeout):
|
"""An abstract base class for actions in Fail2Ban.
|
||||||
self.__timeout = int(timeout)
|
|
||||||
logSys.debug("Set action %s timeout = %i" % (self.__name, timeout))
|
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.
|
||||||
# Returns the action timeout period for commands.
|
Required methods:
|
||||||
#
|
- __init__(jail, name)
|
||||||
# @return the timeout period in seconds
|
- start()
|
||||||
|
- stop()
|
||||||
def getTimeout(self):
|
- ban(aInfo)
|
||||||
return self.__timeout
|
- unban(aInfo)
|
||||||
|
"""
|
||||||
##
|
__metaclass__ = ABCMeta
|
||||||
# Sets a "CInfo".
|
|
||||||
#
|
@classmethod
|
||||||
# CInfo are statically defined properties. They can be definied by
|
def __subclasshook__(cls, C):
|
||||||
# the user and are used to set e-mail addresses, port, host or
|
required = (
|
||||||
# anything that should not change during the life of the server.
|
"start",
|
||||||
#
|
"stop",
|
||||||
# @param key the property name
|
"ban",
|
||||||
# @param value the property value
|
"unban",
|
||||||
|
)
|
||||||
def setCInfo(self, key, value):
|
for method in required:
|
||||||
self.__cInfo[key] = value
|
if not callable(getattr(C, method, None)):
|
||||||
|
|
||||||
##
|
|
||||||
# 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 Action.substituteRecursiveTags(self.__cInfo):
|
|
||||||
logSys.error("Cinfo/definitions contain self referencing definitions and cannot be resolved")
|
|
||||||
return False
|
return False
|
||||||
startCmd = Action.replaceTag(self.__actionStart, self.__cInfo)
|
return True
|
||||||
return Action.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 = Action.replaceTag(self.__actionStop, self.__cInfo)
|
|
||||||
return Action.executeCmd(stopCmd, self.__timeout)
|
|
||||||
|
|
||||||
##
|
def __init__(self, jail, name):
|
||||||
# Sort out tag definitions within other tags
|
"""Initialise action.
|
||||||
#
|
|
||||||
# so: becomes:
|
Called when action is created, but before the jail/actions is
|
||||||
# a = 3 a = 3
|
started. This should carry out necessary methods to initialise
|
||||||
# b = <a>_3 b = 3_3
|
the action but not "start" the action.
|
||||||
# @param tags, a dictionary
|
|
||||||
# @returns tags altered or False if there is a recursive definition
|
Parameters
|
||||||
#@staticmethod
|
----------
|
||||||
|
jail : Jail
|
||||||
|
The jail in which the action belongs to.
|
||||||
|
name : str
|
||||||
|
Name assigned to the action.
|
||||||
|
|
||||||
|
Notes
|
||||||
|
-----
|
||||||
|
Any additional arguments specified in `jail.conf` or passed
|
||||||
|
via `fail2ban-client` will be passed as keyword arguments.
|
||||||
|
"""
|
||||||
|
self._jail = jail
|
||||||
|
self._name = name
|
||||||
|
self._logSys = logging.getLogger(
|
||||||
|
'%s.%s' % (__name__, self.__class__.__name__))
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
"""Executed when the jail/action is started.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
"""Executed when the jail/action is stopped.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def ban(self, aInfo):
|
||||||
|
"""Executed when a ban occurs.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
aInfo : dict
|
||||||
|
Dictionary which includes information in relation to
|
||||||
|
the ban.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def unban(self, aInfo):
|
||||||
|
"""Executed when a ban expires.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
aInfo : dict
|
||||||
|
Dictionary which includes information in relation to
|
||||||
|
the ban.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
class CommandAction(ActionBase):
|
||||||
|
"""A action which executes OS shell commands.
|
||||||
|
|
||||||
|
This is the default type of action which Fail2Ban uses.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, jail, name):
|
||||||
|
"""Initialise action.
|
||||||
|
|
||||||
|
Default sets all commands for actions as empty string, such
|
||||||
|
no command is executed.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
jail : Jail
|
||||||
|
The jail in which the action belongs to.
|
||||||
|
name : str
|
||||||
|
Name assigned to the action.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super(CommandAction, self).__init__(jail, name)
|
||||||
|
self.timeout = 60
|
||||||
|
## Command executed in order to initialize the system.
|
||||||
|
self.actionstart = ''
|
||||||
|
## Command executed when an IP address gets banned.
|
||||||
|
self.actionban = ''
|
||||||
|
## Command executed when an IP address gets removed.
|
||||||
|
self.actionunban = ''
|
||||||
|
## Command executed in order to check requirements.
|
||||||
|
self.actioncheck = ''
|
||||||
|
## Command executed in order to stop the system.
|
||||||
|
self.actionstop = ''
|
||||||
|
self._logSys.debug("Created %s" % self.__class__)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def __subclasshook__(cls, C):
|
||||||
|
return NotImplemented # Standard checks
|
||||||
|
|
||||||
|
@property
|
||||||
|
def timeout(self):
|
||||||
|
"""Time out 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._name, self.timeout))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _properties(self):
|
||||||
|
"""A dictionary of the actions properties.
|
||||||
|
|
||||||
|
This is used to subsitute "tags" in the commands.
|
||||||
|
"""
|
||||||
|
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.
|
||||||
|
|
||||||
|
Replaces the tags in the action command with actions properties
|
||||||
|
and ban information, and executes the resulting command.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
aInfo : dict
|
||||||
|
Dictionary which includes information in relation to
|
||||||
|
the ban.
|
||||||
|
"""
|
||||||
|
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.
|
||||||
|
|
||||||
|
Replaces the tags in the action command with actions properties
|
||||||
|
and ban information, and executes the resulting command.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
aInfo : dict
|
||||||
|
Dictionary which includes information in relation to
|
||||||
|
the ban.
|
||||||
|
"""
|
||||||
|
if not self._processCmd(self.actionunban, aInfo):
|
||||||
|
raise RuntimeError("Error unbanning %(ip)s" % aInfo)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def actioncheck(self):
|
||||||
|
"""The command used to check the environment.
|
||||||
|
|
||||||
|
This is used prior to a ban taking place to ensure the
|
||||||
|
environment is appropriate. If this check fails, `stop` and
|
||||||
|
`start` is executed prior to the check being called again.
|
||||||
|
"""
|
||||||
|
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.
|
||||||
|
|
||||||
|
Replaces 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")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
def substituteRecursiveTags(tags):
|
def substituteRecursiveTags(tags):
|
||||||
|
"""Sort out tag definitions within other tags.
|
||||||
|
|
||||||
|
so: becomes:
|
||||||
|
a = 3 a = 3
|
||||||
|
b = <a>_3 b = 3_3
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
tags : dict
|
||||||
|
Dictionary of tags(keys) and their values.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
dict
|
||||||
|
Dictionary of tags(keys) and their values, with tags
|
||||||
|
within the values recursively replaced.
|
||||||
|
"""
|
||||||
t = re.compile(r'<([^ >]+)>')
|
t = re.compile(r'<([^ >]+)>')
|
||||||
for tag, value in tags.iteritems():
|
for tag, value in tags.iteritems():
|
||||||
value = str(value)
|
value = str(value)
|
||||||
|
@ -291,98 +387,130 @@ class Action:
|
||||||
m = t.search(value, m.start() + 1)
|
m = t.search(value, m.start() + 1)
|
||||||
tags[tag] = value
|
tags[tag] = value
|
||||||
return tags
|
return tags
|
||||||
substituteRecursiveTags = staticmethod(substituteRecursiveTags)
|
|
||||||
|
|
||||||
#@staticmethod
|
@staticmethod
|
||||||
def escapeTag(tag):
|
def escapeTag(value):
|
||||||
|
"""Escape characters which may be used for command injection.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
value : str
|
||||||
|
A string of which characters will be escaped.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
`value` with certain characters escaped.
|
||||||
|
|
||||||
|
Notes
|
||||||
|
-----
|
||||||
|
The following characters are escaped::
|
||||||
|
|
||||||
|
\\#&;`|*?~<>^()[]{}$'"
|
||||||
|
|
||||||
|
"""
|
||||||
for c in '\\#&;`|*?~<>^()[]{}$\'"':
|
for c in '\\#&;`|*?~<>^()[]{}$\'"':
|
||||||
if c in tag:
|
if c in value:
|
||||||
tag = tag.replace(c, '\\' + c)
|
value = value.replace(c, '\\' + c)
|
||||||
return tag
|
return value
|
||||||
escapeTag = staticmethod(escapeTag)
|
|
||||||
|
|
||||||
##
|
@classmethod
|
||||||
# Replaces tags in query with property values in aInfo.
|
def replaceTag(cls, query, aInfo):
|
||||||
#
|
"""Replaces tags in `query` with property values.
|
||||||
# @param query the query string with tags
|
|
||||||
# @param aInfo the properties
|
Parameters
|
||||||
# @return a string
|
----------
|
||||||
|
query : str
|
||||||
#@staticmethod
|
String with tags.
|
||||||
def replaceTag(query, aInfo):
|
aInfo : dict
|
||||||
""" Replace tags in query
|
Tags(keys) and associated values for substitution in query.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
`query` string with tags replaced.
|
||||||
"""
|
"""
|
||||||
string = query
|
string = query
|
||||||
for tag, value in aInfo.iteritems():
|
for tag in aInfo:
|
||||||
if "<%s>" % tag in query:
|
if "<%s>" % tag in query:
|
||||||
if callable(value):
|
value = str(aInfo[tag]) # assure string
|
||||||
value = value()
|
|
||||||
value = str(value) # assure string
|
|
||||||
if tag.endswith('matches'):
|
if tag.endswith('matches'):
|
||||||
# That one needs to be escaped since its content is
|
# That one needs to be escaped since its content is
|
||||||
# out of our control
|
# out of our control
|
||||||
value = Action.escapeTag(value)
|
value = cls.escapeTag(value)
|
||||||
string = string.replace('<' + tag + '>', value)
|
string = string.replace('<' + tag + '>', value)
|
||||||
# New line
|
# New line
|
||||||
string = string.replace("<br>", '\n')
|
string = string.replace("<br>", '\n')
|
||||||
return string
|
return string
|
||||||
replaceTag = staticmethod(replaceTag)
|
|
||||||
|
def _processCmd(self, cmd, aInfo = None):
|
||||||
##
|
"""Executes a command with preliminary checks and substitutions.
|
||||||
# Executes a command with preliminary checks and substitutions.
|
|
||||||
#
|
Before executing any commands, executes the "check" command first
|
||||||
# Before executing any commands, executes the "check" command first
|
in order to check if pre-requirements are met. If this check fails,
|
||||||
# in order to check if pre-requirements are met. If this check fails,
|
it tries to restore a sane environment before executing the real
|
||||||
# it tries to restore a sane environment before executing the real
|
command.
|
||||||
# command.
|
|
||||||
# Replaces "aInfo" and "cInfo" in the query too.
|
Parameters
|
||||||
#
|
----------
|
||||||
# @param cmd The command to execute
|
cmd : str
|
||||||
# @param aInfo Dynamic properties
|
The command to execute.
|
||||||
# @return True if the command succeeded
|
aInfo : dictionary
|
||||||
|
Dynamic properties.
|
||||||
def __processCmd(self, cmd, aInfo = None):
|
|
||||||
""" Executes an OS command.
|
Returns
|
||||||
|
-------
|
||||||
|
bool
|
||||||
|
True if the command succeeded.
|
||||||
"""
|
"""
|
||||||
if cmd == "":
|
if cmd == "":
|
||||||
logSys.debug("Nothing to do")
|
self._logSys.debug("Nothing to do")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
checkCmd = Action.replaceTag(self.__actionCheck, self.__cInfo)
|
checkCmd = self.replaceTag(self.actioncheck, self._properties)
|
||||||
if not Action.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 Action.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
|
||||||
if not aInfo is None:
|
if not aInfo is None:
|
||||||
realCmd = Action.replaceTag(cmd, aInfo)
|
realCmd = self.replaceTag(cmd, aInfo)
|
||||||
else:
|
else:
|
||||||
realCmd = cmd
|
realCmd = cmd
|
||||||
|
|
||||||
# Replace static fields
|
# Replace static fields
|
||||||
realCmd = Action.replaceTag(realCmd, self.__cInfo)
|
realCmd = self.replaceTag(realCmd, self._properties)
|
||||||
|
|
||||||
return Action.executeCmd(realCmd, self.__timeout)
|
return self.executeCmd(realCmd, self.timeout)
|
||||||
|
|
||||||
##
|
@staticmethod
|
||||||
# Executes a command.
|
|
||||||
#
|
|
||||||
# We need a shell here because commands are mainly shell script. They
|
|
||||||
# contain pipe, redirection, etc.
|
|
||||||
#
|
|
||||||
# @todo Force the use of bash!?
|
|
||||||
# @todo Kill the command after a given timeout
|
|
||||||
#
|
|
||||||
# @param realCmd the command to execute
|
|
||||||
# @return True if the command succeeded
|
|
||||||
|
|
||||||
#@staticmethod
|
|
||||||
def executeCmd(realCmd, timeout=60):
|
def executeCmd(realCmd, timeout=60):
|
||||||
|
"""Executes a command.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
realCmd : str
|
||||||
|
The command to execute.
|
||||||
|
timeout : int
|
||||||
|
The time out in seconds for the command.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
bool
|
||||||
|
True if the command succeeded.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
OSError
|
||||||
|
If command fails to be executed.
|
||||||
|
RuntimeError
|
||||||
|
If command execution times out.
|
||||||
|
"""
|
||||||
logSys.debug(realCmd)
|
logSys.debug(realCmd)
|
||||||
if not realCmd:
|
if not realCmd:
|
||||||
logSys.debug("Nothing to do")
|
logSys.debug("Nothing to do")
|
||||||
|
@ -412,7 +540,6 @@ class Action:
|
||||||
retcode = popen.poll()
|
retcode = popen.poll()
|
||||||
except OSError, e:
|
except OSError, e:
|
||||||
logSys.error("%s -- failed with %s" % (realCmd, e))
|
logSys.error("%s -- failed with %s" % (realCmd, e))
|
||||||
return False
|
|
||||||
finally:
|
finally:
|
||||||
_cmd_lock.release()
|
_cmd_lock.release()
|
||||||
|
|
||||||
|
@ -439,6 +566,6 @@ class Action:
|
||||||
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
|
||||||
executeCmd = staticmethod(executeCmd)
|
raise RuntimeError("Command execution failed: %s" % realCmd)
|
||||||
|
|
||||||
|
|
|
@ -24,94 +24,113 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
from banmanager import BanManager
|
|
||||||
from jailthread import JailThread
|
|
||||||
from action import Action
|
|
||||||
from mytime import MyTime
|
|
||||||
import time, logging
|
import time, logging
|
||||||
|
import os
|
||||||
|
import imp
|
||||||
|
from collections import Mapping
|
||||||
|
|
||||||
|
from .banmanager import BanManager
|
||||||
|
from .jailthread import JailThread
|
||||||
|
from .action import ActionBase, CommandAction, CallingMap
|
||||||
|
from .mytime import MyTime
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = logging.getLogger(__name__)
|
||||||
|
|
||||||
##
|
class Actions(JailThread, Mapping):
|
||||||
# Execute commands.
|
"""Handles jail actions.
|
||||||
#
|
|
||||||
# This class reads the failures from the Jail queue and decide if an
|
This class handles the actions of the jail. Creation, deletion or to
|
||||||
# action has to be taken. A BanManager take care of the banned IP
|
actions must be done through this class. This class is based on the
|
||||||
# addresses.
|
Mapping type, and the `add` method must be used to add new actions.
|
||||||
|
This class also starts and stops the actions, and fetches bans from
|
||||||
|
the jail executing these bans via the actions.
|
||||||
|
"""
|
||||||
|
|
||||||
class Actions(JailThread):
|
|
||||||
|
|
||||||
##
|
|
||||||
# Constructor.
|
|
||||||
#
|
|
||||||
# Initialize the filter object with default values.
|
|
||||||
# @param jail the jail object
|
|
||||||
|
|
||||||
def __init__(self, jail):
|
def __init__(self, jail):
|
||||||
|
"""Initialise an empty Actions instance.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
jail: Jail
|
||||||
|
The jail of which the actions belongs to.
|
||||||
|
"""
|
||||||
JailThread.__init__(self)
|
JailThread.__init__(self)
|
||||||
## The jail which contains this action.
|
## The jail which contains this action.
|
||||||
self.jail = jail
|
self._jail = jail
|
||||||
self.__actions = list()
|
self._actions = dict()
|
||||||
## The ban manager.
|
## The ban manager.
|
||||||
self.__banManager = BanManager()
|
self.__banManager = BanManager()
|
||||||
|
|
||||||
##
|
def add(self, name, pythonModule=None, initOpts=None):
|
||||||
# Adds an action.
|
"""Adds a new action.
|
||||||
#
|
|
||||||
# @param name The action name
|
Add a new action if not already present, defaulting to standard
|
||||||
|
`CommandAction`, or specified Python module.
|
||||||
def addAction(self, name):
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
name : str
|
||||||
|
The name of the action.
|
||||||
|
pythonModule : str, optional
|
||||||
|
Path to Python file which must contain `Action` class.
|
||||||
|
Default None, which means `CommandAction` is used.
|
||||||
|
initOpts : dict, optional
|
||||||
|
Options for Python Action, used as keyword arguments for
|
||||||
|
initialisation. Default None.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
ValueError
|
||||||
|
If action name already exists.
|
||||||
|
RuntimeError
|
||||||
|
If external Python module does not have `Action` class
|
||||||
|
or does not implement necessary methods as per `ActionBase`
|
||||||
|
abstract class.
|
||||||
|
"""
|
||||||
# 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 self._actions:
|
||||||
raise ValueError("Action %s already exists" % name)
|
raise ValueError("Action %s already exists" % name)
|
||||||
action = Action(name)
|
if pythonModule is None:
|
||||||
self.__actions.append(action)
|
action = CommandAction(self._jail, name)
|
||||||
|
else:
|
||||||
##
|
pythonModuleName = os.path.basename(pythonModule.strip(".py"))
|
||||||
# Removes an action.
|
customActionModule = imp.load_source(
|
||||||
#
|
pythonModuleName, pythonModule)
|
||||||
# @param name The action name
|
if not hasattr(customActionModule, "Action"):
|
||||||
|
raise RuntimeError(
|
||||||
def delAction(self, name):
|
"%s module does not have 'Action' class" % pythonModule)
|
||||||
for action in self.__actions:
|
elif not issubclass(customActionModule.Action, ActionBase):
|
||||||
if action.getName() == name:
|
raise RuntimeError(
|
||||||
self.__actions.remove(action)
|
"%s module %s does not implement required methods" % (
|
||||||
return
|
pythonModule, customActionModule.Action.__name__))
|
||||||
raise KeyError("Invalid Action name: %s" % name)
|
action = customActionModule.Action(self._jail, name, **initOpts)
|
||||||
|
self._actions[name] = action
|
||||||
##
|
|
||||||
# Returns an action.
|
def __getitem__(self, name):
|
||||||
#
|
try:
|
||||||
# Raises a KeyError exception if the action does not exist.
|
return self._actions[name]
|
||||||
#
|
except KeyError:
|
||||||
# @param name the action name
|
raise KeyError("Invalid Action name: %s" % name)
|
||||||
# @return the action
|
|
||||||
|
def __delitem__(self, name):
|
||||||
def getAction(self, name):
|
try:
|
||||||
for action in self.__actions:
|
del self._actions[name]
|
||||||
if action.getName() == name:
|
except KeyError:
|
||||||
return action
|
raise KeyError("Invalid Action name: %s" % name)
|
||||||
raise KeyError("Invalid Action name")
|
|
||||||
|
def __iter__(self):
|
||||||
##
|
return iter(self._actions)
|
||||||
# Returns the last defined action.
|
|
||||||
#
|
def __len__(self):
|
||||||
# @return The last defined action.
|
return len(self._actions)
|
||||||
|
|
||||||
def getLastAction(self):
|
def __eq__(self, other): # Required for Threading
|
||||||
action = self.__actions.pop()
|
return False
|
||||||
self.__actions.append(action)
|
|
||||||
return action
|
def __hash__(self): # Required for Threading
|
||||||
|
return id(self)
|
||||||
##
|
|
||||||
# Returns the list of actions
|
|
||||||
#
|
|
||||||
# @return list of actions
|
|
||||||
|
|
||||||
def getActions(self):
|
|
||||||
return self.__actions
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Set the ban time.
|
# Set the ban time.
|
||||||
#
|
#
|
||||||
|
@ -128,34 +147,52 @@ class Actions(JailThread):
|
||||||
|
|
||||||
def getBanTime(self):
|
def getBanTime(self):
|
||||||
return self.__banManager.getBanTime()
|
return self.__banManager.getBanTime()
|
||||||
|
|
||||||
##
|
|
||||||
# Remove a banned IP now, rather than waiting for it to expire, even if set to never expire.
|
|
||||||
#
|
|
||||||
# @return the IP string or 'None' if not unbanned.
|
|
||||||
def removeBannedIP(self, ip):
|
def removeBannedIP(self, ip):
|
||||||
|
"""Removes banned IP calling actions' unban method
|
||||||
|
|
||||||
|
Remove a banned IP now, rather than waiting for it to expire,
|
||||||
|
even if set to never expire.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
ip : str
|
||||||
|
The IP address to unban
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
ValueError
|
||||||
|
If `ip` is not banned
|
||||||
|
"""
|
||||||
# Find the ticket with the IP.
|
# Find the ticket with the IP.
|
||||||
ticket = self.__banManager.getTicketByIP(ip)
|
ticket = self.__banManager.getTicketByIP(ip)
|
||||||
if ticket is not None:
|
if ticket is not None:
|
||||||
# Unban the IP.
|
# Unban the IP.
|
||||||
self.__unBan(ticket)
|
self.__unBan(ticket)
|
||||||
return ip
|
else:
|
||||||
raise ValueError("IP %s is not banned" % ip)
|
raise ValueError("IP %s is not banned" % ip)
|
||||||
|
|
||||||
##
|
|
||||||
# Main loop.
|
|
||||||
#
|
|
||||||
# This function is the main loop of the thread. It checks the Jail
|
|
||||||
# queue and executes commands when an IP address is banned.
|
|
||||||
# @return True when the thread exits nicely
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
"""Main loop for Threading.
|
||||||
|
|
||||||
|
This function is the main loop of the thread. It checks the jail
|
||||||
|
queue and executes commands when an IP address is banned.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
bool
|
||||||
|
True when the thread exits nicely.
|
||||||
|
"""
|
||||||
self.setActive(True)
|
self.setActive(True)
|
||||||
for action in self.__actions:
|
for name, action in self._actions.iteritems():
|
||||||
action.execActionStart()
|
try:
|
||||||
|
action.start()
|
||||||
|
except Exception as e:
|
||||||
|
logSys.error("Failed to start jail '%s' action '%s': %s",
|
||||||
|
self._jail.getName(), name, 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")
|
||||||
ret = self.__checkBan()
|
ret = self.__checkBan()
|
||||||
if not ret:
|
if not ret:
|
||||||
self.__checkUnBan()
|
self.__checkUnBan()
|
||||||
|
@ -163,94 +200,116 @@ class Actions(JailThread):
|
||||||
else:
|
else:
|
||||||
time.sleep(self.getSleepTime())
|
time.sleep(self.getSleepTime())
|
||||||
self.__flushBan()
|
self.__flushBan()
|
||||||
for action in self.__actions:
|
for name, action in self._actions.iteritems():
|
||||||
action.execActionStop()
|
try:
|
||||||
logSys.debug(self.jail.getName() + ": action terminated")
|
action.stop()
|
||||||
|
except Exception as e:
|
||||||
|
logSys.error("Failed to stop jail '%s' action '%s': %s",
|
||||||
|
self._jail.getName(), name, e)
|
||||||
|
logSys.debug(self._jail.getName() + ": action terminated")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
##
|
|
||||||
# Check for IP address to ban.
|
|
||||||
#
|
|
||||||
# Look in the Jail queue for FailTicket. If a ticket is available,
|
|
||||||
# it executes the "ban" command and add a ticket to the BanManager.
|
|
||||||
# @return True if an IP address get banned
|
|
||||||
|
|
||||||
def __checkBan(self):
|
def __checkBan(self):
|
||||||
ticket = self.jail.getFailTicket()
|
"""Check for IP address to ban.
|
||||||
|
|
||||||
|
Look in the jail queue for FailTicket. If a ticket is available,
|
||||||
|
it executes the "ban" command and adds a ticket to the BanManager.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
bool
|
||||||
|
True if an IP address get banned.
|
||||||
|
"""
|
||||||
|
ticket = self._jail.getFailTicket()
|
||||||
if ticket != False:
|
if ticket != False:
|
||||||
aInfo = dict()
|
aInfo = CallingMap()
|
||||||
bTicket = BanManager.createBanTicket(ticket)
|
bTicket = BanManager.createBanTicket(ticket)
|
||||||
aInfo["ip"] = bTicket.getIP()
|
aInfo["ip"] = bTicket.getIP()
|
||||||
aInfo["failures"] = bTicket.getAttempt()
|
aInfo["failures"] = bTicket.getAttempt()
|
||||||
aInfo["time"] = bTicket.getTime()
|
aInfo["time"] = bTicket.getTime()
|
||||||
aInfo["matches"] = "\n".join(bTicket.getMatches())
|
aInfo["matches"] = "\n".join(bTicket.getMatches())
|
||||||
if self.jail.getDatabase() is not None:
|
if self._jail.getDatabase() is not None:
|
||||||
aInfo["ipmatches"] = lambda: "\n".join(
|
aInfo["ipmatches"] = lambda: "\n".join(
|
||||||
self.jail.getDatabase().getBansMerged(
|
self._jail.getDatabase().getBansMerged(
|
||||||
ip=bTicket.getIP()).getMatches())
|
ip=bTicket.getIP()).getMatches())
|
||||||
aInfo["ipjailmatches"] = lambda: "\n".join(
|
aInfo["ipjailmatches"] = lambda: "\n".join(
|
||||||
self.jail.getDatabase().getBansMerged(
|
self._jail.getDatabase().getBansMerged(
|
||||||
ip=bTicket.getIP(), jail=self.jail).getMatches())
|
ip=bTicket.getIP(), jail=self._jail).getMatches())
|
||||||
aInfo["ipfailures"] = lambda: "\n".join(
|
aInfo["ipfailures"] = lambda: "\n".join(
|
||||||
self.jail.getDatabase().getBansMerged(
|
self._jail.getDatabase().getBansMerged(
|
||||||
ip=bTicket.getIP()).getAttempt())
|
ip=bTicket.getIP()).getAttempt())
|
||||||
aInfo["ipjailfailures"] = lambda: "\n".join(
|
aInfo["ipjailfailures"] = lambda: "\n".join(
|
||||||
self.jail.getDatabase().getBansMerged(
|
self._jail.getDatabase().getBansMerged(
|
||||||
ip=bTicket.getIP(), jail=self.jail).getAttempt())
|
ip=bTicket.getIP(), jail=self._jail).getAttempt())
|
||||||
if self.__banManager.addBanTicket(bTicket):
|
if self.__banManager.addBanTicket(bTicket):
|
||||||
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 name, action in self._actions.iteritems():
|
||||||
action.execActionBan(aInfo)
|
try:
|
||||||
|
action.ban(aInfo)
|
||||||
|
except Exception as e:
|
||||||
|
logSys.error(
|
||||||
|
"Failed to execute ban jail '%s' action '%s': %s",
|
||||||
|
self._jail.getName(), name, 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(),
|
||||||
aInfo["ip"]))
|
aInfo["ip"]))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
##
|
|
||||||
# Check for IP address to unban.
|
|
||||||
#
|
|
||||||
# Unban IP address which are outdated.
|
|
||||||
|
|
||||||
def __checkUnBan(self):
|
def __checkUnBan(self):
|
||||||
|
"""Check for IP address to unban.
|
||||||
|
|
||||||
|
Unban IP addresses which are outdated.
|
||||||
|
"""
|
||||||
for ticket in self.__banManager.unBanList(MyTime.time()):
|
for ticket in self.__banManager.unBanList(MyTime.time()):
|
||||||
self.__unBan(ticket)
|
self.__unBan(ticket)
|
||||||
|
|
||||||
##
|
|
||||||
# Flush the ban list.
|
|
||||||
#
|
|
||||||
# Unban all IP address which are still in the banning list.
|
|
||||||
|
|
||||||
def __flushBan(self):
|
def __flushBan(self):
|
||||||
|
"""Flush the ban list.
|
||||||
|
|
||||||
|
Unban all IP address which are still in the banning list.
|
||||||
|
"""
|
||||||
logSys.debug("Flush ban list")
|
logSys.debug("Flush ban list")
|
||||||
for ticket in self.__banManager.flushBanList():
|
for ticket in self.__banManager.flushBanList():
|
||||||
self.__unBan(ticket)
|
self.__unBan(ticket)
|
||||||
|
|
||||||
##
|
|
||||||
# Unbans host corresponding to the ticket.
|
|
||||||
#
|
|
||||||
# Executes the actions in order to unban the host given in the
|
|
||||||
# ticket.
|
|
||||||
|
|
||||||
def __unBan(self, ticket):
|
def __unBan(self, ticket):
|
||||||
|
"""Unbans host corresponding to the ticket.
|
||||||
|
|
||||||
|
Executes the actions in order to unban the host given in the
|
||||||
|
ticket.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
ticket : FailTicket
|
||||||
|
Ticket of failures of which to unban
|
||||||
|
"""
|
||||||
aInfo = dict()
|
aInfo = dict()
|
||||||
aInfo["ip"] = ticket.getIP()
|
aInfo["ip"] = ticket.getIP()
|
||||||
aInfo["failures"] = ticket.getAttempt()
|
aInfo["failures"] = ticket.getAttempt()
|
||||||
aInfo["time"] = ticket.getTime()
|
aInfo["time"] = ticket.getTime()
|
||||||
aInfo["matches"] = "".join(ticket.getMatches())
|
aInfo["matches"] = "".join(ticket.getMatches())
|
||||||
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 name, action in self._actions.iteritems():
|
||||||
action.execActionUnban(aInfo)
|
try:
|
||||||
|
action.unban(aInfo)
|
||||||
|
except Exception as e:
|
||||||
##
|
logSys.error(
|
||||||
# Get the status of the filter.
|
"Failed to execute unban jail '%s' action '%s': %s",
|
||||||
#
|
self._jail.getName(), name, e)
|
||||||
# Get some informations about the filter state such as the total
|
|
||||||
# number of failures.
|
|
||||||
# @return a list with tuple
|
|
||||||
|
|
||||||
def status(self):
|
def status(self):
|
||||||
|
"""Get the status of the filter.
|
||||||
|
|
||||||
|
Get some informations about the filter state such as the total
|
||||||
|
number of failures.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
list
|
||||||
|
List of tuple pairs, each containing a description and value
|
||||||
|
for general status information.
|
||||||
|
"""
|
||||||
ret = [("Currently banned", self.__banManager.size()),
|
ret = [("Currently banned", self.__banManager.size()),
|
||||||
("Total banned", self.__banManager.getBanTotal()),
|
("Total banned", self.__banManager.getBanTotal()),
|
||||||
("IP list", self.__banManager.getBanList())]
|
("IP list", self.__banManager.getBanList())]
|
||||||
|
|
|
@ -27,7 +27,7 @@ __license__ = "GPL"
|
||||||
from pickle import dumps, loads, HIGHEST_PROTOCOL
|
from pickle import dumps, loads, HIGHEST_PROTOCOL
|
||||||
import asyncore, asynchat, socket, os, logging, sys, traceback, fcntl
|
import asyncore, asynchat, socket, os, logging, sys, traceback, fcntl
|
||||||
|
|
||||||
from fail2ban import helpers
|
from .. import helpers
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = logging.getLogger(__name__)
|
||||||
|
|
|
@ -24,10 +24,11 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
from ticket import BanTicket
|
|
||||||
from threading import Lock
|
|
||||||
from mytime import MyTime
|
|
||||||
import logging
|
import logging
|
||||||
|
from threading import Lock
|
||||||
|
|
||||||
|
from .ticket import BanTicket
|
||||||
|
from .mytime import MyTime
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = logging.getLogger(__name__)
|
||||||
|
|
|
@ -29,8 +29,8 @@ import json
|
||||||
import locale
|
import locale
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
from fail2ban.server.mytime import MyTime
|
from .mytime import MyTime
|
||||||
from fail2ban.server.ticket import FailTicket
|
from .ticket import FailTicket
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = logging.getLogger(__name__)
|
||||||
|
|
|
@ -22,10 +22,10 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import sys, time, logging
|
import sys, time, logging
|
||||||
|
|
||||||
from datetemplate import DatePatternRegex, DateTai64n, DateEpoch, DateISO8601
|
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
|
|
||||||
|
from .datetemplate import DatePatternRegex, DateTai64n, DateEpoch, DateISO8601
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
|
@ -25,14 +25,13 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import re, time, calendar
|
import re, time, calendar
|
||||||
|
import logging
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
from mytime import MyTime
|
from .mytime import MyTime
|
||||||
import iso8601
|
from . import iso8601
|
||||||
|
|
||||||
import logging
|
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -24,11 +24,12 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
from faildata import FailData
|
|
||||||
from ticket import FailTicket
|
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from .faildata import FailData
|
||||||
|
from .ticket import FailTicket
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
|
@ -21,18 +21,17 @@ __author__ = "Cyril Jaquier and Fail2Ban Contributors"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2013 Yaroslav Halchenko"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2013 Yaroslav Halchenko"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
from failmanager import FailManagerEmpty
|
|
||||||
from failmanager import FailManager
|
|
||||||
from ticket import FailTicket
|
|
||||||
from jailthread import JailThread
|
|
||||||
from datedetector import DateDetector
|
|
||||||
from datetemplate import DatePatternRegex, DateISO8601, DateEpoch, DateTai64n
|
|
||||||
from mytime import MyTime
|
|
||||||
from failregex import FailRegex, Regex, RegexException
|
|
||||||
from action import Action
|
|
||||||
|
|
||||||
import logging, re, os, fcntl, time, sys, locale, codecs
|
import logging, re, os, fcntl, time, sys, locale, codecs
|
||||||
|
|
||||||
|
from .failmanager import FailManagerEmpty, FailManager
|
||||||
|
from .ticket import FailTicket
|
||||||
|
from .jailthread import JailThread
|
||||||
|
from .datedetector import DateDetector
|
||||||
|
from .datetemplate import DatePatternRegex, DateISO8601, DateEpoch, DateTai64n
|
||||||
|
from .mytime import MyTime
|
||||||
|
from .failregex import FailRegex, Regex, RegexException
|
||||||
|
from .action import CommandAction
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -378,9 +377,9 @@ class Filter(JailThread):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if self.__ignoreCommand:
|
if self.__ignoreCommand:
|
||||||
command = Action.replaceTag(self.__ignoreCommand, { 'ip': ip } )
|
command = CommandAction.replaceTag(self.__ignoreCommand, { 'ip': ip } )
|
||||||
logSys.debug('ignore command: ' + command)
|
logSys.debug('ignore command: ' + command)
|
||||||
return Action.executeCmd(command)
|
return CommandAction.executeCmd(command)
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
|
@ -23,11 +23,13 @@ __author__ = "Cyril Jaquier, Yaroslav Halchenko"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2012 Yaroslav Halchenko"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2012 Yaroslav Halchenko"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
from failmanager import FailManagerEmpty
|
import time, logging, fcntl
|
||||||
from filter import FileFilter
|
|
||||||
from mytime import MyTime
|
|
||||||
|
|
||||||
import time, logging, gamin, fcntl
|
import gamin
|
||||||
|
|
||||||
|
from .failmanager import FailManagerEmpty
|
||||||
|
from .filter import FileFilter
|
||||||
|
from .mytime import MyTime
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = logging.getLogger(__name__)
|
||||||
|
|
|
@ -24,12 +24,12 @@ __author__ = "Cyril Jaquier, Yaroslav Halchenko"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier; 2012 Yaroslav Halchenko"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier; 2012 Yaroslav Halchenko"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
from failmanager import FailManagerEmpty
|
|
||||||
from filter import FileFilter
|
|
||||||
from mytime import MyTime
|
|
||||||
|
|
||||||
import time, logging, os
|
import time, logging, os
|
||||||
|
|
||||||
|
from .failmanager import FailManagerEmpty
|
||||||
|
from .filter import FileFilter
|
||||||
|
from .mytime import MyTime
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
|
@ -24,13 +24,12 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2012 Lee Clemens, 2012 Y
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import time, logging, pyinotify
|
import time, logging, pyinotify
|
||||||
|
|
||||||
from distutils.version import LooseVersion
|
from distutils.version import LooseVersion
|
||||||
from os.path import dirname, sep as pathsep
|
from os.path import dirname, sep as pathsep
|
||||||
|
|
||||||
from failmanager import FailManagerEmpty
|
from .failmanager import FailManagerEmpty
|
||||||
from filter import FileFilter
|
from .filter import FileFilter
|
||||||
from mytime import MyTime
|
from .mytime import MyTime
|
||||||
|
|
||||||
|
|
||||||
if not hasattr(pyinotify, '__version__') \
|
if not hasattr(pyinotify, '__version__') \
|
||||||
|
|
|
@ -29,9 +29,9 @@ from systemd import journal
|
||||||
if LooseVersion(getattr(journal, '__version__', "0")) < '204':
|
if LooseVersion(getattr(journal, '__version__', "0")) < '204':
|
||||||
raise ImportError("Fail2Ban requires systemd >= 204")
|
raise ImportError("Fail2Ban requires systemd >= 204")
|
||||||
|
|
||||||
from failmanager import FailManagerEmpty
|
from .failmanager import FailManagerEmpty
|
||||||
from filter import JournalFilter
|
from .filter import JournalFilter
|
||||||
from mytime import MyTime
|
from .mytime import MyTime
|
||||||
|
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
|
|
|
@ -25,7 +25,7 @@ __license__ = "GPL"
|
||||||
|
|
||||||
import Queue, logging
|
import Queue, logging
|
||||||
|
|
||||||
from actions import Actions
|
from .actions import Actions
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = logging.getLogger(__name__)
|
||||||
|
@ -71,7 +71,7 @@ class Jail:
|
||||||
"%r was requested" % (b, backend))
|
"%r was requested" % (b, backend))
|
||||||
else:
|
else:
|
||||||
logSys.info("Initiated %r backend" % b)
|
logSys.info("Initiated %r backend" % b)
|
||||||
self.__action = Actions(self)
|
self.__actions = Actions(self)
|
||||||
return # we are done
|
return # we are done
|
||||||
except ImportError, e:
|
except ImportError, e:
|
||||||
logSys.debug(
|
logSys.debug(
|
||||||
|
@ -123,11 +123,17 @@ class Jail:
|
||||||
def getDatabase(self):
|
def getDatabase(self):
|
||||||
return self.__db
|
return self.__db
|
||||||
|
|
||||||
def getFilter(self):
|
@property
|
||||||
|
def filter(self):
|
||||||
|
"""The filter which the jail is using to monitor log files.
|
||||||
|
"""
|
||||||
return self.__filter
|
return self.__filter
|
||||||
|
|
||||||
def getAction(self):
|
@property
|
||||||
return self.__action
|
def actions(self):
|
||||||
|
"""Actions object used to manage actions for jail.
|
||||||
|
"""
|
||||||
|
return self.__actions
|
||||||
|
|
||||||
def putFailTicket(self, ticket):
|
def putFailTicket(self, ticket):
|
||||||
self.__queue.put(ticket)
|
self.__queue.put(ticket)
|
||||||
|
@ -142,36 +148,36 @@ class Jail:
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
self.__filter.start()
|
self.__filter.start()
|
||||||
self.__action.start()
|
self.__actions.start()
|
||||||
# Restore any previous valid bans from the database
|
# Restore any previous valid bans from the database
|
||||||
if self.__db is not None:
|
if self.__db is not None:
|
||||||
for ticket in self.__db.getBans(
|
for ticket in self.__db.getBans(
|
||||||
jail=self, bantime=self.__action.getBanTime()):
|
jail=self, bantime=self.__actions.getBanTime()):
|
||||||
self.__queue.put(ticket)
|
self.__queue.put(ticket)
|
||||||
logSys.info("Jail '%s' started" % self.__name)
|
logSys.info("Jail '%s' started" % self.__name)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.__filter.stop()
|
self.__filter.stop()
|
||||||
self.__action.stop()
|
self.__actions.stop()
|
||||||
self.__filter.join()
|
self.__filter.join()
|
||||||
self.__action.join()
|
self.__actions.join()
|
||||||
logSys.info("Jail '%s' stopped" % self.__name)
|
logSys.info("Jail '%s' stopped" % self.__name)
|
||||||
|
|
||||||
def isAlive(self):
|
def isAlive(self):
|
||||||
isAlive0 = self.__filter.isAlive()
|
isAlive0 = self.__filter.isAlive()
|
||||||
isAlive1 = self.__action.isAlive()
|
isAlive1 = self.__actions.isAlive()
|
||||||
return isAlive0 or isAlive1
|
return isAlive0 or isAlive1
|
||||||
|
|
||||||
def setIdle(self, value):
|
def setIdle(self, value):
|
||||||
self.__filter.setIdle(value)
|
self.__filter.setIdle(value)
|
||||||
self.__action.setIdle(value)
|
self.__actions.setIdle(value)
|
||||||
|
|
||||||
def getIdle(self):
|
def getIdle(self):
|
||||||
return self.__filter.getIdle() or self.__action.getIdle()
|
return self.__filter.getIdle() or self.__actions.getIdle()
|
||||||
|
|
||||||
def getStatus(self):
|
def getStatus(self):
|
||||||
fStatus = self.__filter.status()
|
fStatus = self.__filter.status()
|
||||||
aStatus = self.__action.status()
|
aStatus = self.__actions.status()
|
||||||
ret = [("filter", fStatus),
|
ret = [("filter", fStatus),
|
||||||
("action", aStatus)]
|
("action", aStatus)]
|
||||||
return ret
|
return ret
|
||||||
|
|
|
@ -21,137 +21,84 @@ __author__ = "Cyril Jaquier, Yaroslav Halchenko"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2013- Yaroslav Halchenko"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2013- Yaroslav Halchenko"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
from fail2ban.exceptions import DuplicateJailException, UnknownJailException
|
|
||||||
|
|
||||||
from jail import Jail
|
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
|
from collections import Mapping
|
||||||
|
|
||||||
##
|
from ..exceptions import DuplicateJailException, UnknownJailException
|
||||||
# Handles the jails.
|
from .jail import Jail
|
||||||
#
|
|
||||||
# This class handles the jails. Creation, deletion or access to a jail must be
|
|
||||||
# done through this class. This class is thread-safe which is not the case of
|
class Jails(Mapping):
|
||||||
# the jail itself, including filter and actions.
|
"""Handles the jails.
|
||||||
|
|
||||||
|
This class handles the jails. Creation, deletion or access to a jail
|
||||||
|
must be done through this class. This class is thread-safe which is
|
||||||
|
not the case of the jail itself, including filter and actions. This
|
||||||
|
class is based on Mapping type, and the `add` method must be used to
|
||||||
|
add additional jails.
|
||||||
|
"""
|
||||||
|
|
||||||
class Jails:
|
|
||||||
|
|
||||||
##
|
|
||||||
# Constructor.
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
"""Initialise an empty Jails instance.
|
||||||
|
"""
|
||||||
self.__lock = Lock()
|
self.__lock = Lock()
|
||||||
self.__jails = dict()
|
self._jails = dict()
|
||||||
|
|
||||||
##
|
|
||||||
# Adds a jail.
|
|
||||||
#
|
|
||||||
# Adds a new jail which should use the given backend. Raises a
|
|
||||||
# <code>DuplicateJailException</code> if the jail is already defined.
|
|
||||||
# @param name The name of the jail
|
|
||||||
# @param backend The backend to use
|
|
||||||
|
|
||||||
def add(self, name, backend, db=None):
|
def add(self, name, backend, db=None):
|
||||||
|
"""Adds a jail.
|
||||||
|
|
||||||
|
Adds a new jail if not already present which should use the
|
||||||
|
given backend.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
name : str
|
||||||
|
The name of the jail.
|
||||||
|
backend : str
|
||||||
|
The backend to use.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
DuplicateJailException
|
||||||
|
If jail name is already present.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
self.__lock.acquire()
|
self.__lock.acquire()
|
||||||
if self.__jails.has_key(name):
|
if name in self._jails:
|
||||||
raise DuplicateJailException(name)
|
raise DuplicateJailException(name)
|
||||||
else:
|
else:
|
||||||
self.__jails[name] = Jail(name, backend, db)
|
self._jails[name] = Jail(name, backend, db)
|
||||||
finally:
|
|
||||||
self.__lock.release()
|
|
||||||
|
|
||||||
##
|
|
||||||
# Removes a jail.
|
|
||||||
#
|
|
||||||
# Removes the jail <code>name</code>. Raise an <code>UnknownJailException</code>
|
|
||||||
# if the jail does not exist.
|
|
||||||
# @param name The name of the jail
|
|
||||||
|
|
||||||
def remove(self, name):
|
|
||||||
try:
|
|
||||||
self.__lock.acquire()
|
|
||||||
if self.__jails.has_key(name):
|
|
||||||
del self.__jails[name]
|
|
||||||
else:
|
|
||||||
raise UnknownJailException(name)
|
|
||||||
finally:
|
|
||||||
self.__lock.release()
|
|
||||||
|
|
||||||
##
|
|
||||||
# Returns a jail.
|
|
||||||
#
|
|
||||||
# Returns the jail <code>name</code>. Raise an <code>UnknownJailException</code>
|
|
||||||
# if the jail does not exist.
|
|
||||||
# @param name The name of the jail
|
|
||||||
|
|
||||||
def get(self, name):
|
|
||||||
try:
|
|
||||||
self.__lock.acquire()
|
|
||||||
if self.__jails.has_key(name):
|
|
||||||
jail = self.__jails[name]
|
|
||||||
return jail
|
|
||||||
else:
|
|
||||||
raise UnknownJailException(name)
|
|
||||||
finally:
|
|
||||||
self.__lock.release()
|
|
||||||
|
|
||||||
##
|
|
||||||
# Returns an action class instance.
|
|
||||||
#
|
|
||||||
# Returns the action object of the jail <code>name</code>. Raise an
|
|
||||||
# <code>UnknownJailException</code> if the jail does not exist.
|
|
||||||
# @param name The name of the jail
|
|
||||||
|
|
||||||
def getAction(self, name):
|
|
||||||
try:
|
|
||||||
self.__lock.acquire()
|
|
||||||
if self.__jails.has_key(name):
|
|
||||||
action = self.__jails[name].getAction()
|
|
||||||
return action
|
|
||||||
else:
|
|
||||||
raise UnknownJailException(name)
|
|
||||||
finally:
|
|
||||||
self.__lock.release()
|
|
||||||
|
|
||||||
##
|
|
||||||
# Returns a filter class instance.
|
|
||||||
#
|
|
||||||
# Returns the filter object of the jail <code>name</code>. Raise an
|
|
||||||
# <code>UnknownJailException</code> if the jail does not exist.
|
|
||||||
# @param name The name of the jail
|
|
||||||
|
|
||||||
def getFilter(self, name):
|
|
||||||
try:
|
|
||||||
self.__lock.acquire()
|
|
||||||
if self.__jails.has_key(name):
|
|
||||||
action = self.__jails[name].getFilter()
|
|
||||||
return action
|
|
||||||
else:
|
|
||||||
raise UnknownJailException(name)
|
|
||||||
finally:
|
|
||||||
self.__lock.release()
|
|
||||||
|
|
||||||
##
|
|
||||||
# Returns the jails.
|
|
||||||
#
|
|
||||||
# Returns a copy of the jails list.
|
|
||||||
|
|
||||||
def getAll(self):
|
|
||||||
try:
|
|
||||||
self.__lock.acquire()
|
|
||||||
return self.__jails.copy()
|
|
||||||
finally:
|
|
||||||
self.__lock.release()
|
|
||||||
|
|
||||||
##
|
|
||||||
# Returns the size of the jails.
|
|
||||||
#
|
|
||||||
# Returns the number of jails.
|
|
||||||
|
|
||||||
def size(self):
|
|
||||||
try:
|
|
||||||
self.__lock.acquire()
|
|
||||||
return len(self.__jails)
|
|
||||||
finally:
|
finally:
|
||||||
self.__lock.release()
|
self.__lock.release()
|
||||||
|
|
||||||
|
def __getitem__(self, name):
|
||||||
|
try:
|
||||||
|
self.__lock.acquire()
|
||||||
|
return self._jails[name]
|
||||||
|
except KeyError:
|
||||||
|
raise UnknownJailException(name)
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
def __delitem__(self, name):
|
||||||
|
try:
|
||||||
|
self.__lock.acquire()
|
||||||
|
del self._jails[name]
|
||||||
|
except KeyError:
|
||||||
|
raise UnknownJailException(name)
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
try:
|
||||||
|
self.__lock.acquire()
|
||||||
|
return len(self._jails)
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
try:
|
||||||
|
self.__lock.acquire()
|
||||||
|
return iter(self._jails)
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
||||||
|
|
|
@ -25,15 +25,16 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
from threading import Lock, RLock
|
from threading import Lock, RLock
|
||||||
from jails import Jails
|
|
||||||
from filter import FileFilter, JournalFilter
|
|
||||||
from transmitter import Transmitter
|
|
||||||
from asyncserver import AsyncServer
|
|
||||||
from asyncserver import AsyncServerException
|
|
||||||
from database import Fail2BanDb
|
|
||||||
from fail2ban import version
|
|
||||||
import logging, logging.handlers, sys, os, signal
|
import logging, logging.handlers, sys, os, signal
|
||||||
|
|
||||||
|
from .jails import Jails
|
||||||
|
from .filter import FileFilter, JournalFilter
|
||||||
|
from .transmitter import Transmitter
|
||||||
|
from .asyncserver import AsyncServer, AsyncServerException
|
||||||
|
from .database import Fail2BanDb
|
||||||
|
from .action import CommandAction
|
||||||
|
from .. import version
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -121,10 +122,10 @@ class Server:
|
||||||
def addJail(self, name, backend):
|
def addJail(self, name, backend):
|
||||||
self.__jails.add(name, backend, self.__db)
|
self.__jails.add(name, backend, self.__db)
|
||||||
if self.__db is not None:
|
if self.__db is not None:
|
||||||
self.__db.addJail(self.__jails.get(name))
|
self.__db.addJail(self.__jails[name])
|
||||||
|
|
||||||
def delJail(self, name):
|
def delJail(self, name):
|
||||||
self.__jails.remove(name)
|
del self.__jails[name]
|
||||||
if self.__db is not None:
|
if self.__db is not None:
|
||||||
self.__db.delJailName(name)
|
self.__db.delJailName(name)
|
||||||
|
|
||||||
|
@ -132,7 +133,7 @@ class Server:
|
||||||
try:
|
try:
|
||||||
self.__lock.acquire()
|
self.__lock.acquire()
|
||||||
if not self.isAlive(name):
|
if not self.isAlive(name):
|
||||||
self.__jails.get(name).start()
|
self.__jails[name].start()
|
||||||
finally:
|
finally:
|
||||||
self.__lock.release()
|
self.__lock.release()
|
||||||
|
|
||||||
|
@ -141,7 +142,7 @@ class Server:
|
||||||
try:
|
try:
|
||||||
self.__lock.acquire()
|
self.__lock.acquire()
|
||||||
if self.isAlive(name):
|
if self.isAlive(name):
|
||||||
self.__jails.get(name).stop()
|
self.__jails[name].stop()
|
||||||
self.delJail(name)
|
self.delJail(name)
|
||||||
finally:
|
finally:
|
||||||
self.__lock.release()
|
self.__lock.release()
|
||||||
|
@ -150,43 +151,43 @@ class Server:
|
||||||
logSys.info("Stopping all jails")
|
logSys.info("Stopping all jails")
|
||||||
try:
|
try:
|
||||||
self.__lock.acquire()
|
self.__lock.acquire()
|
||||||
for jail in self.__jails.getAll():
|
for jail in self.__jails.keys():
|
||||||
self.stopJail(jail)
|
self.stopJail(jail)
|
||||||
finally:
|
finally:
|
||||||
self.__lock.release()
|
self.__lock.release()
|
||||||
|
|
||||||
def isAlive(self, name):
|
def isAlive(self, name):
|
||||||
return self.__jails.get(name).isAlive()
|
return self.__jails[name].isAlive()
|
||||||
|
|
||||||
def setIdleJail(self, name, value):
|
def setIdleJail(self, name, value):
|
||||||
self.__jails.get(name).setIdle(value)
|
self.__jails[name].setIdle(value)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def getIdleJail(self, name):
|
def getIdleJail(self, name):
|
||||||
return self.__jails.get(name).getIdle()
|
return self.__jails[name].getIdle()
|
||||||
|
|
||||||
# Filter
|
# Filter
|
||||||
def addIgnoreIP(self, name, ip):
|
def addIgnoreIP(self, name, ip):
|
||||||
self.__jails.getFilter(name).addIgnoreIP(ip)
|
self.__jails[name].filter.addIgnoreIP(ip)
|
||||||
|
|
||||||
def delIgnoreIP(self, name, ip):
|
def delIgnoreIP(self, name, ip):
|
||||||
self.__jails.getFilter(name).delIgnoreIP(ip)
|
self.__jails[name].filter.delIgnoreIP(ip)
|
||||||
|
|
||||||
def getIgnoreIP(self, name):
|
def getIgnoreIP(self, name):
|
||||||
return self.__jails.getFilter(name).getIgnoreIP()
|
return self.__jails[name].filter.getIgnoreIP()
|
||||||
|
|
||||||
def addLogPath(self, name, fileName, tail=False):
|
def addLogPath(self, name, fileName, tail=False):
|
||||||
filter_ = self.__jails.getFilter(name)
|
filter_ = self.__jails[name].filter
|
||||||
if isinstance(filter_, FileFilter):
|
if isinstance(filter_, FileFilter):
|
||||||
filter_.addLogPath(fileName, tail)
|
filter_.addLogPath(fileName, tail)
|
||||||
|
|
||||||
def delLogPath(self, name, fileName):
|
def delLogPath(self, name, fileName):
|
||||||
filter_ = self.__jails.getFilter(name)
|
filter_ = self.__jails[name].filter
|
||||||
if isinstance(filter_, FileFilter):
|
if isinstance(filter_, FileFilter):
|
||||||
filter_.delLogPath(fileName)
|
filter_.delLogPath(fileName)
|
||||||
|
|
||||||
def getLogPath(self, name):
|
def getLogPath(self, name):
|
||||||
filter_ = self.__jails.getFilter(name)
|
filter_ = self.__jails[name].filter
|
||||||
if isinstance(filter_, FileFilter):
|
if isinstance(filter_, FileFilter):
|
||||||
return [m.getFileName()
|
return [m.getFileName()
|
||||||
for m in filter_.getLogPath()]
|
for m in filter_.getLogPath()]
|
||||||
|
@ -195,17 +196,17 @@ class Server:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def addJournalMatch(self, name, match): # pragma: systemd no cover
|
def addJournalMatch(self, name, match): # pragma: systemd no cover
|
||||||
filter_ = self.__jails.getFilter(name)
|
filter_ = self.__jails[name].filter
|
||||||
if isinstance(filter_, JournalFilter):
|
if isinstance(filter_, JournalFilter):
|
||||||
filter_.addJournalMatch(match)
|
filter_.addJournalMatch(match)
|
||||||
|
|
||||||
def delJournalMatch(self, name, match): # pragma: systemd no cover
|
def delJournalMatch(self, name, match): # pragma: systemd no cover
|
||||||
filter_ = self.__jails.getFilter(name)
|
filter_ = self.__jails[name].filter
|
||||||
if isinstance(filter_, JournalFilter):
|
if isinstance(filter_, JournalFilter):
|
||||||
filter_.delJournalMatch(match)
|
filter_.delJournalMatch(match)
|
||||||
|
|
||||||
def getJournalMatch(self, name): # pragma: systemd no cover
|
def getJournalMatch(self, name): # pragma: systemd no cover
|
||||||
filter_ = self.__jails.getFilter(name)
|
filter_ = self.__jails[name].filter
|
||||||
if isinstance(filter_, JournalFilter):
|
if isinstance(filter_, JournalFilter):
|
||||||
return filter_.getJournalMatch()
|
return filter_.getJournalMatch()
|
||||||
else:
|
else:
|
||||||
|
@ -213,154 +214,109 @@ class Server:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def setLogEncoding(self, name, encoding):
|
def setLogEncoding(self, name, encoding):
|
||||||
filter_ = self.__jails.getFilter(name)
|
filter_ = self.__jails[name].filter
|
||||||
if isinstance(filter_, FileFilter):
|
if isinstance(filter_, FileFilter):
|
||||||
filter_.setLogEncoding(encoding)
|
filter_.setLogEncoding(encoding)
|
||||||
|
|
||||||
def getLogEncoding(self, name):
|
def getLogEncoding(self, name):
|
||||||
filter_ = self.__jails.getFilter(name)
|
filter_ = self.__jails[name].filter
|
||||||
if isinstance(filter_, FileFilter):
|
if isinstance(filter_, FileFilter):
|
||||||
return filter_.getLogEncoding()
|
return filter_.getLogEncoding()
|
||||||
|
|
||||||
def setFindTime(self, name, value):
|
def setFindTime(self, name, value):
|
||||||
self.__jails.getFilter(name).setFindTime(value)
|
self.__jails[name].filter.setFindTime(value)
|
||||||
|
|
||||||
def getFindTime(self, name):
|
def getFindTime(self, name):
|
||||||
return self.__jails.getFilter(name).getFindTime()
|
return self.__jails[name].filter.getFindTime()
|
||||||
|
|
||||||
def setDatePattern(self, name, pattern):
|
def setDatePattern(self, name, pattern):
|
||||||
self.__jails.getFilter(name).setDatePattern(pattern)
|
self.__jails[name].filter.setDatePattern(pattern)
|
||||||
|
|
||||||
def getDatePattern(self, name):
|
def getDatePattern(self, name):
|
||||||
return self.__jails.getFilter(name).getDatePattern()
|
return self.__jails[name].filter.getDatePattern()
|
||||||
|
|
||||||
def setIgnoreCommand(self, name, value):
|
def setIgnoreCommand(self, name, value):
|
||||||
self.__jails.getFilter(name).setIgnoreCommand(value)
|
self.__jails[name].filter.setIgnoreCommand(value)
|
||||||
|
|
||||||
def getIgnoreCommand(self, name):
|
def getIgnoreCommand(self, name):
|
||||||
return self.__jails.getFilter(name).getIgnoreCommand()
|
return self.__jails[name].filter.getIgnoreCommand()
|
||||||
|
|
||||||
def addFailRegex(self, name, value):
|
def addFailRegex(self, name, value):
|
||||||
self.__jails.getFilter(name).addFailRegex(value)
|
self.__jails[name].filter.addFailRegex(value)
|
||||||
|
|
||||||
def delFailRegex(self, name, index):
|
def delFailRegex(self, name, index):
|
||||||
self.__jails.getFilter(name).delFailRegex(index)
|
self.__jails[name].filter.delFailRegex(index)
|
||||||
|
|
||||||
def getFailRegex(self, name):
|
def getFailRegex(self, name):
|
||||||
return self.__jails.getFilter(name).getFailRegex()
|
return self.__jails[name].filter.getFailRegex()
|
||||||
|
|
||||||
def addIgnoreRegex(self, name, value):
|
def addIgnoreRegex(self, name, value):
|
||||||
self.__jails.getFilter(name).addIgnoreRegex(value)
|
self.__jails[name].filter.addIgnoreRegex(value)
|
||||||
|
|
||||||
def delIgnoreRegex(self, name, index):
|
def delIgnoreRegex(self, name, index):
|
||||||
self.__jails.getFilter(name).delIgnoreRegex(index)
|
self.__jails[name].filter.delIgnoreRegex(index)
|
||||||
|
|
||||||
def getIgnoreRegex(self, name):
|
def getIgnoreRegex(self, name):
|
||||||
return self.__jails.getFilter(name).getIgnoreRegex()
|
return self.__jails[name].filter.getIgnoreRegex()
|
||||||
|
|
||||||
def setUseDns(self, name, value):
|
def setUseDns(self, name, value):
|
||||||
self.__jails.getFilter(name).setUseDns(value)
|
self.__jails[name].filter.setUseDns(value)
|
||||||
|
|
||||||
def getUseDns(self, name):
|
def getUseDns(self, name):
|
||||||
return self.__jails.getFilter(name).getUseDns()
|
return self.__jails[name].filter.getUseDns()
|
||||||
|
|
||||||
def setMaxRetry(self, name, value):
|
def setMaxRetry(self, name, value):
|
||||||
self.__jails.getFilter(name).setMaxRetry(value)
|
self.__jails[name].filter.setMaxRetry(value)
|
||||||
|
|
||||||
def getMaxRetry(self, name):
|
def getMaxRetry(self, name):
|
||||||
return self.__jails.getFilter(name).getMaxRetry()
|
return self.__jails[name].filter.getMaxRetry()
|
||||||
|
|
||||||
def setMaxLines(self, name, value):
|
def setMaxLines(self, name, value):
|
||||||
self.__jails.getFilter(name).setMaxLines(value)
|
self.__jails[name].filter.setMaxLines(value)
|
||||||
|
|
||||||
def getMaxLines(self, name):
|
def getMaxLines(self, name):
|
||||||
return self.__jails.getFilter(name).getMaxLines()
|
return self.__jails[name].filter.getMaxLines()
|
||||||
|
|
||||||
# Action
|
# Action
|
||||||
def addAction(self, name, value):
|
def addAction(self, name, value, *args):
|
||||||
self.__jails.getAction(name).addAction(value)
|
self.__jails[name].actions.add(value, *args)
|
||||||
|
|
||||||
def getLastAction(self, name):
|
|
||||||
return self.__jails.getAction(name).getLastAction()
|
|
||||||
|
|
||||||
def getActions(self, name):
|
def getActions(self, name):
|
||||||
return self.__jails.getAction(name).getActions()
|
return self.__jails[name].actions
|
||||||
|
|
||||||
def delAction(self, name, value):
|
def delAction(self, name, value):
|
||||||
self.__jails.getAction(name).delAction(value)
|
del self.__jails[name].actions[value]
|
||||||
|
|
||||||
def setCInfo(self, name, action, key, value):
|
def getAction(self, name, value):
|
||||||
self.__jails.getAction(name).getAction(action).setCInfo(key, value)
|
return self.__jails[name].actions[value]
|
||||||
|
|
||||||
def getCInfo(self, name, action, key):
|
|
||||||
return self.__jails.getAction(name).getAction(action).getCInfo(key)
|
|
||||||
|
|
||||||
def delCInfo(self, name, action, key):
|
|
||||||
self.__jails.getAction(name).getAction(action).delCInfo(key)
|
|
||||||
|
|
||||||
def setBanTime(self, name, value):
|
def setBanTime(self, name, value):
|
||||||
self.__jails.getAction(name).setBanTime(value)
|
self.__jails[name].actions.setBanTime(value)
|
||||||
|
|
||||||
def setBanIP(self, name, value):
|
def setBanIP(self, name, value):
|
||||||
return self.__jails.getFilter(name).addBannedIP(value)
|
return self.__jails[name].filter.addBannedIP(value)
|
||||||
|
|
||||||
def setUnbanIP(self, name, value):
|
def setUnbanIP(self, name, value):
|
||||||
return self.__jails.getAction(name).removeBannedIP(value)
|
self.__jails[name].actions.removeBannedIP(value)
|
||||||
|
|
||||||
def getBanTime(self, name):
|
def getBanTime(self, name):
|
||||||
return self.__jails.getAction(name).getBanTime()
|
return self.__jails[name].actions.getBanTime()
|
||||||
|
|
||||||
def setActionStart(self, name, action, value):
|
|
||||||
self.__jails.getAction(name).getAction(action).setActionStart(value)
|
|
||||||
|
|
||||||
def getActionStart(self, name, action):
|
|
||||||
return self.__jails.getAction(name).getAction(action).getActionStart()
|
|
||||||
|
|
||||||
def setActionStop(self, name, action, value):
|
|
||||||
self.__jails.getAction(name).getAction(action).setActionStop(value)
|
|
||||||
|
|
||||||
def getActionStop(self, name, action):
|
|
||||||
return self.__jails.getAction(name).getAction(action).getActionStop()
|
|
||||||
|
|
||||||
def setActionCheck(self, name, action, value):
|
|
||||||
self.__jails.getAction(name).getAction(action).setActionCheck(value)
|
|
||||||
|
|
||||||
def getActionCheck(self, name, action):
|
|
||||||
return self.__jails.getAction(name).getAction(action).getActionCheck()
|
|
||||||
|
|
||||||
def setActionBan(self, name, action, value):
|
|
||||||
self.__jails.getAction(name).getAction(action).setActionBan(value)
|
|
||||||
|
|
||||||
def getActionBan(self, name, action):
|
|
||||||
return self.__jails.getAction(name).getAction(action).getActionBan()
|
|
||||||
|
|
||||||
def setActionUnban(self, name, action, value):
|
|
||||||
self.__jails.getAction(name).getAction(action).setActionUnban(value)
|
|
||||||
|
|
||||||
def getActionUnban(self, name, action):
|
|
||||||
return self.__jails.getAction(name).getAction(action).getActionUnban()
|
|
||||||
|
|
||||||
def setActionTimeout(self, name, action, value):
|
|
||||||
self.__jails.getAction(name).getAction(action).setTimeout(value)
|
|
||||||
|
|
||||||
def getActionTimeout(self, name, action):
|
|
||||||
return self.__jails.getAction(name).getAction(action).getTimeout()
|
|
||||||
|
|
||||||
# Status
|
# Status
|
||||||
def status(self):
|
def status(self):
|
||||||
try:
|
try:
|
||||||
self.__lock.acquire()
|
self.__lock.acquire()
|
||||||
jails = list(self.__jails.getAll())
|
jails = list(self.__jails)
|
||||||
jails.sort()
|
jails.sort()
|
||||||
jailList = ", ".join(jails)
|
jailList = ", ".join(jails)
|
||||||
ret = [("Number of jail", self.__jails.size()),
|
ret = [("Number of jail", len(self.__jails)),
|
||||||
("Jail list", jailList)]
|
("Jail list", jailList)]
|
||||||
return ret
|
return ret
|
||||||
finally:
|
finally:
|
||||||
self.__lock.release()
|
self.__lock.release()
|
||||||
|
|
||||||
def statusJail(self, name):
|
def statusJail(self, name):
|
||||||
return self.__jails.get(name).getStatus()
|
return self.__jails[name].getStatus()
|
||||||
|
|
||||||
# Logging
|
# Logging
|
||||||
|
|
||||||
|
@ -488,7 +444,7 @@ class Server:
|
||||||
return "flushed"
|
return "flushed"
|
||||||
|
|
||||||
def setDatabase(self, filename):
|
def setDatabase(self, filename):
|
||||||
if self.__jails.size() == 0:
|
if len(self.__jails) == 0:
|
||||||
if filename.lower() == "none":
|
if filename.lower() == "none":
|
||||||
self.__db = None
|
self.__db = None
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -25,6 +25,7 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import logging, time
|
import logging, time
|
||||||
|
import json
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = logging.getLogger(__name__)
|
||||||
|
@ -226,56 +227,29 @@ class Transmitter:
|
||||||
return self.__server.setBanIP(name,value)
|
return self.__server.setBanIP(name,value)
|
||||||
elif command[1] == "unbanip":
|
elif command[1] == "unbanip":
|
||||||
value = command[2]
|
value = command[2]
|
||||||
return self.__server.setUnbanIP(name,value)
|
self.__server.setUnbanIP(name, value)
|
||||||
|
return value
|
||||||
elif command[1] == "addaction":
|
elif command[1] == "addaction":
|
||||||
value = command[2]
|
args = [command[2]]
|
||||||
self.__server.addAction(name, value)
|
if len(command) > 3:
|
||||||
return self.__server.getLastAction(name).getName()
|
args.extend([command[3], json.loads(command[4])])
|
||||||
|
self.__server.addAction(name, *args)
|
||||||
|
return args[0]
|
||||||
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):
|
||||||
|
@ -327,31 +301,25 @@ 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 self.__server.getActions(name).keys()
|
||||||
elif command[1] == "addaction":
|
elif command[1] == "action":
|
||||||
return self.__server.getLastAction(name).getName()
|
actionname = command[2]
|
||||||
elif command[1] == "actionstart":
|
actionvalue = command[3]
|
||||||
act = command[2]
|
action = self.__server.getAction(name, actionname)
|
||||||
return self.__server.getActionStart(name, act)
|
return getattr(action, actionvalue)
|
||||||
elif command[1] == "actionstop":
|
elif command[1] == "actionproperties":
|
||||||
act = command[2]
|
actionname = command[2]
|
||||||
return self.__server.getActionStop(name, act)
|
action = self.__server.getAction(name, actionname)
|
||||||
elif command[1] == "actioncheck":
|
return [
|
||||||
act = command[2]
|
key for key in dir(action)
|
||||||
return self.__server.getActionCheck(name, act)
|
if not key.startswith("_") and
|
||||||
elif command[1] == "actionban":
|
not callable(getattr(action, key))]
|
||||||
act = command[2]
|
elif command[1] == "actionmethods":
|
||||||
return self.__server.getActionBan(name, act)
|
actionname = command[2]
|
||||||
elif command[1] == "actionunban":
|
action = self.__server.getAction(name, actionname)
|
||||||
act = command[2]
|
return [
|
||||||
return self.__server.getActionUnban(name, act)
|
key for key in dir(action)
|
||||||
elif command[1] == "cinfo":
|
if not key.startswith("_") and callable(getattr(action, key))]
|
||||||
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)
|
|
||||||
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):
|
||||||
|
|
|
@ -26,39 +26,49 @@ __license__ = "GPL"
|
||||||
|
|
||||||
import unittest, time
|
import unittest, time
|
||||||
import sys, os, tempfile
|
import sys, os, tempfile
|
||||||
from fail2ban.server.actions import Actions
|
|
||||||
from dummyjail import DummyJail
|
|
||||||
|
|
||||||
class ExecuteActions(unittest.TestCase):
|
from ..server.actions import Actions
|
||||||
|
from .dummyjail import DummyJail
|
||||||
|
from .utils import LogCaptureTestCase
|
||||||
|
|
||||||
|
TEST_FILES_DIR = os.path.join(os.path.dirname(__file__), "files")
|
||||||
|
|
||||||
|
class ExecuteActions(LogCaptureTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""Call before every test case."""
|
"""Call before every test case."""
|
||||||
|
super(ExecuteActions, self).setUp()
|
||||||
self.__jail = DummyJail()
|
self.__jail = DummyJail()
|
||||||
self.__actions = Actions(self.__jail)
|
self.__actions = Actions(self.__jail)
|
||||||
self.__tmpfile, self.__tmpfilename = tempfile.mkstemp()
|
self.__tmpfile, self.__tmpfilename = tempfile.mkstemp()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
|
super(ExecuteActions, self).tearDown()
|
||||||
os.remove(self.__tmpfilename)
|
os.remove(self.__tmpfilename)
|
||||||
|
|
||||||
def defaultActions(self):
|
def defaultActions(self):
|
||||||
self.__actions.addAction('ip')
|
self.__actions.add('ip')
|
||||||
self.__ip = self.__actions.getAction('ip')
|
self.__ip = self.__actions['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 testActionsAddDuplicateName(self):
|
||||||
|
self.__actions.add('test')
|
||||||
|
self.assertRaises(ValueError, self.__actions.add, 'test')
|
||||||
|
|
||||||
def testActionsManipulation(self):
|
def testActionsManipulation(self):
|
||||||
self.__actions.addAction('test')
|
self.__actions.add('test')
|
||||||
self.assertTrue(self.__actions.getAction('test'))
|
self.assertTrue(self.__actions['test'])
|
||||||
self.assertTrue(self.__actions.getLastAction())
|
self.assertTrue('test' in self.__actions)
|
||||||
self.assertRaises(KeyError,self.__actions.getAction,*['nonexistant action'])
|
self.assertFalse('nonexistant action' in self.__actions)
|
||||||
self.__actions.addAction('test1')
|
self.__actions.add('test1')
|
||||||
self.__actions.delAction('test')
|
del self.__actions['test']
|
||||||
self.__actions.delAction('test1')
|
del self.__actions['test1']
|
||||||
self.assertRaises(KeyError, self.__actions.getAction, *['test'])
|
self.assertFalse('test' in self.__actions)
|
||||||
self.assertRaises(IndexError,self.__actions.getLastAction)
|
self.assertEqual(len(self.__actions), 0)
|
||||||
|
|
||||||
self.__actions.setBanTime(127)
|
self.__actions.setBanTime(127)
|
||||||
self.assertEqual(self.__actions.getBanTime(),127)
|
self.assertEqual(self.__actions.getBanTime(),127)
|
||||||
|
@ -77,3 +87,55 @@ class ExecuteActions(unittest.TestCase):
|
||||||
self.assertEqual(self.__actions.status(),[("Currently banned", 0 ),
|
self.assertEqual(self.__actions.status(),[("Currently banned", 0 ),
|
||||||
("Total banned", 0 ), ("IP list", [] )])
|
("Total banned", 0 ), ("IP list", [] )])
|
||||||
|
|
||||||
|
|
||||||
|
def testAddActionPython(self):
|
||||||
|
self.__actions.add(
|
||||||
|
"Action", os.path.join(TEST_FILES_DIR, "action.d/action.py"),
|
||||||
|
{'opt1': 'value'})
|
||||||
|
|
||||||
|
self.assertTrue(self._is_logged("TestAction initialised"))
|
||||||
|
|
||||||
|
self.__actions.start()
|
||||||
|
time.sleep(3)
|
||||||
|
self.assertTrue(self._is_logged("TestAction action start"))
|
||||||
|
|
||||||
|
self.__actions.stop()
|
||||||
|
self.__actions.join()
|
||||||
|
self.assertTrue(self._is_logged("TestAction action stop"))
|
||||||
|
|
||||||
|
self.assertRaises(IOError,
|
||||||
|
self.__actions.add, "Action3", "/does/not/exist.py", {})
|
||||||
|
|
||||||
|
# With optional argument
|
||||||
|
self.__actions.add(
|
||||||
|
"Action4", os.path.join(TEST_FILES_DIR, "action.d/action.py"),
|
||||||
|
{'opt1': 'value', 'opt2': 'value2'})
|
||||||
|
# With too many arguments
|
||||||
|
self.assertRaises(
|
||||||
|
TypeError, self.__actions.add, "Action5",
|
||||||
|
os.path.join(TEST_FILES_DIR, "action.d/action.py"),
|
||||||
|
{'opt1': 'value', 'opt2': 'value2', 'opt3': 'value3'})
|
||||||
|
# Missing required argument
|
||||||
|
self.assertRaises(
|
||||||
|
TypeError, self.__actions.add, "Action5",
|
||||||
|
os.path.join(TEST_FILES_DIR, "action.d/action.py"), {})
|
||||||
|
|
||||||
|
def testAddPythonActionNOK(self):
|
||||||
|
self.assertRaises(RuntimeError, self.__actions.add,
|
||||||
|
"Action", os.path.join(TEST_FILES_DIR,
|
||||||
|
"action.d/action_noAction.py"),
|
||||||
|
{})
|
||||||
|
self.assertRaises(RuntimeError, self.__actions.add,
|
||||||
|
"Action", os.path.join(TEST_FILES_DIR,
|
||||||
|
"action.d/action_nomethod.py"),
|
||||||
|
{})
|
||||||
|
self.__actions.add(
|
||||||
|
"Action", os.path.join(TEST_FILES_DIR,
|
||||||
|
"action.d/action_errors.py"),
|
||||||
|
{})
|
||||||
|
self.__actions.start()
|
||||||
|
time.sleep(3)
|
||||||
|
self.assertTrue(self._is_logged("Failed to start"))
|
||||||
|
self.__actions.stop()
|
||||||
|
self.__actions.join()
|
||||||
|
self.assertTrue(self._is_logged("Failed to stop"))
|
||||||
|
|
|
@ -27,27 +27,22 @@ __license__ = "GPL"
|
||||||
import time
|
import time
|
||||||
import logging, sys
|
import logging, sys
|
||||||
|
|
||||||
from fail2ban.server.action import Action
|
from ..server.action import CommandAction, CallingMap
|
||||||
|
|
||||||
from fail2ban.tests.utils import LogCaptureTestCase
|
from .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 = Action("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 testNameChange(self):
|
|
||||||
self.assertEqual(self.__action.getName(), "Test")
|
|
||||||
self.__action.setName("Tricky Test")
|
|
||||||
self.assertEqual(self.__action.getName(), "Tricky Test")
|
|
||||||
|
|
||||||
def testSubstituteRecursiveTags(self):
|
def testSubstituteRecursiveTags(self):
|
||||||
aInfo = {
|
aInfo = {
|
||||||
'HOST': "192.0.2.0",
|
'HOST': "192.0.2.0",
|
||||||
|
@ -55,15 +50,15 @@ class ExecuteAction(LogCaptureTestCase):
|
||||||
'xyz': "890 <ABC>",
|
'xyz': "890 <ABC>",
|
||||||
}
|
}
|
||||||
# Recursion is bad
|
# Recursion is bad
|
||||||
self.assertFalse(Action.substituteRecursiveTags({'A': '<A>'}))
|
self.assertFalse(CommandAction.substituteRecursiveTags({'A': '<A>'}))
|
||||||
self.assertFalse(Action.substituteRecursiveTags({'A': '<B>', 'B': '<A>'}))
|
self.assertFalse(CommandAction.substituteRecursiveTags({'A': '<B>', 'B': '<A>'}))
|
||||||
self.assertFalse(Action.substituteRecursiveTags({'A': '<B>', 'B': '<C>', 'C': '<A>'}))
|
self.assertFalse(CommandAction.substituteRecursiveTags({'A': '<B>', 'B': '<C>', 'C': '<A>'}))
|
||||||
# missing tags are ok
|
# missing tags are ok
|
||||||
self.assertEqual(Action.substituteRecursiveTags({'A': '<C>'}), {'A': '<C>'})
|
self.assertEqual(CommandAction.substituteRecursiveTags({'A': '<C>'}), {'A': '<C>'})
|
||||||
self.assertEqual(Action.substituteRecursiveTags({'A': '<C> <D> <X>','X':'fun'}), {'A': '<C> <D> fun', 'X':'fun'})
|
self.assertEqual(CommandAction.substituteRecursiveTags({'A': '<C> <D> <X>','X':'fun'}), {'A': '<C> <D> fun', 'X':'fun'})
|
||||||
self.assertEqual(Action.substituteRecursiveTags({'A': '<C> <B>', 'B': 'cool'}), {'A': '<C> cool', 'B': 'cool'})
|
self.assertEqual(CommandAction.substituteRecursiveTags({'A': '<C> <B>', 'B': 'cool'}), {'A': '<C> cool', 'B': 'cool'})
|
||||||
# rest is just cool
|
# rest is just cool
|
||||||
self.assertEqual(Action.substituteRecursiveTags(aInfo),
|
self.assertEqual(CommandAction.substituteRecursiveTags(aInfo),
|
||||||
{ 'HOST': "192.0.2.0",
|
{ 'HOST': "192.0.2.0",
|
||||||
'ABC': '123 192.0.2.0',
|
'ABC': '123 192.0.2.0',
|
||||||
'xyz': '890 123 192.0.2.0',
|
'xyz': '890 123 192.0.2.0',
|
||||||
|
@ -99,90 +94,102 @@ class ExecuteAction(LogCaptureTestCase):
|
||||||
|
|
||||||
# Callable
|
# Callable
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.__action.replaceTag("09 <callable> 11",
|
self.__action.replaceTag("09 <callme> 11",
|
||||||
{'callable': lambda: str(10)}),
|
CallingMap(callme=lambda: str(10))),
|
||||||
"09 10 11")
|
"09 10 11")
|
||||||
|
|
||||||
# As tag not present, therefore callable should not be called
|
# As tag not present, therefore callable should not be called
|
||||||
# Will raise ValueError if it is
|
# Will raise ValueError if it is
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.__action.replaceTag("abc",
|
self.__action.replaceTag("abc",
|
||||||
{'callable': 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):
|
||||||
Action.executeCmd('/bin/ls >/dev/null\nbogusXXX now 2>/dev/null')
|
CommandAction.executeCmd('/bin/ls >/dev/null\nbogusXXX now 2>/dev/null')
|
||||||
self.assertTrue(self._is_logged('HINT on 127: "Command not found"'))
|
self.assertTrue(self._is_logged('HINT on 127: "Command not found"'))
|
||||||
|
|
||||||
def testExecuteTimeout(self):
|
def testExecuteTimeout(self):
|
||||||
stime = time.time()
|
stime = time.time()
|
||||||
Action.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'))
|
||||||
|
|
||||||
def testCaptureStdOutErr(self):
|
def testCaptureStdOutErr(self):
|
||||||
Action.executeCmd('echo "How now brown cow"')
|
CommandAction.executeCmd('echo "How now brown cow"')
|
||||||
self.assertTrue(self._is_logged("'How now brown cow\\n'"))
|
self.assertTrue(self._is_logged("'How now brown cow\\n'"))
|
||||||
Action.executeCmd(
|
CommandAction.executeCmd(
|
||||||
'echo "The rain in Spain stays mainly in the plain" 1>&2')
|
'echo "The rain in Spain stays mainly in the plain" 1>&2')
|
||||||
self.assertTrue(self._is_logged(
|
self.assertTrue(self._is_logged(
|
||||||
"'The rain in Spain stays mainly in the plain\\n'"))
|
"'The rain in Spain stays mainly in the plain\\n'"))
|
||||||
|
|
||||||
|
def testCallingMap(self):
|
||||||
|
mymap = CallingMap(callme=lambda: str(10), error=lambda: int('a'),
|
||||||
|
dontcallme= "string", number=17)
|
||||||
|
|
||||||
|
# Should work fine
|
||||||
|
self.assertEqual(
|
||||||
|
"%(callme)s okay %(dontcallme)s %(number)i" % mymap,
|
||||||
|
"10 okay string 17")
|
||||||
|
# Error will now trip, demonstrating delayed call
|
||||||
|
self.assertRaises(ValueError, lambda x: "%(error)i" % x, mymap)
|
||||||
|
|
|
@ -26,8 +26,8 @@ __license__ = "GPL"
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from fail2ban.server.banmanager import BanManager
|
from ..server.banmanager import BanManager
|
||||||
from fail2ban.server.ticket import BanTicket
|
from ..server.ticket import BanTicket
|
||||||
|
|
||||||
class AddFailure(unittest.TestCase):
|
class AddFailure(unittest.TestCase):
|
||||||
|
|
||||||
|
|
|
@ -23,13 +23,13 @@ __license__ = "GPL"
|
||||||
|
|
||||||
import os, shutil, sys, tempfile, unittest
|
import os, shutil, sys, tempfile, unittest
|
||||||
|
|
||||||
from fail2ban.client.configreader import ConfigReader
|
from ..client.configreader import ConfigReader
|
||||||
from fail2ban.client.jailreader import JailReader
|
from ..client.jailreader import JailReader
|
||||||
from fail2ban.client.filterreader import FilterReader
|
from ..client.filterreader import FilterReader
|
||||||
from fail2ban.client.jailsreader import JailsReader
|
from ..client.jailsreader import JailsReader
|
||||||
from fail2ban.client.actionreader import ActionReader
|
from ..client.actionreader import ActionReader
|
||||||
from fail2ban.client.configurator import Configurator
|
from ..client.configurator import Configurator
|
||||||
from fail2ban.tests.utils import LogCaptureTestCase
|
from .utils import LogCaptureTestCase
|
||||||
|
|
||||||
TEST_FILES_DIR = os.path.join(os.path.dirname(__file__), "files")
|
TEST_FILES_DIR = os.path.join(os.path.dirname(__file__), "files")
|
||||||
if os.path.exists(os.path.join('config','fail2ban.conf')):
|
if os.path.exists(os.path.join('config','fail2ban.conf')):
|
||||||
|
@ -356,13 +356,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],
|
||||||
|
@ -489,7 +494,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):
|
||||||
|
@ -542,6 +547,8 @@ class JailsReaderTest(LogCaptureTestCase):
|
||||||
[testjail1]
|
[testjail1]
|
||||||
action = testaction1[actname=test1]
|
action = testaction1[actname=test1]
|
||||||
testaction1[actname=test2]
|
testaction1[actname=test2]
|
||||||
|
testaction.py
|
||||||
|
testaction.py[actname=test3]
|
||||||
filter = testfilter1
|
filter = testfilter1
|
||||||
""")
|
""")
|
||||||
jailfd.close()
|
jailfd.close()
|
||||||
|
@ -550,8 +557,12 @@ filter = testfilter1
|
||||||
self.assertTrue(jails.getOptions())
|
self.assertTrue(jails.getOptions())
|
||||||
comm_commands = jails.convert(allow_no_files=True)
|
comm_commands = jails.convert(allow_no_files=True)
|
||||||
|
|
||||||
action_names = [comm[-1] for comm in comm_commands if comm[:3] == ['set', 'testjail1', 'addaction']]
|
add_actions = [comm[3:] for comm in comm_commands
|
||||||
|
if comm[:3] == ['set', 'testjail1', 'addaction']]
|
||||||
|
|
||||||
self.assertNotEqual(len(set(action_names)), 1)
|
self.assertEqual(len(set(action[0] for action in add_actions)), 4)
|
||||||
|
|
||||||
|
# Python actions should not be passed `actname`
|
||||||
|
self.assertEqual(add_actions[-1][-1], "{}")
|
||||||
|
|
||||||
shutil.rmtree(basedir)
|
shutil.rmtree(basedir)
|
||||||
|
|
|
@ -28,11 +28,11 @@ import tempfile
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
from fail2ban.server.database import Fail2BanDb
|
from ..server.database import Fail2BanDb
|
||||||
from fail2ban.server.filter import FileContainer
|
from ..server.filter import FileContainer
|
||||||
from fail2ban.server.mytime import MyTime
|
from ..server.mytime import MyTime
|
||||||
from fail2ban.server.ticket import FailTicket
|
from ..server.ticket import FailTicket
|
||||||
from fail2ban.tests.dummyjail import DummyJail
|
from .dummyjail import DummyJail
|
||||||
|
|
||||||
class DatabaseTest(unittest.TestCase):
|
class DatabaseTest(unittest.TestCase):
|
||||||
|
|
||||||
|
|
|
@ -25,10 +25,10 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import unittest, calendar, time, datetime, re, pprint
|
import unittest, calendar, time, datetime, re, pprint
|
||||||
from fail2ban.server.datedetector import DateDetector
|
from ..server.datedetector import DateDetector
|
||||||
from fail2ban.server.datetemplate import DateTemplate
|
from ..server.datetemplate import DateTemplate
|
||||||
from fail2ban.server.iso8601 import Utc
|
from ..server.iso8601 import Utc
|
||||||
from fail2ban.tests.utils import setUpMyTime, tearDownMyTime
|
from .utils import setUpMyTime, tearDownMyTime
|
||||||
|
|
||||||
class DateDetectorTest(unittest.TestCase):
|
class DateDetectorTest(unittest.TestCase):
|
||||||
|
|
||||||
|
|
|
@ -26,8 +26,8 @@ __license__ = "GPL"
|
||||||
|
|
||||||
import unittest, socket, time, pickle
|
import unittest, socket, time, pickle
|
||||||
|
|
||||||
from fail2ban.server.failmanager import FailManager, FailManagerEmpty
|
from ..server.failmanager import FailManager, FailManagerEmpty
|
||||||
from fail2ban.server.ticket import FailTicket
|
from ..server.ticket import FailTicket
|
||||||
|
|
||||||
class AddFailure(unittest.TestCase):
|
class AddFailure(unittest.TestCase):
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
|
||||||
|
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__)
|
||||||
|
self.opt1 = opt1
|
||||||
|
self.opt2 = opt2
|
||||||
|
self._opt3 = "Hello"
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
self._logSys.debug("%s action start" % self.__class__.__name__)
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self._logSys.debug("%s action stop" % self.__class__.__name__)
|
||||||
|
|
||||||
|
def ban(self, aInfo):
|
||||||
|
self._logSys.debug("%s action ban" % 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
|
|
@ -0,0 +1,21 @@
|
||||||
|
|
||||||
|
from fail2ban.server.action import ActionBase
|
||||||
|
|
||||||
|
class TestAction(ActionBase):
|
||||||
|
|
||||||
|
def __init__(self, jail, name):
|
||||||
|
super(TestAction, self).__init__(jail, name)
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
raise Exception()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
raise Exception()
|
||||||
|
|
||||||
|
def ban(self):
|
||||||
|
raise Exception()
|
||||||
|
|
||||||
|
def unban(self):
|
||||||
|
raise Exception()
|
||||||
|
|
||||||
|
Action = TestAction
|
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
from fail2ban.server.action import ActionBase
|
||||||
|
|
||||||
|
class TestAction(ActionBase):
|
||||||
|
pass
|
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
from fail2ban.server.action import ActionBase
|
||||||
|
|
||||||
|
class TestAction():
|
||||||
|
|
||||||
|
def __init__(self, jail, name):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
Action = TestAction
|
|
@ -34,19 +34,16 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
journal = None
|
journal = None
|
||||||
|
|
||||||
from fail2ban.server.jail import Jail
|
from ..server.jail import Jail
|
||||||
from fail2ban.server.filterpoll import FilterPoll
|
from ..server.filterpoll import FilterPoll
|
||||||
from fail2ban.server.filter import Filter, FileFilter, DNSUtils
|
from ..server.filter import Filter, FileFilter, DNSUtils
|
||||||
from fail2ban.server.failmanager import FailManager
|
from ..server.failmanager import FailManager, FailManagerEmpty
|
||||||
from fail2ban.server.failmanager import FailManagerEmpty
|
from ..server.mytime import MyTime
|
||||||
from fail2ban.server.mytime import MyTime
|
from .utils import setUpMyTime, tearDownMyTime, mtimesleep, LogCaptureTestCase
|
||||||
from fail2ban.tests.utils import setUpMyTime, tearDownMyTime
|
from .dummyjail import DummyJail
|
||||||
from fail2ban.tests.utils import mtimesleep, LogCaptureTestCase
|
|
||||||
|
|
||||||
TEST_FILES_DIR = os.path.join(os.path.dirname(__file__), "files")
|
TEST_FILES_DIR = os.path.join(os.path.dirname(__file__), "files")
|
||||||
|
|
||||||
from fail2ban.tests.dummyjail import DummyJail
|
|
||||||
|
|
||||||
# yoh: per Steven Hiscocks's insight while troubleshooting
|
# yoh: per Steven Hiscocks's insight while troubleshooting
|
||||||
# https://github.com/fail2ban/fail2ban/issues/103#issuecomment-15542836
|
# https://github.com/fail2ban/fail2ban/issues/103#issuecomment-15542836
|
||||||
# adding a sufficiently large buffer might help to guarantee that
|
# adding a sufficiently large buffer might help to guarantee that
|
||||||
|
|
|
@ -28,8 +28,8 @@ import shutil
|
||||||
|
|
||||||
from glob import glob
|
from glob import glob
|
||||||
|
|
||||||
from utils import mbasename, TraceBack, FormatterWithTraceBack
|
from .utils import mbasename, TraceBack, FormatterWithTraceBack
|
||||||
from fail2ban.helpers import formatExceptionInfo
|
from ..helpers import formatExceptionInfo
|
||||||
|
|
||||||
class HelpersTest(unittest.TestCase):
|
class HelpersTest(unittest.TestCase):
|
||||||
|
|
||||||
|
|
|
@ -30,9 +30,9 @@ else:
|
||||||
import simplejson as json
|
import simplejson as json
|
||||||
next = lambda x: x.next()
|
next = lambda x: x.next()
|
||||||
|
|
||||||
from fail2ban.server.filter import Filter
|
from ..server.filter import Filter
|
||||||
from fail2ban.client.filterreader import FilterReader
|
from ..client.filterreader import FilterReader
|
||||||
from fail2ban.tests.utils import setUpMyTime, tearDownMyTime
|
from .utils import setUpMyTime, tearDownMyTime
|
||||||
|
|
||||||
TEST_FILES_DIR = os.path.join(os.path.dirname(__file__), "files")
|
TEST_FILES_DIR = os.path.join(os.path.dirname(__file__), "files")
|
||||||
if os.path.exists('config/fail2ban.conf'):
|
if os.path.exists('config/fail2ban.conf'):
|
||||||
|
|
|
@ -26,11 +26,11 @@ __license__ = "GPL"
|
||||||
|
|
||||||
import unittest, socket, time, tempfile, os, locale, sys, logging
|
import unittest, socket, time, tempfile, os, locale, sys, logging
|
||||||
|
|
||||||
from fail2ban.server.failregex import Regex, FailRegex, RegexException
|
from ..server.failregex import Regex, FailRegex, RegexException
|
||||||
from fail2ban.server.server import Server, logSys
|
from ..server.server import Server, logSys
|
||||||
from fail2ban.server.jail import Jail
|
from ..server.jail import Jail
|
||||||
from fail2ban.exceptions import UnknownJailException
|
from ..exceptions import UnknownJailException
|
||||||
from fail2ban.tests.utils import LogCaptureTestCase
|
from .utils import LogCaptureTestCase
|
||||||
#from bin.fail2ban-client import Fail2banClient
|
#from bin.fail2ban-client import Fail2banClient
|
||||||
try:
|
try:
|
||||||
from fail2ban.server import filtersystemd
|
from fail2ban.server import filtersystemd
|
||||||
|
@ -518,45 +518,38 @@ class Transmitter(TransmitterBase):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.transm.proceed(["set", self.jailName, "addaction", action]),
|
self.transm.proceed(["set", self.jailName, "addaction", action]),
|
||||||
(0, action))
|
(0, action))
|
||||||
self.assertEqual(
|
|
||||||
self.transm.proceed(["get", self.jailName, "addaction"]),
|
|
||||||
(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"]),
|
["set", self.jailName, "action", action, "timeout", "10"]),
|
||||||
(0, None))
|
|
||||||
self.assertEqual(
|
|
||||||
self.transm.proceed(
|
|
||||||
["set", self.jailName, "timeout", action, "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]),
|
||||||
|
@ -565,6 +558,42 @@ class Transmitter(TransmitterBase):
|
||||||
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.transm.proceed(["set", self.jailName, "addaction", action,
|
||||||
|
os.path.join(TEST_FILES_DIR, "action.d", "action.py"),
|
||||||
|
'{"opt1": "value"}']),
|
||||||
|
(0, action))
|
||||||
|
self.assertEqual(
|
||||||
|
sorted(self.transm.proceed(["get", self.jailName,
|
||||||
|
"actionproperties", action])[1]),
|
||||||
|
['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])[1]),
|
||||||
|
['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)
|
||||||
|
|
||||||
|
|
|
@ -26,8 +26,8 @@ __license__ = "GPL"
|
||||||
|
|
||||||
import unittest, time, tempfile, os, threading
|
import unittest, time, tempfile, os, threading
|
||||||
|
|
||||||
from fail2ban.server.asyncserver import AsyncServer, AsyncServerException
|
from ..server.asyncserver import AsyncServer, AsyncServerException
|
||||||
from fail2ban.client.csocket import CSocket
|
from ..client.csocket import CSocket
|
||||||
|
|
||||||
class Socket(unittest.TestCase):
|
class Socket(unittest.TestCase):
|
||||||
|
|
||||||
|
|
|
@ -25,16 +25,9 @@ __license__ = "GPL"
|
||||||
import logging, os, re, traceback, time, unittest, sys
|
import logging, os, re, traceback, time, unittest, sys
|
||||||
from os.path import basename, dirname
|
from os.path import basename, dirname
|
||||||
from StringIO import StringIO
|
from StringIO import StringIO
|
||||||
|
import json
|
||||||
|
|
||||||
if sys.version_info >= (2, 6):
|
from ..server.mytime import MyTime
|
||||||
import json
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
import simplejson as json
|
|
||||||
except ImportError:
|
|
||||||
json = None
|
|
||||||
|
|
||||||
from fail2ban.server.mytime import MyTime
|
|
||||||
|
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -138,19 +131,18 @@ def tearDownMyTime():
|
||||||
def gatherTests(regexps=None, no_network=False):
|
def gatherTests(regexps=None, no_network=False):
|
||||||
# Import all the test cases here instead of a module level to
|
# Import all the test cases here instead of a module level to
|
||||||
# avoid circular imports
|
# avoid circular imports
|
||||||
from fail2ban.tests import banmanagertestcase
|
from . import banmanagertestcase
|
||||||
from fail2ban.tests import clientreadertestcase
|
from . import clientreadertestcase
|
||||||
from fail2ban.tests import failmanagertestcase
|
from . import failmanagertestcase
|
||||||
from fail2ban.tests import filtertestcase
|
from . import filtertestcase
|
||||||
from fail2ban.tests import servertestcase
|
from . import servertestcase
|
||||||
from fail2ban.tests import datedetectortestcase
|
from . import datedetectortestcase
|
||||||
from fail2ban.tests import actiontestcase
|
from . import actiontestcase
|
||||||
from fail2ban.tests import actionstestcase
|
from . import actionstestcase
|
||||||
from fail2ban.tests import sockettestcase
|
from . import sockettestcase
|
||||||
from fail2ban.tests import misctestcase
|
from . import misctestcase
|
||||||
from fail2ban.tests import databasetestcase
|
from . import databasetestcase
|
||||||
if json:
|
from . import samplestestcase
|
||||||
from fail2ban.tests import samplestestcase
|
|
||||||
|
|
||||||
if not regexps: # pragma: no cover
|
if not regexps: # pragma: no cover
|
||||||
tests = unittest.TestSuite()
|
tests = unittest.TestSuite()
|
||||||
|
@ -172,7 +164,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))
|
||||||
|
@ -207,30 +199,27 @@ def gatherTests(regexps=None, no_network=False):
|
||||||
|
|
||||||
# DateDetector
|
# DateDetector
|
||||||
tests.addTest(unittest.makeSuite(datedetectortestcase.DateDetectorTest))
|
tests.addTest(unittest.makeSuite(datedetectortestcase.DateDetectorTest))
|
||||||
if json:
|
# Filter Regex tests with sample logs
|
||||||
# Filter Regex tests with sample logs
|
tests.addTest(unittest.makeSuite(samplestestcase.FilterSamplesRegex))
|
||||||
tests.addTest(unittest.makeSuite(samplestestcase.FilterSamplesRegex))
|
|
||||||
else:
|
|
||||||
logSys.warning("I: Skipping filter samples testing. No simplejson/json module")
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Extensive use-tests of different available filters backends
|
# Extensive use-tests of different available filters backends
|
||||||
#
|
#
|
||||||
|
|
||||||
from fail2ban.server.filterpoll import FilterPoll
|
from ..server.filterpoll import FilterPoll
|
||||||
filters = [FilterPoll] # always available
|
filters = [FilterPoll] # always available
|
||||||
|
|
||||||
# Additional filters available only if external modules are available
|
# Additional filters available only if external modules are available
|
||||||
# yoh: Since I do not know better way for parametric tests
|
# yoh: Since I do not know better way for parametric tests
|
||||||
# with good old unittest
|
# with good old unittest
|
||||||
try:
|
try:
|
||||||
from fail2ban.server.filtergamin import FilterGamin
|
from ..server.filtergamin import FilterGamin
|
||||||
filters.append(FilterGamin)
|
filters.append(FilterGamin)
|
||||||
except Exception, e: # pragma: no cover
|
except Exception, e: # pragma: no cover
|
||||||
logSys.warning("Skipping gamin backend testing. Got exception '%s'" % e)
|
logSys.warning("Skipping gamin backend testing. Got exception '%s'" % e)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from fail2ban.server.filterpyinotify import FilterPyinotify
|
from ..server.filterpyinotify import FilterPyinotify
|
||||||
filters.append(FilterPyinotify)
|
filters.append(FilterPyinotify)
|
||||||
except Exception, e: # pragma: no cover
|
except Exception, e: # pragma: no cover
|
||||||
logSys.warning("I: Skipping pyinotify backend testing. Got exception '%s'" % e)
|
logSys.warning("I: Skipping pyinotify backend testing. Got exception '%s'" % e)
|
||||||
|
@ -239,7 +228,7 @@ def gatherTests(regexps=None, no_network=False):
|
||||||
tests.addTest(unittest.makeSuite(
|
tests.addTest(unittest.makeSuite(
|
||||||
filtertestcase.get_monitor_failures_testcase(Filter_)))
|
filtertestcase.get_monitor_failures_testcase(Filter_)))
|
||||||
try: # pragma: systemd no cover
|
try: # pragma: systemd no cover
|
||||||
from fail2ban.server.filtersystemd import FilterSystemd
|
from ..server.filtersystemd import FilterSystemd
|
||||||
tests.addTest(unittest.makeSuite(filtertestcase.get_monitor_failures_journal_testcase(FilterSystemd)))
|
tests.addTest(unittest.makeSuite(filtertestcase.get_monitor_failures_journal_testcase(FilterSystemd)))
|
||||||
except Exception, e: # pragma: no cover
|
except Exception, e: # pragma: no cover
|
||||||
logSys.warning("I: Skipping systemd backend testing. Got exception '%s'" % e)
|
logSys.warning("I: Skipping systemd backend testing. Got exception '%s'" % e)
|
||||||
|
|
|
@ -7,7 +7,7 @@ jail.conf \- configuration for the fail2ban server
|
||||||
|
|
||||||
.I jail.conf / jail.local
|
.I jail.conf / jail.local
|
||||||
|
|
||||||
.I action.d/*.conf action.d/*.local
|
.I action.d/*.conf action.d/*.local action.d/*.py
|
||||||
|
|
||||||
.I filter.d/*.conf filter.d/*.local
|
.I filter.d/*.conf filter.d/*.local
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
|
@ -113,13 +113,15 @@ 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. In the case where multiple of the same action are to be used, the \fBactname\fR option can be assigned to the action to avoid duplicatione.g.:
|
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]
|
||||||
enabled = true
|
enabled = true
|
||||||
action = sendmail[name=ssh, dest=john@example.com, actname=mail-john]
|
action = sendmail[name=ssh, dest=john@example.com, actname=mail-john]
|
||||||
sendmail[name=ssh, dest=paul@example.com, actname=mail-paul]
|
sendmail[name=ssh, dest=paul@example.com, actname=mail-paul]
|
||||||
|
smtp.py[dest=chris@example.com, actname=smtp-chris]
|
||||||
|
smtp.py[dest=sally@example.com, actname=smtp-sally]
|
||||||
.fi
|
.fi
|
||||||
|
|
||||||
.SH "ACTION FILES"
|
.SH "ACTION FILES"
|
||||||
|
@ -160,6 +162,8 @@ 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
|
||||||
|
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/*.conf")
|
glob("config/action.d/*.conf") +
|
||||||
|
glob("config/action.d/*.py")
|
||||||
),
|
),
|
||||||
('/etc/fail2ban/fail2ban.d',
|
('/etc/fail2ban/fail2ban.d',
|
||||||
''
|
''
|
||||||
|
|
Loading…
Reference in New Issue