Merge branch 'master' into py3

Conflicts:
	.travis.yml
	server/datetemplate.py
	server/server.py
	testcases/filtertestcase.py
pull/128/merge^2
Steven Hiscocks 2013-03-30 22:51:36 +00:00
commit 77aa523f22
78 changed files with 1420 additions and 523 deletions

4
.coveragerc Normal file
View File

@ -0,0 +1,4 @@
[run]
branch = True
omit = /usr*

5
.gitignore vendored
View File

@ -1,3 +1,8 @@
*~
build
dist
*.pyc
htmlcov
.coverage
*.orig
*.rej

View File

@ -8,8 +8,11 @@ python:
- "3.2"
- "3.3"
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
before_script:
- if [[ $TRAVIS_PYTHON_VERSION == 3* ]]; then ./fail2ban-2to3; fi
script:
- python ./fail2ban-testcases
- 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
after_script:
- if [[ $TRAVIS_PYTHON_VERSION == 2.[6-7] ]] || [[ $TRAVIS_PYTHON_VERSION == 3.3 ]]; then coveralls; fi

7
.travis_coveragerc Normal file
View File

@ -0,0 +1,7 @@
[run]
branch = True
omit =
/usr/*
/home/travis/virtualenv/*
server/filtergamin.py

132
DEVELOP
View File

@ -24,14 +24,100 @@ Request feature. You can find more details on the Fail2Ban wiki
Testing
=======
Existing tests can be run by executing `fail2ban-testcases`.
Existing tests can be run by executing `fail2ban-testcases`. This has options
like --log-level that will probably be useful. `fail2ban-testcases --help` for
full options.
Test cases should cover all usual cases, all exception cases and all inside
/ outside boundary conditions.
Test cases should cover all branches. The coverage tool will help identify
missing branches. Also see http://nedbatchelder.com/code/coverage/branch.html
for more details.
Install the package python-coverage to visualise your test coverage. Run the
following (note: on Debian-based systems, the script is called
`python-coverage`):
coverage run fail2ban-testcases
coverage html
Then look at htmlcov/index.html and see how much coverage your test cases
exert over the codebase. Full coverage is a good thing however it may not be
complete. Try to ensure tests cover as many independent paths through the
code.
Manual Execution. To run in a development environment do:
./fail2ban-client -c config/ -s /tmp/f2b.sock -i start
some quick commands:
status
add test pyinotify
status test
set test addaction iptables
set test actionban iptables echo <ip> <cidr> >> /tmp/ban
set test actionunban iptables echo <ip> <cidr> >> /tmp/unban
get test actionban iptables
get test actionunban iptables
set test banip 192.168.2.2
status test
Documentation about creating tests (when tests are required and some guidelines
for creating good tests) will be added soon.
Coding Standards
================
Coming Soon.
Style
-----
Please use tabs for now. Keep to 80 columns, at least for readable text.
Tests
-----
Add tests. They should test all the code you add in a meaning way.
Coverage
--------
Test coverage should always increase as you add code.
You may use "# pragma: no cover" in the code for branches of code that support
older versions on python. For all other uses of "pragma: no cover" or
"pragma: no branch" document the reason why its not covered. "I haven't written
a test case" isn't a sufficient reason.
Documentation
-------------
Ensure this documentation is up to date after changes. Also ensure that the man
pages still are accurate. Ensure that there is sufficient documentation for
your new features to be used.
Bugs
----
Remove them and don't add any more.
Git
---
Use the following tags in your commit messages:
'BF:' for bug fixes
'DOC:' for documentation fixes
'ENH:' for enhancements
'TST:' for commits concerning tests only (thus not touching the main code-base)
Multiple tags could be joined with +, e.g. "BF+TST:".
Adding Actions
--------------
If you add an action.d/*.conf file also add a example in config/jail.conf
with enabled=false and maxretry=5 for ssh.
Design
@ -127,12 +213,14 @@ FileContainer
.__pos
Keeps the position pointer
dnsutils.py
~~~~~~~~~~~
DNSUtils
Utility class for DNS and IP handling
RF-Note: convert to functions within a separate submodule
filter*.py
~~~~~~~~~~
@ -156,3 +244,35 @@ action.py
~~~~~~~~~
Takes care about executing start/check/ban/unban/stop commands
Releasing
=========
# Ensure the version is correct in ./common/version.py
# Add/finalize the corresponding entry in the ChangeLog
# Update man pages
(cd man ; ./generate-man )
git commit -m 'update man pages for release' man/*
# Make sure the tests pass
./fail2ban-testcases-all
# Prepare/upload source and rpm binary distributions
python setup.py check
python setup.py sdist
python setup.py bdist_rpm
python setup.py upload
# Run the following and update the wiki with output:
python -c 'import common.protocol; common.protocol.printWiki()'
# Email users and development list of release
TODO notifying distributors etc.

View File

@ -3,6 +3,8 @@ ChangeLog
TODO
THANKS
COPYING
DEVELOP
doc/run-rootless.txt
fail2ban-client
fail2ban-server
fail2ban-testcases
@ -41,6 +43,7 @@ server/banmanager.py
server/datetemplate.py
server/mytime.py
server/failregex.py
testcases/files/testcase-usedns.log
testcases/banmanagertestcase.py
testcases/failmanagertestcase.py
testcases/clientreadertestcase.py
@ -49,6 +52,7 @@ testcases/__init__.py
testcases/datedetectortestcase.py
testcases/actiontestcase.py
testcases/servertestcase.py
testcases/sockettestcase.py
testcases/files/testcase01.log
testcases/files/testcase02.log
testcases/files/testcase03.log
@ -56,6 +60,7 @@ testcases/files/testcase04.log
setup.py
setup.cfg
common/__init__.py
common/exceptions.py
common/helpers.py
common/version.py
common/protocol.py
@ -87,6 +92,17 @@ config/filter.d/vsftpd.conf
config/filter.d/webmin-auth.conf
config/filter.d/wuftpd.conf
config/filter.d/xinetd-fail.conf
config/filter.d/asterisk.conf
config/filter.d/dovecot.conf
config/filter.d/dropbear.conf
config/filter.d/lighttpd-auth.conf
config/filter.d/recidive.conf
config/filter.d/roundcube-auth.conf
config/action.d/dummy.conf
config/action.d/iptables-ipset-proto4.conf
config/action.d/iptables-ipset-proto6.conf
config/action.d/iptables-xt_recent-echo.conf
config/action.d/route.conf
config/action.d/complain.conf
config/action.d/dshield.conf
config/action.d/hostsdeny.conf
@ -109,6 +125,8 @@ config/action.d/sendmail-whois-lines.conf
config/action.d/shorewall.conf
config/fail2ban.conf
man/fail2ban-client.1
man/fail2ban.1
man/jail.conf.5
man/fail2ban-client.h2m
man/fail2ban-server.1
man/fail2ban-server.h2m

14
README
View File

@ -51,10 +51,12 @@ call fail2ban-server directly.
Configuration:
--------------
You can configure Fail2ban using the files in /etc/fail2ban. It is possible to
configure the server using commands sent to it by fail2ban-client. The available
commands are described in the man page of fail2ban-client. Please refer to it or
to the website: http://www.fail2ban.org
You can configure Fail2Ban using the files in /etc/fail2ban. It is
possible to configure the server using commands sent to it by
fail2ban-client. The available commands are described in the
fail2ban-client(1) manpage. Also see fail2ban(1) manpage for further
references and find even more documentation on the website:
http://www.fail2ban.org
Contact:
--------
@ -91,5 +93,5 @@ 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., 59 Temple Place,
Suite 330, Boston, MA 02111-1307 USA
Fail2Ban; if not, write to the Free Software Foundation, Inc., 51 Franklin
Street, Fifth Floor, Boston, MA 02110, USA

2
THANKS
View File

@ -13,6 +13,7 @@ Christian Rauch
Christoph Haas
Christos Psonis
Daniel B. Cid
Daniel Black
David Nutter
Eric Gerbier
Guillaume Delvit
@ -38,6 +39,7 @@ Robert Edeker
Russell Odom
Sireyessire
Stephen Gildea
Steven Hiscocks
Tom Pike
Tyler
Vaclav Misek

7
TODO
View File

@ -13,9 +13,12 @@ Legend:
# partially done
* done
- Removed relative imports
- Run tests though all filters/examples files - (see sshd example file) as unit
test
- Cleanup fail2ban-client and fail2ban-server. Move code to server/ and client/
* Removed relative imports
* Cleanup fail2ban-client and fail2ban-server. Move code to server/ and client/
- Add timeout to external commands (signal alarm, watchdog thread, etc)

View File

@ -35,8 +35,8 @@ logSys = logging.getLogger("fail2ban.client.config")
class ActionReader(ConfigReader):
def __init__(self, action, name):
ConfigReader.__init__(self)
def __init__(self, action, name, **kwargs):
ConfigReader.__init__(self, **kwargs)
self.__file = action[0]
self.__cInfo = action[1]
self.__name = name

View File

@ -27,7 +27,7 @@ __date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import logging, os
import glob, logging, os
from configparserinc import SafeConfigParserWithIncludes
from ConfigParser import NoOptionError, NoSectionError
@ -35,36 +35,64 @@ from ConfigParser import NoOptionError, NoSectionError
logSys = logging.getLogger("fail2ban.client.config")
class ConfigReader(SafeConfigParserWithIncludes):
DEFAULT_BASEDIR = '/etc/fail2ban'
BASE_DIRECTORY = "/etc/fail2ban/"
def __init__(self):
def __init__(self, basedir=None):
SafeConfigParserWithIncludes.__init__(self)
self.setBaseDir(basedir)
self.__opts = None
#@staticmethod
def setBaseDir(folderName):
path = folderName.rstrip('/')
ConfigReader.BASE_DIRECTORY = path + '/'
setBaseDir = staticmethod(setBaseDir)
#@staticmethod
def getBaseDir():
return ConfigReader.BASE_DIRECTORY
getBaseDir = staticmethod(getBaseDir)
def setBaseDir(self, basedir):
if basedir is None:
basedir = ConfigReader.DEFAULT_BASEDIR # stock system location
self._basedir = basedir.rstrip('/')
def getBaseDir(self):
return self._basedir
def read(self, filename):
basename = ConfigReader.BASE_DIRECTORY + filename
logSys.debug("Reading " + basename)
bConf = basename + ".conf"
bLocal = basename + ".local"
if os.path.exists(bConf) or os.path.exists(bLocal):
SafeConfigParserWithIncludes.read(self, [bConf, bLocal])
if not (os.path.exists(self._basedir) and os.access(self._basedir, os.R_OK | os.X_OK)):
raise ValueError("Base configuration directory %s either does not exist "
"or is not accessible" % self._basedir)
basename = os.path.join(self._basedir, filename)
logSys.debug("Reading configs for %s under %s " % (basename, self._basedir))
config_files = [ basename + ".conf",
basename + ".local" ]
# choose only existing ones
config_files = filter(os.path.exists, config_files)
# possible further customizations under a .conf.d directory
config_dir = basename + '.d'
if os.path.exists(config_dir):
if os.path.isdir(config_dir) and os.access(config_dir, os.X_OK | os.R_OK):
# files must carry .conf suffix as well
config_files += sorted(glob.glob('%s/*.conf' % config_dir))
else:
logSys.warn("%s exists but not a directory or not accessible"
% config_dir)
# check if files are accessible, warn if any is not accessible
# and remove it from the list
config_files_accessible = []
for f in config_files:
if os.access(f, os.R_OK):
config_files_accessible.append(f)
else:
logSys.warn("%s exists but not accessible - skipping" % f)
if len(config_files_accessible):
# at least one config exists and accessible
SafeConfigParserWithIncludes.read(self, config_files_accessible)
return True
else:
logSys.error(bConf + " and " + bLocal + " do not exist")
logSys.error("Found no accessible config files for %r " % filename
+ (["under %s" % self.getBaseDir(),
"among existing ones: " + ', '.join(config_files)][bool(len(config_files))]))
return False
##
# Read the options.
#
@ -94,8 +122,8 @@ class ConfigReader(SafeConfigParserWithIncludes):
values[option[1]] = option[2]
except NoOptionError:
if not option[2] == None:
logSys.warn("'%s' not defined in '%s'. Using default value"
% (option[1], sec))
logSys.warn("'%s' not defined in '%s'. Using default one: %r"
% (option[1], sec, option[2]))
values[option[1]] = option[2]
except ValueError:
logSys.warn("Wrong value for '" + option[1] + "' in '" + sec +

View File

@ -43,15 +43,19 @@ class Configurator:
self.__fail2ban = Fail2banReader()
self.__jails = JailsReader()
#@staticmethod
def setBaseDir(folderName):
ConfigReader.setBaseDir(folderName)
setBaseDir = staticmethod(setBaseDir)
def setBaseDir(self, folderName):
self.__fail2ban.setBaseDir(folderName)
self.__jails.setBaseDir(folderName)
#@staticmethod
def getBaseDir():
return ConfigReader.getBaseDir()
getBaseDir = staticmethod(getBaseDir)
def getBaseDir(self):
fail2ban_basedir = self.__fail2ban.getBaseDir()
jails_basedir = self.__jails.getBaseDir()
if fail2ban_basedir != jails_basedir:
logSys.error("fail2ban.conf and jails.conf readers have differing "
"basedirs: %r and %r. "
"Returning the one for fail2ban.conf"
% (fail2ban_basedir, jails_basedir))
return fail2ban_basedir
def readEarly(self):
self.__fail2ban.read()

View File

@ -35,8 +35,8 @@ logSys = logging.getLogger("fail2ban.client.config")
class Fail2banReader(ConfigReader):
def __init__(self):
ConfigReader.__init__(self)
def __init__(self, **kwargs):
ConfigReader.__init__(self, **kwargs)
def read(self):
ConfigReader.read(self, "fail2ban")

View File

@ -35,8 +35,8 @@ logSys = logging.getLogger("fail2ban.client.config")
class FilterReader(ConfigReader):
def __init__(self, fileName, name):
ConfigReader.__init__(self)
def __init__(self, fileName, name, **kwargs):
ConfigReader.__init__(self, **kwargs)
self.__file = fileName
self.__name = name

View File

@ -40,10 +40,11 @@ class JailReader(ConfigReader):
actionCRE = re.compile("^((?:\w|-|_|\.)+)(?:\[(.*)\])?$")
def __init__(self, name):
ConfigReader.__init__(self)
def __init__(self, name, force_enable=False, **kwargs):
ConfigReader.__init__(self, **kwargs)
self.__name = name
self.__filter = None
self.__force_enable = force_enable
self.__actions = list()
def setName(self, value):
@ -53,10 +54,10 @@ class JailReader(ConfigReader):
return self.__name
def read(self):
ConfigReader.read(self, "jail")
return ConfigReader.read(self, "jail")
def isEnabled(self):
return self.__opts["enabled"]
return self.__force_enable or self.__opts["enabled"]
def getOptions(self):
opts = [["bool", "enabled", "false"],
@ -76,7 +77,8 @@ class JailReader(ConfigReader):
if self.isEnabled():
# Read filter
self.__filter = FilterReader(self.__opts["filter"], self.__name)
self.__filter = FilterReader(self.__opts["filter"], self.__name,
basedir=self.getBaseDir())
ret = self.__filter.read()
if ret:
self.__filter.getOptions(self.__opts)
@ -87,8 +89,10 @@ class JailReader(ConfigReader):
# Read action
for act in self.__opts["action"].split('\n'):
try:
if not act: # skip empty actions
continue
splitAct = JailReader.splitAction(act)
action = ActionReader(splitAct, self.__name)
action = ActionReader(splitAct, self.__name, basedir=self.getBaseDir())
ret = action.read()
if ret:
action.getOptions(self.__opts)
@ -97,8 +101,10 @@ class JailReader(ConfigReader):
raise AttributeError("Unable to read action")
except Exception, e:
logSys.error("Error in action definition " + act)
logSys.debug(e)
logSys.debug("Caught exception: %s" % (e,))
return False
if not len(self.__actions):
logSys.warn("No actions were defined for %s" % self.__name)
return True
def convert(self):
@ -145,12 +151,20 @@ class JailReader(ConfigReader):
def splitAction(action):
m = JailReader.actionCRE.match(action)
d = dict()
if not m.group(2) == None:
mgroups = m.groups()
if len(mgroups) == 2:
action_name, action_opts = mgroups
elif len(mgroups) == 1:
action_name, action_opts = mgroups[0], None
else:
raise ValueError("While reading action %s we should have got up to "
"2 groups. Got: %r" % (action, mgroups))
if not action_opts is None:
# Huge bad hack :( This method really sucks. TODO Reimplement it.
actions = ""
escapeChar = None
allowComma = False
for c in m.group(2):
for c in action_opts:
if c in ('"', "'") and not allowComma:
# Start
escapeChar = c
@ -175,6 +189,6 @@ class JailReader(ConfigReader):
try:
d[p[0].strip()] = p[1].strip()
except IndexError:
logSys.error("Invalid argument %s in '%s'" % (p, m.group(2)))
return [m.group(1), d]
logSys.error("Invalid argument %s in '%s'" % (p, action_opts))
return [action_name, d]
splitAction = staticmethod(splitAction)

View File

@ -36,12 +36,20 @@ logSys = logging.getLogger("fail2ban.client.config")
class JailsReader(ConfigReader):
def __init__(self):
ConfigReader.__init__(self)
def __init__(self, force_enable=False, **kwargs):
"""
Parameters
----------
force_enable : bool, optional
Passed to JailReader to force enable the jails.
It is for internal use
"""
ConfigReader.__init__(self, **kwargs)
self.__jails = list()
self.__force_enable = force_enable
def read(self):
ConfigReader.read(self, "jail")
return ConfigReader.read(self, "jail")
def getOptions(self, section = None):
opts = []
@ -49,7 +57,7 @@ class JailsReader(ConfigReader):
if section:
# Get the options of a specific jail.
jail = JailReader(section)
jail = JailReader(section, basedir=self.getBaseDir(), force_enable=self.__force_enable)
jail.read()
ret = jail.getOptions()
if ret:
@ -62,7 +70,7 @@ class JailsReader(ConfigReader):
else:
# Get the options of all jails.
for sec in self.sections():
jail = JailReader(sec)
jail = JailReader(sec, basedir=self.getBaseDir(), force_enable=self.__force_enable)
jail.read()
ret = jail.getOptions()
if ret:

View File

@ -40,6 +40,7 @@ protocol = [
["stop", "stops all jails and terminate the server"],
["status", "gets the current status of the server"],
["ping", "tests if the server is alive"],
["help", "return this output"],
['', "LOGGING", ""],
["set loglevel <LEVEL>", "sets logging level to <LEVEL>. 0 is minimal, 4 is debug"],
["get loglevel", "gets the logging level"],

View File

@ -52,10 +52,7 @@ actioncheck =
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <failtime> unix timestamp of the last failure
# <bantime> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = ADDRESSES=`whois <ip> | perl -e 'while (<STDIN>) { next if /^changed|@(ripe|apnic)\.net/io; $m += (/abuse|trouble:|report|spam|security/io?3:0); if (/([a-z0-9_\-\.+]+@[a-z0-9\-]+(\.[[a-z0-9\-]+)+)/io) { while (s/([a-z0-9_\-\.+]+@[a-z0-9\-]+(\.[[a-z0-9\-]+)+)//io) { if ($m) { $a{lc($1)}=$m } else { $b{lc($1)}=$m } } $m=0 } else { $m && --$m } } if (%%a) {print join(",",keys(%%a))} else {print join(",",keys(%%b))}'`
@ -67,9 +64,7 @@ actionban = ADDRESSES=`whois <ip> | perl -e 'while (<STDIN>) { next if /^changed
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <bantime> unix timestamp of the ban time
# <unbantime> unix timestamp of the unban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionunban =

View File

@ -54,9 +54,7 @@ actioncheck =
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
# See http://www.dshield.org/specs.html for more on report format/notes
@ -91,9 +89,7 @@ actionban = TZONE=`date +%%z | sed 's/\([+-]..\)\(..\)/\1:\2/'`
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionunban = if [ -f <tmpfile>.first ]; then
@ -116,7 +112,7 @@ actionunban = if [ -f <tmpfile>.first ]; then
port = ???
# Option: userid
# Notes.: Your DSheild user ID. Should be provided either in the jail config or
# Notes.: Your DShield user ID. Should be provided either in the jail config or
# in a .local file.
# Register at https://secure.dshield.org/register.html
# Values: [ NUM ] Default: 0
@ -124,13 +120,13 @@ port = ???
userid = 0
# Option: myip
# Notes.: TThe target IP for the attack (your public IP). Should be provided
# Notes.: The target IP for the attack (your public IP). Should be provided
# either in the jail config or in a .local file unless your PUBLIC IP
# is the first IP assigned to eth0
# Values: [ an IP address ] Default: Tries to find the IP address of eth0,
# which in most cases will be a private IP, and therefore incorrect
#
myip = `ip -4 addr show dev eth0 | grep inet | head -1 | sed -r 's/.*inet ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}).*/\1/'`
myip = `ip -4 addr show dev eth0 | grep inet | head -n 1 | sed -r 's/.*inet ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}).*/\1/'`
# Option: protocol
# Notes.: The protocol over which the attack is happening
@ -159,7 +155,6 @@ minreportinterval = 3600
# submit the batch, even if we haven't reached <lines> yet. Note that
# this is only checked on each ban/unban, and that we always send
# anything in the buffer on shutdown. Must be greater than
# <minreportinterval>.
# Values: [ NUM ] Default: 21600 (6 hours)
#
maxbufferage = 21600

