Merge branch '0.9' into py3

Conflicts:
	.travis.yml
	MANIFEST
	bin/fail2ban-regex
	fail2ban/server/filter.py
	fail2ban/tests/servertestcase.py
	setup.py
pull/171/head
Steven Hiscocks 2013-04-13 16:54:22 +01:00
commit fa0f8f9e6d
93 changed files with 1031 additions and 315 deletions

View File

@ -7,12 +7,16 @@ python:
- "2.7" - "2.7"
- "3.2" - "3.2"
- "3.3" - "3.3"
before_install:
- sudo apt-get update -qq
install: install:
- pip install pyinotify - pip install pyinotify
- if [[ $TRAVIS_PYTHON_VERSION == 2.[6-7] ]] || [[ $TRAVIS_PYTHON_VERSION == 3.3 ]]; then pip install -q coveralls; fi - if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then sudo apt-get install -qq python-gamin; fi
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then pip install -q coveralls; fi
before_script: before_script:
- if [[ $TRAVIS_PYTHON_VERSION == 3* ]]; then ./fail2ban-2to3; fi - if [[ $TRAVIS_PYTHON_VERSION == 3* ]]; then ./fail2ban-2to3; fi
script: script:
- if [[ $TRAVIS_PYTHON_VERSION == 2.[6-7] ]] || [[ $TRAVIS_PYTHON_VERSION == 3.3 ]]; then coverage run --rcfile=.travis_coveragerc fail2ban-testcases; else python ./fail2ban-testcases; fi - if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then export PYTHONPATH="$PYTHONPATH:/usr/share/pyshared:/usr/lib/pyshared/python2.7"; fi
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then coverage run --rcfile=.travis_coveragerc bin/fail2ban-testcases; else python bin/fail2ban-testcases; fi
after_script: after_script:
- if [[ $TRAVIS_PYTHON_VERSION == 2.[6-7] ]] || [[ $TRAVIS_PYTHON_VERSION == 3.3 ]]; then coveralls; fi - if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then coveralls; fi

View File

