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 build
dist
*.pyc *.pyc
htmlcov
.coverage
*.orig
*.rej

View File

@ -8,8 +8,11 @@ python:
- "3.2" - "3.2"
- "3.3" - "3.3"
install: install:
- "pip install pyinotify" - pip install pyinotify
- if [[ $TRAVIS_PYTHON_VERSION == 2.[6-7] ]] || [[ $TRAVIS_PYTHON_VERSION == 3.3 ]]; then pip install -q coveralls; fi
before_script: before_script:
- if [[ $TRAVIS_PYTHON_VERSION == 3* ]]; then ./fail2ban-2to3; fi - if [[ $TRAVIS_PYTHON_VERSION == 3* ]]; then ./fail2ban-2to3; fi
script: 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 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 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 Design
@ -127,12 +213,14 @@ FileContainer
.__pos .__pos
Keeps the position pointer Keeps the position pointer
dnsutils.py
~~~~~~~~~~~
DNSUtils DNSUtils
Utility class for DNS and IP handling Utility class for DNS and IP handling
RF-Note: convert to functions within a separate submodule
filter*.py filter*.py
~~~~~~~~~~ ~~~~~~~~~~
@ -156,3 +244,35 @@ action.py
~~~~~~~~~ ~~~~~~~~~
Takes care about executing start/check/ban/unban/stop commands 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 TODO
THANKS THANKS
COPYING COPYING
DEVELOP
doc/run-rootless.txt
fail2ban-client fail2ban-client
fail2ban-server fail2ban-server
fail2ban-testcases fail2ban-testcases
@ -41,6 +43,7 @@ server/banmanager.py
server/datetemplate.py server/datetemplate.py
server/mytime.py server/mytime.py
server/failregex.py server/failregex.py
testcases/files/testcase-usedns.log
testcases/banmanagertestcase.py testcases/banmanagertestcase.py
testcases/failmanagertestcase.py testcases/failmanagertestcase.py
testcases/clientreadertestcase.py testcases/clientreadertestcase.py
@ -49,6 +52,7 @@ testcases/__init__.py
testcases/datedetectortestcase.py testcases/datedetectortestcase.py
testcases/actiontestcase.py testcases/actiontestcase.py
testcases/servertestcase.py testcases/servertestcase.py
testcases/sockettestcase.py
testcases/files/testcase01.log testcases/files/testcase01.log
testcases/files/testcase02.log testcases/files/testcase02.log
testcases/files/testcase03.log testcases/files/testcase03.log
@ -56,6 +60,7 @@ testcases/files/testcase04.log
setup.py setup.py
setup.cfg setup.cfg
common/__init__.py common/__init__.py
common/exceptions.py
common/helpers.py common/helpers.py
common/version.py common/version.py
common/protocol.py common/protocol.py
@ -87,6 +92,17 @@ config/filter.d/vsftpd.conf
config/filter.d/webmin-auth.conf config/filter.d/webmin-auth.conf
config/filter.d/wuftpd.conf config/filter.d/wuftpd.conf
config/filter.d/xinetd-fail.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/complain.conf
config/action.d/dshield.conf config/action.d/dshield.conf
config/action.d/hostsdeny.conf config/action.d/hostsdeny.conf
@ -109,6 +125,8 @@ config/action.d/sendmail-whois-lines.conf
config/action.d/shorewall.conf config/action.d/shorewall.conf
config/fail2ban.conf config/fail2ban.conf
man/fail2ban-client.1 man/fail2ban-client.1
man/fail2ban.1
man/jail.conf.5
man/fail2ban-client.h2m man/fail2ban-client.h2m
man/fail2ban-server.1 man/fail2ban-server.1
man/fail2ban-server.h2m man/fail2ban-server.h2m

14
README
View File

@ -51,10 +51,12 @@ call fail2ban-server directly.
Configuration: Configuration:
-------------- --------------
You can configure Fail2ban using the files in /etc/fail2ban. It is possible to You can configure Fail2Ban using the files in /etc/fail2ban. It is
configure the server using commands sent to it by fail2ban-client. The available possible to configure the server using commands sent to it by
commands are described in the man page of fail2ban-client. Please refer to it or fail2ban-client. The available commands are described in the
to the website: http://www.fail2ban.org 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: 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. 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 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, Fail2Ban; if not, write to the Free Software Foundation, Inc., 51 Franklin
Suite 330, Boston, MA 02111-1307 USA Street, Fifth Floor, Boston, MA 02110, USA

2
THANKS
View File

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

7
TODO
View File

@ -13,9 +13,12 @@ Legend:
# partially done # partially done
* 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) - 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): class ActionReader(ConfigReader):
def __init__(self, action, name): def __init__(self, action, name, **kwargs):
ConfigReader.__init__(self) ConfigReader.__init__(self, **kwargs)
self.__file = action[0] self.__file = action[0]
self.__cInfo = action[1] self.__cInfo = action[1]
self.__name = name self.__name = name

View File