View File

@ -29,9 +29,7 @@ actioncheck =
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = printf %%b "+<ip>\n" >> /tmp/fail2ban.dummy
@ -39,9 +37,7 @@ actionban = printf %%b "+<ip>\n" >> /tmp/fail2ban.dummy
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionunban = printf %%b "-<ip>\n" >> /tmp/fail2ban.dummy

View File

@ -28,9 +28,7 @@ actioncheck =
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = IP=<ip> &&
@ -39,9 +37,7 @@ actionban = IP=<ip> &&
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionunban = IP=<ip> && sed -i.old /ALL:\ $IP/d <file>

View File

@ -34,9 +34,7 @@ actioncheck =
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = echo block in quick from <ip>/32 | /sbin/ipf -f -
@ -45,9 +43,7 @@ actionban = echo block in quick from <ip>/32 | /sbin/ipf -f -
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
# note -r option used to remove matching rule

View File

@ -32,9 +32,7 @@ actioncheck =
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = ipfw add deny tcp from <ip> to <localhost> <port>
@ -43,9 +41,7 @@ actionban = ipfw add deny tcp from <ip> to <localhost> <port>
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionunban = ipfw delete `ipfw list | grep -i <ip> | awk '{print $1;}'`

View File

@ -34,9 +34,7 @@ actioncheck = iptables -n -L <chain> | grep -q 'fail2ban-<name>[ \t]'
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j DROP
@ -44,16 +42,14 @@ actionban = iptables -I fail2ban-<name> 1 -s <ip> -j DROP
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionunban = iptables -D fail2ban-<name> -s <ip> -j DROP
[Init]
# Defaut name of the chain
# Default name of the chain
#
name = default

View File

@ -38,7 +38,7 @@ actionstop = iptables -D INPUT -p <protocol> -m multiport --dports <port> -m set
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = ipset --test fail2ban-<name> <ip> || ipset --add fail2ban-<name> <ip>
@ -46,14 +46,14 @@ actionban = ipset --test fail2ban-<name> <ip> || ipset --add fail2ban-<name> <i
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionunban = ipset --test fail2ban-<name> <ip> && ipset --del fail2ban-<name> <ip>
[Init]
# Defaut name of the ipset
# Default name of the ipset
#
name = default

View File

@ -38,7 +38,7 @@ actionstop = iptables -D INPUT -p <protocol> -m multiport --dports <port> -m set
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = ipset add fail2ban-<name> <ip> timeout <bantime> -exist
@ -46,14 +46,14 @@ actionban = ipset add fail2ban-<name> <ip> timeout <bantime> -exist
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionunban = ipset del fail2ban-<name> <ip> -exist
[Init]
# Defaut name of the ipset
# Default name of the ipset
#
name = default

View File

@ -42,9 +42,7 @@ actioncheck = iptables -n -L fail2ban-<name>-log >/dev/null
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j fail2ban-<name>-log
@ -52,16 +50,14 @@ actionban = iptables -I fail2ban-<name> 1 -s <ip> -j fail2ban-<name>-log
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionunban = iptables -D fail2ban-<name> -s <ip> -j fail2ban-<name>-log
[Init]
# Defaut name of the chain
# Default name of the chain
#
name = default

View File

@ -32,9 +32,7 @@ actioncheck = iptables -n -L <chain> | grep -q 'fail2ban-<name>[ \t]'
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j DROP
@ -42,16 +40,14 @@ actionban = iptables -I fail2ban-<name> 1 -s <ip> -j DROP
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionunban = iptables -D fail2ban-<name> -s <ip> -j DROP
[Init]
# Defaut name of the chain
# Default name of the chain
#
name = default

View File

@ -34,9 +34,7 @@ actioncheck = iptables -n -L <chain> | grep -q 'fail2ban-<name>[ \t]'
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j DROP
@ -44,16 +42,14 @@ actionban = iptables -I fail2ban-<name> 1 -s <ip> -j DROP
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionunban = iptables -D fail2ban-<name> -s <ip> -j DROP
[Init]
# Defaut name of the chain
# Default name of the chain
#
name = default

View File

@ -11,7 +11,7 @@
# Notes.: command executed once at the start of Fail2Ban.
# Values: CMD
#
# Changing iptables rules requires root priviledges. If fail2ban is
# Changing iptables rules requires root privileges. If fail2ban is
# configured to run as root, firewall setup can be performed by
# fail2ban automatically. However, if fail2ban is configured to run as
# a normal user, the configuration must be done by some other means
@ -46,9 +46,7 @@ actioncheck = test -e /proc/net/xt_recent/fail2ban-<name>
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = echo +<ip> > /proc/net/xt_recent/fail2ban-<name>
@ -56,16 +54,14 @@ actionban = echo +<ip> > /proc/net/xt_recent/fail2ban-<name>
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionunban = echo -<ip> > /proc/net/xt_recent/fail2ban-<name>
[Init]
# Defaut name of the chain
# Default name of the chain
#
name = default

View File

@ -32,9 +32,7 @@ actioncheck = iptables -n -L <chain> | grep -q 'fail2ban-<name>[ \t]'
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j DROP
@ -42,16 +40,14 @@ actionban = iptables -I fail2ban-<name> 1 -s <ip> -j DROP
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionunban = iptables -D fail2ban-<name> -s <ip> -j DROP
[Init]
# Defaut name of the chain
# Default name of the chain
#
name = default

View File

@ -43,9 +43,7 @@ actioncheck =
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = printf %%b "`date`: <ip> (<failures> failures)\n" >> <tmpfile>
@ -62,9 +60,7 @@ actionban = printf %%b "`date`: <ip> (<failures> failures)\n" >> <tmpfile>
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionunban =

View File

@ -34,10 +34,7 @@ actioncheck =
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <failtime> unix timestamp of the last failure
# <bantime> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = printf %%b "Hi,\n
@ -53,16 +50,14 @@ actionban = printf %%b "Hi,\n
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <bantime> unix timestamp of the ban time
# <unbantime> unix timestamp of the unban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionunban =
[Init]
# Defaut name of the chain
# Default name of the chain
#
name = default

View File

@ -34,9 +34,7 @@ actioncheck =
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = printf %%b "Hi,\n
@ -50,16 +48,14 @@ actionban = printf %%b "Hi,\n
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionunban =
[Init]
# Defaut name of the chain
# Default name of the chain
#
name = default

View File

@ -34,9 +34,7 @@ actioncheck =
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = printf %%b "Hi,\n
@ -48,16 +46,14 @@ actionban = printf %%b "Hi,\n
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionunban =
[Init]
# Defaut name of the chain
# Default name of the chain
#
name = default

View File