@ -4,4 +4,3 @@ branch = True
omit = omit =
/usr/* /usr/*
/home/travis/virtualenv/* /home/travis/virtualenv/*
server/filtergamin.py

View File

@ -4,9 +4,21 @@
|_| \__,_|_|_/___|_.__/\__,_|_||_| |_| \__,_|_|_/___|_.__/\__,_|_||_|
================================================================================ ================================================================================
Fail2Ban (version 0.8.8) 2012/12/06 Fail2Ban (version 0.9.0a) 20??/??/??
================================================================================ ================================================================================
ver. 0.9.0 (20??/??/??) - alpha
----------
Will carry all fixes in 0.8.x series and new features and enhancements
- Fixes:
- New features:
Steven Hiscocks
* Multiline failregex. Close gh-54
- Enhancements:
ver. 0.8.8 (2012/12/06) - stable ver. 0.8.8 (2012/12/06) - stable
---------- ----------
- Fixes: - Fixes:

12
DEVELOP
View File

@ -24,9 +24,9 @@ Request feature. You can find more details on the Fail2Ban wiki
Testing Testing
======= =======
Existing tests can be run by executing `fail2ban-testcases`. This has options Existing tests can be run by executing `bin/fail2ban-testcases`. This has
like --log-level that will probably be useful. `fail2ban-testcases --help` for options like --log-level that will probably be useful.
full options. `bin/fail2ban-testcases --help` forfull options.
Test cases should cover all usual cases, all exception cases and all inside Test cases should cover all usual cases, all exception cases and all inside
/ outside boundary conditions. / outside boundary conditions.
@ -39,7 +39,7 @@ Install the package python-coverage to visualise your test coverage. Run the
following (note: on Debian-based systems, the script is called following (note: on Debian-based systems, the script is called
`python-coverage`): `python-coverage`):
coverage run fail2ban-testcases coverage run bin/fail2ban-testcases
coverage html coverage html
Then look at htmlcov/index.html and see how much coverage your test cases Then look at htmlcov/index.html and see how much coverage your test cases
@ -249,7 +249,7 @@ Takes care about executing start/check/ban/unban/stop commands
Releasing Releasing
========= =========
# Ensure the version is correct in ./common/version.py # Ensure the version is correct in ./fail2ban/version.py
# Add/finalize the corresponding entry in the ChangeLog # Add/finalize the corresponding entry in the ChangeLog
@ -271,7 +271,7 @@ Releasing
# Run the following and update the wiki with output: # Run the following and update the wiki with output:
python -c 'import common.protocol; common.protocol.printWiki()' python -c 'import fail2ban.protocol; fail2ban.protocol.printWiki()'
# Email users and development list of release # Email users and development list of release

113
MANIFEST
View File

@ -5,65 +5,66 @@ THANKS
COPYING COPYING
DEVELOP DEVELOP
doc/run-rootless.txt doc/run-rootless.txt
fail2ban-client
fail2ban-server
fail2ban-testcases
fail2ban-regex
fail2ban-2to3 fail2ban-2to3
client/configreader.py bin/fail2ban-client
client/configparserinc.py bin/fail2ban-server
client/jailreader.py bin/fail2ban-testcases
client/fail2banreader.py bin/fail2ban-regex
client/jailsreader.py fail2ban/client/configreader.py
client/beautifier.py fail2ban/client/configparserinc.py
client/filterreader.py fail2ban/client/jailreader.py
client/actionreader.py fail2ban/client/fail2banreader.py
client/__init__.py fail2ban/client/jailsreader.py
client/configurator.py fail2ban/client/beautifier.py
client/csocket.py fail2ban/client/filterreader.py
server/asyncserver.py fail2ban/client/actionreader.py
server/filter.py fail2ban/client/__init__.py
server/filterpyinotify.py fail2ban/client/configurator.py
server/filtergamin.py fail2ban/client/csocket.py
server/filterpoll.py fail2ban/server/asyncserver.py
server/iso8601.py fail2ban/server/filter.py
server/server.py fail2ban/server/filterpyinotify.py
server/actions.py fail2ban/server/filtergamin.py
server/faildata.py fail2ban/server/filterpoll.py
server/failmanager.py fail2ban/server/iso8601.py
server/datedetector.py fail2ban/server/server.py
server/jailthread.py fail2ban/server/actions.py
server/transmitter.py fail2ban/server/faildata.py
server/action.py fail2ban/server/failmanager.py
server/ticket.py fail2ban/server/datedetector.py
server/jail.py fail2ban/server/jailthread.py
server/jails.py fail2ban/server/transmitter.py
server/__init__.py fail2ban/server/action.py
server/banmanager.py fail2ban/server/ticket.py
server/datetemplate.py fail2ban/server/jail.py
server/mytime.py fail2ban/server/jails.py
server/failregex.py fail2ban/server/__init__.py
testcases/files/testcase-usedns.log fail2ban/server/banmanager.py
testcases/banmanagertestcase.py fail2ban/server/datetemplate.py
testcases/failmanagertestcase.py fail2ban/server/mytime.py
testcases/clientreadertestcase.py fail2ban/server/failregex.py
testcases/filtertestcase.py fail2ban/tests/banmanagertestcase.py
testcases/__init__.py fail2ban/tests/failmanagertestcase.py
testcases/datedetectortestcase.py fail2ban/tests/clientreadertestcase.py
testcases/actiontestcase.py fail2ban/tests/filtertestcase.py
testcases/servertestcase.py fail2ban/tests/__init__.py
testcases/sockettestcase.py fail2ban/tests/datedetectortestcase.py
testcases/files/testcase01.log fail2ban/tests/actiontestcase.py
testcases/files/testcase02.log fail2ban/tests/servertestcase.py
testcases/files/testcase03.log fail2ban/tests/sockettestcase.py
testcases/files/testcase04.log fail2ban/tests/utils.py
fail2ban/tests/files/testcase01.log
fail2ban/tests/files/testcase02.log
fail2ban/tests/files/testcase03.log
fail2ban/tests/files/testcase04.log
fail2ban/tests/files/testcase-usedns.log
setup.py setup.py
setup.cfg setup.cfg
common/__init__.py fail2ban/__init__.py
common/exceptions.py fail2ban/exceptions.py
common/helpers.py fail2ban/helpers.py
common/version.py fail2ban/version.py
common/protocol.py fail2ban/protocol.py
config/jail.conf config/jail.conf
config/filter.d/common.conf config/filter.d/common.conf
config/filter.d/apache-auth.conf config/filter.d/apache-auth.conf

2
README
View File

@ -4,7 +4,7 @@
|_| \__,_|_|_/___|_.__/\__,_|_||_| |_| \__,_|_|_/___|_.__/\__,_|_||_|
================================================================================ ================================================================================
Fail2Ban (version 0.8.8) 2012/07/31 Fail2Ban (version 0.9.0a0) 20??/??/??
================================================================================ ================================================================================
Fail2Ban scans log files like /var/log/pwdfail and bans IP that makes too many Fail2Ban scans log files like /var/log/pwdfail and bans IP that makes too many

View File

@ -25,19 +25,11 @@ __license__ = "GPL"
import sys, string, os, pickle, re, logging, signal import sys, string, os, pickle, re, logging, signal
import getopt, time, shlex, socket import getopt, time, shlex, socket
# Inserts our own modules path first in the list from fail2ban.version import version
# fix for bug #343821 from fail2ban.protocol import printFormatted
try: from fail2ban.client.csocket import CSocket
from common.version import version from fail2ban.client.configurator import Configurator
except ImportError, e: from fail2ban.client.beautifier import Beautifier
sys.path.insert(1, "/usr/share/fail2ban")
from common.version import version
# Now we can import the rest of modules
from common.protocol import printFormatted
from client.csocket import CSocket
from client.configurator import Configurator
from client.beautifier import Beautifier
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.client") logSys = logging.getLogger("fail2ban.client")

View File

@ -23,19 +23,12 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2012 Yaroslav Halchenko"
__license__ = "GPL" __license__ = "GPL"
import getopt, sys, time, logging, os, locale import getopt, sys, time, logging, os, locale
# Inserts our own modules path first in the list
# fix for bug #343821
try:
from common.version import version
except ImportError, e:
sys.path.insert(1, "/usr/share/fail2ban")
from common.version import version
from client.configparserinc import SafeConfigParserWithIncludes
from ConfigParser import NoOptionError, NoSectionError, MissingSectionHeaderError from ConfigParser import NoOptionError, NoSectionError, MissingSectionHeaderError
from server.filter import Filter
from server.failregex import RegexException from fail2ban.version import version
from fail2ban.client.configparserinc import SafeConfigParserWithIncludes
from fail2ban.server.filter import Filter
from fail2ban.server.failregex import RegexException
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.regex") logSys = logging.getLogger("fail2ban.regex")
@ -116,6 +109,7 @@ class Fail2banRegex:
print " -h, --help display this help message" print " -h, --help display this help message"
print " -V, --version print the version" print " -V, --version print the version"
print " -v, --verbose verbose output" print " -v, --verbose verbose output"
print " -l INT, --maxlines=INT set maxlines for multi-line regex default: 1"
print print
print "Log:" print "Log:"
print " string a string representing a log line" print " string a string representing a log line"
@ -146,6 +140,14 @@ class Fail2banRegex:
self.__verbose = True self.__verbose = True
elif opt[0] in ["-e", "--encoding"]: elif opt[0] in ["-e", "--encoding"]:
self.encoding = opt[1] self.encoding = opt[1]
elif opt[0] in ["-l", "--maxlines"]:
try:
self.__filter.setMaxLines(int(opt[1]))
except ValueError:
print "Invlaid value for maxlines: %s" % (
opt[1])
fail2banRegex.dispUsage()
sys.exit(-1)
#@staticmethod #@staticmethod
def logIsFile(value): def logIsFile(value):
@ -323,8 +325,8 @@ if __name__ == "__main__":
fail2banRegex = Fail2banRegex() fail2banRegex = Fail2banRegex()
# Reads the command line options. # Reads the command line options.
try: try:
cmdOpts = 'e:hVcv' cmdOpts = 'hVcvl:e:'
cmdLongOpts = ['encoding=', 'help', 'version', 'verbose'] cmdLongOpts = ['help', 'version', 'verbose', 'maxlines=', 'encoding=']
optList, args = getopt.getopt(sys.argv[1:], cmdOpts, cmdLongOpts) optList, args = getopt.getopt(sys.argv[1:], cmdOpts, cmdLongOpts)
except getopt.GetoptError: except getopt.GetoptError:
fail2banRegex.dispUsage() fail2banRegex.dispUsage()

View File

@ -24,15 +24,8 @@ __license__ = "GPL"
import getopt, sys, logging, os import getopt, sys, logging, os
# Inserts our own modules path first in the list from fail2ban.version import version
# fix for bug #343821 from fail2ban.server.server import Server
try:
from common.version import version
except ImportError, e:
sys.path.insert(1, "/usr/share/fail2ban")
from common.version import version
from server.server import Server
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = logging.getLogger("fail2ban") logSys = logging.getLogger("fail2ban")

View File

@ -27,18 +27,23 @@ __license__ = "GPL"
import unittest, logging, sys, time, os import unittest, logging, sys, time, os
from common.version import version # Check if local fail2ban module exists, and use if it exists by
from testcases import banmanagertestcase # modifying the path. This is such that tests can be used in dev
from testcases import clientreadertestcase # environment.
from testcases import failmanagertestcase if os.path.exists("fail2ban/__init__.py"):
from testcases import filtertestcase sys.path.insert(0, ".")
from testcases import servertestcase from fail2ban.version import version
from testcases import datedetectortestcase from fail2ban.tests import banmanagertestcase
from testcases import actiontestcase from fail2ban.tests import clientreadertestcase
from testcases import sockettestcase from fail2ban.tests import failmanagertestcase
from fail2ban.tests import filtertestcase
from fail2ban.tests import servertestcase
from fail2ban.tests import datedetectortestcase
from fail2ban.tests import actiontestcase
from fail2ban.tests import sockettestcase
from testcases.utils import FormatterWithTraceBack from fail2ban.tests.utils import FormatterWithTraceBack
from server.mytime import MyTime from fail2ban.server.mytime import MyTime
from optparse import OptionParser, Option from optparse import OptionParser, Option
@ -147,6 +152,7 @@ tests.addTest(unittest.makeSuite(banmanagertestcase.AddFailure))
# ClientReaders # ClientReaders
tests.addTest(unittest.makeSuite(clientreadertestcase.ConfigReaderTest)) tests.addTest(unittest.makeSuite(clientreadertestcase.ConfigReaderTest))
tests.addTest(unittest.makeSuite(clientreadertestcase.JailReaderTest)) tests.addTest(unittest.makeSuite(clientreadertestcase.JailReaderTest))
tests.addTest(unittest.makeSuite(clientreadertestcase.FilterReaderTest))
tests.addTest(unittest.makeSuite(clientreadertestcase.JailsReaderTest)) tests.addTest(unittest.makeSuite(clientreadertestcase.JailsReaderTest))
# CSocket and AsyncServer # CSocket and AsyncServer
tests.addTest(unittest.makeSuite(sockettestcase.Socket)) tests.addTest(unittest.makeSuite(sockettestcase.Socket))
@ -168,20 +174,20 @@ tests.addTest(unittest.makeSuite(datedetectortestcase.DateDetectorTest))
# Extensive use-tests of different available filters backends # Extensive use-tests of different available filters backends
# #
from server.filterpoll import FilterPoll from fail2ban.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 server.filtergamin import FilterGamin from fail2ban.server.filtergamin import FilterGamin
filters.append(FilterGamin) filters.append(FilterGamin)
except Exception, e: # pragma: no cover except Exception, e: # pragma: no cover
print "I: Skipping gamin backend testing. Got exception '%s'" % e print "I: Skipping gamin backend testing. Got exception '%s'" % e
try: try:
from server.filterpyinotify import FilterPyinotify from fail2ban.server.filterpyinotify import FilterPyinotify
filters.append(FilterPyinotify) filters.append(FilterPyinotify)
except Exception, e: # pragma: no cover except Exception, e: # pragma: no cover
print "I: Skipping pyinotify backend testing. Got exception '%s'" % e print "I: Skipping pyinotify backend testing. Got exception '%s'" % e

View File

@ -0,0 +1,31 @@
# Fail2Ban configuration file for unsuccesfull MySQL authentication attempts
#
# Authors: Artur Penttinen
# Yaroslav O. Halchenko
#
[INCLUDES]
# Read common prefixes. If any customizations available -- read them from
# common.local
before = common.conf
[Definition]
#_daemon = mysqld
# Option: failregex
# Notes.: regex to match the password failures messages in the logfile. The
# host must be matched by a group named "host". The tag "<HOST>" can
# be used for standard IP/hostname matching and is only an alias for
# (?:::f{4,6}:)?(?P<host>[\w\-.^_]+)
# Values: TEXT
# 130322 11:26:54 [Warning] Access denied for user 'root'@'127.0.0.1' (using password: YES)
failregex = Access denied for user '\w+'@'<HOST>' (to database '[^']*'|\(using password: (YES|NO)\))*\s*$
# Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored.
# Values: TEXT
#
ignoreregex =

View File

@ -352,6 +352,19 @@ action = iptables-multiport[name=asterisk-udp, port="5060,5061", protocol=udp]
logpath = /var/log/asterisk/messages logpath = /var/log/asterisk/messages
maxretry = 10 maxretry = 10
# To log wrong MySQL access attempts add to /etc/my.cnf:
# log-error=/var/log/mysqld.log
# log-warning = 2
[mysqld-iptables]
enabled = false
filter = mysqld-auth
action = iptables[name=mysql, port=3306, protocol=tcp]
sendmail-whois[name=MySQL, dest=root, sender=fail2ban@example.com]
logpath = /var/log/mysqld.log
maxretry = 5
# Jail for more extended banning of persistent abusers # Jail for more extended banning of persistent abusers
# !!! WARNING !!! # !!! WARNING !!!
# Make sure that your loglevel specified in fail2ban.conf/.local # Make sure that your loglevel specified in fail2ban.conf/.local

View File

@ -9,7 +9,7 @@ for python in /usr/{,local/}bin/python2.[0-9]{,.*}{,-dbg}
do do
[ -e "$python" ] || continue [ -e "$python" ] || continue
echo "Testing using $python" echo "Testing using $python"
$python ./fail2ban-testcases "$@" || failed+=" $python" $python bin/fail2ban-testcases "$@" || failed+=" $python"
done done
if [ ! -z "$failed" ]; then if [ ! -z "$failed" ]; then

View File

@ -31,7 +31,7 @@ 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("fail2ban.client.config") logSys = logging.getLogger(__name__)
class ActionReader(ConfigReader): class ActionReader(ConfigReader):

View File

@ -23,10 +23,10 @@ __license__ = "GPL"
import logging import logging
from common.exceptions import UnknownJailException, DuplicateJailException from fail2ban.exceptions import UnknownJailException, DuplicateJailException
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.client.config") logSys = logging.getLogger(__name__)
## ##
# Beautify the output of the client. # Beautify the output of the client.

View File

@ -31,7 +31,7 @@ import logging, os
from ConfigParser import SafeConfigParser from ConfigParser import SafeConfigParser
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.client.config") logSys = logging.getLogger(__name__)
class SafeConfigParserWithIncludes(SafeConfigParser): class SafeConfigParserWithIncludes(SafeConfigParser):
""" """

View File

@ -32,7 +32,7 @@ from configparserinc import SafeConfigParserWithIncludes
from ConfigParser import NoOptionError, NoSectionError from ConfigParser import NoOptionError, NoSectionError
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.client.config") logSys = logging.getLogger(__name__)
class ConfigReader(SafeConfigParserWithIncludes): class ConfigReader(SafeConfigParserWithIncludes):

View File

@ -33,7 +33,7 @@ from fail2banreader import Fail2banReader
from jailsreader import JailsReader from jailsreader import JailsReader
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.client.config") logSys = logging.getLogger(__name__)
class Configurator: class Configurator:

View File

@ -31,7 +31,7 @@ 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("fail2ban.client.config") logSys = logging.getLogger(__name__)
class Fail2banReader(ConfigReader): class Fail2banReader(ConfigReader):

View File

@ -31,7 +31,7 @@ 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("fail2ban.client.config") logSys = logging.getLogger(__name__)
class FilterReader(ConfigReader): class FilterReader(ConfigReader):

View File

@ -34,7 +34,7 @@ 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("fail2ban.client.config") logSys = logging.getLogger(__name__)
class JailReader(ConfigReader): class JailReader(ConfigReader):
@ -65,6 +65,7 @@ class JailReader(ConfigReader):
["string", "logencoding", "auto"], ["string", "logencoding", "auto"],
["string", "backend", "auto"], ["string", "backend", "auto"],
["int", "maxretry", 3], ["int", "maxretry", 3],
["int", "maxlines", 1],
["int", "findtime", 600], ["int", "findtime", 600],
["int", "bantime", 600], ["int", "bantime", 600],
["string", "usedns", "warn"], ["string", "usedns", "warn"],
@ -123,6 +124,8 @@ class JailReader(ConfigReader):
backend = self.__opts[opt] backend = self.__opts[opt]
elif opt == "maxretry": elif opt == "maxretry":
stream.append(["set", self.__name, "maxretry", self.__opts[opt]]) stream.append(["set", self.__name, "maxretry", self.__opts[opt]])
elif opt == "maxlines":
stream.append(["set", self.__name, "maxlines", self.__opts[opt]])
elif opt == "ignoreip": elif opt == "ignoreip":
for ip in self.__opts[opt].split(): for ip in self.__opts[opt].split():
# Do not send a command if the rule is empty. # Do not send a command if the rule is empty.

View File

@ -32,7 +32,7 @@ from configreader import ConfigReader
from jailreader import JailReader from jailreader import JailReader
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.client.config") logSys = logging.getLogger(__name__)
class JailsReader(ConfigReader): class JailsReader(ConfigReader):

View File

@ -68,6 +68,7 @@ protocol = [
["set <JAIL> banip <IP>", "manually Ban <IP> for <JAIL>"], ["set <JAIL> banip <IP>", "manually Ban <IP> for <JAIL>"],
["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> addaction <ACT>", "adds a new action named <NAME> for <JAIL>"], ["set <JAIL> addaction <ACT>", "adds a new action named <NAME> for <JAIL>"],
["set <JAIL> delaction <ACT>", "removes the action <NAME> from <JAIL>"], ["set <JAIL> delaction <ACT>", "removes the action <NAME> from <JAIL>"],
["set <JAIL> setcinfo <ACT> <KEY> <VALUE>", "sets <VALUE> for <KEY> of the action <NAME> for <JAIL>"], ["set <JAIL> setcinfo <ACT> <KEY> <VALUE>", "sets <VALUE> for <KEY> of the action <NAME> for <JAIL>"],
@ -87,6 +88,7 @@ protocol = [
["get <JAIL> bantime", "gets the time a host is banned for <JAIL>"], ["get <JAIL> bantime", "gets the time a host is banned for <JAIL>"],
["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> addaction", "gets the last action which has been added for <JAIL>"], ["get <JAIL> addaction", "gets the last action which has been added for <JAIL>"],
["get <JAIL> actionstart <ACT>", "gets the start command for the action <ACT> for <JAIL>"], ["get <JAIL> actionstart <ACT>", "gets the start command for the action <ACT> for <JAIL>"],
["get <JAIL> actionstop <ACT>", "gets the stop command for the action <ACT> for <JAIL>"], ["get <JAIL> actionstop <ACT>", "gets the stop command for the action <ACT> for <JAIL>"],

View File

@ -32,7 +32,7 @@ import threading
#from subprocess import call #from subprocess import call
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.actions.action") logSys = logging.getLogger(__name__)
# Create a lock for running system commands # Create a lock for running system commands
_cmd_lock = threading.Lock() _cmd_lock = threading.Lock()

View File

@ -34,7 +34,7 @@ from mytime import MyTime
import time, logging import time, logging
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.actions") logSys = logging.getLogger(__name__)
## ##
# Execute commands. # Execute commands.

View File

@ -28,11 +28,12 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
from pickle import dumps, loads, HIGHEST_PROTOCOL from pickle import dumps, loads, HIGHEST_PROTOCOL
from common import helpers
import asyncore, asynchat, socket, os, logging, sys, traceback import asyncore, asynchat, socket, os, logging, sys, traceback
from fail2ban import helpers
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.server") logSys = logging.getLogger(__name__)
## ##
# Request handler class. # Request handler class.

View File

@ -33,7 +33,7 @@ from mytime import MyTime
import logging import logging
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.action") logSys = logging.getLogger(__name__)
## ##
# Banning Manager. # Banning Manager.

View File

@ -33,7 +33,7 @@ from datetemplate import DateStrptime, DateTai64n, DateEpoch, DateISO8601
from threading import Lock from threading import Lock
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.filter.datedetector") logSys = logging.getLogger(__name__)
class DateDetector: class DateDetector:
@ -155,6 +155,12 @@ class DateDetector:
template.setRegex("^<\d{2}/\d{2}/\d{2}@\d{2}:\d{2}:\d{2}>") template.setRegex("^<\d{2}/\d{2}/\d{2}@\d{2}:\d{2}:\d{2}>")
template.setPattern("<%m/%d/%y@%H:%M:%S>") template.setPattern("<%m/%d/%y@%H:%M:%S>")
self._appendTemplate(template) self._appendTemplate(template)
# MySQL: 130322 11:46:11
template = DateStrptime()
template.setName("MonthDayYear Hour:Minute:Second")
template.setRegex("^\d{2}\d{2}\d{2} +\d{1,2}:\d{2}:\d{2}")
template.setPattern("%y%m%d %H:%M:%S")
self._appendTemplate(template)
finally: finally:
self.__lock.release() self.__lock.release()

View File

@ -33,7 +33,7 @@ from mytime import MyTime
import iso8601 import iso8601
import logging import logging
logSys = logging.getLogger("fail2ban.datetemplate") logSys = logging.getLogger(__name__)
class DateTemplate: class DateTemplate:

View File

@ -30,7 +30,7 @@ __license__ = "GPL"
import logging import logging
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = logging.getLogger("fail2ban") logSys = logging.getLogger(__name__)
class FailData: class FailData:

View File

@ -33,7 +33,7 @@ from threading import Lock
import logging import logging
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.filter") logSys = logging.getLogger(__name__)
class FailManager: class FailManager:

View File

@ -48,10 +48,15 @@ class Regex:
# Perform shortcuts expansions. # Perform shortcuts expansions.
# Replace "<HOST>" with default regular expression for host. # Replace "<HOST>" with default regular expression for host.
regex = regex.replace("<HOST>", "(?:::f{4,6}:)?(?P<host>[\w\-.^_]+)") regex = regex.replace("<HOST>", "(?:::f{4,6}:)?(?P<host>[\w\-.^_]+)")
# Replace "<SKIPLINES>" with regular expression for multiple lines.
regexSplit = regex.split("<SKIPLINES>")
regex = regexSplit[0]
for n, regexLine in enumerate(regexSplit[1:]):
regex += "\n(?P<skiplines%i>(?:(.*\n)*?))" % n + regexLine
if regex.lstrip() == '': if regex.lstrip() == '':
raise RegexException("Cannot add empty regex") raise RegexException("Cannot add empty regex")
try: try:
self._regexObj = re.compile(regex) self._regexObj = re.compile(regex, re.MULTILINE)
self._regex = regex self._regex = regex
except sre_constants.error: except sre_constants.error:
raise RegexException("Unable to compile regular expression '%s'" % raise RegexException("Unable to compile regular expression '%s'" %
@ -76,6 +81,19 @@ class Regex:
def search(self, value): def search(self, value):
self._matchCache = self._regexObj.search(value) self._matchCache = self._regexObj.search(value)
if self.hasMatched():
# Find start of the first line where the match was found
try:
self._matchLineStart = self._matchCache.string.rindex(
"\n", 0, self._matchCache.start() +1 ) + 1
except ValueError:
self._matchLineStart = 0
# Find end of the last line where the match was found
try:
self._matchLineEnd = self._matchCache.string.index(
"\n", self._matchCache.end() - 1) + 1
except ValueError:
self._matchLineEnd = len(self._matchCache.string)
## ##
# Checks if the previous call to search() matched. # Checks if the previous call to search() matched.
@ -88,6 +106,54 @@ class Regex:
else: else:
return False return False
##
# Returns skipped lines.
#
# This returns skipped lines captured by the <SKIPLINES> tag.
# @return list of skipped lines
def getSkippedLines(self):
if not self._matchCache:
return []
skippedLines = ""
n = 0
while True:
try:
skippedLines += self._matchCache.group("skiplines%i" % n)
n += 1
except IndexError:
break
return skippedLines.splitlines(True)
##
# Returns unmatched lines.
#
# This returns unmatched lines including captured by the <SKIPLINES> tag.
# @return list of unmatched lines
def getUnmatchedLines(self):
if not self.hasMatched():
return []
unmatchedLines = (
self._matchCache.string[:self._matchLineStart].splitlines(True)
+ self.getSkippedLines()
+ self._matchCache.string[self._matchLineEnd:].splitlines(True))
return unmatchedLines
##
# Returns matched lines.
#
# This returns matched lines by excluding those captured
# by the <SKIPLINES> tag.
# @return list of matched lines
def getMatchedLines(self):
if not self.hasMatched():
return []
matchedLines = self._matchCache.string[
self._matchLineStart:self._matchLineEnd].splitlines(True)
return [line for line in matchedLines
if line not in self.getSkippedLines()]
## ##
# Exception dedicated to the class Regex. # Exception dedicated to the class Regex.

View File

@ -38,7 +38,7 @@ from failregex import FailRegex, Regex, RegexException
import logging, re, os, fcntl, time, sys, locale, codecs import logging, re, os, fcntl, time, sys, locale, codecs
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.filter") logSys = logging.getLogger(__name__)
## ##
# Log reader class. # Log reader class.
@ -71,6 +71,12 @@ class Filter(JailThread):
self.__findTime = 6000 self.__findTime = 6000
## The ignore IP list. ## The ignore IP list.
self.__ignoreIpList = [] self.__ignoreIpList = []
## Size of line buffer
self.__lineBufferSize = 1
## Line buffer
self.__lineBuffer = []
## Store last time stamp, applicable for multi-line
self.__lastTimeLine = ""
self.dateDetector = DateDetector() self.dateDetector = DateDetector()
self.dateDetector.addDefaultTemplate() self.dateDetector.addDefaultTemplate()
@ -206,6 +212,23 @@ class Filter(JailThread):
def getMaxRetry(self): def getMaxRetry(self):
return self.failManager.getMaxRetry() return self.failManager.getMaxRetry()
##
# Set the maximum line buffer size.
#
# @param value the line buffer size
def setMaxLines(self, value):
self.__lineBufferSize = max(1, value)
logSys.info("Set maxLines = %i" % self.__lineBufferSize)
##
# Get the maximum line buffer size.
#
# @return the line buffer size
def getMaxLines(self):
return self.__lineBufferSize
## ##
# Main loop. # Main loop.
# #
@ -295,14 +318,17 @@ class Filter(JailThread):
if timeMatch: if timeMatch:
# Lets split into time part and log part of the line # Lets split into time part and log part of the line
timeLine = timeMatch.group() timeLine = timeMatch.group()
self.__lastTimeLine = timeLine
# Lets leave the beginning in as well, so if there is no # Lets leave the beginning in as well, so if there is no
# anchore at the beginning of the time regexp, we don't # anchore at the beginning of the time regexp, we don't
# at least allow injection. Should be harmless otherwise # at least allow injection. Should be harmless otherwise
logLine = line[:timeMatch.start()] + line[timeMatch.end():] logLine = line[:timeMatch.start()] + line[timeMatch.end():]
else: else:
timeLine = line timeLine = self.__lastTimeLine or line
logLine = line logLine = line
return self.findFailure(timeLine, logLine) self.__lineBuffer = ((self.__lineBuffer +
[logLine])[-self.__lineBufferSize:])
return self.findFailure(timeLine, "".join(self.__lineBuffer))
def processLineAndAdd(self, line): def processLineAndAdd(self, line):
"""Processes the line for failures and populates failManager """Processes the line for failures and populates failManager
@ -345,14 +371,15 @@ class Filter(JailThread):
def findFailure(self, timeLine, logLine): def findFailure(self, timeLine, logLine):
failList = list() failList = list()
# Checks if we must ignore this line.
if self.ignoreLine(logLine):
# The ignoreregex matched. Return.
return failList
# Iterates over all the regular expressions. # Iterates over all the regular expressions.
for failRegex in self.__failRegex: for failRegex in self.__failRegex:
failRegex.search(logLine) failRegex.search(logLine)
if failRegex.hasMatched(): if failRegex.hasMatched():
# Checks if we must ignore this match.
if self.ignoreLine("".join(failRegex.getMatchedLines())):
# The ignoreregex matched. Remove ignored match.
self.__lineBuffer = failRegex.getUnmatchedLines()
continue
# The failregex matched. # The failregex matched.
date = self.dateDetector.getUnixTime(timeLine) date = self.dateDetector.getUnixTime(timeLine)
if date == None: if date == None:
@ -362,6 +389,7 @@ class Filter(JailThread):
"in order to get support for this format." "in order to get support for this format."
% (logLine, timeLine)) % (logLine, timeLine))
else: else:
self.__lineBuffer = failRegex.getUnmatchedLines()
try: try:
host = failRegex.getHost() host = failRegex.getHost()
ipMatch = DNSUtils.textToIp(host, self.__useDns) ipMatch = DNSUtils.textToIp(host, self.__useDns)

View File

@ -30,7 +30,7 @@ from mytime import MyTime
import time, logging, gamin import time, logging, gamin
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.filter") logSys = logging.getLogger(__name__)
## ##
# Log reader class. # Log reader class.

View File

@ -33,7 +33,7 @@ from mytime import MyTime
import time, logging, os import time, logging, os
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.filter") logSys = logging.getLogger(__name__)
## ##
# Log reader class. # Log reader class.

View File

@ -38,7 +38,7 @@ if not hasattr(pyinotify, '__version__') \
from os.path import dirname, sep as pathsep from os.path import dirname, sep as pathsep
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.filter") logSys = logging.getLogger(__name__)
## ##
# Log reader class. # Log reader class.

View File

@ -28,7 +28,7 @@ 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("fail2ban.jail") logSys = logging.getLogger(__name__)
class Jail: class Jail:

View File

@ -21,7 +21,7 @@ __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 common.exceptions import DuplicateJailException, UnknownJailException from fail2ban.exceptions import DuplicateJailException, UnknownJailException
from jail import Jail from jail import Jail
from threading import Lock from threading import Lock

View File

@ -31,7 +31,7 @@ from threading import Thread
import logging import logging
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.server") logSys = logging.getLogger(__name__)
class JailThread(Thread): class JailThread(Thread):

View File

@ -32,11 +32,11 @@ from jails import Jails
from transmitter import Transmitter from transmitter import Transmitter
from asyncserver import AsyncServer from asyncserver import AsyncServer
from asyncserver import AsyncServerException from asyncserver import AsyncServerException
from common import version from fail2ban import version
import logging, logging.handlers, sys, os, signal import logging, logging.handlers, sys, os, signal
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.server") logSys = logging.getLogger(__name__)
class Server: class Server:
@ -223,6 +223,12 @@ class Server:
def getMaxRetry(self, name): def getMaxRetry(self, name):
return self.__jails.getFilter(name).getMaxRetry() return self.__jails.getFilter(name).getMaxRetry()
def setMaxLines(self, name, value):
self.__jails.getFilter(name).setMaxLines(value)
def getMaxLines(self, name):
return self.__jails.getFilter(name).getMaxLines()
# Action # Action
def addAction(self, name, value): def addAction(self, name, value):
self.__jails.getAction(name).addAction(value) self.__jails.getAction(name).addAction(value)
@ -326,7 +332,7 @@ class Server:
logLevel = logging.WARNING logLevel = logging.WARNING
elif value == 3: elif value == 3:
logLevel = logging.INFO logLevel = logging.INFO
logging.getLogger("fail2ban").setLevel(logLevel) logging.getLogger(__name__).parent.parent.setLevel(logLevel)
finally: finally:
self.__loggingLock.release() self.__loggingLock.release()
@ -375,9 +381,10 @@ class Server:
return False return False
# Removes previous handlers -- in reverse order since removeHandler # Removes previous handlers -- in reverse order since removeHandler
# alter the list in-place and that can confuses the iterable # alter the list in-place and that can confuses the iterable
for handler in logging.getLogger("fail2ban").handlers[::-1]: logger = logging.getLogger(__name__).parent.parent
for handler in logger.handlers[::-1]:
# Remove the handler. # Remove the handler.
logging.getLogger("fail2ban").removeHandler(handler) logger.removeHandler(handler)
# And try to close -- it might be closed already # And try to close -- it might be closed already
try: try:
handler.flush() handler.flush()
@ -390,7 +397,7 @@ class Server:
# with older Pythons -- seems to be safe to ignore there # with older Pythons -- seems to be safe to ignore there
# tell the handler to use this format # tell the handler to use this format
hdlr.setFormatter(formatter) hdlr.setFormatter(formatter)
logging.getLogger("fail2ban").addHandler(hdlr) logger.addHandler(hdlr)
# Does not display this message at startup. # Does not display this message at startup.
if not self.__logTarget == None: if not self.__logTarget == None:
logSys.info("Changed logging target to %s for Fail2ban v%s" % logSys.info("Changed logging target to %s for Fail2ban v%s" %

View File

@ -30,7 +30,7 @@ __license__ = "GPL"
import logging import logging
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = logging.getLogger("fail2ban") logSys = logging.getLogger(__name__)
class Ticket: class Ticket:

View File

@ -30,7 +30,7 @@ __license__ = "GPL"
import logging, time import logging, time
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.comm") logSys = logging.getLogger(__name__)
class Transmitter: class Transmitter:
@ -175,6 +175,10 @@ class Transmitter:
value = command[2] value = command[2]
self.__server.setMaxRetry(name, int(value)) self.__server.setMaxRetry(name, int(value))
return self.__server.getMaxRetry(name) return self.__server.getMaxRetry(name)
elif command[1] == "maxlines":
value = command[2]
self.__server.setMaxLines(name, int(value))
return self.__server.getMaxLines(name)
# command # command
elif command[1] == "bantime": elif command[1] == "bantime":
value = command[2] value = command[2]
@ -256,6 +260,8 @@ class Transmitter:
return self.__server.getFindTime(name) return self.__server.getFindTime(name)
elif command[1] == "maxretry": elif command[1] == "maxretry":
return self.__server.getMaxRetry(name) return self.__server.getMaxRetry(name)
elif command[1] == "maxlines":
return self.__server.getMaxLines(name)
# Action # Action
elif command[1] == "bantime": elif command[1] == "bantime":
return self.__server.getBanTime(name) return self.__server.getBanTime(name)

View File

@ -29,9 +29,10 @@ __license__ = "GPL"
import unittest, time import unittest, time
import logging, sys import logging, sys
from server.action import Action
from StringIO import StringIO from StringIO import StringIO
from fail2ban.server.action import Action
class ExecuteAction(unittest.TestCase): class ExecuteAction(unittest.TestCase):
def setUp(self): def setUp(self):

View File

@ -28,8 +28,9 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
import unittest import unittest
from server.banmanager import BanManager
from server.ticket import BanTicket from fail2ban.server.banmanager import BanManager
from fail2ban.server.ticket import BanTicket
class AddFailure(unittest.TestCase): class AddFailure(unittest.TestCase):

View File

@ -22,10 +22,18 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2013 Yaroslav Halchenko"
__license__ = "GPL" __license__ = "GPL"
import os, shutil, tempfile, unittest import os, shutil, tempfile, unittest
from client.configreader import ConfigReader
from client.jailreader import JailReader from fail2ban.client.configreader import ConfigReader
from client.jailsreader import JailsReader from fail2ban.client.jailreader import JailReader
from client.configurator import Configurator from fail2ban.client.filterreader import FilterReader
from fail2ban.client.jailsreader import JailsReader
from fail2ban.client.configurator import Configurator
TEST_FILES_DIR = os.path.join(os.path.dirname(__file__), "files")
if os.path.exists('config/fail2ban.conf'):
CONFIG_DIR='config'
else:
CONFIG_DIR='/etc/fail2ban'
class ConfigReaderTest(unittest.TestCase): class ConfigReaderTest(unittest.TestCase):
@ -98,7 +106,7 @@ option = %s
class JailReaderTest(unittest.TestCase): class JailReaderTest(unittest.TestCase):
def testStockSSHJail(self): def testStockSSHJail(self):
jail = JailReader('ssh-iptables', basedir='config') # we are running tests from root project dir atm jail = JailReader('ssh-iptables', basedir=CONFIG_DIR) # we are running tests from root project dir atm
self.assertTrue(jail.read()) self.assertTrue(jail.read())
self.assertTrue(jail.getOptions()) self.assertTrue(jail.getOptions())
self.assertFalse(jail.isEnabled()) self.assertFalse(jail.isEnabled())
@ -110,6 +118,36 @@ class JailReaderTest(unittest.TestCase):
result = JailReader.splitAction(action) result = JailReader.splitAction(action)
self.assertEquals(expected, result) self.assertEquals(expected, result)
class FilterReaderTest(unittest.TestCase):
def testConvert(self):
output = [['set', 'testcase01', 'addfailregex',
"^\\s*(?:\\S+ )?(?:kernel: \\[\\d+\\.\\d+\\] )?(?:@vserver_\\S+ )"
"?(?:(?:\\[\\d+\\])?:\\s+[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?|"
"[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?(?:\\[\\d+\\])?:)?\\s*(?:"
"error: PAM: )?Authentication failure for .* from <HOST>\\s*$"],
['set', 'testcase01', 'addfailregex',
"^\\s*(?:\\S+ )?(?:kernel: \\[\\d+\\.\\d+\\] )?(?:@vserver_\\S+ )"
"?(?:(?:\\[\\d+\\])?:\\s+[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?|"
"[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?(?:\\[\\d+\\])?:)?\\s*(?:"
"error: PAM: )?User not known to the underlying authentication mo"
"dule for .* from <HOST>\\s*$"],
['set', 'testcase01', 'addfailregex',
"^\\s*(?:\\S+ )?(?:kernel: \\[\\d+\\.\\d+\\] )?(?:@vserver_\\S+ )"
"?(?:(?:\\[\\d+\\])?:\\s+[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?|"
"[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?(?:\\[\\d+\\])?:)?\\s*(?:"
"error: PAM: )?User not known to the\\nunderlying authentication."
"+$<SKIPLINES>^.+ module for .* from <HOST>\\s*$"],
['set', 'testcase01', 'addignoreregex',
"^.+ john from host 192.168.1.1\\s*$"]]
filterReader = FilterReader("testcase01", "testcase01")
filterReader.setBaseDir(TEST_FILES_DIR)
filterReader.read()
#filterReader.getOptions(["failregex", "ignoreregex"])
filterReader.getOptions(None)
self.assertEquals(filterReader.convert(), output)
class JailsReaderTest(unittest.TestCase): class JailsReaderTest(unittest.TestCase):
def testProvidingBadBasedir(self): def testProvidingBadBasedir(self):
@ -118,7 +156,7 @@ class JailsReaderTest(unittest.TestCase):
self.assertRaises(ValueError, reader.read) self.assertRaises(ValueError, reader.read)
def testReadStockJailConf(self): def testReadStockJailConf(self):
jails = JailsReader(basedir='config') # we are running tests from root project dir atm jails = JailsReader(basedir=CONFIG_DIR) # we are running tests from root project dir atm
self.assertTrue(jails.read()) # opens fine self.assertTrue(jails.read()) # opens fine
self.assertTrue(jails.getOptions()) # reads fine self.assertTrue(jails.getOptions()) # reads fine
comm_commands = jails.convert() comm_commands = jails.convert()
@ -129,7 +167,7 @@ class JailsReaderTest(unittest.TestCase):
def testReadStockJailConfForceEnabled(self): def testReadStockJailConfForceEnabled(self):
# more of a smoke test to make sure that no obvious surprises # more of a smoke test to make sure that no obvious surprises
# on users' systems when enabling shipped jails # on users' systems when enabling shipped jails
jails = JailsReader(basedir='config', force_enable=True) # we are running tests from root project dir atm jails = JailsReader(basedir=CONFIG_DIR, force_enable=True) # we are running tests from root project dir atm
self.assertTrue(jails.read()) # opens fine self.assertTrue(jails.read()) # opens fine
self.assertTrue(jails.getOptions()) # reads fine self.assertTrue(jails.getOptions()) # reads fine
comm_commands = jails.convert() comm_commands = jails.convert()
@ -151,8 +189,8 @@ class JailsReaderTest(unittest.TestCase):
def testConfigurator(self): def testConfigurator(self):
configurator = Configurator() configurator = Configurator()
configurator.setBaseDir('config') configurator.setBaseDir(CONFIG_DIR)
self.assertEqual(configurator.getBaseDir(), 'config') self.assertEqual(configurator.getBaseDir(), CONFIG_DIR)
configurator.readEarly() configurator.readEarly()
opts = configurator.getEarlyOptions() opts = configurator.getEarlyOptions()
@ -165,4 +203,4 @@ class JailsReaderTest(unittest.TestCase):
# otherwise just a code smoke test) # otherwise just a code smoke test)
configurator._Configurator__jails.setBaseDir('/tmp') configurator._Configurator__jails.setBaseDir('/tmp')
self.assertEqual(configurator._Configurator__jails.getBaseDir(), '/tmp') self.assertEqual(configurator._Configurator__jails.getBaseDir(), '/tmp')
self.assertEqual(configurator.getBaseDir(), 'config') self.assertEqual(configurator.getBaseDir(), CONFIG_DIR)

View File

@ -28,8 +28,9 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
import unittest import unittest
from server.datedetector import DateDetector
from server.datetemplate import DateTemplate from fail2ban.server.datedetector import DateDetector
from fail2ban.server.datetemplate import DateTemplate
class DateDetectorTest(unittest.TestCase): class DateDetectorTest(unittest.TestCase):
@ -84,6 +85,7 @@ class DateDetectorTest(unittest.TestCase):
"2005-01-23T21:59:59.252Z", #ISO 8601 "2005-01-23T21:59:59.252Z", #ISO 8601
"2005-01-23T21:59:59-05:00Z", #ISO 8601 with TZ "2005-01-23T21:59:59-05:00Z", #ISO 8601 with TZ
"<01/23/05@21:59:59>", "<01/23/05@21:59:59>",
"050123 21:59:59", # MySQL
): ):
log = sdate + "[sshd] error: PAM: Authentication failure" log = sdate + "[sshd] error: PAM: Authentication failure"
# exclude # exclude

View File

@ -28,8 +28,9 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
import unittest, socket, time, pickle import unittest, socket, time, pickle
from server.failmanager import FailManager, FailManagerEmpty
from server.ticket import FailTicket from fail2ban.server.failmanager import FailManager, FailManagerEmpty
from fail2ban.server.ticket import FailTicket
class AddFailure(unittest.TestCase): class AddFailure(unittest.TestCase):

View File

@ -0,0 +1,41 @@
# Generic configuration items (to be used as interpolations) in other
# filters or actions configurations
#
# Author: Yaroslav Halchenko
#
# $Revision$
#
[DEFAULT]
# Daemon definition is to be specialized (if needed) in .conf file
_daemon = \S*
#
# Shortcuts for easier comprehension of the failregex
#
# PID.
# EXAMPLES: [123]
__pid_re = (?:\[\d+\])
# Daemon name (with optional source_file:line or whatever)
# EXAMPLES: pam_rhosts_auth, [sshd], pop(pam_unix)
__daemon_re = [\[\(]?%(_daemon)s(?:\(\S+\))?[\]\)]?:?
# Combinations of daemon name and PID
# EXAMPLES: sshd[31607], pop(pam_unix)[4920]
__daemon_combs_re = (?:%(__pid_re)s?:\s+%(__daemon_re)s|%(__daemon_re)s%(__pid_re)s?:)
# Some messages have a kernel prefix with a timestamp
# EXAMPLES: kernel: [769570.846956]
__kernel_prefix = kernel: \[\d+\.\d+\]
__hostname = \S+
#
# Common line prefixes (beginnings) which could be used in filters
#
# [hostname] [vserver tag] daemon_id spaces
# this can be optional (for instance if we match named native log files)
__prefix_line = \s*(?:%(__hostname)s )?(?:%(__kernel_prefix)s )?(?:@vserver_\S+ )?%(__daemon_combs_re)s?\s*

View File

@ -0,0 +1,34 @@
# Fail2Ban configuration file
#
# Author: Cyril Jaquier
#
# $Revision$
#
[INCLUDES]
# Read common prefixes. If any customizations available -- read them from
# common.local
before = testcase-common.conf
[Definition]
_daemon = sshd
# Option: failregex
# Notes.: regex to match the password failures messages in the logfile. The
# host must be matched by a group named "host". The tag "<HOST>" can
# be used for standard IP/hostname matching and is only an alias for
# (?:::f{4,6}:)?(?P<host>[\w\-.^_]+)
# Values: TEXT
#
failregex = ^%(__prefix_line)s(?:error: PAM: )?Authentication failure for .* from <HOST>\s*$
^%(__prefix_line)s(?:error: PAM: )?User not known to the underlying authentication module for .* from <HOST>\s*$
^%(__prefix_line)s(?:error: PAM: )?User not known to the\nunderlying authentication.+$<SKIPLINES>^.+ module for .* from <HOST>\s*$
# Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored.
# Values: TEXT
#
ignoreregex = ^.+ john from host 192.168.1.1\s*$

View File

@ -0,0 +1,6 @@
130324 0:04:00 [Warning] Access denied for user 'root'@'192.168.1.35' (using password: NO)
130324 8:24:09 [Warning] Access denied for user 'root'@'220.95.238.171' (using password: NO)
130324 17:56:13 [Warning] Access denied for user 'root'@'61.160.223.112' (using password: NO)
130324 17:56:14 [Warning] Access denied for user 'root'@'61.160.223.112' (using password: YES)
130324 19:01:39 [Warning] Access denied for user 'root'@'61.147.108.35' (using password: NO)
130324 19:01:40 [Warning] Access denied for user 'root'@'61.147.108.35' (using password: YES)

View File

@ -0,0 +1,35 @@
Aug 14 11:58:58 yyyy rsyncd[9874]: connect from example.com (192.0.43.10)
Aug 14 11:58:58 yyyy rsyncd[23864]: connect from example.com (192.0.43.10)
Aug 14 11:59:58 yyyy rsyncd[23864]: rsync on xxx/ from example.com (192.0.43.10)
Aug 14 11:59:58 yyyy rsyncd[23864]: building file list
Aug 14 11:59:58 yyyy rsyncd[28101]: connect from irrelevant (192.0.43.11)
Aug 14 11:59:58 yyyy rsyncd[28101]: rsync on xxx/ from irrelevant (192.0.43.11)
Aug 14 11:59:58 yyyy rsyncd[28101]: building file list
Aug 14 11:59:58 yyyy rsyncd[28101]: sent 294382 bytes received 781 bytes total size 29221543998
Aug 14 11:59:58 yyyy rsyncd[18067]: sent 2833586339 bytes received 65115 bytes total size 29221543998
Aug 14 11:59:58 yyyy smartd[2635]: Device: /dev/sdb [SAT], SMART Usage Attribute: 194 Temperature_Celsius changed from 116 to 115
Aug 14 11:59:58 yyyy rsyncd[1762]: connect from irrelevant (192.0.43.11)
Aug 14 11:59:58 yyyy rsyncd[1762]: rsync on xxx/ from irrelevant (192.0.43.11)
Aug 14 11:59:58 yyyy rsyncd[1762]: building file list
Aug 14 11:59:58 yyyy rsyncd[1762]: sent 294382 bytes received 781 bytes total size 29221543998
Aug 14 11:59:58 yyyy sendmail[30222]: r0NNNlC0030222: from=<bounce-25497-9881290652-user=example.com@example.com>, size=6420, class=0, nrcpts=1, msgid=<0.0.9881290652.3772024cf8879cycvau18081.0@example.com>, bodytype=8BITMIME, proto=ESMTP, daemon=MTA, relay=[192.0.43.15] (may be forged)
Aug 14 11:59:58 yyyy smartd[2635]: Device: /dev/sda [SAT], starting scheduled Short Self-Test.
Aug 14 11:59:58 yyyy smartd[2635]: Device: /dev/sdb [SAT], SMART Usage Attribute: 194 Temperature_Celsius changed from 115 to 116
Aug 14 11:59:58 yyyy smartd[2635]: Device: /dev/sdb [SAT], starting scheduled Short Self-Test.
Aug 14 11:59:58 yyyy smartd[2635]: Device: /dev/sda [SAT], previous self-test completed without error
Aug 14 11:59:58 yyyy smartd[2635]: Device: /dev/sdb [SAT], previous self-test completed without error
Aug 14 11:59:58 yyyy rsyncd[7788]: connect from irrelevant (192.0.43.11)
Aug 14 11:59:58 yyyy rsyncd[7788]: rsync on xxx/ from irrelevant (192.0.43.11)
Aug 14 11:59:58 yyyy rsyncd[7788]: building file list
Aug 14 11:59:58 yyyy rsyncd[21919]: sent 2836906453 bytes received 6768 bytes total size 29221543998
Aug 14 11:59:58 yyyy rsyncd[23864]: rsync error: timeout in data send/receive (code 30) at io.c(137) [sender=3.0.9]
Aug 14 11:59:58 yyyy spamd[19119]: spamd: result: Y 11 - AWL,BAYES_50,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HTML_MESSAGE,RCVD_IN_BRBL_LASTEXT,RCVD_IN_PSBL,RCVD_IN_RP_RNBL,RDNS_NONE,URIBL_BLACK,URIBL_DBL_SPAM scantime=1.2,size=6910,user=sa-milt,uid=499,required_score=5.0,rhost=localhost,raddr=127.0.0.1,rport=57429,mid=<0.0.9881290652.3772024cf8879cycvau18081.0@example.com>,bayes=0.536244,autolearn=no
Aug 14 11:59:58 yyyy rsyncd[5534]: connect from irrelevant (192.0.43.11)
Aug 14 11:59:58 yyyy rsyncd[5534]: rsync on xxx/ from irrelevant (192.0.43.11)
Aug 14 11:59:58 yyyy rsyncd[5534]: building file list
Aug 14 11:59:58 yyyy rsyncd[7788]: rsync error: Received SIGINT
Aug 14 11:59:58 yyyy rsyncd[5534]: sent 294382 bytes received 781 bytes total size 29221543998
Aug 14 11:59:59 yyyy rsyncd[9874]: rsync error: timeout in data send/receive (code 30) at io.c(137) [sender=3.0.9]

View File

@ -1,15 +1,15 @@
Sep 21 22:03:07 [sshd] Invalid user toto from 212.41.96.185 Sep 21 22:03:07 [sshd] Invalid user toto from 212.41.96.185
1124012400 [sshd] Invalid user fuck from 212.41.96.185 1124012400 [sshd] Invalid user duck from 212.41.96.185
Sep 21 21:03:38 [sshd] Invalid user toto from 212.41.96.185 Sep 21 21:03:38 [sshd] Invalid user toto from 212.41.96.185
1124012500 [sshd] Invalid user fuck from 212.41.96.185 1124012500 [sshd] Invalid user duck from 212.41.96.185
Sep 21 21:03:46 [sshd] Invalid user toto from 212.41.96.185 Sep 21 21:03:46 [sshd] Invalid user toto from 212.41.96.185
Aug 14 11:58:48 [sshd] Invalid user fuck from 212.41.96.185 Aug 14 11:58:48 [sshd] Invalid user duck from 212.41.96.185
Aug 14 11:59:58 [sshd] Invalid user toto from 212.41.96.185 Aug 14 11:59:58 [sshd] Invalid user toto from 212.41.96.185
Sep 21 21:04:03 [sshd] Invalid user fuck from 212.41.96.185 Sep 21 21:04:03 [sshd] Invalid user duck from 212.41.96.185
- Last output repeated twice - - Last output repeated twice -
2005/08/14 11:57:00 [sshd] Invalid user toto from 212.41.96.186 2005/08/14 11:57:00 [sshd] Invalid user toto from 212.41.96.186
2005/08/14 11:58:00 [sshd] Invalid user fuck from 212.41.96.186 2005/08/14 11:58:00 [sshd] Invalid user duck from 212.41.96.186
2005/08/14 11:59:00 [sshd] Invalid user toto from 212.41.96.186 2005/08/14 11:59:00 [sshd] Invalid user toto from 212.41.96.186
2005/08/14 12:00:00 [sshd] Invalid user fuck from 212.41.96.186 2005/08/14 12:00:00 [sshd] Invalid user duck from 212.41.96.186
- Last output repeated twice - - Last output repeated twice -
Sep 21 21:09:01 [sshd] Invalid user toto from 212.41.96.185 Sep 21 21:09:01 [sshd] Invalid user toto from 212.41.96.185

View File

@ -28,11 +28,13 @@ import sys
import time import time
import tempfile import tempfile
from server.jail import Jail from fail2ban.server.jail import Jail
from server.filterpoll import FilterPoll from fail2ban.server.filterpoll import FilterPoll
from server.filter import FileFilter, DNSUtils from fail2ban.server.filter import FileFilter, DNSUtils
from server.failmanager import FailManager from fail2ban.server.failmanager import FailManager
from server.failmanager import FailManagerEmpty from fail2ban.server.failmanager import FailManagerEmpty
TEST_FILES_DIR = os.path.join(os.path.dirname(__file__), "files")
# #
# Useful helpers # Useful helpers
@ -179,7 +181,7 @@ class IgnoreIP(unittest.TestCase):
class LogFile(unittest.TestCase): class LogFile(unittest.TestCase):
FILENAME = "testcases/files/testcase01.log" FILENAME = os.path.join(TEST_FILES_DIR, "testcase01.log")
def setUp(self): def setUp(self):
"""Call before every test case.""" """Call before every test case."""
@ -519,11 +521,12 @@ def get_monitor_failures_testcase(Filter_):
class GetFailures(unittest.TestCase): class GetFailures(unittest.TestCase):
FILENAME_01 = "testcases/files/testcase01.log" FILENAME_01 = os.path.join(TEST_FILES_DIR, "testcase01.log")
FILENAME_02 = "testcases/files/testcase02.log" FILENAME_02 = os.path.join(TEST_FILES_DIR, "testcase02.log")
FILENAME_03 = "testcases/files/testcase03.log" FILENAME_03 = os.path.join(TEST_FILES_DIR, "testcase03.log")
FILENAME_04 = "testcases/files/testcase04.log" FILENAME_04 = os.path.join(TEST_FILES_DIR, "testcase04.log")
FILENAME_USEDNS = "testcases/files/testcase-usedns.log" FILENAME_USEDNS = os.path.join(TEST_FILES_DIR, "testcase-usedns.log")
FILENAME_MULTILINE = os.path.join(TEST_FILES_DIR, "testcase-multiline.log")
# so that they could be reused by other tests # so that they could be reused by other tests
FAILURES_01 = ('193.168.0.128', 3, 1124013599.0, FAILURES_01 = ('193.168.0.128', 3, 1124013599.0,
@ -629,6 +632,53 @@ class GetFailures(unittest.TestCase):
self.assertRaises(FailManagerEmpty, self.filter.failManager.toBan) self.assertRaises(FailManagerEmpty, self.filter.failManager.toBan)
def testGetFailuresMultiLine(self):
output = [("192.0.43.10", 2, 1124013599.0),
("192.0.43.11", 1, 1124013598.0)]
self.filter.addLogPath(GetFailures.FILENAME_MULTILINE)
self.filter.addFailRegex("^.*rsyncd\[(?P<pid>\d+)\]: connect from .+ \(<HOST>\)$<SKIPLINES>^.+ rsyncd\[(?P=pid)\]: rsync error: .*$")
self.filter.setMaxLines(100)
self.filter.setMaxRetry(1)
self.filter.getFailures(GetFailures.FILENAME_MULTILINE)
_assert_correct_last_attempt(self, self.filter, output.pop())
_assert_correct_last_attempt(self, self.filter, output.pop())
self.assertRaises(FailManagerEmpty, self.filter.failManager.toBan)
def testGetFailuresMultiLineIgnoreRegex(self):
output = [("192.0.43.10", 2, 1124013599.0)]
self.filter.addLogPath(GetFailures.FILENAME_MULTILINE)
self.filter.addFailRegex("^.*rsyncd\[(?P<pid>\d+)\]: connect from .+ \(<HOST>\)$<SKIPLINES>^.+ rsyncd\[(?P=pid)\]: rsync error: .*$")
self.filter.addIgnoreRegex("rsync error: Received SIGINT")
self.filter.setMaxLines(100)
self.filter.setMaxRetry(1)
self.filter.getFailures(GetFailures.FILENAME_MULTILINE)
_assert_correct_last_attempt(self, self.filter, output.pop())
self.assertRaises(FailManagerEmpty, self.filter.failManager.toBan)
def testGetFailuresMultiLineMultiRegex(self):
output = [("192.0.43.10", 2, 1124013599.0),
("192.0.43.11", 1, 1124013598.0),
("192.0.43.15", 1, 1124013598.0)]
self.filter.addLogPath(GetFailures.FILENAME_MULTILINE)
self.filter.addFailRegex("^.*rsyncd\[(?P<pid>\d+)\]: connect from .+ \(<HOST>\)$<SKIPLINES>^.+ rsyncd\[(?P=pid)\]: rsync error: .*$")
self.filter.addFailRegex("^.* sendmail\[.*, msgid=<(?P<msgid>[^>]+).*relay=\[<HOST>\].*$<SKIPLINES>^.+ spamd: result: Y \d+ .*,mid=<(?P=msgid)>(,bayes=[.\d]+)?(,autolearn=\S+)?\s*$")
self.filter.setMaxLines(100)
self.filter.setMaxRetry(1)
self.filter.getFailures(GetFailures.FILENAME_MULTILINE)
_assert_correct_last_attempt(self, self.filter, output.pop())
_assert_correct_last_attempt(self, self.filter, output.pop())
_assert_correct_last_attempt(self, self.filter, output.pop())
self.assertRaises(FailManagerEmpty, self.filter.failManager.toBan)
class DNSUtilsTests(unittest.TestCase): class DNSUtilsTests(unittest.TestCase):
def testUseDns(self): def testUseDns(self):

View File

@ -28,8 +28,11 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
import unittest, socket, time, tempfile, os, locale import unittest, socket, time, tempfile, os, locale
from server.server import Server
from common.exceptions import UnknownJailException from fail2ban.server.server import Server
from fail2ban.exceptions import UnknownJailException
TEST_FILES_DIR = os.path.join(os.path.dirname(__file__), "files")
class StartStop(unittest.TestCase): class StartStop(unittest.TestCase):
@ -279,14 +282,14 @@ class Transmitter(TransmitterBase):
self.jailAddDelTest( self.jailAddDelTest(
"logpath", "logpath",
[ [
"testcases/files/testcase01.log", os.path.join(TEST_FILES_DIR, "testcase01.log"),
"testcases/files/testcase02.log", os.path.join(TEST_FILES_DIR, "testcase02.log"),
"testcases/files/testcase03.log", os.path.join(TEST_FILES_DIR, "testcase03.log"),
], ],
self.jailName self.jailName
) )
# Try duplicates # Try duplicates
value = "testcases/files/testcase04.log" value = os.path.join(TEST_FILES_DIR, "testcase04.log")
self.assertEqual( self.assertEqual(
self.transm.proceed(["set", self.jailName, "addlogpath", value]), self.transm.proceed(["set", self.jailName, "addlogpath", value]),
(0, [value])) (0, [value]))

View File

@ -28,8 +28,9 @@ __copyright__ = "Copyright (c) 2013 Steven Hiscocks"
__license__ = "GPL" __license__ = "GPL"
import unittest, time, tempfile, os, threading import unittest, time, tempfile, os, threading
from server.asyncserver import AsyncServer, AsyncServerException
from client.csocket import CSocket from fail2ban.server.asyncserver import AsyncServer, AsyncServerException
from fail2ban.client.csocket import CSocket
class Socket(unittest.TestCase): class Socket(unittest.TestCase):

View File

@ -22,7 +22,7 @@
# $Revision$ # $Revision$
__author__ = "Cyril Jaquier, Yaroslav Halchenko" __author__ = "Cyril Jaquier, Yaroslav Halchenko"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2012 Yaroslav Halchenko" __copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2013 Yaroslav Halchenko"
__license__ = "GPL" __license__ = "GPL"
version = "0.8.8" version = "0.9.0a0"

104
files/nagios/README Normal file
View File

@ -0,0 +1,104 @@
Description
-----------
This plugin checks if the fail2ban server is running and how many IPs are currently banned.
You can use this plugin to monitor all the jails or just a specific jail.
How to use
----------
Just have to run the following command:
$ ./check_fail2ban --help
If you need to use this script with NRPE you just have to do the
following steps:
1 allow your user to run the script with the sudo rights. Just add
something like that in your /etc/sudoers (use visudo) :
nagios ALL=(ALL) NOPASSWD: /<path-to>/check_fail2ban
2 then just add this kind of line in your NRPE config file :
command[check_fail2ban]=/usr/bin/sudo /<path-to>/check_fail2ban
3 don't forget to restart your NRPE daemon
/!\ be careful to let no one able to update the check_fail2ban ;)
------------------------------------------------------------------------------
Notes (from f2ban.txt)
-----
It seems that Fail2ban is currently not working, please login and check
HELP:
1.) stop the Service
/etc/init.d/fail2ban stop
2.) delete the socket if available
rm /tmp/fail2ban.sock
3.) start the Service
/etc/init.d/fail2ban start
4.) check if fail2ban is working
fail2ban-client ping
Answer should be "pong"
5.) if the answer is not "pong" run away or CRY FOR HELP ;-)
Help
----
Usage: /<path-to>/check_fail2ban [-p] [-D "CHECK FAIL2BAN ACTIVITY"] [-v] [-c 2] [-w 1] [-s /<path-to>/socket] [-P /usr/bin/fail2ban-client]
Options:
-h, --help
Print detailed help screen
-V, --version
Print version information
-D, --display=STRING
To modify the output display
default is "CHECK FAIL2BAN ACTIVITY"
-P, --path-fail2ban_client=STRING
Specify the path to the tw_cli binary
default value is /usr/bin/fail2ban-client
-c, --critical=INT
Specify a critical threshold
default is 2
-w, --warning=INT
Specify a warning threshold
default is 1
-s, --socket=STRING
Specify a socket path
default is unset
-p, --perfdata
If you want to activate the perfdata output
-v, --verbose
Show details for command-line debugging (Nagios may truncate the output)
Example
-------
# for a specific jail
$ ./check_fail2ban --verbose -p -j ssh -w 1 -c 5 -P /usr/bin/fail2ban-client
DEBUG : fail2ban_client_path: /usr/bin/fail2ban-client
DEBUG : /usr/bin/fail2ban-client exists and is executable
DEBUG : final fail2ban command: /usr/bin/fail2ban-client
DEBUG : warning threshold : 1, critical threshold : 5
DEBUG : it seems the connection with the fail2ban server is ok
CHECK FAIL2BAN ACTIVITY - OK - 0 current banned IP(s) for the specific jail ssh | currentBannedIP=0
# for all the current jails
$ ./check_fail2ban --verbose -p -w 1 -c 5 -P /usr/bin/fail2ban-client
DEBUG : fail2ban_client_path: /usr/bin/fail2ban-client
DEBUG : /usr/bin/fail2ban-client exists and is executable
DEBUG : final fail2ban command: /usr/bin/fail2ban-client
DEBUG : warning threshold : 1, critical threshold : 5
DEBUG : it seems the connection with the fail2ban server is ok
DEBUG : jails list: apache, ssh-ddos, ssh
DEBUG : the jail apache has currently 0 banned IPs
DEBUG : the jail ssh-ddos has currently 0 banned IPs
DEBUG : the jail ssh has currently 0 banned IPs
CHECK FAIL2BAN ACTIVITY - OK - 3 detected jails with 0 current banned IP(s) | currentBannedIP=0

View File

@ -1,105 +1,346 @@
#!/bin/bash #!/usr/bin/perl
# -------------------------------------------------------
# -=- <check_fail2ban> -=-
# -------------------------------------------------------
# #
# Usage: ./check_fail2ban # Description : This plugin checks if the fail2ban server is running
############################################################################################### # and how many IPs are currently banned.
# Description:
# This plugin will check the status of Fail2ban.
#
# Created: 2008-10-25 (Sebastian Mueller)
#
# Changes: 2008-10-26 fixed some issues (Sebastian Mueller)
# Changes: 2009-01-25 add the second check, when server is not replying and the
# process is hang-up (Sebastian Mueller)
#
# please visit my website http://www.elchtest.eu or my personal WIKI http://wiki.elchtest.eu
#
################################################################################################
# if you have any questions, send a mail to linux@krabbe-offline.de
#
# this script is for my personal use. read the script before running/using it!!!
# #
# #
# YOU HAVE BEEN WARNED. THIS MAY DESTROY YOUR MACHINE. I ACCEPT NO RESPONSIBILITY. # inspired by the work of Sebastian Mueller - http://www.elchtest.eu
############################################################################################### #
#
# Version : 0.1
# -------------------------------------------------------
# In :
# - see the How to use section
#
# Out :
# - only print on the standard output
#
# Features :
# - perfdata output
# - works with only a specific jail
#
# Fix Me/Todo :
# - too many things ;) but let me know what do you think about it
#
# ####################################################################
# ####################################################################
# GPL v2
# This program 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.
#
# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# ####################################################################
# ####################################################################
# How to use :
# ------------
#
# Just have to run the following command:
# $ ./check_fail2ban --help
#
# If you need to use this script with NRPE you just have to do the
# following steps:
#
# 1 allow your user to run the script with the sudo rights. Just add
# something like that in your /etc/sudoers (use visudo) :
# nagios ALL=(ALL) NOPASSWD: /<path-to>/check_fail2ban
#
# 2 then just add this kind of line in your NRPE config file :
# command[check_fail2ban]=/usr/bin/sudo /<path-to>/check_fail2ban
#
# 3 don't forget to restart your NRPE daemon
#
#
# /!\ be careful to let no one able to update the check_fail2ban ;)
# ------------------------------------------------------------------------------
#
# ####################################################################
# ####################################################################
# Changelog :
# -----------
#
# --------------------------------------------------------------------
# Date:12/03/2013 Version:0.1 Author:Erwan Ben Souiden
# >> creation
# ####################################################################
# ####################################################################
# Don't touch anything under this line!
# You shall not pass - Gandalf is watching you
# ####################################################################
use strict;
use warnings;
use Getopt::Long qw(:config no_ignore_case);
# Generic variables
# -----------------
my $version = '0.1';
my $author = 'Erwan Labynocle Ben Souiden';
my $a_mail = 'erwan@aleikoum.net';
my $script_name = 'check_fail2ban';
my $verbose_value = 0;
my $version_value = 0;
my $more_value = 0;
my $help_value = 0;
my $perfdata_value = 0;
my %ERRORS=('OK'=>0,'WARNING'=>1,'CRITICAL'=>2,'UNKNOWN'=>3,'DEPENDENT'=>4);
# Plugin default variables
# ------------------------
my $display = 'CHECK FAIL2BAN ACTIVITY';
my ($critical,$warning) = (2,1);
my $fail2ban_client_path = '/usr/bin/fail2ban-client';
my $fail2ban_socket = '';
my $jail_specific = '';
GetOptions (
'P=s' => \ $fail2ban_client_path,
'path-fail2ban_client=s' => \ $fail2ban_client_path,
'j=s' => \ $jail_specific,
'jail=s' => \ $jail_specific,
'w=i' => \ $warning,
'warning=i' => \ $warning,
'socket=s' => \ $fail2ban_socket,
'S=s' => \ $fail2ban_socket,
'c=i' => \ $critical,
'critical=i' => \ $critical,
'V' => \ $version_value,
'version' => \ $version_value,
'h' => \ $help_value,
'H' => \ $help_value,
'help' => \ $help_value,
'display=s' => \ $display,
'D=s' => \ $display,
'perfdata' => \ $perfdata_value,
'p' => \ $perfdata_value,
'v' => \ $verbose_value,
'verbose' => \ $verbose_value
);
print_usage() if ($help_value);
print_version() if ($version_value);
SECOND_CHECK=0 # Syntax check of your specified options
STATE_OK=0 # --------------------------------------
STATE_CRITICAL=2
######################################################################
# Read the Status from fail2ban-client
######################################################################
check_processes_fail2ban()
{
F2B=`sudo -u root fail2ban-client ping | awk -F " " '{print $3}'`
exit_fail2ban=0
if [[ $F2B = "pong" ]]; then
exit_fail2ban=$STATE_OK
else
exit_fail2ban=$STATE_CRITICAL
fi
print "DEBUG : fail2ban_client_path: $fail2ban_client_path\n" if ($verbose_value);
if (($fail2ban_client_path eq "")) {
print $display.'- one or more following arguments are missing: fail2ban_client_path'."\n";
exit $ERRORS{"UNKNOWN"};
} }
######################################################################
# first check in the Background, PID will be killed when no response
# after 10 seconds, might be possible, otherwise the script will be
# present in your memory all the time
######################################################################
check_processes_fail2ban & if(! -x $fail2ban_client_path) {
pid=$! print $display.' - '.$fail2ban_client_path.' is not executable by you'."\n";
exit $ERRORS{"UNKNOWN"};
}
print "DEBUG : $fail2ban_client_path exists and is executable\n" if ($verbose_value);
typeset -i i=0 my $fail2ban_cmd = $fail2ban_client_path;
while ps $pid >/dev/null $fail2ban_cmd .= " -s $fail2ban_socket" if ($fail2ban_socket);
do
sleep 1
i=$i+1
if [ $i -ge 10 ]
then
kill $pid
SECOND_CHECK=1
exit_fail2ban=$STATE_CRITICAL
break
fi
done
###################################################################### print "DEBUG : final fail2ban command: $fail2ban_cmd\n" if ($verbose_value);
# when the Server response (does not mean the FAIL2BAN is working)
# in the first step, then it will run again and test the Service print "DEBUG : warning threshold : $warning, critical threshold : $critical\n" if ($verbose_value);
# and provide the real status if (($critical < 0) or ($warning < 0) or ($critical < $warning)) {
###################################################################### print $display.' - the thresholds must be integers and the critical threshold higher or equal than the warning threshold'."\n";
exit $ERRORS{"UNKNOWN"};
}
# Core script
# -----------
my ($how_many_jail,$how_many_banned,$return_print,$plugstate) = (0,0,"","OK");
if [ $SECOND_CHECK -eq 0 ]; then ### Test the connection to the fail2ban server
check_processes_fail2ban my @command_output = `$fail2ban_cmd ping`;
elif [ $SECOND_CHECK -eq 1 ]; then my $return_code = $?;
exit_fail2ban=$STATE_CRITICAL if ($return_code) {
fi print $display.'CRITICAL - non-zero exit code during testing fail2ban-client ping, check if the server is running and if you have the good permissions';
exit $ERRORS{"CRITICAL"};
}
else {
print "DEBUG : it seems the connection with the fail2ban server is ok\n" if ($verbose_value);
}
### Only if you specify one jail
if ($jail_specific) {
my $current_ban_number = currently_ban("$fail2ban_cmd","$jail_specific");
if ($current_ban_number == -1) {
print $display.' - CRITICAL - impossible to retrieve info about the jail '.$jail_specific;
exit $ERRORS{"CRITICAL"};
}
else {
$how_many_banned = int($current_ban_number);
$return_print = $how_many_banned.' current banned IP(s) for the specific jail '.$jail_specific;
}
}
### To analyze all the jail
else {
# Retrieve the jails list
my @jail_list = obtain_jail_list("$fail2ban_cmd");
if ($jail_list[0] eq "-1") {
print $display.' - CRITICAL - impossible to retrieve the jail list'."\n";
exit $ERRORS{"CRITICAL"};
}
###################################################################### foreach (@jail_list) {
# Main Menu $how_many_jail ++;
######################################################################
my $jail_name = $_;
$jail_name =~ tr/ //ds;
my $current_ban_number = currently_ban("$fail2ban_cmd","$jail_name");
if ($current_ban_number == -1) {
print "DEBUG : problem to parse the current banned IPs for jail $jail_name\n" if ($verbose_value);
}
else {
print "DEBUG : the jail $jail_name has currently $current_ban_number banned IPs\n" if ($verbose_value);
$how_many_banned += int($current_ban_number);
}
}
$return_print = $how_many_jail.' detected jails with '.$how_many_banned.' current banned IP(s)';
}
### Final
$plugstate = "CRITICAL" if ($how_many_banned >= $critical);
$plugstate = "WARNING" if (($how_many_banned >= $warning) && ($how_many_banned < $critical));
$return_print = $display." - ".$plugstate." - ".$return_print;
$return_print .= " | currentBannedIP=$how_many_banned" if ($perfdata_value);
print $return_print;
exit $ERRORS{"$plugstate"};
final_exit=$exit_fail2ban # ####################################################################
if [ $final_exit -eq 0 ]; then # function 1 : display the help
echo "SYSTEM OK - Fail2ban is working normally" # -----------------------------
exitstatus=$STATE_OK sub print_usage {
elif [ $final_exit -ne "0" ]; then print <<EOT;
echo "SYSTEM WARNING - Fail2Ban is not working" $script_name version $version by $author
######################################################################
# If don't have a Nagios Server for monitoring, remove the comment and
# add your Mail Address. You can check it with a Cron Job once an hour.
# put a txt file on your server and describe how to fix the issue, this
# could be attached to the mail.
######################################################################
# mutt -s "FAIL2BAN NOT WORKING" your@example.com < /home/f2ban.txt
exitstatus=$STATE_CRITICAL This plugin checks if the fail2ban server is running and how many IPs are currently banned.
fi You can use this plugin to monitor all the jails or just a specific jail.
exit $exitstatus
Usage: /<path-to>/$script_name [-p] [-D "$display"] [-v] [-c 2] [-w 1] [-s /<path-to>/socket] [-P /usr/bin/fail2ban-client]
Options:
-h, --help
Print detailed help screen
-V, --version
Print version information
-D, --display=STRING
To modify the output display
default is "CHECK FAIL2BAN ACTIVITY"
-P, --path-fail2ban_client=STRING
Specify the path to the tw_cli binary
default value is /usr/bin/fail2ban-client
-c, --critical=INT
Specify a critical threshold
default is 2
-w, --warning=INT
Specify a warning threshold
default is 1
-s, --socket=STRING
Specify a socket path
default is unset
-p, --perfdata
If you want to activate the perfdata output
-v, --verbose
Show details for command-line debugging (Nagios may truncate the output)
Send email to $a_mail if you have questions
regarding use of this software. To submit patches or suggest improvements,
send email to $a_mail
This plugin has been created by $author
Hope you will enjoy it ;)
Remember :
This program 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.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
EOT
exit $ERRORS{"UNKNOWN"};
}
# function 2 : display version information
# ----------------------------------------
sub print_version {
print <<EOT;
$script_name version $version
EOT
exit $ERRORS{"UNKNOWN"};
}
# function 3 : return the jail list
# ---------------------------------
sub obtain_jail_list {
my ($fail2ban_client_path) = @_;
my @command_output = `$fail2ban_client_path status`;
my $return_code = $?;
if ($return_code) {
return -1;
}
my @jail_list;
foreach (@command_output) {
if ($_=~/^.*Jail list:\t+(.*)/) {
print "DEBUG : jails list: $1\n" if ($verbose_value);
@jail_list = split(/,/, $1);
}
}
return @jail_list;
}
# function 4 : return how many IP are currently ban for a given jail
# ------------------------------------------------------------------
sub currently_ban {
my ($fail2ban_client_path,$jail_name) = @_;
my @command_output = `$fail2ban_client_path status $jail_name`;
my $return_code = $?;
if ($return_code) {
return -1;
}
foreach (@command_output) {
if ($_=~/^.*Currently banned:\t+(.*)/) {
my $current_count = $1;
$current_count =~ tr/ //ds;
return $current_count;
}
}
return -1;
}

View File

@ -1,18 +0,0 @@
It seems that Fail2ban is currently not working, please login and check
HELP:
1.) stop the Service
/etc/init.d/fail2ban stop
2.) delete the socket if available
rm /tmp/fail2ban.sock
3.) start the Service
/etc/init.d/fail2ban start
4.) check if fail2ban is working
fail2ban-client ping
Answer should be "pong"
5.) if the answer is not "pong" run away or CRY FOR HELP ;-)

View File

@ -1,6 +1,3 @@
[install]
install-purelib=/usr/share/fail2ban
[sdist] [sdist]
formats=bztar formats=bztar

View File

@ -32,11 +32,12 @@ except ImportError:
# python 2.x # python 2.x
from distutils.command.build_py import build_py from distutils.command.build_py import build_py
from distutils.command.build_scripts import build_scripts from distutils.command.build_scripts import build_scripts
from common.version import version
from os.path import isfile, join, isdir from os.path import isfile, join, isdir
import sys import sys
from glob import glob from glob import glob
from fail2ban.version import version
longdesc = ''' longdesc = '''
Fail2Ban scans log files like /var/log/pwdfail or Fail2Ban scans log files like /var/log/pwdfail or
/var/log/apache/error_log and bans IP that makes /var/log/apache/error_log and bans IP that makes
@ -47,7 +48,7 @@ commands.'''
setup( setup(
name = "fail2ban", name = "fail2ban",
version = version, version = version,
description = "Ban IPs that make too many password failure", description = "Ban IPs that make too many password failures",
long_description = longdesc, long_description = longdesc,
author = "Cyril Jaquier", author = "Cyril Jaquier",
author_email = "cyril.jaquier@fail2ban.org", author_email = "cyril.jaquier@fail2ban.org",
@ -56,15 +57,21 @@ setup(
platforms = "Posix", platforms = "Posix",
cmdclass = {'build_py': build_py, 'build_scripts': build_scripts}, cmdclass = {'build_py': build_py, 'build_scripts': build_scripts},
scripts = [ scripts = [
'fail2ban-client', 'bin/fail2ban-client',
'fail2ban-server', 'bin/fail2ban-server',
'fail2ban-regex' 'bin/fail2ban-regex',
'bin/fail2ban-testcases',
], ],
packages = [ packages = [
'common', 'fail2ban',
'client', 'fail2ban.client',
'server' 'fail2ban.server',
'fail2ban.tests',
], ],
package_data = {
'fail2ban.tests':
['files/*.log', 'files/filter.d/*.conf'],
},
data_files = [ data_files = [
('/etc/fail2ban', ('/etc/fail2ban',
glob("config/*.conf") glob("config/*.conf")