@ -27,7 +27,7 @@ __date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
import logging, os import glob, logging, os
from configparserinc import SafeConfigParserWithIncludes from configparserinc import SafeConfigParserWithIncludes
from ConfigParser import NoOptionError, NoSectionError from ConfigParser import NoOptionError, NoSectionError
@ -36,33 +36,61 @@ logSys = logging.getLogger("fail2ban.client.config")
class ConfigReader(SafeConfigParserWithIncludes): class ConfigReader(SafeConfigParserWithIncludes):
BASE_DIRECTORY = "/etc/fail2ban/" DEFAULT_BASEDIR = '/etc/fail2ban'
def __init__(self): def __init__(self, basedir=None):
SafeConfigParserWithIncludes.__init__(self) SafeConfigParserWithIncludes.__init__(self)
self.setBaseDir(basedir)
self.__opts = None self.__opts = None
#@staticmethod def setBaseDir(self, basedir):
def setBaseDir(folderName): if basedir is None:
path = folderName.rstrip('/') basedir = ConfigReader.DEFAULT_BASEDIR # stock system location
ConfigReader.BASE_DIRECTORY = path + '/' self._basedir = basedir.rstrip('/')
setBaseDir = staticmethod(setBaseDir)
#@staticmethod def getBaseDir(self):
def getBaseDir(): return self._basedir
return ConfigReader.BASE_DIRECTORY
getBaseDir = staticmethod(getBaseDir)
def read(self, filename): def read(self, filename):
basename = ConfigReader.BASE_DIRECTORY + filename if not (os.path.exists(self._basedir) and os.access(self._basedir, os.R_OK | os.X_OK)):
logSys.debug("Reading " + basename) raise ValueError("Base configuration directory %s either does not exist "
bConf = basename + ".conf" "or is not accessible" % self._basedir)
bLocal = basename + ".local" basename = os.path.join(self._basedir, filename)
if os.path.exists(bConf) or os.path.exists(bLocal): logSys.debug("Reading configs for %s under %s " % (basename, self._basedir))
SafeConfigParserWithIncludes.read(self, [bConf, bLocal]) 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 return True
else: 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 return False
## ##
@ -94,8 +122,8 @@ class ConfigReader(SafeConfigParserWithIncludes):
values[option[1]] = option[2] values[option[1]] = option[2]
except NoOptionError: except NoOptionError:
if not option[2] == None: if not option[2] == None:
logSys.warn("'%s' not defined in '%s'. Using default value" logSys.warn("'%s' not defined in '%s'. Using default one: %r"
% (option[1], sec)) % (option[1], sec, option[2]))
values[option[1]] = option[2] values[option[1]] = option[2]
except ValueError: except ValueError:
logSys.warn("Wrong value for '" + option[1] + "' in '" + sec + logSys.warn("Wrong value for '" + option[1] + "' in '" + sec +

View File

@ -43,15 +43,19 @@ class Configurator:
self.__fail2ban = Fail2banReader() self.__fail2ban = Fail2banReader()
self.__jails = JailsReader() self.__jails = JailsReader()
#@staticmethod def setBaseDir(self, folderName):
def setBaseDir(folderName): self.__fail2ban.setBaseDir(folderName)
ConfigReader.setBaseDir(folderName) self.__jails.setBaseDir(folderName)
setBaseDir = staticmethod(setBaseDir)
#@staticmethod def getBaseDir(self):
def getBaseDir(): fail2ban_basedir = self.__fail2ban.getBaseDir()
return ConfigReader.getBaseDir() jails_basedir = self.__jails.getBaseDir()
getBaseDir = staticmethod(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): def readEarly(self):
self.__fail2ban.read() self.__fail2ban.read()

View File

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

View File

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

View File

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

View File

@ -36,12 +36,20 @@ logSys = logging.getLogger("fail2ban.client.config")
class JailsReader(ConfigReader): class JailsReader(ConfigReader):
def __init__(self): def __init__(self, force_enable=False, **kwargs):
ConfigReader.__init__(self) """
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.__jails = list()
self.__force_enable = force_enable
def read(self): def read(self):
ConfigReader.read(self, "jail") return ConfigReader.read(self, "jail")
def getOptions(self, section = None): def getOptions(self, section = None):
opts = [] opts = []
@ -49,7 +57,7 @@ class JailsReader(ConfigReader):
if section: if section:
# Get the options of a specific jail. # Get the options of a specific jail.
jail = JailReader(section) jail = JailReader(section, basedir=self.getBaseDir(), force_enable=self.__force_enable)
jail.read() jail.read()
ret = jail.getOptions() ret = jail.getOptions()
if ret: if ret:
@ -62,7 +70,7 @@ class JailsReader(ConfigReader):
else: else:
# Get the options of all jails. # Get the options of all jails.
for sec in self.sections(): for sec in self.sections():
jail = JailReader(sec) jail = JailReader(sec, basedir=self.getBaseDir(), force_enable=self.__force_enable)
jail.read() jail.read()
ret = jail.getOptions() ret = jail.getOptions()
if ret: if ret:

View File

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

View File

@ -52,10 +52,7 @@ actioncheck =
# Option: actionban # Option: actionban
# Notes.: command executed when banning an IP. Take care that the # Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights. # command is executed with Fail2Ban user rights.
# Tags: <ip> IP address # Tags: See jail.conf(5) man page
# <failures> number of failures
# <failtime> unix timestamp of the last failure
# <bantime> unix timestamp of the ban time
# Values: CMD # 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))}'` 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 # Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the # Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights. # command is executed with Fail2Ban user rights.
# Tags: <ip> IP address # Tags: See jail.conf(5) man page
# <bantime> unix timestamp of the ban time
# <unbantime> unix timestamp of the unban time
# Values: CMD # Values: CMD
# #
actionunban = actionunban =

View File

@ -54,9 +54,7 @@ actioncheck =
# Option: actionban # Option: actionban
# Notes.: command executed when banning an IP. Take care that the # Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights. # command is executed with Fail2Ban user rights.
# Tags: <ip> IP address # Tags: See jail.conf(5) man page
# <failures> number of failures
# <time> unix timestamp of the ban time
# Values: CMD # Values: CMD
# #
# See http://www.dshield.org/specs.html for more on report format/notes # 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 # Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the # Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights. # command is executed with Fail2Ban user rights.
# Tags: <ip> IP address # Tags: See jail.conf(5) man page
# <failures> number of failures
# <time> unix timestamp of the ban time
# Values: CMD # Values: CMD
# #
actionunban = if [ -f <tmpfile>.first ]; then actionunban = if [ -f <tmpfile>.first ]; then
@ -116,7 +112,7 @@ actionunban = if [ -f <tmpfile>.first ]; then
port = ??? port = ???
# Option: userid # 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. # in a .local file.
# Register at https://secure.dshield.org/register.html # Register at https://secure.dshield.org/register.html
# Values: [ NUM ] Default: 0 # Values: [ NUM ] Default: 0
@ -124,13 +120,13 @@ port = ???
userid = 0 userid = 0
# Option: myip # 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 # either in the jail config or in a .local file unless your PUBLIC IP
# is the first IP assigned to eth0 # is the first IP assigned to eth0
# Values: [ an IP address ] Default: Tries to find the IP address of 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 # 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 # Option: protocol
# Notes.: The protocol over which the attack is happening # 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 # 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 # this is only checked on each ban/unban, and that we always send
# anything in the buffer on shutdown. Must be greater than # anything in the buffer on shutdown. Must be greater than
# <minreportinterval>.
# Values: [ NUM ] Default: 21600 (6 hours) # Values: [ NUM ] Default: 21600 (6 hours)
# #
maxbufferage = 21600 maxbufferage = 21600

View File

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

View File

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

View File

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

View File

@ -32,9 +32,7 @@ actioncheck =
# Option: actionban # Option: actionban
# Notes.: command executed when banning an IP. Take care that the # Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights. # command is executed with Fail2Ban user rights.
# Tags: <ip> IP address # Tags: See jail.conf(5) man page
# <failures> number of failures
# <time> unix timestamp of the ban time
# Values: CMD # Values: CMD
# #
actionban = ipfw add deny tcp from <ip> to <localhost> <port> 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 # Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the # Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights. # command is executed with Fail2Ban user rights.
# Tags: <ip> IP address # Tags: See jail.conf(5) man page
# <failures> number of failures
# <time> unix timestamp of the ban time
# Values: CMD # Values: CMD
# #
actionunban = ipfw delete `ipfw list | grep -i <ip> | awk '{print $1;}'` 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 # Option: actionban
# Notes.: command executed when banning an IP. Take care that the # Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights. # command is executed with Fail2Ban user rights.
# Tags: <ip> IP address # Tags: See jail.conf(5) man page
# <failures> number of failures
# <time> unix timestamp of the ban time
# Values: CMD # Values: CMD
# #
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j DROP 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 # Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the # Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights. # command is executed with Fail2Ban user rights.
# Tags: <ip> IP address # Tags: See jail.conf(5) man page
# <failures> number of failures
# <time> unix timestamp of the ban time
# Values: CMD # Values: CMD
# #
actionunban = iptables -D fail2ban-<name> -s <ip> -j DROP actionunban = iptables -D fail2ban-<name> -s <ip> -j DROP
[Init] [Init]
# Defaut name of the chain # Default name of the chain
# #
name = default name = default

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -49,9 +49,7 @@ actioncheck =
# Option: actionban # Option: actionban
# Notes.: command executed when banning an IP. Take care that the # Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights. # command is executed with Fail2Ban user rights.
# Tags: <ip> IP address # Tags: See jail.conf(5) man page
# <failures> number of failures
# <time> unix timestamp of the ban time
# Values: CMD # Values: CMD
# #
# #
@ -71,9 +69,7 @@ actionban = MNWLOGIN=`perl -e '$s=shift;$s=~s/([\W])/"%%".uc(sprintf("%%2.2x",or
# Option: actionunban # Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the # Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights. # command is executed with Fail2Ban user rights.
# Tags: <ip> IP address # Tags: See jail.conf(5) man page
# <failures> number of failures
# <time> unix timestamp of the ban time
# Values: CMD # Values: CMD
# #
actionunban = actionunban =
@ -102,13 +98,13 @@ mnwlogin =
mnwpass = mnwpass =
# Option: myip # 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 # either in the jail config or in a .local file unless your PUBLIC IP
# is the first IP assigned to eth0 # is the first IP assigned to eth0
# Values: [ an IP address ] Default: Tries to find the IP address of 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 # 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 # Option: protocol
# Notes.: The protocol over which the attack is happening # Notes.: The protocol over which the attack is happening

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -15,6 +15,7 @@
# Values: TEXT # Values: TEXT
# #
failregex = reject: RCPT from (.*)\[<HOST>\]: 554 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 # Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored. # 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"] action = iptables[name=RoundCube, port="http,https"]
logpath = /var/log/roundcube/userlogins 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 # Ban attackers that try to use PHP's URL-fopen() functionality
# through GET/POST variables. - Experimental, with more than a year # through GET/POST variables. - Experimental, with more than a year
# of usage in production environments. # of usage in production environments.
@ -248,7 +262,7 @@ logpath = /var/log/lighttpd/error.log
maxretry = 2 maxretry = 2
# Same as above for mod_auth # Same as above for mod_auth
# It catches wrong authentifications # It catches wrong authentications
[lighttpd-auth] [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. through calls to /sbin/iptables and also to read the logfiles.
Fail2ban can run as an unpriviledged user provided that those two Fail2ban can run as an unprivileged user provided that those two
capabilites are preserved. The idea is to run fail2ban as a normal 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 user (e.g. fail2ban) who belongs to a group which is allowed to read
logfiles. The user should also be allowed to write to logfiles. The user should also be allowed to write to
/proc/net/xt_recent/fail2ban-<name> (name is specified in the iptables /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 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 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 certain user. After the necessary iptables rules are inserted (which
requires root priviledges), blacklisting can be perfomed by an requires root privileges), blacklisting can be performed by an
unpriviledged user. unprivileged user.
Using fail2ban with xt_recent allows smarter filtering than normal Using fail2ban with xt_recent allows smarter filtering than normal
iptables rules with the xt_recent module can provide. iptables rules with the xt_recent module can provide.
The disadvantage is that fail2ban cannot perform the setup by itself, 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. be done through other means.
The primary advantage is obvious: it's generally better to run 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. without the ability to mess up other iptables rules.
The xt_recent-echo jail can be used under the root user without 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: - Create user:

View File

@ -297,7 +297,7 @@ class Fail2banClient:
delta = -1 delta = -1
elif pos < 2: elif pos < 2:
delta = 1 delta = 1
# The server has 30 secondes to start. # The server has 30 seconds to start.
if cnt >= 300: if cnt >= 300:
if self.__conf["verbose"] > 1: if self.__conf["verbose"] > 1:
sys.stdout.write('\n') sys.stdout.write('\n')
@ -380,7 +380,9 @@ class Fail2banClient:
if cmd == "exit" or cmd == "quit": if cmd == "exit" or cmd == "quit":
# Exit # Exit
return True return True
if not cmd == "": if cmd == "help":
self.dispUsage()
elif not cmd == "":
self.__processCommand(shlex.split(cmd)) self.__processCommand(shlex.split(cmd))
except (EOFError, KeyboardInterrupt): except (EOFError, KeyboardInterrupt):
print print
@ -418,7 +420,7 @@ class Fail2banClient:
class ServerExecutionException(Exception): class ServerExecutionException(Exception):
pass pass
if __name__ == "__main__": if __name__ == "__main__": # pragma: no cover - can't test main
client = Fail2banClient() client = Fail2banClient()
# Exit with correct return value # Exit with correct return value
if client.start(sys.argv): if client.start(sys.argv):

View File

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

View File

@ -49,5 +49,5 @@ details.
You should have received a copy of the GNU General Public You should have received a copy of the GNU General Public
License along with Fail2Ban; if not, write to the Free License along with Fail2Ban; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02111-1307 USA Boston, MA 02110, USA

View File

@ -1,12 +1,12 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.36. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.10.
.TH FAIL2BAN-CLIENT "1" "March 2008" "fail2ban-client v0.8.2" "User Commands" .TH FAIL2BAN-CLIENT "1" "March 2013" "fail2ban-client v0.8.8" "User Commands"
.SH NAME .SH NAME
fail2ban-client \- configure and control the server fail2ban-client \- configure and control the server
.SH SYNOPSIS .SH SYNOPSIS
.B fail2ban-client .B fail2ban-client
[\fIOPTIONS\fR] \fI<COMMAND>\fR [\fIOPTIONS\fR] \fI<COMMAND>\fR
.SH DESCRIPTION .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. and bans the corresponding IP addresses using firewall rules.
.SH OPTIONS .SH OPTIONS
.TP .TP
@ -16,6 +16,9 @@ configuration directory
\fB\-s\fR <FILE> \fB\-s\fR <FILE>
socket path socket path
.TP .TP
\fB\-p\fR <FILE>
pidfile path
.TP
\fB\-d\fR \fB\-d\fR
dump configuration. For debugging dump configuration. For debugging
.TP .TP
@ -110,7 +113,7 @@ adds <FILE> to the monitoring list
of <JAIL> of <JAIL>
.TP .TP
\fBset <JAIL> dellogpath <FILE>\fR \fBset <JAIL> dellogpath <FILE>\fR
removes <FILE> to the monitoring removes <FILE> from the monitoring
list of <JAIL> list of <JAIL>
.TP .TP
\fBset <JAIL> addfailregex <REGEX>\fR \fBset <JAIL> addfailregex <REGEX>\fR
@ -140,6 +143,15 @@ back for <JAIL>
sets the number of seconds <TIME> sets the number of seconds <TIME>
a host will be banned for <JAIL> a host will be banned for <JAIL>
.TP .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 \fBset <JAIL> maxretry <RETRY>\fR
sets the number of failures sets the number of failures
<RETRY> before banning the host <RETRY> before banning the host
@ -191,14 +203,6 @@ files for <JAIL>
gets the list of ignored IP gets the list of ignored IP
addresses for <JAIL> addresses for <JAIL>
.TP .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 \fBget <JAIL> failregex\fR
gets the list of regular gets the list of regular
expressions which matches the expressions which matches the
@ -218,6 +222,9 @@ will look back for failures for
gets the time a host is banned for gets the time a host is banned for
<JAIL> <JAIL>
.TP .TP
\fBget <JAIL> usedns\fR
gets the usedns setting for <JAIL>
.TP
\fBget <JAIL> maxretry\fR \fBget <JAIL> maxretry\fR
gets the number of failures gets the number of failures
allowed for <JAIL> allowed for <JAIL>
@ -245,15 +252,19 @@ action <ACT> for <JAIL>
\fBget <JAIL> actionunban <ACT>\fR \fBget <JAIL> actionunban <ACT>\fR
gets the unban command for the gets the unban command for the
action <ACT> for <JAIL> action <ACT> for <JAIL>
.TP
\fBget <JAIL> cinfo <ACT> <KEY>\fR
gets the value for <KEY> for the
action <ACT> for <JAIL>
.SH FILES .SH FILES
\fI/etc/fail2ban/*\fR \fI/etc/fail2ban/*\fR
.SH AUTHOR .SH AUTHOR
Written by Cyril Jaquier <cyril.jaquier@fail2ban.org>. Written by Cyril Jaquier <cyril.jaquier@fail2ban.org>.
Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>. Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>.
.SH "REPORTING BUGS" .SH "REPORTING BUGS"
Report bugs on https://github.com/fail2ban/fail2ban/issues Report bugs to https://github.com/fail2ban/fail2ban/issues
.SH COPYRIGHT .SH COPYRIGHT
Copyright \(co 2004-2008 Cyril Jaquier Copyright \(co 2004\-2008 Cyril Jaquier, 2008\- Fail2Ban Contributors
.br .br
Copyright of modifications held by their respective authors. Copyright of modifications held by their respective authors.
Licensed under the GNU General Public License v2 (GPL). 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. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.10.
.TH FAIL2BAN-REGEX "1" "March 2008" "fail2ban-regex v0.8.2" "User Commands" .TH FAIL2BAN-REGEX "1" "March 2013" "fail2ban-regex v0.8.8" "User Commands"
.SH NAME .SH NAME
fail2ban-regex \- test Fail2ban "failregex" option fail2ban-regex \- test Fail2ban "failregex" option
.SH SYNOPSIS .SH SYNOPSIS
.B fail2ban-regex .B fail2ban-regex
[\fIOPTIONS\fR] \fI<LOG> <REGEX> \fR[\fIIGNOREREGEX\fR] [\fIOPTIONS\fR] \fI<LOG> <REGEX> \fR[\fIIGNOREREGEX\fR]
.SH DESCRIPTION .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. and bans the corresponding IP addresses using firewall rules.
.PP .PP
This tools can test regular expressions for "fail2ban". This tools can test regular expressions for "fail2ban".
@ -17,6 +17,9 @@ display this help message
.TP .TP
\fB\-V\fR, \fB\-\-version\fR \fB\-V\fR, \fB\-\-version\fR
print the version print the version
.TP
\fB\-v\fR, \fB\-\-verbose\fR
verbose output
.SH LOG .SH LOG
.TP .TP
\fBstring\fR \fBstring\fR
@ -42,9 +45,9 @@ path to a filter file (filter.d/sshd.conf)
Written by Cyril Jaquier <cyril.jaquier@fail2ban.org>. Written by Cyril Jaquier <cyril.jaquier@fail2ban.org>.
Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>. Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>.
.SH "REPORTING BUGS" .SH "REPORTING BUGS"
Report bugs on https://github.com/fail2ban/fail2ban/issues Report bugs to https://github.com/fail2ban/fail2ban/issues
.SH COPYRIGHT .SH COPYRIGHT
Copyright \(co 2004-2008 Cyril Jaquier Copyright \(co 2004\-2008 Cyril Jaquier
.br .br
Copyright of modifications held by their respective authors. Copyright of modifications held by their respective authors.
Licensed under the GNU General Public License v2 (GPL). 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. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.10.
.TH FAIL2BAN-SERVER "1" "March 2008" "fail2ban-server v0.8.2" "User Commands" .TH FAIL2BAN-SERVER "1" "March 2013" "fail2ban-server v0.8.8" "User Commands"
.SH NAME .SH NAME
fail2ban-server \- start the server fail2ban-server \- start the server
.SH SYNOPSIS .SH SYNOPSIS
.B fail2ban-server .B fail2ban-server
[\fIOPTIONS\fR] [\fIOPTIONS\fR]
.SH DESCRIPTION .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. and bans the corresponding IP addresses using firewall rules.
.PP .PP
Only use this command for debugging purpose. Start the server with Only use this command for debugging purpose. Start the server with
@ -23,6 +23,9 @@ start in foreground
\fB\-s\fR <FILE> \fB\-s\fR <FILE>
socket path socket path
.TP .TP
\fB\-p\fR <FILE>
pidfile path
.TP
\fB\-x\fR \fB\-x\fR
force execution of the server (remove socket file) force execution of the server (remove socket file)
.TP .TP
@ -35,9 +38,9 @@ print the version
Written by Cyril Jaquier <cyril.jaquier@fail2ban.org>. Written by Cyril Jaquier <cyril.jaquier@fail2ban.org>.
Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>. Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>.
.SH "REPORTING BUGS" .SH "REPORTING BUGS"
Report bugs on https://github.com/fail2ban/fail2ban/issues Report bugs to https://github.com/fail2ban/fail2ban/issues
.SH COPYRIGHT .SH COPYRIGHT
Copyright \(co 2004-2008 Cyril Jaquier Copyright \(co 2004\-2008 Cyril Jaquier, 2008\- Fail2Ban Contributors
.br .br
Copyright of modifications held by their respective authors. Copyright of modifications held by their respective authors.
Licensed under the GNU General Public License v2 (GPL). 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. # Executes a command with preliminary checks and substitutions.
# #
# Before executing any commands, executes the "check" command first # Before executing any commands, executes the "check" command first
# in order to check if prerequirements are met. If this check fails, # in order to check if pre-requirements are met. If this check fails,
# it tries to restore a sane environnement before executing the real # it tries to restore a sane environment before executing the real
# command. # command.
# Replaces "aInfo" and "cInfo" in the query too. # Replaces "aInfo" and "cInfo" in the query too.
# #

View File

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

View File

@ -148,7 +148,7 @@ class AsyncServer(asyncore.dispatcher):
if sys.version_info >= (2, 6): # if python 2.6 or greater... 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") 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 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") logSys.debug("NOT Python 2.6/3.* - asyncore.loop() using poll")
asyncore.loop(use_poll = True) asyncore.loop(use_poll = True)

View File

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

View File

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

View File

@ -105,9 +105,17 @@ class FailManager:
fData.setLastReset(unixTime) fData.setLastReset(unixTime)
fData.setLastTime(unixTime) fData.setLastTime(unixTime)
self.__failList[ip] = fData self.__failList[ip] = fData
logSys.debug("Currently have failures from %d IPs: %s"
% (len(self.__failList), self.__failList.keys()))
self.__failTotal += 1 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: finally:
self.__lock.release() self.__lock.release()

View File

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

View File

@ -39,7 +39,7 @@ logSys = logging.getLogger("fail2ban.filter")
# Log reader class. # Log reader class.
# #
# This class reads a log file and detects login failures or anything else # 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. # a Jail object.
class FilterPoll(FileFilter): class FilterPoll(FileFilter):

View File

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

View File

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

View File

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

View File

@ -3,3 +3,11 @@ install-purelib=/usr/share/fail2ban
[sdist] [sdist]
formats=bztar 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', ('/var/run/fail2ban',
'' ''
),
('/usr/share/doc/fail2ban',
['README', 'DEVELOP', 'doc/run-rootless.txt']
) )
] ]
) )
@ -83,12 +86,6 @@ elements = {
[ [
"fail2ban.py" "fail2ban.py"
], ],
"/usr/lib/fail2ban/firewall/":
[
"iptables.py",
"ipfwadm.py",
"ipfw.py"
],
"/usr/lib/fail2ban/": "/usr/lib/fail2ban/":
[ [
"version.py", "version.py",

View File

@ -49,11 +49,11 @@ class AddFailure(unittest.TestCase):
self.assertFalse(self.__banManager.addBanTicket(self.__ticket)) self.assertFalse(self.__banManager.addBanTicket(self.__ticket))
self.assertEqual(self.__banManager.size(), 1) self.assertEqual(self.__banManager.size(), 1)
def _testInListOK(self): def testInListOK(self):
ticket = BanTicket('193.168.0.128', 1167605999.0) 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) ticket = BanTicket('111.111.1.111', 1167605999.0)
self.assertFalse(self.__banManager.inBanList(ticket)) self.assertFalse(self.__banManager._inBanList(ticket))

View File

@ -17,26 +17,92 @@
# along with Fail2Ban; if not, write to the Free Software # along with Fail2Ban; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# Author: Cyril Jaquier __author__ = "Cyril Jaquier, Yaroslav Halchenko"
# __copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2013 Yaroslav Halchenko"
# $Revision$
__author__ = "Cyril Jaquier"
__version__ = "$Revision$"
__date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
import unittest import os, shutil, tempfile, unittest
from client.configreader import ConfigReader
from client.jailreader import JailReader 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): def setUp(self):
"""Call before every test case.""" """Call before every test case."""
self.d = tempfile.mkdtemp(prefix="f2b-temp")
self.c = ConfigReader(basedir=self.d)
def tearDown(self): def tearDown(self):
"""Call after every test case.""" """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): def testSplitAction(self):
action = "mail-whois[name=SSH]" action = "mail-whois[name=SSH]"
@ -44,3 +110,59 @@ class JailReaderTest(unittest.TestCase):
result = JailReader.splitAction(action) result = JailReader.splitAction(action)
self.assertEquals(expected, result) 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

@ -51,30 +51,44 @@ class DateDetectorTest(unittest.TestCase):
def testGetTime(self): def testGetTime(self):
log = "Jan 23 21:59:59 [sshd] error: PAM: Authentication failure" 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 dateUnix = 1106513999.0
# yoh: testing only up to 6 elements, since the day of the week
self.assertEqual(self.__datedetector.getTime(log), date) # 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) self.assertEqual(self.__datedetector.getUnixTime(log), dateUnix)
def testVariousTimes(self): def testVariousTimes(self):
"""Test detection of various common date/time formats f2b should understand """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 dateUnix = 1106513999.0
for sdate in ( for sdate in (
"Jan 23 21:59:59", "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", "2005.01.23 21:59:59",
"23/01/2005 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 "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" log = sdate + "[sshd] error: PAM: Authentication failure"
# exclude # exclude
# TODO (Yarik is confused): figure out why for above it is # yoh: on [:6] see in above test
# "1" as day of the week which would be Tue, although it
# was Sun
self.assertEqual(self.__datedetector.getTime(log)[:6], date[:6]) self.assertEqual(self.__datedetector.getTime(log)[:6], date[:6])
self.assertEqual(self.__datedetector.getUnixTime(log), dateUnix) self.assertEqual(self.__datedetector.getUnixTime(log), dateUnix)
@ -89,6 +103,27 @@ class DateDetectorTest(unittest.TestCase):
self.assertRaises(ValueError, self.__datedetector._appendTemplate, self.assertRaises(ValueError, self.__datedetector._appendTemplate,
self.__datedetector.getTemplates()[0]) 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): # def testDefaultTempate(self):
# self.__datedetector.setDefaultRegex("^\S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}") # 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") # 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 # 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,): if sys.version_info >= (3,):
def open_(filename, mode): def open_(filename, mode):
return open(filename, mode, encoding='utf-8', errors='ignore') return open(filename, mode, 50000, encoding='utf-8', errors='ignore')
else: else:
open_ = open def open_(filename, mode):
return open(filename, mode, 50000)
def _killfile(f, name): def _killfile(f, name):
try: try:
@ -54,6 +60,11 @@ def _killfile(f, name):
except: except:
pass pass
# there might as well be the .bak file
if os.path.exists(name + '.bak'):
_killfile(None, name + '.bak')
def _sleep_4_poll(): def _sleep_4_poll():
"""PollFilter relies on file timestamps - so we might need to """PollFilter relies on file timestamps - so we might need to
sleep to guarantee that they differ 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 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 # on old Python st_mtime is int, so we should give at least 1 sec so
# polling filter could detect the change # polling filter could detect the change
time.sleep(1) 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') fin = open_(fin, 'r')
if isinstance(fout, str):
fout = open_(fout, mode)
# Skip # Skip
for i in xrange(skip): for i in xrange(skip):
_ = fin.readline() _ = fin.readline()
# Read/Write # Read
i = 0 i = 0
lines = []
while n is None or i < n: while n is None or i < n:
l = fin.readline() l = fin.readline()
if terminal_line is not None and l == terminal_line: if terminal_line is not None and l == terminal_line:
break break
fout.write(l) lines.append(l)
fout.flush()
i += 1 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 # to give other threads possibly some time to crunch
time.sleep(0.1) time.sleep(0.1)
return fout return fout
@ -330,11 +344,17 @@ def get_monitor_failures_testcase(Filter_):
"""Generator of TestCase's for different filters/backends """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): class MonitorFailures(unittest.TestCase):
count = 0
def setUp(self): def setUp(self):
"""Call before every test case.""" """Call before every test case."""
self.filter = self.name = 'NA' 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.file = open_(self.name, 'a')
self.jail = DummyJail() self.jail = DummyJail()
self.filter = Filter_(self.jail) self.filter = Filter_(self.jail)
@ -357,12 +377,9 @@ def get_monitor_failures_testcase(Filter_):
self.filter.join() # wait for the thread to terminate self.filter.join() # wait for the thread to terminate
#print "D: KILLING THE FILE" #print "D: KILLING THE FILE"
_killfile(self.file, self.name) _killfile(self.file, self.name)
#time.sleep(0.2) # Give FS time to ack the removal
pass pass
def __str__(self):
return "MonitorFailures%s(%s)" \
% (Filter_, hasattr(self, 'name') and self.name or 'tempfile')
def isFilled(self, delay=2.): def isFilled(self, delay=2.):
"""Wait up to `delay` sec to assure that it was modified or not """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) return not self.isFilled(delay)
def assert_correct_last_attempt(self, failures, count=None): 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) _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 # 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, self.file = _copy_lines_between_files(GetFailures.FILENAME_01, self.name,
n=14, mode='w') 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.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... # move aside, but leaving the handle still open...
os.rename(self.name, self.name + '.bak') 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 # yoh: not sure why count here is not 9... TODO
self.assert_correct_last_attempt(GetFailures.FAILURES_01)#, count=9) self.assert_correct_last_attempt(GetFailures.FAILURES_01)#, count=9)
MonitorFailures.__name__ = "MonitorFailures<%s>(%s)" \
% (Filter_.__name__, testclass_name) # 'tempfile')
return MonitorFailures return MonitorFailures