@ -49,9 +49,7 @@ actioncheck =
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
#
@ -71,9 +69,7 @@ actionban = MNWLOGIN=`perl -e '$s=shift;$s=~s/([\W])/"%%".uc(sprintf("%%2.2x",or
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionunban =
@ -102,13 +98,13 @@ mnwlogin =
mnwpass =
# Option: myip
# Notes.: TThe target IP for the attack (your public IP). Should be overridden
# Notes.: The target IP for the attack (your public IP). Should be overridden
# either in the jail config or in a .local file unless your PUBLIC IP
# is the first IP assigned to eth0
# Values: [ an IP address ] Default: Tries to find the IP address of eth0,
# which in most cases will be a private IP, and therefore incorrect
#
myip = `ip -4 addr show dev eth0 | grep inet | head -1 | sed -r 's/.*inet ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}).*/\1/'`
myip = `ip -4 addr show dev eth0 | grep inet | head -n 1 | sed -r 's/.*inet ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}).*/\1/'`
# Option: protocol
# Notes.: The protocol over which the attack is happening

View File

@ -52,9 +52,7 @@ actioncheck =
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = printf %%b "`date`: <ip> (<failures> failures)\n" >> <tmpfile>
@ -74,16 +72,14 @@ actionban = printf %%b "`date`: <ip> (<failures> failures)\n" >> <tmpfile>
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionunban =
[Init]
# Defaut name of the chain
# Default name of the chain
#
name = default

View File

@ -42,9 +42,7 @@ actioncheck =
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip>
@ -64,16 +62,14 @@ actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip>
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionunban =
[Init]
# Defaut name of the chain
# Default name of the chain
#
name = default

View File

@ -42,9 +42,7 @@ actioncheck =
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip>
@ -62,16 +60,14 @@ actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip>
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionunban =
[Init]
# Defaut name of the chain
# Default name of the chain
#
name = default

View File

@ -42,9 +42,7 @@ actioncheck =
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip>
@ -60,16 +58,14 @@ actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip>
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionunban =
[Init]
# Defaut name of the chain
# Default name of the chain
#
name = default

View File

@ -36,9 +36,7 @@ actioncheck =
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = shorewall drop <ip>
@ -46,9 +44,7 @@ actionban = shorewall drop <ip>
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionunban = shorewall allow <ip>

View File

@ -16,7 +16,7 @@ badbots = atSpider/1\.0|autoemailspider|China Local Browse 2\.6|ContentSmartz|Da
# Option: failregex
# Notes.: Regexp to catch known spambots and software alike. Please verify
# that it is your intent to block IPs which were driven by
# abovementioned bots.
# above mentioned bots.
# Values: TEXT
#
failregex = ^<HOST> -.*"(GET|POST).*HTTP.*"(?:%(badbots)s|%(badbotscustom)s)"$

View File

@ -15,6 +15,7 @@
# Values: TEXT
#
failregex = reject: RCPT from (.*)\[<HOST>\]: 554
reject: RCPT from (.*)\[<HOST>\]: 450 4\.7\.1 : Helo command rejected: Host not found; from=<> to=<> proto=ESMTP helo= *$
# Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored.

View File

@ -0,0 +1,20 @@
# /etc/fail2ban/filter.d/sogo-auth.conf
#
# Fail2Ban configuration file
# By Arnd Brandes
# SOGo
#
[Definition]
# Option: failregex
# Filter Ban in /var/log/sogo/sogo.log
# Note: the error log may contain multiple hosts, whereas the first one
# is the client and all others are poxys. We match the first one, only
failregex = Login from '<HOST>' for user '.*' might not have worked( - password policy: \d* grace: -?\d* expire: -?\d* bound: -?\d*)?\s*$
# Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored.
# Values: TEXT
#
ignoreregex =

View File

@ -219,6 +219,20 @@ filter = roundcube-auth
action = iptables[name=RoundCube, port="http,https"]
logpath = /var/log/roundcube/userlogins
# Monitor SOGo groupware server
[sogo-iptables]
enabled = false
filter = sogo-auth
port = http, https
# without proxy this would be:
# port = 20000
action = iptables[name=SOGo, port="http,https"]
logpath = /var/log/sogo/sogo.log
# Ban attackers that try to use PHP's URL-fopen() functionality
# through GET/POST variables. - Experimental, with more than a year
# of usage in production environments.
@ -248,7 +262,7 @@ logpath = /var/log/lighttpd/error.log
maxretry = 2
# Same as above for mod_auth
# It catches wrong authentifications
# It catches wrong authentications
[lighttpd-auth]

View File

@ -1,7 +1,7 @@
Fail2ban normally requires root priviledges to insert iptables rules
Fail2ban normally requires root privileges to insert iptables rules
through calls to /sbin/iptables and also to read the logfiles.
Fail2ban can run as an unpriviledged user provided that those two
capabilites are preserved. The idea is to run fail2ban as a normal
Fail2ban can run as an unprivileged user provided that those two
capabilities are preserved. The idea is to run fail2ban as a normal
user (e.g. fail2ban) who belongs to a group which is allowed to read
logfiles. The user should also be allowed to write to
/proc/net/xt_recent/fail2ban-<name> (name is specified in the iptables
@ -20,14 +20,14 @@ Another way to use xt_recent is by inserting the rules by writing to
action. Files in /proc/net/xt_recent/ are protected by normal
filesystem rules, so can be chown'ed and chmod'ed to be writable by a
certain user. After the necessary iptables rules are inserted (which
requires root priviledges), blacklisting can be perfomed by an
unpriviledged user.
requires root privileges), blacklisting can be performed by an
unprivileged user.
Using fail2ban with xt_recent allows smarter filtering than normal
iptables rules with the xt_recent module can provide.
The disadvantage is that fail2ban cannot perform the setup by itself,
which would require the priviledge to call /sbin/iptables, and it must
which would require the privilege to call /sbin/iptables, and it must
be done through other means.
The primary advantage is obvious: it's generally better to run
@ -46,7 +46,7 @@ some user and thus allow delisting IPs by helper administrators
without the ability to mess up other iptables rules.
The xt_recent-echo jail can be used under the root user without
further configuration. To run not as root, futher setup is necessary:
further configuration. To run not as root, further setup is necessary:
- Create user:

View File

@ -63,7 +63,7 @@ class Fail2banClient:
self.__conf["interactive"] = False
self.__conf["socket"] = None
self.__conf["pidfile"] = None
def dispVersion(self):
print "Fail2Ban v" + version
print
@ -73,7 +73,7 @@ class Fail2banClient:
print
print "Written by Cyril Jaquier <cyril.jaquier@fail2ban.org>."
print "Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>."
def dispUsage(self):
""" Prints Fail2Ban command line options and exits
"""
@ -95,17 +95,17 @@ class Fail2banClient:
print " -V, --version print the version"
print
print "Command:"
# Prints the protocol
printFormatted()
print
print "Report bugs to https://github.com/fail2ban/fail2ban/issues"
def dispInteractive(self):
print "Fail2Ban v" + version + " reads log file that contains password failure report"
print "and bans the corresponding IP addresses using firewall rules."
print
print
def __sigTERMhandler(self, signum, frame):
# Print a new line because we probably come from wait
@ -139,10 +139,10 @@ class Fail2banClient:
elif opt[0] in ["-V", "--version"]:
self.dispVersion()
sys.exit(0)
def __ping(self):
return self.__processCmd([["ping"]], False)
def __processCmd(self, cmd, showRet = True):
beautifier = Beautifier()
for c in cmd:
@ -167,7 +167,7 @@ class Fail2banClient:
logSys.error(e)
return False
return True
##
# Process a command line.
#
@ -241,13 +241,13 @@ class Fail2banClient:
return False
else:
return self.__processCmd([cmd])
##
# Start Fail2Ban server.
#
# Start the Fail2ban server in daemon mode.
def __startServerAsync(self, socket, pidfile, force = False):
# Forks the current process.
pid = os.fork()
@ -278,7 +278,7 @@ class Fail2banClient:
except OSError:
logSys.error("Could not start %s" % self.SERVER)
os.exit(-1)
def __waitOnServer(self):
# Wait for the server to start
cnt = 0
@ -297,7 +297,7 @@ class Fail2banClient:
delta = -1
elif pos < 2:
delta = 1
# The server has 30 secondes to start.
# The server has 30 seconds to start.
if cnt >= 300:
if self.__conf["verbose"] > 1:
sys.stdout.write('\n')
@ -306,16 +306,16 @@ class Fail2banClient:
cnt += 1
if self.__conf["verbose"] > 1:
sys.stdout.write('\n')
def start(self, argv):
# Command line options
self.__argv = argv
# Install signal handlers
signal.signal(signal.SIGTERM, self.__sigTERMhandler)
signal.signal(signal.SIGINT, self.__sigTERMhandler)
# Reads the command line options.
try:
cmdOpts = 'hc:s:p:xdviqV'
@ -324,9 +324,9 @@ class Fail2banClient:
except getopt.GetoptError:
self.dispUsage()
return False
self.__getCmdLineOptions(optList)
verbose = self.__conf["verbose"]
if verbose <= 0:
logSys.setLevel(logging.ERROR)
@ -346,7 +346,7 @@ class Fail2banClient:
# Set the configuration path
self.__configurator.setBaseDir(self.__conf["conf"])
# Set socket path
self.__configurator.readEarly()
conf = self.__configurator.getEarlyOptions()
@ -360,7 +360,7 @@ class Fail2banClient:
ret = self.__readConfig()
self.dumpConfig(self.__stream)
return ret
# Interactive mode
if self.__conf["interactive"]:
try:
@ -380,7 +380,9 @@ class Fail2banClient:
if cmd == "exit" or cmd == "quit":
# Exit
return True
if not cmd == "":
if cmd == "help":
self.dispUsage()
elif not cmd == "":
self.__processCommand(shlex.split(cmd))
except (EOFError, KeyboardInterrupt):
print
@ -399,14 +401,14 @@ class Fail2banClient:
self.__configurator.convertToProtocol()
self.__stream = self.__configurator.getConfigStream()
return ret
def __readJailConfig(self, jail):
self.__configurator.readAll()
ret = self.__configurator.getOptions(jail)
self.__configurator.convertToProtocol()
self.__stream = self.__configurator.getConfigStream()
return ret
#@staticmethod
def dumpConfig(cmd):
for c in cmd:
@ -418,7 +420,7 @@ class Fail2banClient:
class ServerExecutionException(Exception):
pass
if __name__ == "__main__":
if __name__ == "__main__": # pragma: no cover - can't test main
client = Fail2banClient()
# Exit with correct return value
if client.start(sys.argv):

View File

@ -50,24 +50,24 @@ class RegexStat:
def __str__(self):
return "%s(%r) %d failed: %s" \
% (self.__class__, self.__failregex, self.__stats, self.__ipList)
def inc(self):
self.__stats += 1
def getStats(self):
return self.__stats
def getFailRegex(self):
return self.__failregex
def appendIP(self, value):
self.__ipList.extend(value)
def getIPList(self):
return self.__ipList
class Fail2banRegex:
test = None
CONFIG_DEFAULTS = {'configpath' : "/etc/fail2ban/"}
@ -88,7 +88,7 @@ class Fail2banRegex:
self.__logging_level = self.__verbose and logging.DEBUG or logging.WARN
logging.getLogger("fail2ban").addHandler(self.__hdlr)
logging.getLogger("fail2ban").setLevel(logging.ERROR)
#@staticmethod
def dispVersion():
print "Fail2Ban v" + version
@ -100,7 +100,7 @@ class Fail2banRegex:
print "Written by Cyril Jaquier <cyril.jaquier@fail2ban.org>."
print "Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>."
dispVersion = staticmethod(dispVersion)
#@staticmethod
def dispUsage():
print "Usage: "+sys.argv[0]+" [OPTIONS] <LOG> <REGEX> [IGNOREREGEX]"
@ -131,7 +131,7 @@ class Fail2banRegex:
print
print "Report bugs to https://github.com/fail2ban/fail2ban/issues"
dispUsage = staticmethod(dispUsage)
def getCmdLineOptions(self, optList):
""" Gets the command line options
"""
@ -209,7 +209,7 @@ class Fail2banRegex:
print "Use regex line : " + stripReg
self.__failregex = [RegexStat(value)]
return True
def testIgnoreRegex(self, line):
found = False
for regex in self.__ignoreregex:
@ -226,7 +226,7 @@ class Fail2banRegex:
finally:
self.__filter.delIgnoreRegex(0)
logging.getLogger("fail2ban").setLevel(self.__logging_level)
def testRegex(self, line):
found = False
for regex in self.__ignoreregex:
@ -256,7 +256,7 @@ class Fail2banRegex:
logging.getLogger("fail2ban").setLevel(logging.CRITICAL)
for regex in self.__ignoreregex:
self.__filter.delIgnoreRegex(0)
def printStats(self):
print
print "Results"
@ -305,20 +305,20 @@ class Fail2banRegex:
print " %s (%s)%s" % (
ip[0], timeString, ip[2] and " (already matched)" or "")
print
print "Date template hits:"
for template in self.__filter.dateDetector.getTemplates():
if self.__verbose or template.getHits():
print `template.getHits()` + " hit(s): " + template.getName()
print
print "Success, the total number of match is " + str(total)
print
print "However, look at the above section 'Running tests' which could contain important"
print "information."
return True
if __name__ == "__main__":
fail2banRegex = Fail2banRegex()
# Reads the command line options.

View File

@ -46,7 +46,7 @@ logSys = logging.getLogger("fail2ban")
# Its first goal was to protect a SSH server.
class Fail2banServer:
def __init__(self):
self.__server = None
self.__argv = None
@ -55,7 +55,7 @@ class Fail2banServer:
self.__conf["force"] = False
self.__conf["socket"] = "/var/run/fail2ban/fail2ban.sock"
self.__conf["pidfile"] = "/var/run/fail2ban/fail2ban.pid"
def dispVersion(self):
print "Fail2Ban v" + version
print
@ -65,7 +65,7 @@ class Fail2banServer:
print
print "Written by Cyril Jaquier <cyril.jaquier@fail2ban.org>."
print "Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>."
def dispUsage(self):
""" Prints Fail2Ban command line options and exits
"""
@ -88,7 +88,7 @@ class Fail2banServer:
print " -V, --version print the version"
print
print "Report bugs to https://github.com/fail2ban/fail2ban/issues"
def __getCmdLineOptions(self, optList):
""" Gets the command line options
"""
@ -109,11 +109,11 @@ class Fail2banServer:
if opt[0] in ["-V", "--version"]:
self.dispVersion()
sys.exit(0)
def start(self, argv):
# Command line options
self.__argv = argv
# Reads the command line options.
try:
cmdOpts = 'bfs:p:xhV'
@ -122,9 +122,9 @@ class Fail2banServer:
except getopt.GetoptError:
self.dispUsage()
sys.exit(-1)
self.__getCmdLineOptions(optList)
try:
self.__server = Server(self.__conf["background"])
self.__server.start(self.__conf["socket"],
@ -135,7 +135,7 @@ class Fail2banServer:
logSys.exception(e)
self.__server.quit()
return False
if __name__ == "__main__":
server = Fail2banServer()
if server.start(sys.argv):

View File

@ -35,6 +35,9 @@ from testcases import filtertestcase
from testcases import servertestcase
from testcases import datedetectortestcase
from testcases import actiontestcase
from testcases import sockettestcase
from testcases.utils import FormatterWithTraceBack
from server.mytime import MyTime
from optparse import OptionParser, Option
@ -51,12 +54,14 @@ def get_opt_parser():
choices=('debug', 'info', 'warn', 'error', 'fatal'),
default=None,
help="Log level for the logger to use during running tests"),
])
p.add_options([
Option('-n', "--no-network", action="store_true",
dest="no_network",
help="Do not run tests that require the network"),
Option("-t", "--log-traceback", action='store_true',
help="Enrich log-messages with compressed tracebacks"),
Option("--full-traceback", action='store_true',
help="Either to make the tracebacks full, not compressed (as by default)"),
])
return p
@ -77,10 +82,10 @@ verbosity = {'debug': 3,
'fatal': 0,
None: 1}[opts.log_level]
if opts.log_level is not None:
if opts.log_level is not None: # pragma: no cover
# so we had explicit settings
logSys.setLevel(getattr(logging, opts.log_level.upper()))
else:
else: # pragma: no cover
# suppress the logging but it would leave unittests' progress dots
# ticking, unless like with '-l fatal' which would be silent
# unless error occurs
@ -88,18 +93,27 @@ else:
# Add the default logging handler
stdout = logging.StreamHandler(sys.stdout)
# Custom log format for the verbose tests runs
if verbosity > 1:
stdout.setFormatter(logging.Formatter(' %(asctime)-15s %(thread)s %(message)s'))
fmt = ' %(message)s'
if opts.log_traceback:
Formatter = FormatterWithTraceBack
fmt = (opts.full_traceback and ' %(tb)s' or ' %(tbc)s') + fmt
else:
Formatter = logging.Formatter
# Custom log format for the verbose tests runs
if verbosity > 1: # pragma: no cover
stdout.setFormatter(Formatter(' %(asctime)-15s %(thread)s' + fmt))
else: # pragma: no cover
# just prefix with the space
stdout.setFormatter(logging.Formatter(' %(message)s'))
stdout.setFormatter(Formatter(fmt))
logSys.addHandler(stdout)
#
# Let know the version
#
if not opts.log_level or opts.log_level != 'fatal':
if not opts.log_level or opts.log_level != 'fatal': # pragma: no cover
print "Fail2ban %s test suite. Python %s. Please wait..." \
% (version, str(sys.version).replace('\n', ''))
@ -107,9 +121,9 @@ if not opts.log_level or opts.log_level != 'fatal':
#
# Gather the tests
#
if not len(regexps):
if not len(regexps): # pragma: no cover
tests = unittest.TestSuite()
else:
else: # pragma: no cover
import re
class FilteredTestSuite(unittest.TestSuite):
_regexps = [re.compile(r) for r in regexps]
@ -130,8 +144,12 @@ tests.addTest(unittest.makeSuite(actiontestcase.ExecuteAction))
tests.addTest(unittest.makeSuite(failmanagertestcase.AddFailure))
# BanManager
tests.addTest(unittest.makeSuite(banmanagertestcase.AddFailure))
# ClientReader
# ClientReaders
tests.addTest(unittest.makeSuite(clientreadertestcase.ConfigReaderTest))
tests.addTest(unittest.makeSuite(clientreadertestcase.JailReaderTest))
tests.addTest(unittest.makeSuite(clientreadertestcase.JailsReaderTest))
# CSocket and AsyncServer
tests.addTest(unittest.makeSuite(sockettestcase.Socket))
# Filter
if not opts.no_network:
@ -159,19 +177,22 @@ filters = [FilterPoll] # always available
try:
from server.filtergamin import FilterGamin
filters.append(FilterGamin)
except Exception, e:
except Exception, e: # pragma: no cover
print "I: Skipping gamin backend testing. Got exception '%s'" % e
try:
from server.filterpyinotify import FilterPyinotify
filters.append(FilterPyinotify)
except Exception, e:
except Exception, e: # pragma: no cover
print "I: Skipping pyinotify backend testing. Got exception '%s'" % e
for Filter_ in filters:
tests.addTest(unittest.makeSuite(
filtertestcase.get_monitor_failures_testcase(Filter_)))
# Server test for logging elements which break logging used to support
# testcases analysis
tests.addTest(unittest.makeSuite(servertestcase.TransmitterLogging))
#
# Run the tests
@ -189,7 +210,7 @@ try:
tests_results = testRunner.run(tests)
finally:
finally: # pragma: no cover
# Just for the sake of it reset the TZ
# yoh: move all this into setup/teardown methods within tests
os.environ.pop('TZ')
@ -197,5 +218,5 @@ finally:
os.environ['TZ'] = old_TZ
time.tzset()
if not tests_results.wasSuccessful():
if not tests_results.wasSuccessful(): # pragma: no cover
sys.exit(1)

View File

@ -49,5 +49,5 @@ 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., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110, USA

View File

@ -1,12 +1,12 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.36.
.TH FAIL2BAN-CLIENT "1" "March 2008" "fail2ban-client v0.8.2" "User Commands"
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.10.
.TH FAIL2BAN-CLIENT "1" "March 2013" "fail2ban-client v0.8.8" "User Commands"
.SH NAME
fail2ban-client \- configure and control the server
.SH SYNOPSIS
.B fail2ban-client
[\fIOPTIONS\fR] \fI<COMMAND>\fR
.SH DESCRIPTION
Fail2Ban v0.8.2 reads log file that contains password failure report
Fail2Ban v0.8.8 reads log file that contains password failure report
and bans the corresponding IP addresses using firewall rules.
.SH OPTIONS
.TP
@ -16,6 +16,9 @@ configuration directory
\fB\-s\fR <FILE>
socket path
.TP
\fB\-p\fR <FILE>
pidfile path
.TP
\fB\-d\fR
dump configuration. For debugging
.TP
@ -110,7 +113,7 @@ adds <FILE> to the monitoring list
of <JAIL>
.TP
\fBset <JAIL> dellogpath <FILE>\fR
removes <FILE> to the monitoring
removes <FILE> from the monitoring
list of <JAIL>
.TP
\fBset <JAIL> addfailregex <REGEX>\fR
@ -140,6 +143,15 @@ back for <JAIL>
sets the number of seconds <TIME>
a host will be banned for <JAIL>
.TP
\fBset <JAIL> usedns <VALUE>\fR
sets the usedns mode for <JAIL>
.TP
\fBset <JAIL> banip <IP>\fR
manually Ban <IP> for <JAIL>
.TP
\fBset <JAIL> unbanip <IP>\fR
manually Unban <IP> in <JAIL>
.TP
\fBset <JAIL> maxretry <RETRY>\fR
sets the number of failures
<RETRY> before banning the host
@ -191,14 +203,6 @@ files for <JAIL>
gets the list of ignored IP
addresses for <JAIL>
.TP
\fBget <JAIL> timeregex\fR
gets the regular expression used
for the time detection for <JAIL>
.TP
\fBget <JAIL> timepattern\fR
gets the pattern used for the time
detection for <JAIL>
.TP
\fBget <JAIL> failregex\fR
gets the list of regular
expressions which matches the
@ -218,6 +222,9 @@ will look back for failures for
gets the time a host is banned for
<JAIL>
.TP
\fBget <JAIL> usedns\fR
gets the usedns setting for <JAIL>
.TP
\fBget <JAIL> maxretry\fR
gets the number of failures
allowed for <JAIL>
@ -245,15 +252,19 @@ action <ACT> for <JAIL>
\fBget <JAIL> actionunban <ACT>\fR
gets the unban command for the
action <ACT> for <JAIL>
.TP
\fBget <JAIL> cinfo <ACT> <KEY>\fR
gets the value for <KEY> for the
action <ACT> for <JAIL>
.SH FILES
\fI/etc/fail2ban/*\fR
.SH AUTHOR
Written by Cyril Jaquier <cyril.jaquier@fail2ban.org>.
Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>.
.SH "REPORTING BUGS"
Report bugs on https://github.com/fail2ban/fail2ban/issues
Report bugs to https://github.com/fail2ban/fail2ban/issues
.SH COPYRIGHT
Copyright \(co 2004-2008 Cyril Jaquier
Copyright \(co 2004\-2008 Cyril Jaquier, 2008\- Fail2Ban Contributors
.br
Copyright of modifications held by their respective authors.
Licensed under the GNU General Public License v2 (GPL).

View File

@ -1,12 +1,12 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.36.
.TH FAIL2BAN-REGEX "1" "March 2008" "fail2ban-regex v0.8.2" "User Commands"
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.10.
.TH FAIL2BAN-REGEX "1" "March 2013" "fail2ban-regex v0.8.8" "User Commands"
.SH NAME
fail2ban-regex \- test Fail2ban "failregex" option
.SH SYNOPSIS
.B fail2ban-regex
[\fIOPTIONS\fR] \fI<LOG> <REGEX> \fR[\fIIGNOREREGEX\fR]
.SH DESCRIPTION
Fail2Ban v0.8.2 reads log file that contains password failure report
Fail2Ban v0.8.8 reads log file that contains password failure report
and bans the corresponding IP addresses using firewall rules.
.PP
This tools can test regular expressions for "fail2ban".
@ -17,6 +17,9 @@ display this help message
.TP
\fB\-V\fR, \fB\-\-version\fR
print the version
.TP
\fB\-v\fR, \fB\-\-verbose\fR
verbose output
.SH LOG
.TP
\fBstring\fR
@ -42,9 +45,9 @@ path to a filter file (filter.d/sshd.conf)
Written by Cyril Jaquier <cyril.jaquier@fail2ban.org>.
Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>.
.SH "REPORTING BUGS"
Report bugs on https://github.com/fail2ban/fail2ban/issues
Report bugs to https://github.com/fail2ban/fail2ban/issues
.SH COPYRIGHT
Copyright \(co 2004-2008 Cyril Jaquier
Copyright \(co 2004\-2008 Cyril Jaquier
.br
Copyright of modifications held by their respective authors.
Licensed under the GNU General Public License v2 (GPL).

View File

@ -1,12 +1,12 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.36.
.TH FAIL2BAN-SERVER "1" "March 2008" "fail2ban-server v0.8.2" "User Commands"
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.10.
.TH FAIL2BAN-SERVER "1" "March 2013" "fail2ban-server v0.8.8" "User Commands"
.SH NAME
fail2ban-server \- start the server
.SH SYNOPSIS
.B fail2ban-server
[\fIOPTIONS\fR]
.SH DESCRIPTION
Fail2Ban v0.8.2 reads log file that contains password failure report
Fail2Ban v0.8.8 reads log file that contains password failure report
and bans the corresponding IP addresses using firewall rules.
.PP
Only use this command for debugging purpose. Start the server with
@ -23,6 +23,9 @@ start in foreground
\fB\-s\fR <FILE>
socket path
.TP
\fB\-p\fR <FILE>
pidfile path
.TP
\fB\-x\fR
force execution of the server (remove socket file)
.TP
@ -35,9 +38,9 @@ print the version
Written by Cyril Jaquier <cyril.jaquier@fail2ban.org>.
Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>.
.SH "REPORTING BUGS"
Report bugs on https://github.com/fail2ban/fail2ban/issues
Report bugs to https://github.com/fail2ban/fail2ban/issues
.SH COPYRIGHT
Copyright \(co 2004-2008 Cyril Jaquier
Copyright \(co 2004\-2008 Cyril Jaquier, 2008\- Fail2Ban Contributors
.br
Copyright of modifications held by their respective authors.
Licensed under the GNU General Public License v2 (GPL).

44
man/fail2ban.1 Normal file
View File

@ -0,0 +1,44 @@
.TH FAIL2BAN "1" "March 2013" "Fail2Ban"
.SH NAME
fail2ban \- a set of server and client programs to limit brute force authentication attempts.
.SH DESCRIPTION
Fail2Ban consists of a client, server and configuration files to limit
brute force authentication attempts.
The server program \fBfail2ban-server\fR is responsible for monitoring
log files and issuing ban/unban commands. It gets configured through
a simple protocol by \fBfail2ban-client\fR, which can also read
configuration files and issue corresponding configuration commands to
the server.
For details on the configuration of fail2ban see the jail.conf(5)
manual page. A jail (as specified in jail.conf) couples filters and
actions definitions for any given list of files to get monitored.
For details on the command-line options of fail2ban-server see the
fail2ban-server(1) manual page.
For details on the command-line options and commands for configuring
the server via fail2ban-client see the fail2ban-client(1) manual page.
For testing regular expressions specified in a filter using the
fail2ban-regex program may be of use and its manual page is
fail2ban-regex(1).
.SH FILES
\fI/etc/fail2ban/*\fR
.SH AUTHOR
Manual page written by Daniel Black and Yaroslav Halchenko
.SH "REPORTING BUGS"
Report bugs to https://github.com/fail2ban/fail2ban/issues
.SH COPYRIGHT
Copyright \(co 2013
.br
Copyright of modifications held by their respective authors.
Licensed under the GNU General Public License v2 (GPL).
.SH "SEE ALSO"
.br
fail2ban-server(1)
fail2ban-client(1)
fail2ban-regex(1)
jail.conf(5)

168
man/jail.conf.5 Normal file
View File

@ -0,0 +1,168 @@
.TH JAIL.CONF "5" "March 2013" "Fail2Ban" "Fail2Ban Configuration"
.SH NAME
jail.conf \- configuration for the fail2ban server
.SH SYNOPSIS
.I jail.conf / jail.local
.I action.d/*.conf action.d/*.local
.I filter.d/*.conf filter.d/*.local
.SH DESCRIPTION
Fail2ban has three configuration file types. Action files are the commands for banning and unbanning of IP address,
Filter files tell fail2ban how to detect authentication failures, and Jail configurations combine filters with actions into jails.
There are *.conf files that are distributed by fail2ban and *.local file that contain user customizations.
It is recommended that *.conf files should remain unchanged. If needed, customizations should be provided in *.local files.
For instance, if you would like to customize the [ssh-iptables-ipset] jail, create a jail.local to extend jail.conf
(the configuration for the fail2ban server). The jail.local file will be the following if you only need to enable
it:
.TP
\fIjail.local\fR
[ssh-iptables-ipset]
enabled = true
.PP
Override only the settings you need to change and the rest of the configuration will come from the corresponding
*.conf file.
\fI*.d/\fR
.RS
In addition to .local, for any .conf file there can be a corresponding
\fI.d/\fR directory to contain additional .conf files that will be read after the
appropriate .local file. Last parsed file will take precidence over
identical entries, parsed alphabetically, e.g.
.RS
\fIjail.d/01_enable.conf\fR - to enable a specific jail
.RE
.RS
\fIjail.d/02_custom_port.conf\fR - containing specific configuration entry to change the port of the jail specified in the configuration
.RE
.RS
\fIfail2ban.d/01_custom_log.conf\fR - containing specific configuration entry to use a different log path.
.RE
.RE
.SH DEFAULT
The following options are applicable to all jails. Their meaning is described in the default \fIjail.conf\fR file.
.TP
\fBignoreip\fR
.TP
\fBbantime\fR
.TP
\fBfindtime\fR
.TP
\fBmaxretry\fR
.TP
\fBbackend\fR
.TP
\fBusedns\fR
.SH "ACTION FILES"
Action files specify which commands are executed to ban and unban an IP address. They are located under \fI/etc/fail2ban/action.d\fR.
Like with jail.conf files, if you desire local changes create an \fI[actionname].local\fR file in the \fI/etc/fail2ban/action.d\fR directory
and override the required settings.
Action files are ini files that have two sections, \fBDefinition\fR and \fBInit\fR .
The [Init] section allows for action-specific settings. In \fIjail.conf/jail.local\fR these can be overwritten for a particular jail as options to the jail.
The following commands can be present in the [Definition] section.
.TP
\fBactionstart\fR
command(s) executed when the jail starts.
.TP
\fBactionstop\fR
command(s) executed when the jail stops.
.TP
\fBactioncheck\fR
the command ran before any other action. It aims to verify if the environment is still ok.
.TP
\fBactionban\fR
command(s) that bans the IP address after \fBmaxretry\fR log lines matches within last \fBfindtime\fR seconds.
.TP
\fBactionunban\fR
command(s) that unbans the IP address after \fBbantime\fR.
Commands specified in the [Definition] section are executed through a system shell so shell redirection and process control is allowed. The commands should
return 0, otherwise error would be logged. Moreover if \fBactioncheck\fR exits with non-0 status, it is taken as indication that firewall status has changed and fail2ban needs to reinitialize itself (i.e. issue \fBactionstop\fR and \fBactionstart\fR commands).
Tags are enclosed in <>. All the elements of [Init] are tags that are replaced in all action commands. Tags can be added by the
\fBfail2ban-client\fR using the setctag command.
More than a single command is allowed to be specified. Each command needs to be on a separate line and indented with whitespaces without blank lines. The following example defines
two commands to be executed.
actionban = iptables -I fail2ban-<name> --source <ip> -j DROP
echo ip=<ip>, match=<match>, time=<time> >> /var/log/fail2ban.log
.SS "Action Tags"
The following tags are substituted in the actionban, actionunban and actioncheck (when called before actionban/actionunban) commands.
.TP
\fBip\fR
An IPv4 ip address to be banned. e.g. 192.168.0.2
.TP
\fBfailures\fR
The number of times the failure occurred in the log file. e.g. 3
.TP
\fBtime\fR
The unix time of the ban. e.g. 1357508484
.TP
\fBmatches\fR
The concatenated string of the log file lines of the matches that generated the ban. Many characters interpreted by shell get escaped.
.SH FILTER FILES
Filter definitions are those in \fI/etc/fail2ban/filter.d/*.conf\fR and \fIfilter.d/*.local\fR.
These are used to identify failed authentication attempts in logs and to extract the host IP address (or hostname if \fBusedns\fR is \fBtrue\fR).
Like action files, filter files are ini files. The main section is the [Definition] section.
There are two filter definitions used in the [Definition] section:
.TP
\fBfailregex\fR
is the regex (\fBreg\fRular \fBex\fRpression) that will match failed attempts. The tag <HOST> is used as part of the regex and is itself a regex
for IPv4 addresses and hostnames. fail2ban will work out which one of these it actually is.
.TP
\fBignoreregex\fR
is the regex to identify log entries that should be ignored by fail2ban, even if they match failregex.
Using Python "string interpolation" mechanisms, other definitions are allowed and can later be used within other definitions as %(defnname)s. For example.
baduseragents = IE|wget
failregex = useragent=%(baduseragents)s
.PP
Filters can also have a section called [INCLUDES]. This is used to read other configuration files.
.TP
\fBbefore\fR
indicates that this file is read before the [Definition] section.
.TP
\fBafter\fR
indicates that this file is read after the [Definition] section.
.SH AUTHOR
Fail2ban was originally written by Cyril Jaquier <cyril.jaquier@fail2ban.org>.
At the moment it is maintained and further developed by Yaroslav O. Halchenko <debian@onerussian.com> and a number of contributors. See \fBTHANKS\fR file shipped with Fail2Ban for a full list.
.
Manual page written by Daniel Black and Yaroslav Halchenko.
.SH "REPORTING BUGS"
Report bugs to https://github.com/fail2ban/fail2ban/issues
.SH COPYRIGHT
Copyright \(co 2013 Daniel Black
.br
Copyright of modifications held by their respective authors.
Licensed under the GNU General Public License v2 (GPL).
.SH "SEE ALSO"
.br
fail2ban-server(1)

View File

@ -277,8 +277,8 @@ class Action:
# Executes a command with preliminary checks and substitutions.
#
# Before executing any commands, executes the "check" command first
# in order to check if prerequirements are met. If this check fails,
# it tries to restore a sane environnement before executing the real
# in order to check if pre-requirements are met. If this check fails,
# it tries to restore a sane environment before executing the real
# command.
# Replaces "aInfo" and "cInfo" in the query too.
#

View File

@ -77,7 +77,8 @@ class Actions(JailThread):
for action in self.__actions:
if action.getName() == name:
self.__actions.remove(action)
break
return
raise KeyError("Invalid Action name: %s" % name)
##
# Returns an action.
@ -91,7 +92,7 @@ class Actions(JailThread):
for action in self.__actions:
if action.getName() == name:
return action
raise KeyError
raise KeyError("Invalid Action name")
##
# Returns the last defined action.
@ -131,7 +132,7 @@ class Actions(JailThread):
# Unban the IP.
self.__unBan(ticket)
return ip
return 'None'
raise ValueError("IP %s is not banned" % ip)
##
# Main loop.

View File

@ -148,7 +148,7 @@ class AsyncServer(asyncore.dispatcher):
if sys.version_info >= (2, 6): # if python 2.6 or greater...
logSys.debug("Detected Python 2.6 or greater. asyncore.loop() not using poll")
asyncore.loop(use_poll = False) # fixes the "Unexpected communication problem" issue on Python 2.6 and 3.0
else:
else: # pragma: no cover
logSys.debug("NOT Python 2.6/3.* - asyncore.loop() using poll")
asyncore.loop(use_poll = True)

View File

@ -148,7 +148,7 @@ class BanManager:
def addBanTicket(self, ticket):
try:
self.__lock.acquire()
if not self.__inBanList(ticket):
if not self._inBanList(ticket):
self.__banList.append(ticket)
self.__banTotal += 1
return True
@ -177,7 +177,7 @@ class BanManager:
# @param ticket the ticket
# @return True if a ticket already exists
def __inBanList(self, ticket):
def _inBanList(self, ticket):
for i in self.__banList:
if ticket.getIP() == i.getIP():
return True

View File

@ -50,8 +50,11 @@ class DateTemplate:
def getName(self):
return self.__name
def setRegex(self, regex):
self.__regex = regex.strip()
def setRegex(self, regex, wordBegin=True):
regex = regex.strip()
if (wordBegin and not re.search(r'^\^', regex)):
regex = r'\b' + regex
self.__regex = regex
self.__cRegex = re.compile(regex)
def getRegex(self):
@ -158,6 +161,7 @@ class DateStrptime(DateTemplate):
"pattern" % (opattern, e))
if date[0] < 2000:
# There is probably no year field in the logs
# NOTE: Possibly makes week/year day incorrect
date[0] = MyTime.gmtime()[0]
# Bug fix for #1241756
# If the date is greater than the current time, we suppose
@ -166,10 +170,12 @@ class DateStrptime(DateTemplate):
logSys.debug(
u"Correcting deduced year from %d to %d since %f > %f" %
(date[0], date[0]-1, time.mktime(tuple(date)), MyTime.time()))
# NOTE: Possibly makes week/year day incorrect
date[0] -= 1
elif date[1] == 1 and date[2] == 1:
# If it is Jan 1st, it is either really Jan 1st or there
# is neither month nor day in the log.
# NOTE: Possibly makes week/year day incorrect
date[1] = MyTime.gmtime()[1]
date[2] = MyTime.gmtime()[2]
return date
@ -180,7 +186,8 @@ class DateTai64n(DateTemplate):
def __init__(self):
DateTemplate.__init__(self)
# We already know the format for TAI64N
self.setRegex("@[0-9a-f]{24}")
# yoh: we should not add an additional front anchor
self.setRegex("@[0-9a-f]{24}", wordBegin=False)
def getDate(self, line):
date = None

View File

@ -105,9 +105,17 @@ class FailManager:
fData.setLastReset(unixTime)
fData.setLastTime(unixTime)
self.__failList[ip] = fData
logSys.debug("Currently have failures from %d IPs: %s"
% (len(self.__failList), self.__failList.keys()))
self.__failTotal += 1
if logSys.getEffectiveLevel() <= logging.DEBUG:
# yoh: Since composing this list might be somewhat time consuming
# in case of having many active failures, it should be ran only
# if debug level is "low" enough
failures_summary = ', '.join(['%s:%d' % (k, v.getRetry())
for k,v in self.__failList.iteritems()])
logSys.debug("Total # of detected failures: %d. Current failures from %d IPs (IP:count): %s"
% (self.__failTotal, len(self.__failList), failures_summary))
finally:
self.__lock.release()

View File

@ -44,7 +44,7 @@ logSys = logging.getLogger("fail2ban.filter")
# Log reader class.
#
# This class reads a log file and detects login failures or anything else
# that matches a given regular expression. This class is instanciated by
# that matches a given regular expression. This class is instantiated by
# a Jail object.
class Filter(JailThread):
@ -93,6 +93,7 @@ class Filter(JailThread):
self.__failRegex.append(regex)
except RegexException, e:
logSys.error(e)
raise e
def delFailRegex(self, index):
@ -117,7 +118,7 @@ class Filter(JailThread):
# Add the regular expression which matches the failure.
#
# The regular expression can also match any other pattern than failures
# and thus can be used for many purporse.
# and thus can be used for many purpose.
# @param value the regular expression
def addIgnoreRegex(self, value):
@ -126,6 +127,7 @@ class Filter(JailThread):
self.__ignoreRegex.append(regex)
except RegexException, e:
logSys.error(e)
raise e
def delIgnoreRegex(self, index):
try:
@ -211,7 +213,7 @@ class Filter(JailThread):
# file has been modified and looks for failures.
# @return True when the thread exits nicely
def run(self):
def run(self): # pragma: no cover
raise Exception("run() is abstract")
##
@ -226,7 +228,7 @@ class Filter(JailThread):
self.failManager.addFailure(FailTicket(ip, unixTime))
# Perform the banning of the IP now.
try:
try: # pragma: no branch - exception is the only way out
while True:
ticket = self.failManager.toBan()
self.jail.putFailTicket(ticket)
@ -368,7 +370,7 @@ class Filter(JailThread):
failList.append([ip, date])
# We matched a regex, it is enough to stop.
break
except RegexException, e:
except RegexException, e: # pragma: no cover - unsure if reachable
logSys.error(e)
return failList
@ -410,7 +412,7 @@ class FileFilter(Filter):
def _addLogPath(self, path):
# nothing to do by default
# to be overriden by backends
# to be overridden by backends
pass
@ -429,7 +431,7 @@ class FileFilter(Filter):
def _delLogPath(self, path):
# nothing to do by default
# to be overriden by backends
# to be overridden by backends
pass
##
@ -495,10 +497,19 @@ class FileFilter(Filter):
# Try to open log file.
try:
container.open()
except Exception, e:
# see http://python.org/dev/peps/pep-3151/
except IOError, e:
logSys.error("Unable to open %s" % filename)
logSys.exception(e)
return False
except OSError, e: # pragma: no cover - requires race condition to tigger this
logSys.error("Error opening %s" % filename)
logSys.exception(e)
return False
except OSError, e: # pragma: no cover - Requires implemention error in FileContainer to generate
logSys.error("Internal errror in FileContainer open method - please report as a bug to https://github.com/fail2ban/fail2ban/issues")
logSys.exception(e)
return False
while True:
line = container.readline()
@ -525,7 +536,7 @@ class FileFilter(Filter):
try:
import hashlib
md5sum = hashlib.md5
except ImportError:
except ImportError: # pragma: no cover
# hashlib was introduced in Python 2.5. For compatibility with those
# elderly Pythons, import from md5
import md5
@ -576,7 +587,7 @@ class FileContainer:
stats = os.fstat(self.__handler.fileno())
# Compare hash and inode
if self.__hash != myHash or self.__ino != stats.st_ino:
logSys.info("Log rotation detected for %s" % self.__filename)
logSys.debug("Log rotation detected for %s" % self.__filename)
self.__hash = myHash
self.__ino = stats.st_ino
self.__pos = 0

View File

@ -39,7 +39,7 @@ logSys = logging.getLogger("fail2ban.filter")
# Log reader class.
#
# This class reads a log file and detects login failures or anything else
# that matches a given regular expression. This class is instanciated by
# that matches a given regular expression. This class is instantiated by
# a Jail object.
class FilterPoll(FileFilter):

View File

@ -63,16 +63,17 @@ class FilterPyinotify(FileFilter):
logSys.debug("Created FilterPyinotify")
def callback(self, event):
def callback(self, event, origin=''):
logSys.debug("%sCallback for Event: %s", origin, event)
path = event.pathname
if event.mask & pyinotify.IN_CREATE:
# skip directories altogether
if event.mask & pyinotify.IN_ISDIR:
logSys.debug("Ignoring creation of directory %s" % path)
logSys.debug("Ignoring creation of directory %s", path)
return
# check if that is a file we care about
if not path in self.__watches:
logSys.debug("Ignoring creation of %s we do not monitor" % path)
logSys.debug("Ignoring creation of %s we do not monitor", path)
return
else:
# we need to substitute the watcher with a new one, so first
@ -104,8 +105,8 @@ class FilterPyinotify(FileFilter):
def _addFileWatcher(self, path):
wd = self.__monitor.add_watch(path, pyinotify.IN_MODIFY)
self.__watches.update(wd)
logSys.debug("Added file watcher for %s" % path)
# process the file since we did get even
logSys.debug("Added file watcher for %s", path)
# process the file since we did get even
self._process_file(path)
@ -114,7 +115,7 @@ class FilterPyinotify(FileFilter):
wd = self.__monitor.rm_watch(wdInt)
if wd[wdInt]:
del self.__watches[path]
logSys.debug("Removed file watcher for %s" % path)
logSys.debug("Removed file watcher for %s", path)
return True
else:
return False
@ -130,7 +131,7 @@ class FilterPyinotify(FileFilter):
# we need to watch also the directory for IN_CREATE
self.__watches.update(
self.__monitor.add_watch(path_dir, pyinotify.IN_CREATE))
logSys.debug("Added monitor for the parent directory %s" % path_dir)
logSys.debug("Added monitor for the parent directory %s", path_dir)
self._addFileWatcher(path)
@ -151,7 +152,7 @@ class FilterPyinotify(FileFilter):
# since there is no other monitored file under this directory
wdInt = self.__watches.pop(path_dir)
_ = self.__monitor.rm_watch(wdInt)
logSys.debug("Removed monitor for the parent directory %s" % path_dir)
logSys.debug("Removed monitor for the parent directory %s", path_dir)
##
@ -165,7 +166,7 @@ class FilterPyinotify(FileFilter):
self.__notifier = pyinotify.ThreadedNotifier(self.__monitor,
ProcessPyinotify(self))
self.__notifier.start()
logSys.debug("pyinotifier started for %s." % self.jail.getName())
logSys.debug("pyinotifier started for %s.", self.jail.getName())
# TODO: verify that there is nothing really to be done for
# idle jails
return True
@ -201,5 +202,4 @@ class ProcessPyinotify(pyinotify.ProcessEvent):
# just need default, since using mask on watch to limit events
def process_default(self, event):
logSys.debug("Callback for Event: %s" % event)
self.__FileFilter.callback(event)
self.__FileFilter.callback(event, origin='Default ')

View File

@ -382,7 +382,7 @@ class Server:
try:
handler.flush()
handler.close()
except ValueError:
except (ValueError, KeyError):
if (2,6) <= sys.version_info < (3,) or \
(3,2) <= sys.version_info:
raise

View File

@ -112,14 +112,18 @@ class Transmitter:
return self.__server.getLogLevel()
elif name == "logtarget":
value = command[1]
self.__server.setLogTarget(value)
return self.__server.getLogTarget()
if self.__server.setLogTarget(value):
return self.__server.getLogTarget()
else:
raise Exception("Failed to change log target")
# Jail
elif command[1] == "idle":
if command[2] == "on":
self.__server.setIdleJail(name, True)
elif command[2] == "off":
self.__server.setIdleJail(name, False)
else:
raise Exception("Invalid idle option, must be 'yes' or 'no'")
return self.__server.getIdleJail(name)
# Filter
elif command[1] == "addignoreip":
@ -193,7 +197,7 @@ class Transmitter:
elif command[1] == "setcinfo":
act = command[2]
key = command[3]
value = command[4]
value = " ".join(command[4:])
self.__server.setCInfo(name, act, key, value)
return self.__server.getCInfo(name, act, key)
elif command[1] == "delcinfo":
@ -203,27 +207,27 @@ class Transmitter:
return None
elif command[1] == "actionstart":
act = command[2]
value = command[3]
value = " ".join(command[3:])
self.__server.setActionStart(name, act, value)
return self.__server.getActionStart(name, act)
elif command[1] == "actionstop":
act = command[2]
value = command[3]
value = " ".join(command[3:])
self.__server.setActionStop(name, act, value)
return self.__server.getActionStop(name, act)
elif command[1] == "actioncheck":
act = command[2]
value = command[3]
value = " ".join(command[3:])
self.__server.setActionCheck(name, act, value)
return self.__server.getActionCheck(name, act)
elif command[1] == "actionban":
act = command[2]
value = command[3]
value = " ".join(command[3:])
self.__server.setActionBan(name, act, value)
return self.__server.getActionBan(name, act)
elif command[1] == "actionunban":
act = command[2]
value = command[3]
value = " ".join(command[3:])
self.__server.setActionUnban(name, act, value)
return self.__server.getActionUnban(name, act)
raise Exception("Invalid command (no set action or not yet implemented)")
@ -281,7 +285,7 @@ class Transmitter:
def status(self, command):
if len(command) == 0:
return self.__server.status()
else:
elif len(command) == 1:
name = command[0]
return self.__server.statusJail(name)
raise Exception("Invalid command (no status)")

View File

@ -3,3 +3,11 @@ install-purelib=/usr/share/fail2ban
[sdist]
formats=bztar
[bdist_rpm]
release = 1
packager = Yaroslav Halchenko <debian@onerussian.com>, Daniel Black <grooverdan@users.sourceforge.net>
doc_files = DEVELOP
README
THANKS
doc/run-rootless.txt

View File

@ -67,6 +67,9 @@ setup(
),
('/var/run/fail2ban',
''
),
('/usr/share/doc/fail2ban',
['README', 'DEVELOP', 'doc/run-rootless.txt']
)
]
)
@ -82,13 +85,7 @@ elements = {
"/usr/bin/":
[
"fail2ban.py"
],
"/usr/lib/fail2ban/firewall/":
[
"iptables.py",
"ipfwadm.py",
"ipfw.py"
],
],
"/usr/lib/fail2ban/":
[
"version.py",

View File

@ -49,11 +49,11 @@ class AddFailure(unittest.TestCase):
self.assertFalse(self.__banManager.addBanTicket(self.__ticket))
self.assertEqual(self.__banManager.size(), 1)
def _testInListOK(self):
def testInListOK(self):
ticket = BanTicket('193.168.0.128', 1167605999.0)
self.assertTrue(self.__banManager.inBanList(ticket))
self.assertTrue(self.__banManager._inBanList(ticket))
def _testInListNOK(self):
def testInListNOK(self):
ticket = BanTicket('111.111.1.111', 1167605999.0)
self.assertFalse(self.__banManager.inBanList(ticket))
self.assertFalse(self.__banManager._inBanList(ticket))

View File

@ -17,30 +17,152 @@
# along with Fail2Ban; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# Author: Cyril Jaquier
#
# $Revision$
__author__ = "Cyril Jaquier"
__version__ = "$Revision$"
__date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__author__ = "Cyril Jaquier, Yaroslav Halchenko"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2013 Yaroslav Halchenko"
__license__ = "GPL"
import unittest
import os, shutil, tempfile, unittest
from client.configreader import ConfigReader
from client.jailreader import JailReader
from client.jailsreader import JailsReader
from client.configurator import Configurator
class JailReaderTest(unittest.TestCase):
class ConfigReaderTest(unittest.TestCase):
def setUp(self):
"""Call before every test case."""
self.d = tempfile.mkdtemp(prefix="f2b-temp")
self.c = ConfigReader(basedir=self.d)
def tearDown(self):
"""Call after every test case."""
shutil.rmtree(self.d)
def _write(self, fname, value):
# verify if we don't need to create .d directory
if os.path.sep in fname:
d = os.path.dirname(fname)
d_ = os.path.join(self.d, d)
if not os.path.exists(d_):
os.makedirs(d_)
open("%s/%s" % (self.d, fname), "w").write("""
[section]
option = %s
""" % value)
def _remove(self, fname):
os.unlink("%s/%s" % (self.d, fname))
self.assertTrue(self.c.read('c')) # we still should have some
def _getoption(self, f='c'):
self.assertTrue(self.c.read(f)) # we got some now
return self.c.getOptions('section', [("int", 'option')])['option']
def testInaccessibleFile(self):
f = os.path.join(self.d, "d.conf") # inaccessible file
self._write('d.conf', 0)
self.assertEqual(self._getoption('d'), 0)
os.chmod(f, 0)
self.assertFalse(self.c.read('d')) # should not be readable BUT present
def testOptionalDotDDir(self):
self.assertFalse(self.c.read('c')) # nothing is there yet
self._write("c.conf", "1")
self.assertEqual(self._getoption(), 1)
self._write("c.conf", "2") # overwrite
self.assertEqual(self._getoption(), 2)
self._write("c.local", "3") # add override in .local
self.assertEqual(self._getoption(), 3)
self._write("c.d/98.conf", "998") # add 1st override in .d/
self.assertEqual(self._getoption(), 998)
self._write("c.d/90.conf", "990") # add previously sorted override in .d/
self.assertEqual(self._getoption(), 998) # should stay the same
self._write("c.d/99.conf", "999") # now override in a way without sorting we possibly get a failure
self.assertEqual(self._getoption(), 999)
self._remove("c.d/99.conf")
self.assertEqual(self._getoption(), 998)
self._remove("c.d/98.conf")
self.assertEqual(self._getoption(), 990)
self._remove("c.d/90.conf")
self.assertEqual(self._getoption(), 3)
self._remove("c.conf") # we allow to stay without .conf
self.assertEqual(self._getoption(), 3)
self._write("c.conf", "1")
self._remove("c.local")
self.assertEqual(self._getoption(), 1)
class JailReaderTest(unittest.TestCase):
def testStockSSHJail(self):
jail = JailReader('ssh-iptables', basedir='config') # we are running tests from root project dir atm
self.assertTrue(jail.read())
self.assertTrue(jail.getOptions())
self.assertFalse(jail.isEnabled())
self.assertEqual(jail.getName(), 'ssh-iptables')
def testSplitAction(self):
action = "mail-whois[name=SSH]"
expected = ['mail-whois', {'name': 'SSH'}]
result = JailReader.splitAction(action)
self.assertEquals(expected, result)
class JailsReaderTest(unittest.TestCase):
def testProvidingBadBasedir(self):
if not os.path.exists('/XXX'):
reader = JailsReader(basedir='/XXX')
self.assertRaises(ValueError, reader.read)
def testReadStockJailConf(self):
jails = JailsReader(basedir='config') # we are running tests from root project dir atm
self.assertTrue(jails.read()) # opens fine
self.assertTrue(jails.getOptions()) # reads fine
comm_commands = jails.convert()
# by default None of the jails is enabled and we get no
# commands to communicate to the server
self.assertEqual(comm_commands, [])
def testReadStockJailConfForceEnabled(self):
# more of a smoke test to make sure that no obvious surprises
# on users' systems when enabling shipped jails
jails = JailsReader(basedir='config', force_enable=True) # we are running tests from root project dir atm
self.assertTrue(jails.read()) # opens fine
self.assertTrue(jails.getOptions()) # reads fine
comm_commands = jails.convert()
# by default we have lots of jails ;)
self.assertTrue(len(comm_commands))
# and we know even some of them by heart
for j in ['ssh-iptables', 'recidive']:
# by default we have 'auto' backend ATM
self.assertTrue(['add', j, 'auto'] in comm_commands)
# and warn on useDNS
self.assertTrue(['set', j, 'usedns', 'warn'] in comm_commands)
self.assertTrue(['start', j] in comm_commands)
# last commands should be the 'start' commands
self.assertEqual(comm_commands[-1][0], 'start')
# TODO: make sure that all of the jails have actions assigned,
# otherwise it makes little to no sense
def testConfigurator(self):
configurator = Configurator()
configurator.setBaseDir('config')
self.assertEqual(configurator.getBaseDir(), 'config')
configurator.readEarly()
opts = configurator.getEarlyOptions()
# our current default settings
self.assertEqual(opts['socket'], '/var/run/fail2ban/fail2ban.sock')
self.assertEqual(opts['pidfile'], '/var/run/fail2ban/fail2ban.pid')
# and if we force change configurator's fail2ban's baseDir
# there should be an error message (test visually ;) --
# otherwise just a code smoke test)
configurator._Configurator__jails.setBaseDir('/tmp')
self.assertEqual(configurator._Configurator__jails.getBaseDir(), '/tmp')
self.assertEqual(configurator.getBaseDir(), 'config')

View File

@ -45,36 +45,50 @@ class DateDetectorTest(unittest.TestCase):
log = "1138049999 [sshd] error: PAM: Authentication failure"
date = [2006, 1, 23, 21, 59, 59, 0, 23, 0]
dateUnix = 1138049999.0
self.assertEqual(self.__datedetector.getTime(log), date)
self.assertEqual(self.__datedetector.getUnixTime(log), dateUnix)
def testGetTime(self):
log = "Jan 23 21:59:59 [sshd] error: PAM: Authentication failure"
date = [2005, 1, 23, 21, 59, 59, 1, 23, -1]
date = [2005, 1, 23, 21, 59, 59, 6, 23, -1]
dateUnix = 1106513999.0
self.assertEqual(self.__datedetector.getTime(log), date)
# yoh: testing only up to 6 elements, since the day of the week
# is not correctly determined atm, since year is not present
# in the log entry. Since this doesn't effect the operation
# of fail2ban -- we just ignore incorrect day of the week
self.assertEqual(self.__datedetector.getTime(log)[:6], date[:6])
self.assertEqual(self.__datedetector.getUnixTime(log), dateUnix)
def testVariousTimes(self):
"""Test detection of various common date/time formats f2b should understand
"""
date = [2005, 1, 23, 21, 59, 59, 1, 23, -1]
date = [2005, 1, 23, 21, 59, 59, 6, 23, -1]
dateUnix = 1106513999.0
for sdate in (
"Jan 23 21:59:59",
"Sun Jan 23 21:59:59 2005",
"Sun Jan 23 21:59:59",
"2005/01/23 21:59:59",
"2005.01.23 21:59:59",
"23/01/2005 21:59:59",
"23/01/05 21:59:59",
"23/Jan/2005:21:59:59",
"01/23/2005:21:59:59",
"2005-01-23 21:59:59",
"23-Jan-2005 21:59:59",
"23-01-2005 21:59:59",
"01-23-2005 21:59:59.252", # reported on f2b, causes Feb29 fix to break
"@4000000041f4104f00000000", # TAI64N
"2005-01-23T21:59:59.252Z", #ISO 8601
"2005-01-23T21:59:59-05:00Z", #ISO 8601 with TZ
"<01/23/05@21:59:59>",
):
log = sdate + "[sshd] error: PAM: Authentication failure"
# exclude
# TODO (Yarik is confused): figure out why for above it is
# "1" as day of the week which would be Tue, although it
# was Sun
# yoh: on [:6] see in above test
self.assertEqual(self.__datedetector.getTime(log)[:6], date[:6])
self.assertEqual(self.__datedetector.getUnixTime(log), dateUnix)
@ -89,6 +103,27 @@ class DateDetectorTest(unittest.TestCase):
self.assertRaises(ValueError, self.__datedetector._appendTemplate,
self.__datedetector.getTemplates()[0])
def testFullYearMatch_gh130(self):
# see https://github.com/fail2ban/fail2ban/pull/130
# yoh: unfortunately this test is not really effective to reproduce the
# situation but left in place to assure consistent behavior
m1 = [2012, 10, 11, 2, 37, 17]
self.assertEqual(
self.__datedetector.getTime('2012/10/11 02:37:17 [error] 18434#0')[:6],
m1)
self.__datedetector.sortTemplate()
# confuse it with year being at the end
for i in xrange(10):
self.assertEqual(
self.__datedetector.getTime('11/10/2012 02:37:17 [error] 18434#0')[:6],
m1)
self.__datedetector.sortTemplate()
# and now back to the original
self.assertEqual(
self.__datedetector.getTime('2012/10/11 02:37:17 [error] 18434#0')[:6],
m1)
# def testDefaultTempate(self):
# self.__datedetector.setDefaultRegex("^\S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}")
# self.__datedetector.setDefaultPattern("%b %d %H:%M:%S")

View File

@ -0,0 +1,3 @@
# per https://github.com/fail2ban/fail2ban/issues/125
# and https://github.com/fail2ban/fail2ban/issues/126
Feb 21 09:21:54 xxx postfix/smtpd[14398]: NOQUEUE: reject: RCPT from example.com[192.0.43.10]: 450 4.7.1 : Helo command rejected: Host not found; from=<> to=<> proto=ESMTP helo=

View File

@ -0,0 +1,17 @@
# yoh: Kept original apache log lines as well, just in case they might come useful
# for (testing) multiline regular expressions which would further constraint
# SOGo log lines
Mar 24 08:58:32 sogod [26818]: <0x0xb8537990[LDAPSource]> <NSException: 0xb87c3008> NAME:LDAPException REASON:operation bind failed: Invalid credentials (0x31) INFO:{login = "uid=hack0r,ou=users,dc=mail,dc=example,dc=org"; }
Mar 24 08:58:32 sogod [26818]: SOGoRootPage Login from '173.194.44.31' for user 'hack0r' might not have worked - password policy: 65535 grace: -1 expire: -1 bound: 0
173.194.44.31 - - [24/Mar/2013:08:58:32 GMT] "POST /SOGo/connect HTTP/1.1" 403 34/38 0.311 - - 2M
Mar 24 08:58:40 sogod [26818]: <0x0xb8537990[LDAPSource]> <NSException: 0xb87bb8d8> NAME:LDAPException REASON:operation bind failed: Invalid credentials (0x31) INFO:{login = "uid=kiddy,ou=users,dc=mail,dc=example,dc=org"; }
Mar 24 08:58:40 sogod [26818]: SOGoRootPage Login from '173.194.44.31' for user 'kiddy' might not have worked - password policy: 65535 grace: -1 expire: -1 bound: 0
173.194.44.31 - - [24/Mar/2013:08:58:40 GMT] "POST /SOGo/connect HTTP/1.1" 403 34/37 0.007 - - 32K
Mar 24 08:58:50 sogod [26818]: <0x0xb8537990[LDAPSource]> <NSException: 0xb87c27f8> NAME:LDAPException REASON:operation bind failed: Invalid credentials (0x31) INFO:{login = "uid=plsBanMe,ou=users,dc=mail,dc=example,dc=org"; }
Mar 24 08:58:50 sogod [26818]: SOGoRootPage Login from '173.194.44.31' for user 'plsBanMe' might not have worked - password policy: 65535 grace: -1 expire: -1 bound: 0
173.194.44.31 - - [24/Mar/2013:08:58:50 GMT] "POST /SOGo/connect HTTP/1.1" 403 34/40 0.008 - - 0
Mar 24 08:58:59 sogod [26818]: <0x0xb8537990[LDAPSource]> <NSException: 0xb87be830> NAME:LDAPException REASON:operation bind failed: Invalid credentials (0x31) INFO:{login = "uid=root,ou=users,dc=mail,dc=example,dc=org"; }
Mar 24 08:58:59 sogod [26818]: SOGoRootPage Login from '173.194.44.31' for user 'root' might not have worked - password policy: 65535 grace: -1 expire: -1 bound: 0
173.194.44.31 - - [24/Mar/2013:08:58:59 GMT] "POST /SOGo/connect HTTP/1.1" 403 34/36 0.007 - - 0
Mar 24 08:59:04 sogod [26818]: <0x0xb8537990[LDAPSource]> <NSException: 0xb87bc088> NAME:LDAPException REASON:operation bind failed: Invalid credentials (0x31) INFO:{login = "uid=admin,ou=users,dc=mail,dc=example,dc=org"; }
Mar 24 08:59:04 sogod [26818]: SOGoRootPage Login from '173.194.44.31' for user 'admin' might not have worked - password policy: 65535 grace: -1 expire: -1 bound: 0

View File

@ -38,11 +38,17 @@ from server.failmanager import FailManagerEmpty
# Useful helpers
#
# yoh: per Steven Hiscocks's insight while troubleshooting
# https://github.com/fail2ban/fail2ban/issues/103#issuecomment-15542836
# adding a sufficiently large buffer might help to guarantee that
# writes happen atomically.
# Overload also for python3 to use utf-8 encoding by default
if sys.version_info >= (3,):
def open_(filename, mode):
return open(filename, mode, encoding='utf-8', errors='ignore')
return open(filename, mode, 50000, encoding='utf-8', errors='ignore')
else:
open_ = open
def open_(filename, mode):
return open(filename, mode, 50000)
def _killfile(f, name):
try:
@ -54,6 +60,11 @@ def _killfile(f, name):
except:
pass
# there might as well be the .bak file
if os.path.exists(name + '.bak'):
_killfile(None, name + '.bak')
def _sleep_4_poll():
"""PollFilter relies on file timestamps - so we might need to
sleep to guarantee that they differ
@ -105,26 +116,29 @@ def _copy_lines_between_files(fin, fout, n=None, skip=0, mode='a', terminal_line
Returns open fout
"""
if sys.version_info[:2] <= (2,4):
if sys.version_info[:2] <= (2,4): # pragma: no cover
# on old Python st_mtime is int, so we should give at least 1 sec so
# polling filter could detect the change
time.sleep(1)
if isinstance(fin, str):
if isinstance(fin, str): # pragma: no branch - only used with str in test cases
fin = open_(fin, 'r')
if isinstance(fout, str):
fout = open_(fout, mode)
# Skip
for i in xrange(skip):
_ = fin.readline()
# Read/Write
# Read
i = 0
lines = []
while n is None or i < n:
l = fin.readline()
if terminal_line is not None and l == terminal_line:
break
fout.write(l)
fout.flush()
lines.append(l)
i += 1
# Write: all at once and flush
if isinstance(fout, str):
fout = open_(fout, mode)
fout.write('\n'.join(lines))
fout.flush()
# to give other threads possibly some time to crunch
time.sleep(0.1)
return fout
@ -330,11 +344,17 @@ def get_monitor_failures_testcase(Filter_):
"""Generator of TestCase's for different filters/backends
"""
# add Filter_'s name so we could easily identify bad cows
testclass_name = tempfile.mktemp(
'fail2ban', 'monitorfailures_%s' % (Filter_.__name__,))
class MonitorFailures(unittest.TestCase):
count = 0
def setUp(self):
"""Call before every test case."""
self.filter = self.name = 'NA'
_, self.name = tempfile.mkstemp('fail2ban', 'monitorfailures')
self.name = '%s-%d' % (testclass_name, self.count)
MonitorFailures.count += 1 # so we have unique filenames across tests
self.file = open_(self.name, 'a')
self.jail = DummyJail()
self.filter = Filter_(self.jail)
@ -357,12 +377,9 @@ def get_monitor_failures_testcase(Filter_):
self.filter.join() # wait for the thread to terminate
#print "D: KILLING THE FILE"
_killfile(self.file, self.name)
#time.sleep(0.2) # Give FS time to ack the removal
pass
def __str__(self):
return "MonitorFailures%s(%s)" \
% (Filter_, hasattr(self, 'name') and self.name or 'tempfile')
def isFilled(self, delay=2.):
"""Wait up to `delay` sec to assure that it was modified or not
"""
@ -385,7 +402,7 @@ def get_monitor_failures_testcase(Filter_):
return not self.isFilled(delay)
def assert_correct_last_attempt(self, failures, count=None):
self.assertTrue(self.isFilled(10)) # give Filter a chance to react
self.assertTrue(self.isFilled(20)) # give Filter a chance to react
_assert_correct_last_attempt(self, self.jail, failures, count=count)
@ -437,9 +454,10 @@ def get_monitor_failures_testcase(Filter_):
# if we move file into a new location while it has been open already
self.file = _copy_lines_between_files(GetFailures.FILENAME_01, self.name,
n=14, mode='w')
self.assertTrue(self.isEmpty(2))
# Poll might need more time
self.assertTrue(self.isEmpty(4 + int(isinstance(self.filter, FilterPoll))*2))
self.assertRaises(FailManagerEmpty, self.filter.failManager.toBan)
self.assertEqual(self.filter.failManager.getFailTotal(), 2) # Fails with Poll from time to time
self.assertEqual(self.filter.failManager.getFailTotal(), 2)
# move aside, but leaving the handle still open...
os.rename(self.name, self.name + '.bak')
@ -494,7 +512,8 @@ def get_monitor_failures_testcase(Filter_):
# yoh: not sure why count here is not 9... TODO
self.assert_correct_last_attempt(GetFailures.FAILURES_01)#, count=9)
MonitorFailures.__name__ = "MonitorFailures<%s>(%s)" \
% (Filter_.__name__, testclass_name) # 'tempfile')
return MonitorFailures

View File

@ -29,6 +29,7 @@ __license__ = "GPL"
import unittest, socket, time, tempfile, os, locale
from server.server import Server
from common.exceptions import UnknownJailException
class StartStop(unittest.TestCase):
@ -49,27 +50,29 @@ class StartStop(unittest.TestCase):
time.sleep(1)
self.__server.stopJail(name)
class TestServer(Server):
def setLogLevel(self, *args, **kwargs):
pass
def setLogTarget(self, *args, **kwargs):
pass
class Transmitter(unittest.TestCase):
class TransmitterBase(unittest.TestCase):
def setUp(self):
"""Call before every test case."""
self.__server = Server()
self.__transm = self.__server._Server__transm
self.__server.setLogTarget("/dev/null")
self.__server.setLogLevel(0)
self.transm = self.server._Server__transm
sock_fd, sock_name = tempfile.mkstemp('fail2ban.sock', 'transmitter')
os.close(sock_fd)
pidfile_fd, pidfile_name = tempfile.mkstemp(
'fail2ban.pid', 'transmitter')
os.close(pidfile_fd)
self.__server.start(sock_name, pidfile_name, force=False)
self.server.start(sock_name, pidfile_name, force=False)
self.jailName = "TestJail1"
self.__server.addJail(self.jailName, "auto")
self.server.addJail(self.jailName, "auto")
def tearDown(self):
"""Call after every test case."""
self.__server.quit()
self.server.quit()
def setGetTest(self, cmd, inValue, outValue=None, jail=None):
setCmd = ["set", cmd, inValue]
@ -80,8 +83,8 @@ class Transmitter(unittest.TestCase):
if outValue is None:
outValue = inValue
self.assertEqual(self.__transm.proceed(setCmd), (0, outValue))
self.assertEqual(self.__transm.proceed(getCmd), (0, outValue))
self.assertEqual(self.transm.proceed(setCmd), (0, outValue))
self.assertEqual(self.transm.proceed(getCmd), (0, outValue))
def setGetTestNOK(self, cmd, inValue, jail=None):
setCmd = ["set", cmd, inValue]
@ -91,30 +94,30 @@ class Transmitter(unittest.TestCase):
getCmd.insert(1, jail)
# Get initial value before trying invalid value
initValue = self.__transm.proceed(getCmd)[1]
self.assertEqual(self.__transm.proceed(setCmd)[0], 1)
initValue = self.transm.proceed(getCmd)[1]
self.assertEqual(self.transm.proceed(setCmd)[0], 1)
# Check after failed set that value is same as previous
self.assertEqual(self.__transm.proceed(getCmd), (0, initValue))
self.assertEqual(self.transm.proceed(getCmd), (0, initValue))
def jailAddDelTest(self, cmd, values, jail):
cmdAdd = "add" + cmd
cmdDel = "del" + cmd
self.assertEqual(
self.__transm.proceed(["get", jail, cmd]), (0, []))
self.transm.proceed(["get", jail, cmd]), (0, []))
for n, value in enumerate(values):
self.assertEqual(
self.__transm.proceed(["set", jail, cmdAdd, value]),
self.transm.proceed(["set", jail, cmdAdd, value]),
(0, values[:n+1]))
self.assertEqual(
self.__transm.proceed(["get", jail, cmd]),
self.transm.proceed(["get", jail, cmd]),
(0, values[:n+1]))
for n, value in enumerate(values):
self.assertEqual(
self.__transm.proceed(["set", jail, cmdDel, value]),
self.transm.proceed(["set", jail, cmdDel, value]),
(0, values[n+1:]))
self.assertEqual(
self.__transm.proceed(["get", jail, cmd]),
self.transm.proceed(["get", jail, cmd]),
(0, values[n+1:]))
def jailAddDelRegexTest(self, cmd, inValues, outValues, jail):
@ -125,87 +128,95 @@ class Transmitter(unittest.TestCase):
outValues = inValues
self.assertEqual(
self.__transm.proceed(["get", jail, cmd]), (0, []))
self.transm.proceed(["get", jail, cmd]), (0, []))
for n, value in enumerate(inValues):
self.assertEqual(
self.__transm.proceed(["set", jail, cmdAdd, value]),
self.transm.proceed(["set", jail, cmdAdd, value]),
(0, outValues[:n+1]))
self.assertEqual(
self.__transm.proceed(["get", jail, cmd]),
self.transm.proceed(["get", jail, cmd]),
(0, outValues[:n+1]))
for n, value in enumerate(inValues):
self.assertEqual(
self.__transm.proceed(["set", jail, cmdDel, 0]), # First item
self.transm.proceed(["set", jail, cmdDel, 0]), # First item
(0, outValues[n+1:]))
self.assertEqual(
self.__transm.proceed(["get", jail, cmd]),
self.transm.proceed(["get", jail, cmd]),
(0, outValues[n+1:]))
class Transmitter(TransmitterBase):
def setUp(self):
self.server = TestServer()
super(Transmitter, self).setUp()
def testStopServer(self):
self.assertEqual(self.transm.proceed(["stop"]), (0, None))
def testPing(self):
self.assertEqual(self.__transm.proceed(["ping"]), (0, "pong"))
self.assertEqual(self.transm.proceed(["ping"]), (0, "pong"))
def testSleep(self):
t0 = time.time()
self.assertEqual(self.__transm.proceed(["sleep", "1"]), (0, None))
self.assertEqual(self.transm.proceed(["sleep", "1"]), (0, None))
t1 = time.time()
# Approx 1 second delay
self.assertAlmostEqual(t1 - t0, 1, places=2)
def testLogTarget(self):
logTargets = []
for _ in xrange(3):
tmpFile = tempfile.mkstemp("fail2ban", "transmitter")
logTargets.append(tmpFile[1])
os.close(tmpFile[0])
for logTarget in logTargets:
self.setGetTest("logtarget", logTarget)
# If path is invalid, do not change logtarget
value = "/this/path/should/not/exist"
self.assertEqual(
self.__transm.proceed(["set", "logtarget", value]),
(0, logTarget)) #NOTE: Shouldn't this return 1
self.assertEqual(
self.__transm.proceed(["get", "logtarget"]), (0, logTargets[-1]))
self.__transm.proceed(["set", "/dev/null"])
for logTarget in logTargets:
os.remove(logTarget)
def testLogLevel(self):
self.setGetTest("loglevel", "4", 4)
self.setGetTest("loglevel", "2", 2)
self.setGetTest("loglevel", "-1", -1)
self.setGetTestNOK("loglevel", "Bird")
def testAddJail(self):
jail2 = "TestJail2"
jail3 = "TestJail3"
jail4 = "TestJail4"
self.assertEqual(
self.__transm.proceed(["add", jail2, "polling"]), (0, jail2))
self.assertEqual(self.__transm.proceed(["add", jail3]), (0, jail3))
self.transm.proceed(["add", jail2, "polling"]), (0, jail2))
self.assertEqual(self.transm.proceed(["add", jail3]), (0, jail3))
self.assertEqual(
self.__transm.proceed(["add", jail4, "invalid backend"])[0], 1)
self.transm.proceed(["add", jail4, "invalid backend"])[0], 1)
self.assertEqual(
self.__transm.proceed(["add", jail4, "auto"]), (0, jail4))
self.transm.proceed(["add", jail4, "auto"]), (0, jail4))
# Duplicate Jail
self.assertEqual(
self.__transm.proceed(["add", self.jailName, "polling"])[0], 1)
self.transm.proceed(["add", self.jailName, "polling"])[0], 1)
# All name is reserved
self.assertEqual(
self.__transm.proceed(["add", "all", "polling"])[0], 1)
self.transm.proceed(["add", "all", "polling"])[0], 1)
def testStartStopJail(self):
self.assertEqual(
self.transm.proceed(["start", self.jailName]), (0, None))
time.sleep(1)
self.assertEqual(
self.transm.proceed(["stop", self.jailName]), (0, None))
self.assertRaises(
UnknownJailException, self.server.isAlive, self.jailName)
def testStartStopAllJail(self):
self.server.addJail("TestJail2", "auto")
self.assertEqual(
self.transm.proceed(["start", self.jailName]), (0, None))
self.assertEqual(
self.transm.proceed(["start", "TestJail2"]), (0, None))
# yoh: workaround for gh-146. I still think that there is some
# race condition and missing locking somewhere, but for now
# giving it a small delay reliably helps to proceed with tests
time.sleep(0.1)
self.assertEqual(self.transm.proceed(["stop", "all"]), (0, None))
time.sleep(1)
self.assertRaises(
UnknownJailException, self.server.isAlive, self.jailName)
self.assertRaises(
UnknownJailException, self.server.isAlive, "TestJail2")
def testJailIdle(self):
self.assertEqual(
self.__transm.proceed(["set", self.jailName, "idle", "on"]),
self.transm.proceed(["set", self.jailName, "idle", "on"]),
(0, True))
self.assertEqual(
self.__transm.proceed(["set", self.jailName, "idle", "off"]),
self.transm.proceed(["set", self.jailName, "idle", "off"]),
(0, False))
self.assertEqual(
self.__transm.proceed(["set", self.jailName, "idle", "CAT"])[0],
0) #NOTE: Should this return 1
self.transm.proceed(["set", self.jailName, "idle", "CAT"])[0],
1)
def testJailFindTime(self):
self.setGetTest("findtime", "120", 120, jail=self.jailName)
@ -227,30 +238,29 @@ class Transmitter(unittest.TestCase):
# Safe default should be "no"
value = "Fish"
self.assertEqual(
self.__transm.proceed(["set", self.jailName, "usedns", value]),
self.transm.proceed(["set", self.jailName, "usedns", value]),
(0, "no"))
def testJailBanIP(self):
self.__server.startJail(self.jailName) # Jail must be started
self.server.startJail(self.jailName) # Jail must be started
self.assertEqual(
self.__transm.proceed(["set", self.jailName, "banip", "127.0.0.1"]),
self.transm.proceed(["set", self.jailName, "banip", "127.0.0.1"]),
(0, "127.0.0.1"))
time.sleep(1) # Give chance to ban
self.assertEqual(
self.__transm.proceed(["set", self.jailName, "banip", "Badger"]),
self.transm.proceed(["set", self.jailName, "banip", "Badger"]),
(0, "Badger")) #NOTE: Is IP address validated? Is DNS Lookup done?
time.sleep(1) # Give chance to ban
# Unban IP
self.assertEqual(
self.__transm.proceed(
self.transm.proceed(
["set", self.jailName, "unbanip", "127.0.0.1"]),
(0, "127.0.0.1"))
# Unban IP which isn't banned
self.assertEqual(
self.__transm.proceed(
["set", self.jailName, "unbanip", "192.168.1.1"]),
(0, "None")) #NOTE: Should this return 1?
self.transm.proceed(
["set", self.jailName, "unbanip", "192.168.1.1"])[0],1)
def testJailMaxRetry(self):
self.setGetTest("maxretry", "5", 5, jail=self.jailName)
@ -278,22 +288,22 @@ class Transmitter(unittest.TestCase):
# Try duplicates
value = "testcases/files/testcase04.log"
self.assertEqual(
self.__transm.proceed(["set", self.jailName, "addlogpath", value]),
self.transm.proceed(["set", self.jailName, "addlogpath", value]),
(0, [value]))
# Will silently ignore duplicate
self.assertEqual(
self.__transm.proceed(["set", self.jailName, "addlogpath", value]),
self.transm.proceed(["set", self.jailName, "addlogpath", value]),
(0, [value]))
self.assertEqual(
self.__transm.proceed(["get", self.jailName, "logpath"]),
self.transm.proceed(["get", self.jailName, "logpath"]),
(0, [value]))
self.assertEqual(
self.__transm.proceed(["set", self.jailName, "dellogpath", value]),
self.transm.proceed(["set", self.jailName, "dellogpath", value]),
(0, []))
# Invalid file
value = "this_file_shouldn't_exist"
result = self.__transm.proceed(
result = self.transm.proceed(
["set", self.jailName, "addlogpath", value])
self.assertTrue(isinstance(result[1], IOError))
@ -311,18 +321,18 @@ class Transmitter(unittest.TestCase):
# Try duplicates
value = "127.0.0.1"
self.assertEqual(
self.__transm.proceed(["set", self.jailName, "addignoreip", value]),
self.transm.proceed(["set", self.jailName, "addignoreip", value]),
(0, [value]))
# Will allow duplicate
#NOTE: Should duplicates be allowed, or silent ignore like logpath?
self.assertEqual(
self.__transm.proceed(["set", self.jailName, "addignoreip", value]),
self.transm.proceed(["set", self.jailName, "addignoreip", value]),
(0, [value, value]))
self.assertEqual(
self.__transm.proceed(["get", self.jailName, "ignoreip"]),
self.transm.proceed(["get", self.jailName, "ignoreip"]),
(0, [value, value]))
self.assertEqual(
self.__transm.proceed(["set", self.jailName, "delignoreip", value]),
self.transm.proceed(["set", self.jailName, "delignoreip", value]),
(0, [value]))
def testJailRegex(self):
@ -341,11 +351,11 @@ class Transmitter(unittest.TestCase):
)
self.assertEqual(
self.__transm.proceed(
["set", self.jailName, "addfailregex", "No host regex"]),
(0, [])) #NOTE: Shouldn't this return 1?
self.transm.proceed(
["set", self.jailName, "addfailregex", "No host regex"])[0],
1)
self.assertEqual(
self.__transm.proceed(
self.transm.proceed(
["set", self.jailName, "addfailregex", 654])[0],
1)
@ -365,21 +375,25 @@ class Transmitter(unittest.TestCase):
)
self.assertEqual(
self.__transm.proceed(
self.transm.proceed(
["set", self.jailName, "addignoreregex", "Invalid [regex"])[0],
1)
self.assertEqual(
self.transm.proceed(
["set", self.jailName, "addignoreregex", 50])[0],
1)
def testStatus(self):
jails = [self.jailName]
self.assertEqual(self.__transm.proceed(["status"]),
self.assertEqual(self.transm.proceed(["status"]),
(0, [('Number of jail', len(jails)), ('Jail list', ", ".join(jails))]))
self.__server.addJail("TestJail2", "auto")
self.server.addJail("TestJail2", "auto")
jails.append("TestJail2")
self.assertEqual(self.__transm.proceed(["status"]),
self.assertEqual(self.transm.proceed(["status"]),
(0, [('Number of jail', len(jails)), ('Jail list', ", ".join(jails))]))
def testJailStatus(self):
self.assertEqual(self.__transm.proceed(["status", self.jailName]),
self.assertEqual(self.transm.proceed(["status", self.jailName]),
(0,
[
('filter', [
@ -414,37 +428,90 @@ class Transmitter(unittest.TestCase):
]
self.assertEqual(
self.__transm.proceed(["set", self.jailName, "addaction", action]),
self.transm.proceed(["set", self.jailName, "addaction", action]),
(0, action))
self.assertEqual(
self.transm.proceed(["get", self.jailName, "addaction", action]),
(0, action))
for cmd, value in zip(cmdList, cmdValueList):
self.assertEqual(
self.__transm.proceed(
self.transm.proceed(
["set", self.jailName, cmd, action, value]),
(0, value))
for cmd, value in zip(cmdList, cmdValueList):
self.assertEqual(
self.__transm.proceed(["get", self.jailName, cmd, action]),
self.transm.proceed(["get", self.jailName, cmd, action]),
(0, value))
self.assertEqual(
self.__transm.proceed(
self.transm.proceed(
["set", self.jailName, "setcinfo", action, "KEY", "VALUE"]),
(0, "VALUE"))
self.assertEqual(
self.__transm.proceed(
self.transm.proceed(
["get", self.jailName, "cinfo", action, "KEY"]),
(0, "VALUE"))
self.assertEqual(
self.__transm.proceed(
self.transm.proceed(
["get", self.jailName, "cinfo", action, "InvalidKey"])[0],
1)
self.assertEqual(
self.__transm.proceed(
self.transm.proceed(
["set", self.jailName, "delcinfo", action, "KEY"]),
(0, None))
self.assertEqual(
self.__transm.proceed(["set", self.jailName, "delaction", action]),
self.transm.proceed(["set", self.jailName, "delaction", action]),
(0, None))
self.assertEqual(
self.__transm.proceed(
["set", self.jailName, "delaction", "Doesn't exist"]),
(0, None)) #NOTE: Should this return 1?
self.transm.proceed(
["set", self.jailName, "delaction", "Doesn't exist"])[0],1)
def testNOK(self):
self.assertEqual(self.transm.proceed(["INVALID", "COMMAND"])[0],1)
def testSetNOK(self):
self.assertEqual(
self.transm.proceed(["set", "INVALID", "COMMAND"])[0],1)
def testGetNOK(self):
self.assertEqual(
self.transm.proceed(["get", "INVALID", "COMMAND"])[0],1)
def testStatusNOK(self):
self.assertEqual(
self.transm.proceed(["status", "INVALID", "COMMAND"])[0],1)
class TransmitterLogging(TransmitterBase):
def setUp(self):
self.server = Server()
self.server.setLogTarget("/dev/null")
self.server.setLogLevel(0)
super(TransmitterLogging, self).setUp()
def testLogTarget(self):
logTargets = []
for _ in xrange(3):
tmpFile = tempfile.mkstemp("fail2ban", "transmitter")
logTargets.append(tmpFile[1])
os.close(tmpFile[0])
for logTarget in logTargets:
self.setGetTest("logtarget", logTarget)
# If path is invalid, do not change logtarget
value = "/this/path/should/not/exist"
self.setGetTestNOK("logtarget", value)
self.transm.proceed(["set", "/dev/null"])
for logTarget in logTargets:
os.remove(logTarget)
self.setGetTest("logtarget", "STDOUT")
self.setGetTest("logtarget", "STDERR")
self.setGetTest("logtarget", "SYSLOG")
def testLogLevel(self):
self.setGetTest("loglevel", "4", 4)
self.setGetTest("loglevel", "2", 2)
self.setGetTest("loglevel", "-1", -1)
self.setGetTest("loglevel", "0", 0)
self.setGetTestNOK("loglevel", "Bird")

View File

@ -0,0 +1,82 @@
# 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.
# Author: Steven Hiscocks
#
# $Revision$
__author__ = "Steven Hiscocks"
__version__ = "$Revision$"
__date__ = "$Date$"
__copyright__ = "Copyright (c) 2013 Steven Hiscocks"
__license__ = "GPL"
import unittest, time, tempfile, os, threading
from server.asyncserver import AsyncServer, AsyncServerException
from client.csocket import CSocket
class Socket(unittest.TestCase):
def setUp(self):
"""Call before every test case."""
self.server = AsyncServer(self)
sock_fd, sock_name = tempfile.mkstemp('fail2ban.sock', 'socket')
os.close(sock_fd)
os.remove(sock_name)
self.sock_name = sock_name
def tearDown(self):
"""Call after every test case."""
@staticmethod
def proceed(message):
"""Test transmitter proceed method which just returns first arg"""
return message
def testSocket(self):
serverThread = threading.Thread(
target=self.server.start, args=(self.sock_name, False))
serverThread.daemon = True
serverThread.start()
time.sleep(1)
client = CSocket(self.sock_name)
testMessage = ["A", "test", "message"]
self.assertEqual(client.send(testMessage), testMessage)
self.server.stop()
serverThread.join(1)
self.assertFalse(os.path.exists(self.sock_name))
def testSocketForce(self):
open(self.sock_name, 'w').close() # Create sock file
# Try to start without force
self.assertRaises(
AsyncServerException, self.server.start, self.sock_name, False)
# Try agin with force set
serverThread = threading.Thread(
target=self.server.start, args=(self.sock_name, True))
serverThread.daemon = True
serverThread.start()
time.sleep(1)
self.server.stop()
serverThread.join(1)
self.assertFalse(os.path.exists(self.sock_name))

101
testcases/utils.py Normal file
View File

@ -0,0 +1,101 @@
# 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.
__author__ = "Yaroslav Halchenko"
__copyright__ = "Copyright (c) 2013 Yaroslav Halchenko"
__license__ = "GPL"
import logging, os, re, traceback
from os.path import basename, dirname
#
# Following "traceback" functions are adopted from PyMVPA distributed
# under MIT/Expat and copyright by PyMVPA developers (i.e. me and
# Michael). Hereby I re-license derivative work on these pieces under GPL
# to stay in line with the main Fail2Ban license
#
def mbasename(s):
"""Custom function to include directory name if filename is too common
Also strip .py at the end
"""
base = basename(s)
if base.endswith('.py'):
base = base[:-3]
if base in set(['base', '__init__']):
base = basename(dirname(s)) + '.' + base
return base
class TraceBack(object):
"""Customized traceback to be included in debug messages
"""
def __init__(self, compress=False):
"""Initialize TrackBack metric
Parameters
----------
compress : bool
if True then prefix common with previous invocation gets
replaced with ...
"""
self.__prev = ""
self.__compress = compress
def __call__(self):
ftb = traceback.extract_stack(limit=100)[:-2]
entries = [[mbasename(x[0]), str(x[1])] for x in ftb]
entries = [ e for e in entries
if not e[0] in ['unittest', 'logging.__init__' ]]
# lets make it more consize
entries_out = [entries[0]]
for entry in entries[1:]:
if entry[0] == entries_out[-1][0]:
entries_out[-1][1] += ',%s' % entry[1]
else:
entries_out.append(entry)
sftb = '>'.join(['%s:%s' % (mbasename(x[0]),
x[1]) for x in entries_out])
if self.__compress:
# lets remove part which is common with previous invocation
prev_next = sftb
common_prefix = os.path.commonprefix((self.__prev, sftb))
common_prefix2 = re.sub('>[^>]*$', '', common_prefix)
if common_prefix2 != "":
sftb = '...' + sftb[len(common_prefix2):]
self.__prev = prev_next
return sftb
class FormatterWithTraceBack(logging.Formatter):
"""Custom formatter which expands %(tb) and %(tbc) with tracebacks
TODO: might need locking in case of compressed tracebacks
"""
def __init__(self, fmt, *args, **kwargs):
logging.Formatter.__init__(self, fmt=fmt, *args, **kwargs)
compress = '%(tbc)s' in fmt
self._tb = TraceBack(compress=compress)
def format(self, record):
record.tbc = record.tb = self._tb()
return logging.Formatter.format(self, record)