View File

@ -29,6 +29,7 @@ __license__ = "GPL"
import unittest, socket, time, tempfile, os, locale import unittest, socket, time, tempfile, os, locale
from server.server import Server from server.server import Server
from common.exceptions import UnknownJailException
class StartStop(unittest.TestCase): class StartStop(unittest.TestCase):
@ -49,27 +50,29 @@ class StartStop(unittest.TestCase):
time.sleep(1) time.sleep(1)
self.__server.stopJail(name) 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): def setUp(self):
"""Call before every test case.""" """Call before every test case."""
self.__server = Server() self.transm = self.server._Server__transm
self.__transm = self.__server._Server__transm
self.__server.setLogTarget("/dev/null")
self.__server.setLogLevel(0)
sock_fd, sock_name = tempfile.mkstemp('fail2ban.sock', 'transmitter') sock_fd, sock_name = tempfile.mkstemp('fail2ban.sock', 'transmitter')
os.close(sock_fd) os.close(sock_fd)
pidfile_fd, pidfile_name = tempfile.mkstemp( pidfile_fd, pidfile_name = tempfile.mkstemp(
'fail2ban.pid', 'transmitter') 'fail2ban.pid', 'transmitter')
os.close(pidfile_fd) 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.jailName = "TestJail1"
self.__server.addJail(self.jailName, "auto") self.server.addJail(self.jailName, "auto")
def tearDown(self): def tearDown(self):
"""Call after every test case.""" """Call after every test case."""
self.__server.quit() self.server.quit()
def setGetTest(self, cmd, inValue, outValue=None, jail=None): def setGetTest(self, cmd, inValue, outValue=None, jail=None):
setCmd = ["set", cmd, inValue] setCmd = ["set", cmd, inValue]
@ -80,8 +83,8 @@ class Transmitter(unittest.TestCase):
if outValue is None: if outValue is None:
outValue = inValue outValue = inValue
self.assertEqual(self.__transm.proceed(setCmd), (0, outValue)) self.assertEqual(self.transm.proceed(setCmd), (0, outValue))
self.assertEqual(self.__transm.proceed(getCmd), (0, outValue)) self.assertEqual(self.transm.proceed(getCmd), (0, outValue))
def setGetTestNOK(self, cmd, inValue, jail=None): def setGetTestNOK(self, cmd, inValue, jail=None):
setCmd = ["set", cmd, inValue] setCmd = ["set", cmd, inValue]
@ -91,30 +94,30 @@ class Transmitter(unittest.TestCase):
getCmd.insert(1, jail) getCmd.insert(1, jail)
# Get initial value before trying invalid value # Get initial value before trying invalid value
initValue = self.__transm.proceed(getCmd)[1] initValue = self.transm.proceed(getCmd)[1]
self.assertEqual(self.__transm.proceed(setCmd)[0], 1) self.assertEqual(self.transm.proceed(setCmd)[0], 1)
# Check after failed set that value is same as previous # 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): def jailAddDelTest(self, cmd, values, jail):
cmdAdd = "add" + cmd cmdAdd = "add" + cmd
cmdDel = "del" + cmd cmdDel = "del" + cmd
self.assertEqual( self.assertEqual(
self.__transm.proceed(["get", jail, cmd]), (0, [])) self.transm.proceed(["get", jail, cmd]), (0, []))
for n, value in enumerate(values): for n, value in enumerate(values):
self.assertEqual( self.assertEqual(
self.__transm.proceed(["set", jail, cmdAdd, value]), self.transm.proceed(["set", jail, cmdAdd, value]),
(0, values[:n+1])) (0, values[:n+1]))
self.assertEqual( self.assertEqual(
self.__transm.proceed(["get", jail, cmd]), self.transm.proceed(["get", jail, cmd]),
(0, values[:n+1])) (0, values[:n+1]))
for n, value in enumerate(values): for n, value in enumerate(values):
self.assertEqual( self.assertEqual(
self.__transm.proceed(["set", jail, cmdDel, value]), self.transm.proceed(["set", jail, cmdDel, value]),
(0, values[n+1:])) (0, values[n+1:]))
self.assertEqual( self.assertEqual(
self.__transm.proceed(["get", jail, cmd]), self.transm.proceed(["get", jail, cmd]),
(0, values[n+1:])) (0, values[n+1:]))
def jailAddDelRegexTest(self, cmd, inValues, outValues, jail): def jailAddDelRegexTest(self, cmd, inValues, outValues, jail):
@ -125,87 +128,95 @@ class Transmitter(unittest.TestCase):
outValues = inValues outValues = inValues
self.assertEqual( self.assertEqual(
self.__transm.proceed(["get", jail, cmd]), (0, [])) self.transm.proceed(["get", jail, cmd]), (0, []))
for n, value in enumerate(inValues): for n, value in enumerate(inValues):
self.assertEqual( self.assertEqual(
self.__transm.proceed(["set", jail, cmdAdd, value]), self.transm.proceed(["set", jail, cmdAdd, value]),
(0, outValues[:n+1])) (0, outValues[:n+1]))
self.assertEqual( self.assertEqual(
self.__transm.proceed(["get", jail, cmd]), self.transm.proceed(["get", jail, cmd]),
(0, outValues[:n+1])) (0, outValues[:n+1]))
for n, value in enumerate(inValues): for n, value in enumerate(inValues):
self.assertEqual( self.assertEqual(
self.__transm.proceed(["set", jail, cmdDel, 0]), # First item self.transm.proceed(["set", jail, cmdDel, 0]), # First item
(0, outValues[n+1:])) (0, outValues[n+1:]))
self.assertEqual( self.assertEqual(
self.__transm.proceed(["get", jail, cmd]), self.transm.proceed(["get", jail, cmd]),
(0, outValues[n+1:])) (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): def testPing(self):
self.assertEqual(self.__transm.proceed(["ping"]), (0, "pong")) self.assertEqual(self.transm.proceed(["ping"]), (0, "pong"))
def testSleep(self): def testSleep(self):
t0 = time.time() t0 = time.time()
self.assertEqual(self.__transm.proceed(["sleep", "1"]), (0, None)) self.assertEqual(self.transm.proceed(["sleep", "1"]), (0, None))
t1 = time.time() t1 = time.time()
# Approx 1 second delay # Approx 1 second delay
self.assertAlmostEqual(t1 - t0, 1, places=2) 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): def testAddJail(self):
jail2 = "TestJail2" jail2 = "TestJail2"
jail3 = "TestJail3" jail3 = "TestJail3"
jail4 = "TestJail4" jail4 = "TestJail4"
self.assertEqual( self.assertEqual(
self.__transm.proceed(["add", jail2, "polling"]), (0, jail2)) self.transm.proceed(["add", jail2, "polling"]), (0, jail2))
self.assertEqual(self.__transm.proceed(["add", jail3]), (0, jail3)) self.assertEqual(self.transm.proceed(["add", jail3]), (0, jail3))
self.assertEqual( self.assertEqual(
self.__transm.proceed(["add", jail4, "invalid backend"])[0], 1) self.transm.proceed(["add", jail4, "invalid backend"])[0], 1)
self.assertEqual( self.assertEqual(
self.__transm.proceed(["add", jail4, "auto"]), (0, jail4)) self.transm.proceed(["add", jail4, "auto"]), (0, jail4))
# Duplicate Jail # Duplicate Jail
self.assertEqual( self.assertEqual(
self.__transm.proceed(["add", self.jailName, "polling"])[0], 1) self.transm.proceed(["add", self.jailName, "polling"])[0], 1)
# All name is reserved # All name is reserved
self.assertEqual( 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): def testJailIdle(self):
self.assertEqual( self.assertEqual(
self.__transm.proceed(["set", self.jailName, "idle", "on"]), self.transm.proceed(["set", self.jailName, "idle", "on"]),
(0, True)) (0, True))
self.assertEqual( self.assertEqual(
self.__transm.proceed(["set", self.jailName, "idle", "off"]), self.transm.proceed(["set", self.jailName, "idle", "off"]),
(0, False)) (0, False))
self.assertEqual( self.assertEqual(
self.__transm.proceed(["set", self.jailName, "idle", "CAT"])[0], self.transm.proceed(["set", self.jailName, "idle", "CAT"])[0],
0) #NOTE: Should this return 1 1)
def testJailFindTime(self): def testJailFindTime(self):
self.setGetTest("findtime", "120", 120, jail=self.jailName) self.setGetTest("findtime", "120", 120, jail=self.jailName)
@ -227,30 +238,29 @@ class Transmitter(unittest.TestCase):
# Safe default should be "no" # Safe default should be "no"
value = "Fish" value = "Fish"
self.assertEqual( self.assertEqual(
self.__transm.proceed(["set", self.jailName, "usedns", value]), self.transm.proceed(["set", self.jailName, "usedns", value]),
(0, "no")) (0, "no"))
def testJailBanIP(self): def testJailBanIP(self):
self.__server.startJail(self.jailName) # Jail must be started self.server.startJail(self.jailName) # Jail must be started
self.assertEqual( 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")) (0, "127.0.0.1"))
time.sleep(1) # Give chance to ban time.sleep(1) # Give chance to ban
self.assertEqual( 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? (0, "Badger")) #NOTE: Is IP address validated? Is DNS Lookup done?
time.sleep(1) # Give chance to ban time.sleep(1) # Give chance to ban
# Unban IP # Unban IP
self.assertEqual( self.assertEqual(
self.__transm.proceed( self.transm.proceed(
["set", self.jailName, "unbanip", "127.0.0.1"]), ["set", self.jailName, "unbanip", "127.0.0.1"]),
(0, "127.0.0.1")) (0, "127.0.0.1"))
# Unban IP which isn't banned # Unban IP which isn't banned
self.assertEqual( self.assertEqual(
self.__transm.proceed( self.transm.proceed(
["set", self.jailName, "unbanip", "192.168.1.1"]), ["set", self.jailName, "unbanip", "192.168.1.1"])[0],1)
(0, "None")) #NOTE: Should this return 1?
def testJailMaxRetry(self): def testJailMaxRetry(self):
self.setGetTest("maxretry", "5", 5, jail=self.jailName) self.setGetTest("maxretry", "5", 5, jail=self.jailName)
@ -278,22 +288,22 @@ class Transmitter(unittest.TestCase):
# Try duplicates # Try duplicates
value = "testcases/files/testcase04.log" value = "testcases/files/testcase04.log"
self.assertEqual( self.assertEqual(
self.__transm.proceed(["set", self.jailName, "addlogpath", value]), self.transm.proceed(["set", self.jailName, "addlogpath", value]),
(0, [value])) (0, [value]))
# Will silently ignore duplicate # Will silently ignore duplicate
self.assertEqual( self.assertEqual(
self.__transm.proceed(["set", self.jailName, "addlogpath", value]), self.transm.proceed(["set", self.jailName, "addlogpath", value]),
(0, [value])) (0, [value]))
self.assertEqual( self.assertEqual(
self.__transm.proceed(["get", self.jailName, "logpath"]), self.transm.proceed(["get", self.jailName, "logpath"]),
(0, [value])) (0, [value]))
self.assertEqual( self.assertEqual(
self.__transm.proceed(["set", self.jailName, "dellogpath", value]), self.transm.proceed(["set", self.jailName, "dellogpath", value]),
(0, [])) (0, []))
# Invalid file # Invalid file
value = "this_file_shouldn't_exist" value = "this_file_shouldn't_exist"
result = self.__transm.proceed( result = self.transm.proceed(
["set", self.jailName, "addlogpath", value]) ["set", self.jailName, "addlogpath", value])
self.assertTrue(isinstance(result[1], IOError)) self.assertTrue(isinstance(result[1], IOError))
@ -311,18 +321,18 @@ class Transmitter(unittest.TestCase):
# Try duplicates # Try duplicates
value = "127.0.0.1" value = "127.0.0.1"
self.assertEqual( self.assertEqual(
self.__transm.proceed(["set", self.jailName, "addignoreip", value]), self.transm.proceed(["set", self.jailName, "addignoreip", value]),
(0, [value])) (0, [value]))
# Will allow duplicate # Will allow duplicate
#NOTE: Should duplicates be allowed, or silent ignore like logpath? #NOTE: Should duplicates be allowed, or silent ignore like logpath?
self.assertEqual( self.assertEqual(
self.__transm.proceed(["set", self.jailName, "addignoreip", value]), self.transm.proceed(["set", self.jailName, "addignoreip", value]),
(0, [value, value])) (0, [value, value]))
self.assertEqual( self.assertEqual(
self.__transm.proceed(["get", self.jailName, "ignoreip"]), self.transm.proceed(["get", self.jailName, "ignoreip"]),
(0, [value, value])) (0, [value, value]))
self.assertEqual( self.assertEqual(
self.__transm.proceed(["set", self.jailName, "delignoreip", value]), self.transm.proceed(["set", self.jailName, "delignoreip", value]),
(0, [value])) (0, [value]))
def testJailRegex(self): def testJailRegex(self):
@ -341,11 +351,11 @@ class Transmitter(unittest.TestCase):
) )
self.assertEqual( self.assertEqual(
self.__transm.proceed( self.transm.proceed(
["set", self.jailName, "addfailregex", "No host regex"]), ["set", self.jailName, "addfailregex", "No host regex"])[0],
(0, [])) #NOTE: Shouldn't this return 1? 1)
self.assertEqual( self.assertEqual(
self.__transm.proceed( self.transm.proceed(
["set", self.jailName, "addfailregex", 654])[0], ["set", self.jailName, "addfailregex", 654])[0],
1) 1)
@ -365,21 +375,25 @@ class Transmitter(unittest.TestCase):
) )
self.assertEqual( 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], ["set", self.jailName, "addignoreregex", 50])[0],
1) 1)
def testStatus(self): def testStatus(self):
jails = [self.jailName] 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))])) (0, [('Number of jail', len(jails)), ('Jail list', ", ".join(jails))]))
self.__server.addJail("TestJail2", "auto") self.server.addJail("TestJail2", "auto")
jails.append("TestJail2") 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))])) (0, [('Number of jail', len(jails)), ('Jail list', ", ".join(jails))]))
def testJailStatus(self): def testJailStatus(self):
self.assertEqual(self.__transm.proceed(["status", self.jailName]), self.assertEqual(self.transm.proceed(["status", self.jailName]),
(0, (0,
[ [
('filter', [ ('filter', [
@ -414,37 +428,90 @@ class Transmitter(unittest.TestCase):
] ]
self.assertEqual( 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)) (0, action))
for cmd, value in zip(cmdList, cmdValueList): for cmd, value in zip(cmdList, cmdValueList):
self.assertEqual( self.assertEqual(
self.__transm.proceed( self.transm.proceed(
["set", self.jailName, cmd, action, value]), ["set", self.jailName, cmd, action, value]),
(0, value)) (0, value))
for cmd, value in zip(cmdList, cmdValueList): for cmd, value in zip(cmdList, cmdValueList):
self.assertEqual( self.assertEqual(
self.__transm.proceed(["get", self.jailName, cmd, action]), self.transm.proceed(["get", self.jailName, cmd, action]),
(0, value)) (0, value))
self.assertEqual( self.assertEqual(
self.__transm.proceed( self.transm.proceed(
["set", self.jailName, "setcinfo", action, "KEY", "VALUE"]), ["set", self.jailName, "setcinfo", action, "KEY", "VALUE"]),
(0, "VALUE")) (0, "VALUE"))
self.assertEqual( self.assertEqual(
self.__transm.proceed( self.transm.proceed(
["get", self.jailName, "cinfo", action, "KEY"]), ["get", self.jailName, "cinfo", action, "KEY"]),
(0, "VALUE")) (0, "VALUE"))
self.assertEqual( self.assertEqual(
self.__transm.proceed( self.transm.proceed(
["get", self.jailName, "cinfo", action, "InvalidKey"])[0], ["get", self.jailName, "cinfo", action, "InvalidKey"])[0],
1) 1)
self.assertEqual( self.assertEqual(
self.__transm.proceed( self.transm.proceed(
["set", self.jailName, "delcinfo", action, "KEY"]), ["set", self.jailName, "delcinfo", action, "KEY"]),
(0, None)) (0, None))
self.assertEqual( self.assertEqual(
self.__transm.proceed(["set", self.jailName, "delaction", action]), self.transm.proceed(["set", self.jailName, "delaction", action]),
(0, None)) (0, None))
self.assertEqual( self.assertEqual(
self.__transm.proceed( self.transm.proceed(
["set", self.jailName, "delaction", "Doesn't exist"]), ["set", self.jailName, "delaction", "Doesn't exist"])[0],1)
(0, None)) #NOTE: Should this return 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)