mirror of https://github.com/fail2ban/fail2ban
Merge branch 'master' into py3
Conflicts: .travis.yml server/datetemplate.py server/server.py testcases/filtertestcase.pypull/128/merge^2
commit
77aa523f22
|
@ -0,0 +1,4 @@
|
|||
|
||||
[run]
|
||||
branch = True
|
||||
omit = /usr*
|
|
@ -1,3 +1,8 @@
|
|||
*~
|
||||
build
|
||||
dist
|
||||
*.pyc
|
||||
htmlcov
|
||||
.coverage
|
||||
*.orig
|
||||
*.rej
|
||||
|
|
|
@ -8,8 +8,11 @@ python:
|
|||
- "3.2"
|
||||
- "3.3"
|
||||
install:
|
||||
- "pip install pyinotify"
|
||||
- pip install pyinotify
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.[6-7] ]] || [[ $TRAVIS_PYTHON_VERSION == 3.3 ]]; then pip install -q coveralls; fi
|
||||
before_script:
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 3* ]]; then ./fail2ban-2to3; fi
|
||||
script:
|
||||
- python ./fail2ban-testcases
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.[6-7] ]] || [[ $TRAVIS_PYTHON_VERSION == 3.3 ]]; then coverage run --rcfile=.travis_coveragerc fail2ban-testcases; else python ./fail2ban-testcases; fi
|
||||
after_script:
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.[6-7] ]] || [[ $TRAVIS_PYTHON_VERSION == 3.3 ]]; then coveralls; fi
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
[run]
|
||||
branch = True
|
||||
omit =
|
||||
/usr/*
|
||||
/home/travis/virtualenv/*
|
||||
server/filtergamin.py
|
132
DEVELOP
132
DEVELOP
|
@ -24,14 +24,100 @@ Request feature. You can find more details on the Fail2Ban wiki
|
|||
Testing
|
||||
=======
|
||||
|
||||
Existing tests can be run by executing `fail2ban-testcases`.
|
||||
Existing tests can be run by executing `fail2ban-testcases`. This has options
|
||||
like --log-level that will probably be useful. `fail2ban-testcases --help` for
|
||||
full options.
|
||||
|
||||
Test cases should cover all usual cases, all exception cases and all inside
|
||||
/ outside boundary conditions.
|
||||
|
||||
Test cases should cover all branches. The coverage tool will help identify
|
||||
missing branches. Also see http://nedbatchelder.com/code/coverage/branch.html
|
||||
for more details.
|
||||
|
||||
Install the package python-coverage to visualise your test coverage. Run the
|
||||
following (note: on Debian-based systems, the script is called
|
||||
`python-coverage`):
|
||||
|
||||
coverage run fail2ban-testcases
|
||||
coverage html
|
||||
|
||||
Then look at htmlcov/index.html and see how much coverage your test cases
|
||||
exert over the codebase. Full coverage is a good thing however it may not be
|
||||
complete. Try to ensure tests cover as many independent paths through the
|
||||
code.
|
||||
|
||||
Manual Execution. To run in a development environment do:
|
||||
|
||||
./fail2ban-client -c config/ -s /tmp/f2b.sock -i start
|
||||
|
||||
some quick commands:
|
||||
|
||||
status
|
||||
add test pyinotify
|
||||
status test
|
||||
set test addaction iptables
|
||||
set test actionban iptables echo <ip> <cidr> >> /tmp/ban
|
||||
set test actionunban iptables echo <ip> <cidr> >> /tmp/unban
|
||||
get test actionban iptables
|
||||
get test actionunban iptables
|
||||
set test banip 192.168.2.2
|
||||
status test
|
||||
|
||||
|
||||
Documentation about creating tests (when tests are required and some guidelines
|
||||
for creating good tests) will be added soon.
|
||||
|
||||
Coding Standards
|
||||
================
|
||||
Coming Soon.
|
||||
|
||||
Style
|
||||
-----
|
||||
|
||||
Please use tabs for now. Keep to 80 columns, at least for readable text.
|
||||
|
||||
Tests
|
||||
-----
|
||||
|
||||
Add tests. They should test all the code you add in a meaning way.
|
||||
|
||||
Coverage
|
||||
--------
|
||||
|
||||
Test coverage should always increase as you add code.
|
||||
|
||||
You may use "# pragma: no cover" in the code for branches of code that support
|
||||
older versions on python. For all other uses of "pragma: no cover" or
|
||||
"pragma: no branch" document the reason why its not covered. "I haven't written
|
||||
a test case" isn't a sufficient reason.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
Ensure this documentation is up to date after changes. Also ensure that the man
|
||||
pages still are accurate. Ensure that there is sufficient documentation for
|
||||
your new features to be used.
|
||||
|
||||
Bugs
|
||||
----
|
||||
|
||||
Remove them and don't add any more.
|
||||
|
||||
Git
|
||||
---
|
||||
|
||||
Use the following tags in your commit messages:
|
||||
|
||||
'BF:' for bug fixes
|
||||
'DOC:' for documentation fixes
|
||||
'ENH:' for enhancements
|
||||
'TST:' for commits concerning tests only (thus not touching the main code-base)
|
||||
|
||||
Multiple tags could be joined with +, e.g. "BF+TST:".
|
||||
|
||||
Adding Actions
|
||||
--------------
|
||||
|
||||
If you add an action.d/*.conf file also add a example in config/jail.conf
|
||||
with enabled=false and maxretry=5 for ssh.
|
||||
|
||||
|
||||
Design
|
||||
|
@ -127,12 +213,14 @@ FileContainer
|
|||
.__pos
|
||||
Keeps the position pointer
|
||||
|
||||
|
||||
dnsutils.py
|
||||
~~~~~~~~~~~
|
||||
|
||||
DNSUtils
|
||||
|
||||
Utility class for DNS and IP handling
|
||||
|
||||
RF-Note: convert to functions within a separate submodule
|
||||
|
||||
|
||||
filter*.py
|
||||
~~~~~~~~~~
|
||||
|
@ -156,3 +244,35 @@ action.py
|
|||
~~~~~~~~~
|
||||
|
||||
Takes care about executing start/check/ban/unban/stop commands
|
||||
|
||||
|
||||
Releasing
|
||||
=========
|
||||
|
||||
# Ensure the version is correct in ./common/version.py
|
||||
|
||||
# Add/finalize the corresponding entry in the ChangeLog
|
||||
|
||||
# Update man pages
|
||||
|
||||
(cd man ; ./generate-man )
|
||||
git commit -m 'update man pages for release' man/*
|
||||
|
||||
# Make sure the tests pass
|
||||
|
||||
./fail2ban-testcases-all
|
||||
|
||||
# Prepare/upload source and rpm binary distributions
|
||||
|
||||
python setup.py check
|
||||
python setup.py sdist
|
||||
python setup.py bdist_rpm
|
||||
python setup.py upload
|
||||
|
||||
# Run the following and update the wiki with output:
|
||||
|
||||
python -c 'import common.protocol; common.protocol.printWiki()'
|
||||
|
||||
# Email users and development list of release
|
||||
|
||||
TODO notifying distributors etc.
|
||||
|
|
18
MANIFEST
18
MANIFEST
|
@ -3,6 +3,8 @@ ChangeLog
|
|||
TODO
|
||||
THANKS
|
||||
COPYING
|
||||
DEVELOP
|
||||
doc/run-rootless.txt
|
||||
fail2ban-client
|
||||
fail2ban-server
|
||||
fail2ban-testcases
|
||||
|
@ -41,6 +43,7 @@ server/banmanager.py
|
|||
server/datetemplate.py
|
||||
server/mytime.py
|
||||
server/failregex.py
|
||||
testcases/files/testcase-usedns.log
|
||||
testcases/banmanagertestcase.py
|
||||
testcases/failmanagertestcase.py
|
||||
testcases/clientreadertestcase.py
|
||||
|
@ -49,6 +52,7 @@ testcases/__init__.py
|
|||
testcases/datedetectortestcase.py
|
||||
testcases/actiontestcase.py
|
||||
testcases/servertestcase.py
|
||||
testcases/sockettestcase.py
|
||||
testcases/files/testcase01.log
|
||||
testcases/files/testcase02.log
|
||||
testcases/files/testcase03.log
|
||||
|
@ -56,6 +60,7 @@ testcases/files/testcase04.log
|
|||
setup.py
|
||||
setup.cfg
|
||||
common/__init__.py
|
||||
common/exceptions.py
|
||||
common/helpers.py
|
||||
common/version.py
|
||||
common/protocol.py
|
||||
|
@ -87,6 +92,17 @@ config/filter.d/vsftpd.conf
|
|||
config/filter.d/webmin-auth.conf
|
||||
config/filter.d/wuftpd.conf
|
||||
config/filter.d/xinetd-fail.conf
|
||||
config/filter.d/asterisk.conf
|
||||
config/filter.d/dovecot.conf
|
||||
config/filter.d/dropbear.conf
|
||||
config/filter.d/lighttpd-auth.conf
|
||||
config/filter.d/recidive.conf
|
||||
config/filter.d/roundcube-auth.conf
|
||||
config/action.d/dummy.conf
|
||||
config/action.d/iptables-ipset-proto4.conf
|
||||
config/action.d/iptables-ipset-proto6.conf
|
||||
config/action.d/iptables-xt_recent-echo.conf
|
||||
config/action.d/route.conf
|
||||
config/action.d/complain.conf
|
||||
config/action.d/dshield.conf
|
||||
config/action.d/hostsdeny.conf
|
||||
|
@ -109,6 +125,8 @@ config/action.d/sendmail-whois-lines.conf
|
|||
config/action.d/shorewall.conf
|
||||
config/fail2ban.conf
|
||||
man/fail2ban-client.1
|
||||
man/fail2ban.1
|
||||
man/jail.conf.5
|
||||
man/fail2ban-client.h2m
|
||||
man/fail2ban-server.1
|
||||
man/fail2ban-server.h2m
|
||||
|
|
14
README
14
README
|
@ -51,10 +51,12 @@ call fail2ban-server directly.
|
|||
Configuration:
|
||||
--------------
|
||||
|
||||
You can configure Fail2ban using the files in /etc/fail2ban. It is possible to
|
||||
configure the server using commands sent to it by fail2ban-client. The available
|
||||
commands are described in the man page of fail2ban-client. Please refer to it or
|
||||
to the website: http://www.fail2ban.org
|
||||
You can configure Fail2Ban using the files in /etc/fail2ban. It is
|
||||
possible to configure the server using commands sent to it by
|
||||
fail2ban-client. The available commands are described in the
|
||||
fail2ban-client(1) manpage. Also see fail2ban(1) manpage for further
|
||||
references and find even more documentation on the website:
|
||||
http://www.fail2ban.org
|
||||
|
||||
Contact:
|
||||
--------
|
||||
|
@ -91,5 +93,5 @@ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Fail2Ban; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
|
||||
Suite 330, Boston, MA 02111-1307 USA
|
||||
Fail2Ban; if not, write to the Free Software Foundation, Inc., 51 Franklin
|
||||
Street, Fifth Floor, Boston, MA 02110, USA
|
||||
|
|
2
THANKS
2
THANKS
|
@ -13,6 +13,7 @@ Christian Rauch
|
|||
Christoph Haas
|
||||
Christos Psonis
|
||||
Daniel B. Cid
|
||||
Daniel Black
|
||||
David Nutter
|
||||
Eric Gerbier
|
||||
Guillaume Delvit
|
||||
|
@ -38,6 +39,7 @@ Robert Edeker
|
|||
Russell Odom
|
||||
Sireyessire
|
||||
Stephen Gildea
|
||||
Steven Hiscocks
|
||||
Tom Pike
|
||||
Tyler
|
||||
Vaclav Misek
|
||||
|
|
7
TODO
7
TODO
|
@ -13,9 +13,12 @@ Legend:
|
|||
# partially done
|
||||
* done
|
||||
|
||||
- Removed relative imports
|
||||
- Run tests though all filters/examples files - (see sshd example file) as unit
|
||||
test
|
||||
|
||||
- Cleanup fail2ban-client and fail2ban-server. Move code to server/ and client/
|
||||
* Removed relative imports
|
||||
|
||||
* Cleanup fail2ban-client and fail2ban-server. Move code to server/ and client/
|
||||
|
||||
- Add timeout to external commands (signal alarm, watchdog thread, etc)
|
||||
|
||||
|
|
|
@ -35,8 +35,8 @@ logSys = logging.getLogger("fail2ban.client.config")
|
|||
|
||||
class ActionReader(ConfigReader):
|
||||
|
||||
def __init__(self, action, name):
|
||||
ConfigReader.__init__(self)
|
||||
def __init__(self, action, name, **kwargs):
|
||||
ConfigReader.__init__(self, **kwargs)
|
||||
self.__file = action[0]
|
||||
self.__cInfo = action[1]
|
||||
self.__name = name
|
||||
|
|
|
@ -27,7 +27,7 @@ __date__ = "$Date$"
|
|||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
import logging, os
|
||||
import glob, logging, os
|
||||
from configparserinc import SafeConfigParserWithIncludes
|
||||
from ConfigParser import NoOptionError, NoSectionError
|
||||
|
||||
|
@ -36,33 +36,61 @@ logSys = logging.getLogger("fail2ban.client.config")
|
|||
|
||||
class ConfigReader(SafeConfigParserWithIncludes):
|
||||
|
||||
BASE_DIRECTORY = "/etc/fail2ban/"
|
||||
DEFAULT_BASEDIR = '/etc/fail2ban'
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, basedir=None):
|
||||
SafeConfigParserWithIncludes.__init__(self)
|
||||
self.setBaseDir(basedir)
|
||||
self.__opts = None
|
||||
|
||||
#@staticmethod
|
||||
def setBaseDir(folderName):
|
||||
path = folderName.rstrip('/')
|
||||
ConfigReader.BASE_DIRECTORY = path + '/'
|
||||
setBaseDir = staticmethod(setBaseDir)
|
||||
def setBaseDir(self, basedir):
|
||||
if basedir is None:
|
||||
basedir = ConfigReader.DEFAULT_BASEDIR # stock system location
|
||||
self._basedir = basedir.rstrip('/')
|
||||
|
||||
#@staticmethod
|
||||
def getBaseDir():
|
||||
return ConfigReader.BASE_DIRECTORY
|
||||
getBaseDir = staticmethod(getBaseDir)
|
||||
def getBaseDir(self):
|
||||
return self._basedir
|
||||
|
||||
def read(self, filename):
|
||||
basename = ConfigReader.BASE_DIRECTORY + filename
|
||||
logSys.debug("Reading " + basename)
|
||||
bConf = basename + ".conf"
|
||||
bLocal = basename + ".local"
|
||||
if os.path.exists(bConf) or os.path.exists(bLocal):
|
||||
SafeConfigParserWithIncludes.read(self, [bConf, bLocal])
|
||||
if not (os.path.exists(self._basedir) and os.access(self._basedir, os.R_OK | os.X_OK)):
|
||||
raise ValueError("Base configuration directory %s either does not exist "
|
||||
"or is not accessible" % self._basedir)
|
||||
basename = os.path.join(self._basedir, filename)
|
||||
logSys.debug("Reading configs for %s under %s " % (basename, self._basedir))
|
||||
config_files = [ basename + ".conf",
|
||||
basename + ".local" ]
|
||||
|
||||
# choose only existing ones
|
||||
config_files = filter(os.path.exists, config_files)
|
||||
|
||||
# possible further customizations under a .conf.d directory
|
||||
config_dir = basename + '.d'
|
||||
if os.path.exists(config_dir):
|
||||
if os.path.isdir(config_dir) and os.access(config_dir, os.X_OK | os.R_OK):
|
||||
# files must carry .conf suffix as well
|
||||
config_files += sorted(glob.glob('%s/*.conf' % config_dir))
|
||||
else:
|
||||
logSys.warn("%s exists but not a directory or not accessible"
|
||||
% config_dir)
|
||||
|
||||
# check if files are accessible, warn if any is not accessible
|
||||
# and remove it from the list
|
||||
config_files_accessible = []
|
||||
for f in config_files:
|
||||
if os.access(f, os.R_OK):
|
||||
config_files_accessible.append(f)
|
||||
else:
|
||||
logSys.warn("%s exists but not accessible - skipping" % f)
|
||||
|
||||
if len(config_files_accessible):
|
||||
# at least one config exists and accessible
|
||||
SafeConfigParserWithIncludes.read(self, config_files_accessible)
|
||||
return True
|
||||
else:
|
||||
logSys.error(bConf + " and " + bLocal + " do not exist")
|
||||
logSys.error("Found no accessible config files for %r " % filename
|
||||
+ (["under %s" % self.getBaseDir(),
|
||||
"among existing ones: " + ', '.join(config_files)][bool(len(config_files))]))
|
||||
|
||||
return False
|
||||
|
||||
##
|
||||
|
@ -94,8 +122,8 @@ class ConfigReader(SafeConfigParserWithIncludes):
|
|||
values[option[1]] = option[2]
|
||||
except NoOptionError:
|
||||
if not option[2] == None:
|
||||
logSys.warn("'%s' not defined in '%s'. Using default value"
|
||||
% (option[1], sec))
|
||||
logSys.warn("'%s' not defined in '%s'. Using default one: %r"
|
||||
% (option[1], sec, option[2]))
|
||||
values[option[1]] = option[2]
|
||||
except ValueError:
|
||||
logSys.warn("Wrong value for '" + option[1] + "' in '" + sec +
|
||||
|
|
|
@ -43,15 +43,19 @@ class Configurator:
|
|||
self.__fail2ban = Fail2banReader()
|
||||
self.__jails = JailsReader()
|
||||
|
||||
#@staticmethod
|
||||
def setBaseDir(folderName):
|
||||
ConfigReader.setBaseDir(folderName)
|
||||
setBaseDir = staticmethod(setBaseDir)
|
||||
def setBaseDir(self, folderName):
|
||||
self.__fail2ban.setBaseDir(folderName)
|
||||
self.__jails.setBaseDir(folderName)
|
||||
|
||||
#@staticmethod
|
||||
def getBaseDir():
|
||||
return ConfigReader.getBaseDir()
|
||||
getBaseDir = staticmethod(getBaseDir)
|
||||
def getBaseDir(self):
|
||||
fail2ban_basedir = self.__fail2ban.getBaseDir()
|
||||
jails_basedir = self.__jails.getBaseDir()
|
||||
if fail2ban_basedir != jails_basedir:
|
||||
logSys.error("fail2ban.conf and jails.conf readers have differing "
|
||||
"basedirs: %r and %r. "
|
||||
"Returning the one for fail2ban.conf"
|
||||
% (fail2ban_basedir, jails_basedir))
|
||||
return fail2ban_basedir
|
||||
|
||||
def readEarly(self):
|
||||
self.__fail2ban.read()
|
||||
|
|
|
@ -35,8 +35,8 @@ logSys = logging.getLogger("fail2ban.client.config")
|
|||
|
||||
class Fail2banReader(ConfigReader):
|
||||
|
||||
def __init__(self):
|
||||
ConfigReader.__init__(self)
|
||||
def __init__(self, **kwargs):
|
||||
ConfigReader.__init__(self, **kwargs)
|
||||
|
||||
def read(self):
|
||||
ConfigReader.read(self, "fail2ban")
|
||||
|
|
|
@ -35,8 +35,8 @@ logSys = logging.getLogger("fail2ban.client.config")
|
|||
|
||||
class FilterReader(ConfigReader):
|
||||
|
||||
def __init__(self, fileName, name):
|
||||
ConfigReader.__init__(self)
|
||||
def __init__(self, fileName, name, **kwargs):
|
||||
ConfigReader.__init__(self, **kwargs)
|
||||
self.__file = fileName
|
||||
self.__name = name
|
||||
|
||||
|
|
|
@ -40,10 +40,11 @@ class JailReader(ConfigReader):
|
|||
|
||||
actionCRE = re.compile("^((?:\w|-|_|\.)+)(?:\[(.*)\])?$")
|
||||
|
||||
def __init__(self, name):
|
||||
ConfigReader.__init__(self)
|
||||
def __init__(self, name, force_enable=False, **kwargs):
|
||||
ConfigReader.__init__(self, **kwargs)
|
||||
self.__name = name
|
||||
self.__filter = None
|
||||
self.__force_enable = force_enable
|
||||
self.__actions = list()
|
||||
|
||||
def setName(self, value):
|
||||
|
@ -53,10 +54,10 @@ class JailReader(ConfigReader):
|
|||
return self.__name
|
||||
|
||||
def read(self):
|
||||
ConfigReader.read(self, "jail")
|
||||
return ConfigReader.read(self, "jail")
|
||||
|
||||
def isEnabled(self):
|
||||
return self.__opts["enabled"]
|
||||
return self.__force_enable or self.__opts["enabled"]
|
||||
|
||||
def getOptions(self):
|
||||
opts = [["bool", "enabled", "false"],
|
||||
|
@ -76,7 +77,8 @@ class JailReader(ConfigReader):
|
|||
|
||||
if self.isEnabled():
|
||||
# Read filter
|
||||
self.__filter = FilterReader(self.__opts["filter"], self.__name)
|
||||
self.__filter = FilterReader(self.__opts["filter"], self.__name,
|
||||
basedir=self.getBaseDir())
|
||||
ret = self.__filter.read()
|
||||
if ret:
|
||||
self.__filter.getOptions(self.__opts)
|
||||
|
@ -87,8 +89,10 @@ class JailReader(ConfigReader):
|
|||
# Read action
|
||||
for act in self.__opts["action"].split('\n'):
|
||||
try:
|
||||
if not act: # skip empty actions
|
||||
continue
|
||||
splitAct = JailReader.splitAction(act)
|
||||
action = ActionReader(splitAct, self.__name)
|
||||
action = ActionReader(splitAct, self.__name, basedir=self.getBaseDir())
|
||||
ret = action.read()
|
||||
if ret:
|
||||
action.getOptions(self.__opts)
|
||||
|
@ -97,8 +101,10 @@ class JailReader(ConfigReader):
|
|||
raise AttributeError("Unable to read action")
|
||||
except Exception, e:
|
||||
logSys.error("Error in action definition " + act)
|
||||
logSys.debug(e)
|
||||
logSys.debug("Caught exception: %s" % (e,))
|
||||
return False
|
||||
if not len(self.__actions):
|
||||
logSys.warn("No actions were defined for %s" % self.__name)
|
||||
return True
|
||||
|
||||
def convert(self):
|
||||
|
@ -145,12 +151,20 @@ class JailReader(ConfigReader):
|
|||
def splitAction(action):
|
||||
m = JailReader.actionCRE.match(action)
|
||||
d = dict()
|
||||
if not m.group(2) == None:
|
||||
mgroups = m.groups()
|
||||
if len(mgroups) == 2:
|
||||
action_name, action_opts = mgroups
|
||||
elif len(mgroups) == 1:
|
||||
action_name, action_opts = mgroups[0], None
|
||||
else:
|
||||
raise ValueError("While reading action %s we should have got up to "
|
||||
"2 groups. Got: %r" % (action, mgroups))
|
||||
if not action_opts is None:
|
||||
# Huge bad hack :( This method really sucks. TODO Reimplement it.
|
||||
actions = ""
|
||||
escapeChar = None
|
||||
allowComma = False
|
||||
for c in m.group(2):
|
||||
for c in action_opts:
|
||||
if c in ('"', "'") and not allowComma:
|
||||
# Start
|
||||
escapeChar = c
|
||||
|
@ -175,6 +189,6 @@ class JailReader(ConfigReader):
|
|||
try:
|
||||
d[p[0].strip()] = p[1].strip()
|
||||
except IndexError:
|
||||
logSys.error("Invalid argument %s in '%s'" % (p, m.group(2)))
|
||||
return [m.group(1), d]
|
||||
logSys.error("Invalid argument %s in '%s'" % (p, action_opts))
|
||||
return [action_name, d]
|
||||
splitAction = staticmethod(splitAction)
|
||||
|
|
|
@ -36,12 +36,20 @@ logSys = logging.getLogger("fail2ban.client.config")
|
|||
|
||||
class JailsReader(ConfigReader):
|
||||
|
||||
def __init__(self):
|
||||
ConfigReader.__init__(self)
|
||||
def __init__(self, force_enable=False, **kwargs):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
force_enable : bool, optional
|
||||
Passed to JailReader to force enable the jails.
|
||||
It is for internal use
|
||||
"""
|
||||
ConfigReader.__init__(self, **kwargs)
|
||||
self.__jails = list()
|
||||
self.__force_enable = force_enable
|
||||
|
||||
def read(self):
|
||||
ConfigReader.read(self, "jail")
|
||||
return ConfigReader.read(self, "jail")
|
||||
|
||||
def getOptions(self, section = None):
|
||||
opts = []
|
||||
|
@ -49,7 +57,7 @@ class JailsReader(ConfigReader):
|
|||
|
||||
if section:
|
||||
# Get the options of a specific jail.
|
||||
jail = JailReader(section)
|
||||
jail = JailReader(section, basedir=self.getBaseDir(), force_enable=self.__force_enable)
|
||||
jail.read()
|
||||
ret = jail.getOptions()
|
||||
if ret:
|
||||
|
@ -62,7 +70,7 @@ class JailsReader(ConfigReader):
|
|||
else:
|
||||
# Get the options of all jails.
|
||||
for sec in self.sections():
|
||||
jail = JailReader(sec)
|
||||
jail = JailReader(sec, basedir=self.getBaseDir(), force_enable=self.__force_enable)
|
||||
jail.read()
|
||||
ret = jail.getOptions()
|
||||
if ret:
|
||||
|
|
|
@ -40,6 +40,7 @@ protocol = [
|
|||
["stop", "stops all jails and terminate the server"],
|
||||
["status", "gets the current status of the server"],
|
||||
["ping", "tests if the server is alive"],
|
||||
["help", "return this output"],
|
||||
['', "LOGGING", ""],
|
||||
["set loglevel <LEVEL>", "sets logging level to <LEVEL>. 0 is minimal, 4 is debug"],
|
||||
["get loglevel", "gets the logging level"],
|
||||
|
|
|
@ -52,10 +52,7 @@ actioncheck =
|
|||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <failtime> unix timestamp of the last failure
|
||||
# <bantime> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = ADDRESSES=`whois <ip> | perl -e 'while (<STDIN>) { next if /^changed|@(ripe|apnic)\.net/io; $m += (/abuse|trouble:|report|spam|security/io?3:0); if (/([a-z0-9_\-\.+]+@[a-z0-9\-]+(\.[[a-z0-9\-]+)+)/io) { while (s/([a-z0-9_\-\.+]+@[a-z0-9\-]+(\.[[a-z0-9\-]+)+)//io) { if ($m) { $a{lc($1)}=$m } else { $b{lc($1)}=$m } } $m=0 } else { $m && --$m } } if (%%a) {print join(",",keys(%%a))} else {print join(",",keys(%%b))}'`
|
||||
|
@ -67,9 +64,7 @@ actionban = ADDRESSES=`whois <ip> | perl -e 'while (<STDIN>) { next if /^changed
|
|||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <bantime> unix timestamp of the ban time
|
||||
# <unbantime> unix timestamp of the unban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban =
|
||||
|
|
|
@ -54,9 +54,7 @@ actioncheck =
|
|||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
# See http://www.dshield.org/specs.html for more on report format/notes
|
||||
|
@ -91,9 +89,7 @@ actionban = TZONE=`date +%%z | sed 's/\([+-]..\)\(..\)/\1:\2/'`
|
|||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban = if [ -f <tmpfile>.first ]; then
|
||||
|
@ -116,7 +112,7 @@ actionunban = if [ -f <tmpfile>.first ]; then
|
|||
port = ???
|
||||
|
||||
# Option: userid
|
||||
# Notes.: Your DSheild user ID. Should be provided either in the jail config or
|
||||
# Notes.: Your DShield user ID. Should be provided either in the jail config or
|
||||
# in a .local file.
|
||||
# Register at https://secure.dshield.org/register.html
|
||||
# Values: [ NUM ] Default: 0
|
||||
|
@ -124,13 +120,13 @@ port = ???
|
|||
userid = 0
|
||||
|
||||
# Option: myip
|
||||
# Notes.: TThe target IP for the attack (your public IP). Should be provided
|
||||
# Notes.: The target IP for the attack (your public IP). Should be provided
|
||||
# either in the jail config or in a .local file unless your PUBLIC IP
|
||||
# is the first IP assigned to eth0
|
||||
# Values: [ an IP address ] Default: Tries to find the IP address of eth0,
|
||||
# which in most cases will be a private IP, and therefore incorrect
|
||||
#
|
||||
myip = `ip -4 addr show dev eth0 | grep inet | head -1 | sed -r 's/.*inet ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}).*/\1/'`
|
||||
myip = `ip -4 addr show dev eth0 | grep inet | head -n 1 | sed -r 's/.*inet ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}).*/\1/'`
|
||||
|
||||
# Option: protocol
|
||||
# Notes.: The protocol over which the attack is happening
|
||||
|
@ -159,7 +155,6 @@ minreportinterval = 3600
|
|||
# submit the batch, even if we haven't reached <lines> yet. Note that
|
||||
# this is only checked on each ban/unban, and that we always send
|
||||
# anything in the buffer on shutdown. Must be greater than
|
||||
# <minreportinterval>.
|
||||
# Values: [ NUM ] Default: 21600 (6 hours)
|
||||
#
|
||||
maxbufferage = 21600
|
||||
|
|
|
@ -29,9 +29,7 @@ actioncheck =
|
|||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = printf %%b "+<ip>\n" >> /tmp/fail2ban.dummy
|
||||
|
@ -39,9 +37,7 @@ actionban = printf %%b "+<ip>\n" >> /tmp/fail2ban.dummy
|
|||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban = printf %%b "-<ip>\n" >> /tmp/fail2ban.dummy
|
||||
|
|
|
@ -28,9 +28,7 @@ actioncheck =
|
|||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = IP=<ip> &&
|
||||
|
@ -39,9 +37,7 @@ actionban = IP=<ip> &&
|
|||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban = IP=<ip> && sed -i.old /ALL:\ $IP/d <file>
|
||||
|
|
|
@ -34,9 +34,7 @@ actioncheck =
|
|||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = echo block in quick from <ip>/32 | /sbin/ipf -f -
|
||||
|
@ -45,9 +43,7 @@ actionban = echo block in quick from <ip>/32 | /sbin/ipf -f -
|
|||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
# note -r option used to remove matching rule
|
||||
|
|
|
@ -32,9 +32,7 @@ actioncheck =
|
|||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = ipfw add deny tcp from <ip> to <localhost> <port>
|
||||
|
@ -43,9 +41,7 @@ actionban = ipfw add deny tcp from <ip> to <localhost> <port>
|
|||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban = ipfw delete `ipfw list | grep -i <ip> | awk '{print $1;}'`
|
||||
|
|
|
@ -34,9 +34,7 @@ actioncheck = iptables -n -L <chain> | grep -q 'fail2ban-<name>[ \t]'
|
|||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j DROP
|
||||
|
@ -44,16 +42,14 @@ actionban = iptables -I fail2ban-<name> 1 -s <ip> -j DROP
|
|||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban = iptables -D fail2ban-<name> -s <ip> -j DROP
|
||||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the chain
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ actionstop = iptables -D INPUT -p <protocol> -m multiport --dports <port> -m set
|
|||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = ipset --test fail2ban-<name> <ip> || ipset --add fail2ban-<name> <ip>
|
||||
|
@ -46,14 +46,14 @@ actionban = ipset --test fail2ban-<name> <ip> || ipset --add fail2ban-<name> <i
|
|||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban = ipset --test fail2ban-<name> <ip> && ipset --del fail2ban-<name> <ip>
|
||||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the ipset
|
||||
# Default name of the ipset
|
||||
#
|
||||
name = default
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ actionstop = iptables -D INPUT -p <protocol> -m multiport --dports <port> -m set
|
|||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = ipset add fail2ban-<name> <ip> timeout <bantime> -exist
|
||||
|
@ -46,14 +46,14 @@ actionban = ipset add fail2ban-<name> <ip> timeout <bantime> -exist
|
|||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban = ipset del fail2ban-<name> <ip> -exist
|
||||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the ipset
|
||||
# Default name of the ipset
|
||||
#
|
||||
name = default
|
||||
|
||||
|
|
|
@ -42,9 +42,7 @@ actioncheck = iptables -n -L fail2ban-<name>-log >/dev/null
|
|||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j fail2ban-<name>-log
|
||||
|
@ -52,16 +50,14 @@ actionban = iptables -I fail2ban-<name> 1 -s <ip> -j fail2ban-<name>-log
|
|||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban = iptables -D fail2ban-<name> -s <ip> -j fail2ban-<name>-log
|
||||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the chain
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
|
|
|
@ -32,9 +32,7 @@ actioncheck = iptables -n -L <chain> | grep -q 'fail2ban-<name>[ \t]'
|
|||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j DROP
|
||||
|
@ -42,16 +40,14 @@ actionban = iptables -I fail2ban-<name> 1 -s <ip> -j DROP
|
|||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban = iptables -D fail2ban-<name> -s <ip> -j DROP
|
||||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the chain
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
|
|
|
@ -34,9 +34,7 @@ actioncheck = iptables -n -L <chain> | grep -q 'fail2ban-<name>[ \t]'
|
|||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j DROP
|
||||
|
@ -44,16 +42,14 @@ actionban = iptables -I fail2ban-<name> 1 -s <ip> -j DROP
|
|||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban = iptables -D fail2ban-<name> -s <ip> -j DROP
|
||||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the chain
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
# Notes.: command executed once at the start of Fail2Ban.
|
||||
# Values: CMD
|
||||
#
|
||||
# Changing iptables rules requires root priviledges. If fail2ban is
|
||||
# Changing iptables rules requires root privileges. If fail2ban is
|
||||
# configured to run as root, firewall setup can be performed by
|
||||
# fail2ban automatically. However, if fail2ban is configured to run as
|
||||
# a normal user, the configuration must be done by some other means
|
||||
|
@ -46,9 +46,7 @@ actioncheck = test -e /proc/net/xt_recent/fail2ban-<name>
|
|||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = echo +<ip> > /proc/net/xt_recent/fail2ban-<name>
|
||||
|
@ -56,16 +54,14 @@ actionban = echo +<ip> > /proc/net/xt_recent/fail2ban-<name>
|
|||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban = echo -<ip> > /proc/net/xt_recent/fail2ban-<name>
|
||||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the chain
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
|
|
|
@ -32,9 +32,7 @@ actioncheck = iptables -n -L <chain> | grep -q 'fail2ban-<name>[ \t]'
|
|||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j DROP
|
||||
|
@ -42,16 +40,14 @@ actionban = iptables -I fail2ban-<name> 1 -s <ip> -j DROP
|
|||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban = iptables -D fail2ban-<name> -s <ip> -j DROP
|
||||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the chain
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
|
|
|
@ -43,9 +43,7 @@ actioncheck =
|
|||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = printf %%b "`date`: <ip> (<failures> failures)\n" >> <tmpfile>
|
||||
|
@ -62,9 +60,7 @@ actionban = printf %%b "`date`: <ip> (<failures> failures)\n" >> <tmpfile>
|
|||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban =
|
||||
|
|
|
@ -34,10 +34,7 @@ actioncheck =
|
|||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <failtime> unix timestamp of the last failure
|
||||
# <bantime> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = printf %%b "Hi,\n
|
||||
|
@ -53,16 +50,14 @@ actionban = printf %%b "Hi,\n
|
|||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <bantime> unix timestamp of the ban time
|
||||
# <unbantime> unix timestamp of the unban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban =
|
||||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the chain
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
|
|
|
@ -34,9 +34,7 @@ actioncheck =
|
|||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = printf %%b "Hi,\n
|
||||
|
@ -50,16 +48,14 @@ actionban = printf %%b "Hi,\n
|
|||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban =
|
||||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the chain
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
|
|
|
@ -34,9 +34,7 @@ actioncheck =
|
|||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = printf %%b "Hi,\n
|
||||
|
@ -48,16 +46,14 @@ actionban = printf %%b "Hi,\n
|
|||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban =
|
||||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the chain
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
|
|
|
@ -49,9 +49,7 @@ actioncheck =
|
|||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
#
|
||||
|
@ -71,9 +69,7 @@ actionban = MNWLOGIN=`perl -e '$s=shift;$s=~s/([\W])/"%%".uc(sprintf("%%2.2x",or
|
|||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban =
|
||||
|
@ -102,13 +98,13 @@ mnwlogin =
|
|||
mnwpass =
|
||||
|
||||
# Option: myip
|
||||
# Notes.: TThe target IP for the attack (your public IP). Should be overridden
|
||||
# Notes.: The target IP for the attack (your public IP). Should be overridden
|
||||
# either in the jail config or in a .local file unless your PUBLIC IP
|
||||
# is the first IP assigned to eth0
|
||||
# Values: [ an IP address ] Default: Tries to find the IP address of eth0,
|
||||
# which in most cases will be a private IP, and therefore incorrect
|
||||
#
|
||||
myip = `ip -4 addr show dev eth0 | grep inet | head -1 | sed -r 's/.*inet ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}).*/\1/'`
|
||||
myip = `ip -4 addr show dev eth0 | grep inet | head -n 1 | sed -r 's/.*inet ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}).*/\1/'`
|
||||
|
||||
# Option: protocol
|
||||
# Notes.: The protocol over which the attack is happening
|
||||
|
|
|
@ -52,9 +52,7 @@ actioncheck =
|
|||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = printf %%b "`date`: <ip> (<failures> failures)\n" >> <tmpfile>
|
||||
|
@ -74,16 +72,14 @@ actionban = printf %%b "`date`: <ip> (<failures> failures)\n" >> <tmpfile>
|
|||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban =
|
||||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the chain
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
|
|
|
@ -42,9 +42,7 @@ actioncheck =
|
|||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip>
|
||||
|
@ -64,16 +62,14 @@ actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip>
|
|||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban =
|
||||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the chain
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
|
|
|
@ -42,9 +42,7 @@ actioncheck =
|
|||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip>
|
||||
|
@ -62,16 +60,14 @@ actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip>
|
|||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban =
|
||||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the chain
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
|
|
|
@ -42,9 +42,7 @@ actioncheck =
|
|||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip>
|
||||
|
@ -60,16 +58,14 @@ actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip>
|
|||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban =
|
||||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the chain
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
|
|
|
@ -36,9 +36,7 @@ actioncheck =
|
|||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = shorewall drop <ip>
|
||||
|
@ -46,9 +44,7 @@ actionban = shorewall drop <ip>
|
|||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban = shorewall allow <ip>
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
# Values: TEXT
|
||||
#
|
||||
failregex = reject: RCPT from (.*)\[<HOST>\]: 554
|
||||
reject: RCPT from (.*)\[<HOST>\]: 450 4\.7\.1 : Helo command rejected: Host not found; from=<> to=<> proto=ESMTP helo= *$
|
||||
|
||||
# Option: ignoreregex
|
||||
# Notes.: regex to ignore. If this regex matches, the line is ignored.
|
||||
|
|
|
@ -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 =
|
|
@ -219,6 +219,20 @@ filter = roundcube-auth
|
|||
action = iptables[name=RoundCube, port="http,https"]
|
||||
logpath = /var/log/roundcube/userlogins
|
||||
|
||||
|
||||
# Monitor SOGo groupware server
|
||||
|
||||
[sogo-iptables]
|
||||
|
||||
enabled = false
|
||||
filter = sogo-auth
|
||||
port = http, https
|
||||
# without proxy this would be:
|
||||
# port = 20000
|
||||
|
||||
action = iptables[name=SOGo, port="http,https"]
|
||||
logpath = /var/log/sogo/sogo.log
|
||||
|
||||
# Ban attackers that try to use PHP's URL-fopen() functionality
|
||||
# through GET/POST variables. - Experimental, with more than a year
|
||||
# of usage in production environments.
|
||||
|
@ -248,7 +262,7 @@ logpath = /var/log/lighttpd/error.log
|
|||
maxretry = 2
|
||||
|
||||
# Same as above for mod_auth
|
||||
# It catches wrong authentifications
|
||||
# It catches wrong authentications
|
||||
|
||||
[lighttpd-auth]
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
Fail2ban normally requires root priviledges to insert iptables rules
|
||||
Fail2ban normally requires root privileges to insert iptables rules
|
||||
through calls to /sbin/iptables and also to read the logfiles.
|
||||
Fail2ban can run as an unpriviledged user provided that those two
|
||||
capabilites are preserved. The idea is to run fail2ban as a normal
|
||||
Fail2ban can run as an unprivileged user provided that those two
|
||||
capabilities are preserved. The idea is to run fail2ban as a normal
|
||||
user (e.g. fail2ban) who belongs to a group which is allowed to read
|
||||
logfiles. The user should also be allowed to write to
|
||||
/proc/net/xt_recent/fail2ban-<name> (name is specified in the iptables
|
||||
|
@ -20,14 +20,14 @@ Another way to use xt_recent is by inserting the rules by writing to
|
|||
action. Files in /proc/net/xt_recent/ are protected by normal
|
||||
filesystem rules, so can be chown'ed and chmod'ed to be writable by a
|
||||
certain user. After the necessary iptables rules are inserted (which
|
||||
requires root priviledges), blacklisting can be perfomed by an
|
||||
unpriviledged user.
|
||||
requires root privileges), blacklisting can be performed by an
|
||||
unprivileged user.
|
||||
|
||||
Using fail2ban with xt_recent allows smarter filtering than normal
|
||||
iptables rules with the xt_recent module can provide.
|
||||
|
||||
The disadvantage is that fail2ban cannot perform the setup by itself,
|
||||
which would require the priviledge to call /sbin/iptables, and it must
|
||||
which would require the privilege to call /sbin/iptables, and it must
|
||||
be done through other means.
|
||||
|
||||
The primary advantage is obvious: it's generally better to run
|
||||
|
@ -46,7 +46,7 @@ some user and thus allow delisting IPs by helper administrators
|
|||
without the ability to mess up other iptables rules.
|
||||
|
||||
The xt_recent-echo jail can be used under the root user without
|
||||
further configuration. To run not as root, futher setup is necessary:
|
||||
further configuration. To run not as root, further setup is necessary:
|
||||
|
||||
- Create user:
|
||||
|
||||
|
|
|
@ -297,7 +297,7 @@ class Fail2banClient:
|
|||
delta = -1
|
||||
elif pos < 2:
|
||||
delta = 1
|
||||
# The server has 30 secondes to start.
|
||||
# The server has 30 seconds to start.
|
||||
if cnt >= 300:
|
||||
if self.__conf["verbose"] > 1:
|
||||
sys.stdout.write('\n')
|
||||
|
@ -380,7 +380,9 @@ class Fail2banClient:
|
|||
if cmd == "exit" or cmd == "quit":
|
||||
# Exit
|
||||
return True
|
||||
if not cmd == "":
|
||||
if cmd == "help":
|
||||
self.dispUsage()
|
||||
elif not cmd == "":
|
||||
self.__processCommand(shlex.split(cmd))
|
||||
except (EOFError, KeyboardInterrupt):
|
||||
print
|
||||
|
@ -418,7 +420,7 @@ class Fail2banClient:
|
|||
class ServerExecutionException(Exception):
|
||||
pass
|
||||
|
||||
if __name__ == "__main__":
|
||||
if __name__ == "__main__": # pragma: no cover - can't test main
|
||||
client = Fail2banClient()
|
||||
# Exit with correct return value
|
||||
if client.start(sys.argv):
|
||||
|
|
|
@ -35,6 +35,9 @@ from testcases import filtertestcase
|
|||
from testcases import servertestcase
|
||||
from testcases import datedetectortestcase
|
||||
from testcases import actiontestcase
|
||||
from testcases import sockettestcase
|
||||
|
||||
from testcases.utils import FormatterWithTraceBack
|
||||
from server.mytime import MyTime
|
||||
|
||||
from optparse import OptionParser, Option
|
||||
|
@ -51,12 +54,14 @@ def get_opt_parser():
|
|||
choices=('debug', 'info', 'warn', 'error', 'fatal'),
|
||||
default=None,
|
||||
help="Log level for the logger to use during running tests"),
|
||||
])
|
||||
|
||||
p.add_options([
|
||||
Option('-n', "--no-network", action="store_true",
|
||||
dest="no_network",
|
||||
help="Do not run tests that require the network"),
|
||||
Option("-t", "--log-traceback", action='store_true',
|
||||
help="Enrich log-messages with compressed tracebacks"),
|
||||
Option("--full-traceback", action='store_true',
|
||||
help="Either to make the tracebacks full, not compressed (as by default)"),
|
||||
|
||||
])
|
||||
|
||||
return p
|
||||
|
@ -77,10 +82,10 @@ verbosity = {'debug': 3,
|
|||
'fatal': 0,
|
||||
None: 1}[opts.log_level]
|
||||
|
||||
if opts.log_level is not None:
|
||||
if opts.log_level is not None: # pragma: no cover
|
||||
# so we had explicit settings
|
||||
logSys.setLevel(getattr(logging, opts.log_level.upper()))
|
||||
else:
|
||||
else: # pragma: no cover
|
||||
# suppress the logging but it would leave unittests' progress dots
|
||||
# ticking, unless like with '-l fatal' which would be silent
|
||||
# unless error occurs
|
||||
|
@ -88,18 +93,27 @@ else:
|
|||
|
||||
# Add the default logging handler
|
||||
stdout = logging.StreamHandler(sys.stdout)
|
||||
# Custom log format for the verbose tests runs
|
||||
if verbosity > 1:
|
||||
stdout.setFormatter(logging.Formatter(' %(asctime)-15s %(thread)s %(message)s'))
|
||||
|
||||
fmt = ' %(message)s'
|
||||
|
||||
if opts.log_traceback:
|
||||
Formatter = FormatterWithTraceBack
|
||||
fmt = (opts.full_traceback and ' %(tb)s' or ' %(tbc)s') + fmt
|
||||
else:
|
||||
Formatter = logging.Formatter
|
||||
|
||||
# Custom log format for the verbose tests runs
|
||||
if verbosity > 1: # pragma: no cover
|
||||
stdout.setFormatter(Formatter(' %(asctime)-15s %(thread)s' + fmt))
|
||||
else: # pragma: no cover
|
||||
# just prefix with the space
|
||||
stdout.setFormatter(logging.Formatter(' %(message)s'))
|
||||
stdout.setFormatter(Formatter(fmt))
|
||||
logSys.addHandler(stdout)
|
||||
|
||||
#
|
||||
# Let know the version
|
||||
#
|
||||
if not opts.log_level or opts.log_level != 'fatal':
|
||||
if not opts.log_level or opts.log_level != 'fatal': # pragma: no cover
|
||||
print "Fail2ban %s test suite. Python %s. Please wait..." \
|
||||
% (version, str(sys.version).replace('\n', ''))
|
||||
|
||||
|
@ -107,9 +121,9 @@ if not opts.log_level or opts.log_level != 'fatal':
|
|||
#
|
||||
# Gather the tests
|
||||
#
|
||||
if not len(regexps):
|
||||
if not len(regexps): # pragma: no cover
|
||||
tests = unittest.TestSuite()
|
||||
else:
|
||||
else: # pragma: no cover
|
||||
import re
|
||||
class FilteredTestSuite(unittest.TestSuite):
|
||||
_regexps = [re.compile(r) for r in regexps]
|
||||
|
@ -130,8 +144,12 @@ tests.addTest(unittest.makeSuite(actiontestcase.ExecuteAction))
|
|||
tests.addTest(unittest.makeSuite(failmanagertestcase.AddFailure))
|
||||
# BanManager
|
||||
tests.addTest(unittest.makeSuite(banmanagertestcase.AddFailure))
|
||||
# ClientReader
|
||||
# ClientReaders
|
||||
tests.addTest(unittest.makeSuite(clientreadertestcase.ConfigReaderTest))
|
||||
tests.addTest(unittest.makeSuite(clientreadertestcase.JailReaderTest))
|
||||
tests.addTest(unittest.makeSuite(clientreadertestcase.JailsReaderTest))
|
||||
# CSocket and AsyncServer
|
||||
tests.addTest(unittest.makeSuite(sockettestcase.Socket))
|
||||
|
||||
# Filter
|
||||
if not opts.no_network:
|
||||
|
@ -159,19 +177,22 @@ filters = [FilterPoll] # always available
|
|||
try:
|
||||
from server.filtergamin import FilterGamin
|
||||
filters.append(FilterGamin)
|
||||
except Exception, e:
|
||||
except Exception, e: # pragma: no cover
|
||||
print "I: Skipping gamin backend testing. Got exception '%s'" % e
|
||||
|
||||
try:
|
||||
from server.filterpyinotify import FilterPyinotify
|
||||
filters.append(FilterPyinotify)
|
||||
except Exception, e:
|
||||
except Exception, e: # pragma: no cover
|
||||
print "I: Skipping pyinotify backend testing. Got exception '%s'" % e
|
||||
|
||||
for Filter_ in filters:
|
||||
tests.addTest(unittest.makeSuite(
|
||||
filtertestcase.get_monitor_failures_testcase(Filter_)))
|
||||
|
||||
# Server test for logging elements which break logging used to support
|
||||
# testcases analysis
|
||||
tests.addTest(unittest.makeSuite(servertestcase.TransmitterLogging))
|
||||
|
||||
#
|
||||
# Run the tests
|
||||
|
@ -189,7 +210,7 @@ try:
|
|||
|
||||
tests_results = testRunner.run(tests)
|
||||
|
||||
finally:
|
||||
finally: # pragma: no cover
|
||||
# Just for the sake of it reset the TZ
|
||||
# yoh: move all this into setup/teardown methods within tests
|
||||
os.environ.pop('TZ')
|
||||
|
@ -197,5 +218,5 @@ finally:
|
|||
os.environ['TZ'] = old_TZ
|
||||
time.tzset()
|
||||
|
||||
if not tests_results.wasSuccessful():
|
||||
if not tests_results.wasSuccessful(): # pragma: no cover
|
||||
sys.exit(1)
|
||||
|
|
|
@ -49,5 +49,5 @@ details.
|
|||
|
||||
You should have received a copy of the GNU General Public
|
||||
License along with Fail2Ban; if not, write to the Free
|
||||
Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||
Boston, MA 02111-1307 USA
|
||||
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110, USA
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.36.
|
||||
.TH FAIL2BAN-CLIENT "1" "March 2008" "fail2ban-client v0.8.2" "User Commands"
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.10.
|
||||
.TH FAIL2BAN-CLIENT "1" "March 2013" "fail2ban-client v0.8.8" "User Commands"
|
||||
.SH NAME
|
||||
fail2ban-client \- configure and control the server
|
||||
.SH SYNOPSIS
|
||||
.B fail2ban-client
|
||||
[\fIOPTIONS\fR] \fI<COMMAND>\fR
|
||||
.SH DESCRIPTION
|
||||
Fail2Ban v0.8.2 reads log file that contains password failure report
|
||||
Fail2Ban v0.8.8 reads log file that contains password failure report
|
||||
and bans the corresponding IP addresses using firewall rules.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
|
@ -16,6 +16,9 @@ configuration directory
|
|||
\fB\-s\fR <FILE>
|
||||
socket path
|
||||
.TP
|
||||
\fB\-p\fR <FILE>
|
||||
pidfile path
|
||||
.TP
|
||||
\fB\-d\fR
|
||||
dump configuration. For debugging
|
||||
.TP
|
||||
|
@ -110,7 +113,7 @@ adds <FILE> to the monitoring list
|
|||
of <JAIL>
|
||||
.TP
|
||||
\fBset <JAIL> dellogpath <FILE>\fR
|
||||
removes <FILE> to the monitoring
|
||||
removes <FILE> from the monitoring
|
||||
list of <JAIL>
|
||||
.TP
|
||||
\fBset <JAIL> addfailregex <REGEX>\fR
|
||||
|
@ -140,6 +143,15 @@ back for <JAIL>
|
|||
sets the number of seconds <TIME>
|
||||
a host will be banned for <JAIL>
|
||||
.TP
|
||||
\fBset <JAIL> usedns <VALUE>\fR
|
||||
sets the usedns mode for <JAIL>
|
||||
.TP
|
||||
\fBset <JAIL> banip <IP>\fR
|
||||
manually Ban <IP> for <JAIL>
|
||||
.TP
|
||||
\fBset <JAIL> unbanip <IP>\fR
|
||||
manually Unban <IP> in <JAIL>
|
||||
.TP
|
||||
\fBset <JAIL> maxretry <RETRY>\fR
|
||||
sets the number of failures
|
||||
<RETRY> before banning the host
|
||||
|
@ -191,14 +203,6 @@ files for <JAIL>
|
|||
gets the list of ignored IP
|
||||
addresses for <JAIL>
|
||||
.TP
|
||||
\fBget <JAIL> timeregex\fR
|
||||
gets the regular expression used
|
||||
for the time detection for <JAIL>
|
||||
.TP
|
||||
\fBget <JAIL> timepattern\fR
|
||||
gets the pattern used for the time
|
||||
detection for <JAIL>
|
||||
.TP
|
||||
\fBget <JAIL> failregex\fR
|
||||
gets the list of regular
|
||||
expressions which matches the
|
||||
|
@ -218,6 +222,9 @@ will look back for failures for
|
|||
gets the time a host is banned for
|
||||
<JAIL>
|
||||
.TP
|
||||
\fBget <JAIL> usedns\fR
|
||||
gets the usedns setting for <JAIL>
|
||||
.TP
|
||||
\fBget <JAIL> maxretry\fR
|
||||
gets the number of failures
|
||||
allowed for <JAIL>
|
||||
|
@ -245,15 +252,19 @@ action <ACT> for <JAIL>
|
|||
\fBget <JAIL> actionunban <ACT>\fR
|
||||
gets the unban command for the
|
||||
action <ACT> for <JAIL>
|
||||
.TP
|
||||
\fBget <JAIL> cinfo <ACT> <KEY>\fR
|
||||
gets the value for <KEY> for the
|
||||
action <ACT> for <JAIL>
|
||||
.SH FILES
|
||||
\fI/etc/fail2ban/*\fR
|
||||
.SH AUTHOR
|
||||
Written by Cyril Jaquier <cyril.jaquier@fail2ban.org>.
|
||||
Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>.
|
||||
.SH "REPORTING BUGS"
|
||||
Report bugs on https://github.com/fail2ban/fail2ban/issues
|
||||
Report bugs to https://github.com/fail2ban/fail2ban/issues
|
||||
.SH COPYRIGHT
|
||||
Copyright \(co 2004-2008 Cyril Jaquier
|
||||
Copyright \(co 2004\-2008 Cyril Jaquier, 2008\- Fail2Ban Contributors
|
||||
.br
|
||||
Copyright of modifications held by their respective authors.
|
||||
Licensed under the GNU General Public License v2 (GPL).
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.36.
|
||||
.TH FAIL2BAN-REGEX "1" "March 2008" "fail2ban-regex v0.8.2" "User Commands"
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.10.
|
||||
.TH FAIL2BAN-REGEX "1" "March 2013" "fail2ban-regex v0.8.8" "User Commands"
|
||||
.SH NAME
|
||||
fail2ban-regex \- test Fail2ban "failregex" option
|
||||
.SH SYNOPSIS
|
||||
.B fail2ban-regex
|
||||
[\fIOPTIONS\fR] \fI<LOG> <REGEX> \fR[\fIIGNOREREGEX\fR]
|
||||
.SH DESCRIPTION
|
||||
Fail2Ban v0.8.2 reads log file that contains password failure report
|
||||
Fail2Ban v0.8.8 reads log file that contains password failure report
|
||||
and bans the corresponding IP addresses using firewall rules.
|
||||
.PP
|
||||
This tools can test regular expressions for "fail2ban".
|
||||
|
@ -17,6 +17,9 @@ display this help message
|
|||
.TP
|
||||
\fB\-V\fR, \fB\-\-version\fR
|
||||
print the version
|
||||
.TP
|
||||
\fB\-v\fR, \fB\-\-verbose\fR
|
||||
verbose output
|
||||
.SH LOG
|
||||
.TP
|
||||
\fBstring\fR
|
||||
|
@ -42,9 +45,9 @@ path to a filter file (filter.d/sshd.conf)
|
|||
Written by Cyril Jaquier <cyril.jaquier@fail2ban.org>.
|
||||
Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>.
|
||||
.SH "REPORTING BUGS"
|
||||
Report bugs on https://github.com/fail2ban/fail2ban/issues
|
||||
Report bugs to https://github.com/fail2ban/fail2ban/issues
|
||||
.SH COPYRIGHT
|
||||
Copyright \(co 2004-2008 Cyril Jaquier
|
||||
Copyright \(co 2004\-2008 Cyril Jaquier
|
||||
.br
|
||||
Copyright of modifications held by their respective authors.
|
||||
Licensed under the GNU General Public License v2 (GPL).
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.36.
|
||||
.TH FAIL2BAN-SERVER "1" "March 2008" "fail2ban-server v0.8.2" "User Commands"
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.10.
|
||||
.TH FAIL2BAN-SERVER "1" "March 2013" "fail2ban-server v0.8.8" "User Commands"
|
||||
.SH NAME
|
||||
fail2ban-server \- start the server
|
||||
.SH SYNOPSIS
|
||||
.B fail2ban-server
|
||||
[\fIOPTIONS\fR]
|
||||
.SH DESCRIPTION
|
||||
Fail2Ban v0.8.2 reads log file that contains password failure report
|
||||
Fail2Ban v0.8.8 reads log file that contains password failure report
|
||||
and bans the corresponding IP addresses using firewall rules.
|
||||
.PP
|
||||
Only use this command for debugging purpose. Start the server with
|
||||
|
@ -23,6 +23,9 @@ start in foreground
|
|||
\fB\-s\fR <FILE>
|
||||
socket path
|
||||
.TP
|
||||
\fB\-p\fR <FILE>
|
||||
pidfile path
|
||||
.TP
|
||||
\fB\-x\fR
|
||||
force execution of the server (remove socket file)
|
||||
.TP
|
||||
|
@ -35,9 +38,9 @@ print the version
|
|||
Written by Cyril Jaquier <cyril.jaquier@fail2ban.org>.
|
||||
Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>.
|
||||
.SH "REPORTING BUGS"
|
||||
Report bugs on https://github.com/fail2ban/fail2ban/issues
|
||||
Report bugs to https://github.com/fail2ban/fail2ban/issues
|
||||
.SH COPYRIGHT
|
||||
Copyright \(co 2004-2008 Cyril Jaquier
|
||||
Copyright \(co 2004\-2008 Cyril Jaquier, 2008\- Fail2Ban Contributors
|
||||
.br
|
||||
Copyright of modifications held by their respective authors.
|
||||
Licensed under the GNU General Public License v2 (GPL).
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
|
@ -277,8 +277,8 @@ class Action:
|
|||
# Executes a command with preliminary checks and substitutions.
|
||||
#
|
||||
# Before executing any commands, executes the "check" command first
|
||||
# in order to check if prerequirements are met. If this check fails,
|
||||
# it tries to restore a sane environnement before executing the real
|
||||
# in order to check if pre-requirements are met. If this check fails,
|
||||
# it tries to restore a sane environment before executing the real
|
||||
# command.
|
||||
# Replaces "aInfo" and "cInfo" in the query too.
|
||||
#
|
||||
|
|
|
@ -77,7 +77,8 @@ class Actions(JailThread):
|
|||
for action in self.__actions:
|
||||
if action.getName() == name:
|
||||
self.__actions.remove(action)
|
||||
break
|
||||
return
|
||||
raise KeyError("Invalid Action name: %s" % name)
|
||||
|
||||
##
|
||||
# Returns an action.
|
||||
|
@ -91,7 +92,7 @@ class Actions(JailThread):
|
|||
for action in self.__actions:
|
||||
if action.getName() == name:
|
||||
return action
|
||||
raise KeyError
|
||||
raise KeyError("Invalid Action name")
|
||||
|
||||
##
|
||||
# Returns the last defined action.
|
||||
|
@ -131,7 +132,7 @@ class Actions(JailThread):
|
|||
# Unban the IP.
|
||||
self.__unBan(ticket)
|
||||
return ip
|
||||
return 'None'
|
||||
raise ValueError("IP %s is not banned" % ip)
|
||||
|
||||
##
|
||||
# Main loop.
|
||||
|
|
|
@ -148,7 +148,7 @@ class AsyncServer(asyncore.dispatcher):
|
|||
if sys.version_info >= (2, 6): # if python 2.6 or greater...
|
||||
logSys.debug("Detected Python 2.6 or greater. asyncore.loop() not using poll")
|
||||
asyncore.loop(use_poll = False) # fixes the "Unexpected communication problem" issue on Python 2.6 and 3.0
|
||||
else:
|
||||
else: # pragma: no cover
|
||||
logSys.debug("NOT Python 2.6/3.* - asyncore.loop() using poll")
|
||||
asyncore.loop(use_poll = True)
|
||||
|
||||
|
|
|
@ -148,7 +148,7 @@ class BanManager:
|
|||
def addBanTicket(self, ticket):
|
||||
try:
|
||||
self.__lock.acquire()
|
||||
if not self.__inBanList(ticket):
|
||||
if not self._inBanList(ticket):
|
||||
self.__banList.append(ticket)
|
||||
self.__banTotal += 1
|
||||
return True
|
||||
|
@ -177,7 +177,7 @@ class BanManager:
|
|||
# @param ticket the ticket
|
||||
# @return True if a ticket already exists
|
||||
|
||||
def __inBanList(self, ticket):
|
||||
def _inBanList(self, ticket):
|
||||
for i in self.__banList:
|
||||
if ticket.getIP() == i.getIP():
|
||||
return True
|
||||
|
|
|
@ -50,8 +50,11 @@ class DateTemplate:
|
|||
def getName(self):
|
||||
return self.__name
|
||||
|
||||
def setRegex(self, regex):
|
||||
self.__regex = regex.strip()
|
||||
def setRegex(self, regex, wordBegin=True):
|
||||
regex = regex.strip()
|
||||
if (wordBegin and not re.search(r'^\^', regex)):
|
||||
regex = r'\b' + regex
|
||||
self.__regex = regex
|
||||
self.__cRegex = re.compile(regex)
|
||||
|
||||
def getRegex(self):
|
||||
|
@ -158,6 +161,7 @@ class DateStrptime(DateTemplate):
|
|||
"pattern" % (opattern, e))
|
||||
if date[0] < 2000:
|
||||
# There is probably no year field in the logs
|
||||
# NOTE: Possibly makes week/year day incorrect
|
||||
date[0] = MyTime.gmtime()[0]
|
||||
# Bug fix for #1241756
|
||||
# If the date is greater than the current time, we suppose
|
||||
|
@ -166,10 +170,12 @@ class DateStrptime(DateTemplate):
|
|||
logSys.debug(
|
||||
u"Correcting deduced year from %d to %d since %f > %f" %
|
||||
(date[0], date[0]-1, time.mktime(tuple(date)), MyTime.time()))
|
||||
# NOTE: Possibly makes week/year day incorrect
|
||||
date[0] -= 1
|
||||
elif date[1] == 1 and date[2] == 1:
|
||||
# If it is Jan 1st, it is either really Jan 1st or there
|
||||
# is neither month nor day in the log.
|
||||
# NOTE: Possibly makes week/year day incorrect
|
||||
date[1] = MyTime.gmtime()[1]
|
||||
date[2] = MyTime.gmtime()[2]
|
||||
return date
|
||||
|
@ -180,7 +186,8 @@ class DateTai64n(DateTemplate):
|
|||
def __init__(self):
|
||||
DateTemplate.__init__(self)
|
||||
# We already know the format for TAI64N
|
||||
self.setRegex("@[0-9a-f]{24}")
|
||||
# yoh: we should not add an additional front anchor
|
||||
self.setRegex("@[0-9a-f]{24}", wordBegin=False)
|
||||
|
||||
def getDate(self, line):
|
||||
date = None
|
||||
|
|
|
@ -105,9 +105,17 @@ class FailManager:
|
|||
fData.setLastReset(unixTime)
|
||||
fData.setLastTime(unixTime)
|
||||
self.__failList[ip] = fData
|
||||
logSys.debug("Currently have failures from %d IPs: %s"
|
||||
% (len(self.__failList), self.__failList.keys()))
|
||||
|
||||
self.__failTotal += 1
|
||||
|
||||
if logSys.getEffectiveLevel() <= logging.DEBUG:
|
||||
# yoh: Since composing this list might be somewhat time consuming
|
||||
# in case of having many active failures, it should be ran only
|
||||
# if debug level is "low" enough
|
||||
failures_summary = ', '.join(['%s:%d' % (k, v.getRetry())
|
||||
for k,v in self.__failList.iteritems()])
|
||||
logSys.debug("Total # of detected failures: %d. Current failures from %d IPs (IP:count): %s"
|
||||
% (self.__failTotal, len(self.__failList), failures_summary))
|
||||
finally:
|
||||
self.__lock.release()
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ logSys = logging.getLogger("fail2ban.filter")
|
|||
# Log reader class.
|
||||
#
|
||||
# This class reads a log file and detects login failures or anything else
|
||||
# that matches a given regular expression. This class is instanciated by
|
||||
# that matches a given regular expression. This class is instantiated by
|
||||
# a Jail object.
|
||||
|
||||
class Filter(JailThread):
|
||||
|
@ -93,6 +93,7 @@ class Filter(JailThread):
|
|||
self.__failRegex.append(regex)
|
||||
except RegexException, e:
|
||||
logSys.error(e)
|
||||
raise e
|
||||
|
||||
|
||||
def delFailRegex(self, index):
|
||||
|
@ -117,7 +118,7 @@ class Filter(JailThread):
|
|||
# Add the regular expression which matches the failure.
|
||||
#
|
||||
# The regular expression can also match any other pattern than failures
|
||||
# and thus can be used for many purporse.
|
||||
# and thus can be used for many purpose.
|
||||
# @param value the regular expression
|
||||
|
||||
def addIgnoreRegex(self, value):
|
||||
|
@ -126,6 +127,7 @@ class Filter(JailThread):
|
|||
self.__ignoreRegex.append(regex)
|
||||
except RegexException, e:
|
||||
logSys.error(e)
|
||||
raise e
|
||||
|
||||
def delIgnoreRegex(self, index):
|
||||
try:
|
||||
|
@ -211,7 +213,7 @@ class Filter(JailThread):
|
|||
# file has been modified and looks for failures.
|
||||
# @return True when the thread exits nicely
|
||||
|
||||
def run(self):
|
||||
def run(self): # pragma: no cover
|
||||
raise Exception("run() is abstract")
|
||||
|
||||
##
|
||||
|
@ -226,7 +228,7 @@ class Filter(JailThread):
|
|||
self.failManager.addFailure(FailTicket(ip, unixTime))
|
||||
|
||||
# Perform the banning of the IP now.
|
||||
try:
|
||||
try: # pragma: no branch - exception is the only way out
|
||||
while True:
|
||||
ticket = self.failManager.toBan()
|
||||
self.jail.putFailTicket(ticket)
|
||||
|
@ -368,7 +370,7 @@ class Filter(JailThread):
|
|||
failList.append([ip, date])
|
||||
# We matched a regex, it is enough to stop.
|
||||
break
|
||||
except RegexException, e:
|
||||
except RegexException, e: # pragma: no cover - unsure if reachable
|
||||
logSys.error(e)
|
||||
return failList
|
||||
|
||||
|
@ -410,7 +412,7 @@ class FileFilter(Filter):
|
|||
|
||||
def _addLogPath(self, path):
|
||||
# nothing to do by default
|
||||
# to be overriden by backends
|
||||
# to be overridden by backends
|
||||
pass
|
||||
|
||||
|
||||
|
@ -429,7 +431,7 @@ class FileFilter(Filter):
|
|||
|
||||
def _delLogPath(self, path):
|
||||
# nothing to do by default
|
||||
# to be overriden by backends
|
||||
# to be overridden by backends
|
||||
pass
|
||||
|
||||
##
|
||||
|
@ -495,10 +497,19 @@ class FileFilter(Filter):
|
|||
# Try to open log file.
|
||||
try:
|
||||
container.open()
|
||||
except Exception, e:
|
||||
# see http://python.org/dev/peps/pep-3151/
|
||||
except IOError, e:
|
||||
logSys.error("Unable to open %s" % filename)
|
||||
logSys.exception(e)
|
||||
return False
|
||||
except OSError, e: # pragma: no cover - requires race condition to tigger this
|
||||
logSys.error("Error opening %s" % filename)
|
||||
logSys.exception(e)
|
||||
return False
|
||||
except OSError, e: # pragma: no cover - Requires implemention error in FileContainer to generate
|
||||
logSys.error("Internal errror in FileContainer open method - please report as a bug to https://github.com/fail2ban/fail2ban/issues")
|
||||
logSys.exception(e)
|
||||
return False
|
||||
|
||||
while True:
|
||||
line = container.readline()
|
||||
|
@ -525,7 +536,7 @@ class FileFilter(Filter):
|
|||
try:
|
||||
import hashlib
|
||||
md5sum = hashlib.md5
|
||||
except ImportError:
|
||||
except ImportError: # pragma: no cover
|
||||
# hashlib was introduced in Python 2.5. For compatibility with those
|
||||
# elderly Pythons, import from md5
|
||||
import md5
|
||||
|
@ -576,7 +587,7 @@ class FileContainer:
|
|||
stats = os.fstat(self.__handler.fileno())
|
||||
# Compare hash and inode
|
||||
if self.__hash != myHash or self.__ino != stats.st_ino:
|
||||
logSys.info("Log rotation detected for %s" % self.__filename)
|
||||
logSys.debug("Log rotation detected for %s" % self.__filename)
|
||||
self.__hash = myHash
|
||||
self.__ino = stats.st_ino
|
||||
self.__pos = 0
|
||||
|
|
|
@ -39,7 +39,7 @@ logSys = logging.getLogger("fail2ban.filter")
|
|||
# Log reader class.
|
||||
#
|
||||
# This class reads a log file and detects login failures or anything else
|
||||
# that matches a given regular expression. This class is instanciated by
|
||||
# that matches a given regular expression. This class is instantiated by
|
||||
# a Jail object.
|
||||
|
||||
class FilterPoll(FileFilter):
|
||||
|
|
|
@ -63,16 +63,17 @@ class FilterPyinotify(FileFilter):
|
|||
logSys.debug("Created FilterPyinotify")
|
||||
|
||||
|
||||
def callback(self, event):
|
||||
def callback(self, event, origin=''):
|
||||
logSys.debug("%sCallback for Event: %s", origin, event)
|
||||
path = event.pathname
|
||||
if event.mask & pyinotify.IN_CREATE:
|
||||
# skip directories altogether
|
||||
if event.mask & pyinotify.IN_ISDIR:
|
||||
logSys.debug("Ignoring creation of directory %s" % path)
|
||||
logSys.debug("Ignoring creation of directory %s", path)
|
||||
return
|
||||
# check if that is a file we care about
|
||||
if not path in self.__watches:
|
||||
logSys.debug("Ignoring creation of %s we do not monitor" % path)
|
||||
logSys.debug("Ignoring creation of %s we do not monitor", path)
|
||||
return
|
||||
else:
|
||||
# we need to substitute the watcher with a new one, so first
|
||||
|
@ -104,7 +105,7 @@ class FilterPyinotify(FileFilter):
|
|||
def _addFileWatcher(self, path):
|
||||
wd = self.__monitor.add_watch(path, pyinotify.IN_MODIFY)
|
||||
self.__watches.update(wd)
|
||||
logSys.debug("Added file watcher for %s" % path)
|
||||
logSys.debug("Added file watcher for %s", path)
|
||||
# process the file since we did get even
|
||||
self._process_file(path)
|
||||
|
||||
|
@ -114,7 +115,7 @@ class FilterPyinotify(FileFilter):
|
|||
wd = self.__monitor.rm_watch(wdInt)
|
||||
if wd[wdInt]:
|
||||
del self.__watches[path]
|
||||
logSys.debug("Removed file watcher for %s" % path)
|
||||
logSys.debug("Removed file watcher for %s", path)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
@ -130,7 +131,7 @@ class FilterPyinotify(FileFilter):
|
|||
# we need to watch also the directory for IN_CREATE
|
||||
self.__watches.update(
|
||||
self.__monitor.add_watch(path_dir, pyinotify.IN_CREATE))
|
||||
logSys.debug("Added monitor for the parent directory %s" % path_dir)
|
||||
logSys.debug("Added monitor for the parent directory %s", path_dir)
|
||||
|
||||
self._addFileWatcher(path)
|
||||
|
||||
|
@ -151,7 +152,7 @@ class FilterPyinotify(FileFilter):
|
|||
# since there is no other monitored file under this directory
|
||||
wdInt = self.__watches.pop(path_dir)
|
||||
_ = self.__monitor.rm_watch(wdInt)
|
||||
logSys.debug("Removed monitor for the parent directory %s" % path_dir)
|
||||
logSys.debug("Removed monitor for the parent directory %s", path_dir)
|
||||
|
||||
|
||||
##
|
||||
|
@ -165,7 +166,7 @@ class FilterPyinotify(FileFilter):
|
|||
self.__notifier = pyinotify.ThreadedNotifier(self.__monitor,
|
||||
ProcessPyinotify(self))
|
||||
self.__notifier.start()
|
||||
logSys.debug("pyinotifier started for %s." % self.jail.getName())
|
||||
logSys.debug("pyinotifier started for %s.", self.jail.getName())
|
||||
# TODO: verify that there is nothing really to be done for
|
||||
# idle jails
|
||||
return True
|
||||
|
@ -201,5 +202,4 @@ class ProcessPyinotify(pyinotify.ProcessEvent):
|
|||
|
||||
# just need default, since using mask on watch to limit events
|
||||
def process_default(self, event):
|
||||
logSys.debug("Callback for Event: %s" % event)
|
||||
self.__FileFilter.callback(event)
|
||||
self.__FileFilter.callback(event, origin='Default ')
|
||||
|
|
|
@ -382,7 +382,7 @@ class Server:
|
|||
try:
|
||||
handler.flush()
|
||||
handler.close()
|
||||
except ValueError:
|
||||
except (ValueError, KeyError):
|
||||
if (2,6) <= sys.version_info < (3,) or \
|
||||
(3,2) <= sys.version_info:
|
||||
raise
|
||||
|
|
|
@ -112,14 +112,18 @@ class Transmitter:
|
|||
return self.__server.getLogLevel()
|
||||
elif name == "logtarget":
|
||||
value = command[1]
|
||||
self.__server.setLogTarget(value)
|
||||
if self.__server.setLogTarget(value):
|
||||
return self.__server.getLogTarget()
|
||||
else:
|
||||
raise Exception("Failed to change log target")
|
||||
# Jail
|
||||
elif command[1] == "idle":
|
||||
if command[2] == "on":
|
||||
self.__server.setIdleJail(name, True)
|
||||
elif command[2] == "off":
|
||||
self.__server.setIdleJail(name, False)
|
||||
else:
|
||||
raise Exception("Invalid idle option, must be 'yes' or 'no'")
|
||||
return self.__server.getIdleJail(name)
|
||||
# Filter
|
||||
elif command[1] == "addignoreip":
|
||||
|
@ -193,7 +197,7 @@ class Transmitter:
|
|||
elif command[1] == "setcinfo":
|
||||
act = command[2]
|
||||
key = command[3]
|
||||
value = command[4]
|
||||
value = " ".join(command[4:])
|
||||
self.__server.setCInfo(name, act, key, value)
|
||||
return self.__server.getCInfo(name, act, key)
|
||||
elif command[1] == "delcinfo":
|
||||
|
@ -203,27 +207,27 @@ class Transmitter:
|
|||
return None
|
||||
elif command[1] == "actionstart":
|
||||
act = command[2]
|
||||
value = command[3]
|
||||
value = " ".join(command[3:])
|
||||
self.__server.setActionStart(name, act, value)
|
||||
return self.__server.getActionStart(name, act)
|
||||
elif command[1] == "actionstop":
|
||||
act = command[2]
|
||||
value = command[3]
|
||||
value = " ".join(command[3:])
|
||||
self.__server.setActionStop(name, act, value)
|
||||
return self.__server.getActionStop(name, act)
|
||||
elif command[1] == "actioncheck":
|
||||
act = command[2]
|
||||
value = command[3]
|
||||
value = " ".join(command[3:])
|
||||
self.__server.setActionCheck(name, act, value)
|
||||
return self.__server.getActionCheck(name, act)
|
||||
elif command[1] == "actionban":
|
||||
act = command[2]
|
||||
value = command[3]
|
||||
value = " ".join(command[3:])
|
||||
self.__server.setActionBan(name, act, value)
|
||||
return self.__server.getActionBan(name, act)
|
||||
elif command[1] == "actionunban":
|
||||
act = command[2]
|
||||
value = command[3]
|
||||
value = " ".join(command[3:])
|
||||
self.__server.setActionUnban(name, act, value)
|
||||
return self.__server.getActionUnban(name, act)
|
||||
raise Exception("Invalid command (no set action or not yet implemented)")
|
||||
|
@ -281,7 +285,7 @@ class Transmitter:
|
|||
def status(self, command):
|
||||
if len(command) == 0:
|
||||
return self.__server.status()
|
||||
else:
|
||||
elif len(command) == 1:
|
||||
name = command[0]
|
||||
return self.__server.statusJail(name)
|
||||
raise Exception("Invalid command (no status)")
|
||||
|
|
|
@ -3,3 +3,11 @@ install-purelib=/usr/share/fail2ban
|
|||
|
||||
[sdist]
|
||||
formats=bztar
|
||||
|
||||
[bdist_rpm]
|
||||
release = 1
|
||||
packager = Yaroslav Halchenko <debian@onerussian.com>, Daniel Black <grooverdan@users.sourceforge.net>
|
||||
doc_files = DEVELOP
|
||||
README
|
||||
THANKS
|
||||
doc/run-rootless.txt
|
||||
|
|
9
setup.py
9
setup.py
|
@ -67,6 +67,9 @@ setup(
|
|||
),
|
||||
('/var/run/fail2ban',
|
||||
''
|
||||
),
|
||||
('/usr/share/doc/fail2ban',
|
||||
['README', 'DEVELOP', 'doc/run-rootless.txt']
|
||||
)
|
||||
]
|
||||
)
|
||||
|
@ -83,12 +86,6 @@ elements = {
|
|||
[
|
||||
"fail2ban.py"
|
||||
],
|
||||
"/usr/lib/fail2ban/firewall/":
|
||||
[
|
||||
"iptables.py",
|
||||
"ipfwadm.py",
|
||||
"ipfw.py"
|
||||
],
|
||||
"/usr/lib/fail2ban/":
|
||||
[
|
||||
"version.py",
|
||||
|
|
|
@ -49,11 +49,11 @@ class AddFailure(unittest.TestCase):
|
|||
self.assertFalse(self.__banManager.addBanTicket(self.__ticket))
|
||||
self.assertEqual(self.__banManager.size(), 1)
|
||||
|
||||
def _testInListOK(self):
|
||||
def testInListOK(self):
|
||||
ticket = BanTicket('193.168.0.128', 1167605999.0)
|
||||
self.assertTrue(self.__banManager.inBanList(ticket))
|
||||
self.assertTrue(self.__banManager._inBanList(ticket))
|
||||
|
||||
def _testInListNOK(self):
|
||||
def testInListNOK(self):
|
||||
ticket = BanTicket('111.111.1.111', 1167605999.0)
|
||||
self.assertFalse(self.__banManager.inBanList(ticket))
|
||||
self.assertFalse(self.__banManager._inBanList(ticket))
|
||||
|
||||
|
|
|
@ -17,26 +17,92 @@
|
|||
# along with Fail2Ban; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__author__ = "Cyril Jaquier, Yaroslav Halchenko"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2013 Yaroslav Halchenko"
|
||||
__license__ = "GPL"
|
||||
|
||||
import unittest
|
||||
import os, shutil, tempfile, unittest
|
||||
from client.configreader import ConfigReader
|
||||
from client.jailreader import JailReader
|
||||
from client.jailsreader import JailsReader
|
||||
from client.configurator import Configurator
|
||||
|
||||
class JailReaderTest(unittest.TestCase):
|
||||
class ConfigReaderTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Call before every test case."""
|
||||
self.d = tempfile.mkdtemp(prefix="f2b-temp")
|
||||
self.c = ConfigReader(basedir=self.d)
|
||||
|
||||
def tearDown(self):
|
||||
"""Call after every test case."""
|
||||
shutil.rmtree(self.d)
|
||||
|
||||
def _write(self, fname, value):
|
||||
# verify if we don't need to create .d directory
|
||||
if os.path.sep in fname:
|
||||
d = os.path.dirname(fname)
|
||||
d_ = os.path.join(self.d, d)
|
||||
if not os.path.exists(d_):
|
||||
os.makedirs(d_)
|
||||
open("%s/%s" % (self.d, fname), "w").write("""
|
||||
[section]
|
||||
option = %s
|
||||
""" % value)
|
||||
|
||||
def _remove(self, fname):
|
||||
os.unlink("%s/%s" % (self.d, fname))
|
||||
self.assertTrue(self.c.read('c')) # we still should have some
|
||||
|
||||
|
||||
def _getoption(self, f='c'):
|
||||
self.assertTrue(self.c.read(f)) # we got some now
|
||||
return self.c.getOptions('section', [("int", 'option')])['option']
|
||||
|
||||
|
||||
def testInaccessibleFile(self):
|
||||
f = os.path.join(self.d, "d.conf") # inaccessible file
|
||||
self._write('d.conf', 0)
|
||||
self.assertEqual(self._getoption('d'), 0)
|
||||
os.chmod(f, 0)
|
||||
self.assertFalse(self.c.read('d')) # should not be readable BUT present
|
||||
|
||||
|
||||
def testOptionalDotDDir(self):
|
||||
self.assertFalse(self.c.read('c')) # nothing is there yet
|
||||
self._write("c.conf", "1")
|
||||
self.assertEqual(self._getoption(), 1)
|
||||
self._write("c.conf", "2") # overwrite
|
||||
self.assertEqual(self._getoption(), 2)
|
||||
self._write("c.local", "3") # add override in .local
|
||||
self.assertEqual(self._getoption(), 3)
|
||||
self._write("c.d/98.conf", "998") # add 1st override in .d/
|
||||
self.assertEqual(self._getoption(), 998)
|
||||
self._write("c.d/90.conf", "990") # add previously sorted override in .d/
|
||||
self.assertEqual(self._getoption(), 998) # should stay the same
|
||||
self._write("c.d/99.conf", "999") # now override in a way without sorting we possibly get a failure
|
||||
self.assertEqual(self._getoption(), 999)
|
||||
self._remove("c.d/99.conf")
|
||||
self.assertEqual(self._getoption(), 998)
|
||||
self._remove("c.d/98.conf")
|
||||
self.assertEqual(self._getoption(), 990)
|
||||
self._remove("c.d/90.conf")
|
||||
self.assertEqual(self._getoption(), 3)
|
||||
self._remove("c.conf") # we allow to stay without .conf
|
||||
self.assertEqual(self._getoption(), 3)
|
||||
self._write("c.conf", "1")
|
||||
self._remove("c.local")
|
||||
self.assertEqual(self._getoption(), 1)
|
||||
|
||||
|
||||
class JailReaderTest(unittest.TestCase):
|
||||
|
||||
def testStockSSHJail(self):
|
||||
jail = JailReader('ssh-iptables', basedir='config') # we are running tests from root project dir atm
|
||||
self.assertTrue(jail.read())
|
||||
self.assertTrue(jail.getOptions())
|
||||
self.assertFalse(jail.isEnabled())
|
||||
self.assertEqual(jail.getName(), 'ssh-iptables')
|
||||
|
||||
def testSplitAction(self):
|
||||
action = "mail-whois[name=SSH]"
|
||||
|
@ -44,3 +110,59 @@ class JailReaderTest(unittest.TestCase):
|
|||
result = JailReader.splitAction(action)
|
||||
self.assertEquals(expected, result)
|
||||
|
||||
class JailsReaderTest(unittest.TestCase):
|
||||
|
||||
def testProvidingBadBasedir(self):
|
||||
if not os.path.exists('/XXX'):
|
||||
reader = JailsReader(basedir='/XXX')
|
||||
self.assertRaises(ValueError, reader.read)
|
||||
|
||||
def testReadStockJailConf(self):
|
||||
jails = JailsReader(basedir='config') # we are running tests from root project dir atm
|
||||
self.assertTrue(jails.read()) # opens fine
|
||||
self.assertTrue(jails.getOptions()) # reads fine
|
||||
comm_commands = jails.convert()
|
||||
# by default None of the jails is enabled and we get no
|
||||
# commands to communicate to the server
|
||||
self.assertEqual(comm_commands, [])
|
||||
|
||||
def testReadStockJailConfForceEnabled(self):
|
||||
# more of a smoke test to make sure that no obvious surprises
|
||||
# on users' systems when enabling shipped jails
|
||||
jails = JailsReader(basedir='config', force_enable=True) # we are running tests from root project dir atm
|
||||
self.assertTrue(jails.read()) # opens fine
|
||||
self.assertTrue(jails.getOptions()) # reads fine
|
||||
comm_commands = jails.convert()
|
||||
|
||||
# by default we have lots of jails ;)
|
||||
self.assertTrue(len(comm_commands))
|
||||
|
||||
# and we know even some of them by heart
|
||||
for j in ['ssh-iptables', 'recidive']:
|
||||
# by default we have 'auto' backend ATM
|
||||
self.assertTrue(['add', j, 'auto'] in comm_commands)
|
||||
# and warn on useDNS
|
||||
self.assertTrue(['set', j, 'usedns', 'warn'] in comm_commands)
|
||||
self.assertTrue(['start', j] in comm_commands)
|
||||
# last commands should be the 'start' commands
|
||||
self.assertEqual(comm_commands[-1][0], 'start')
|
||||
# TODO: make sure that all of the jails have actions assigned,
|
||||
# otherwise it makes little to no sense
|
||||
|
||||
def testConfigurator(self):
|
||||
configurator = Configurator()
|
||||
configurator.setBaseDir('config')
|
||||
self.assertEqual(configurator.getBaseDir(), 'config')
|
||||
|
||||
configurator.readEarly()
|
||||
opts = configurator.getEarlyOptions()
|
||||
# our current default settings
|
||||
self.assertEqual(opts['socket'], '/var/run/fail2ban/fail2ban.sock')
|
||||
self.assertEqual(opts['pidfile'], '/var/run/fail2ban/fail2ban.pid')
|
||||
|
||||
# and if we force change configurator's fail2ban's baseDir
|
||||
# there should be an error message (test visually ;) --
|
||||
# otherwise just a code smoke test)
|
||||
configurator._Configurator__jails.setBaseDir('/tmp')
|
||||
self.assertEqual(configurator._Configurator__jails.getBaseDir(), '/tmp')
|
||||
self.assertEqual(configurator.getBaseDir(), 'config')
|
||||
|
|
|
@ -51,30 +51,44 @@ class DateDetectorTest(unittest.TestCase):
|
|||
|
||||
def testGetTime(self):
|
||||
log = "Jan 23 21:59:59 [sshd] error: PAM: Authentication failure"
|
||||
date = [2005, 1, 23, 21, 59, 59, 1, 23, -1]
|
||||
date = [2005, 1, 23, 21, 59, 59, 6, 23, -1]
|
||||
dateUnix = 1106513999.0
|
||||
|
||||
self.assertEqual(self.__datedetector.getTime(log), date)
|
||||
# yoh: testing only up to 6 elements, since the day of the week
|
||||
# is not correctly determined atm, since year is not present
|
||||
# in the log entry. Since this doesn't effect the operation
|
||||
# of fail2ban -- we just ignore incorrect day of the week
|
||||
self.assertEqual(self.__datedetector.getTime(log)[:6], date[:6])
|
||||
self.assertEqual(self.__datedetector.getUnixTime(log), dateUnix)
|
||||
|
||||
def testVariousTimes(self):
|
||||
"""Test detection of various common date/time formats f2b should understand
|
||||
"""
|
||||
date = [2005, 1, 23, 21, 59, 59, 1, 23, -1]
|
||||
date = [2005, 1, 23, 21, 59, 59, 6, 23, -1]
|
||||
dateUnix = 1106513999.0
|
||||
|
||||
for sdate in (
|
||||
"Jan 23 21:59:59",
|
||||
"Sun Jan 23 21:59:59 2005",
|
||||
"Sun Jan 23 21:59:59",
|
||||
"2005/01/23 21:59:59",
|
||||
"2005.01.23 21:59:59",
|
||||
"23/01/2005 21:59:59",
|
||||
"23/01/05 21:59:59",
|
||||
"23/Jan/2005:21:59:59",
|
||||
"01/23/2005:21:59:59",
|
||||
"2005-01-23 21:59:59",
|
||||
"23-Jan-2005 21:59:59",
|
||||
"23-01-2005 21:59:59",
|
||||
"01-23-2005 21:59:59.252", # reported on f2b, causes Feb29 fix to break
|
||||
"@4000000041f4104f00000000", # TAI64N
|
||||
"2005-01-23T21:59:59.252Z", #ISO 8601
|
||||
"2005-01-23T21:59:59-05:00Z", #ISO 8601 with TZ
|
||||
"<01/23/05@21:59:59>",
|
||||
):
|
||||
log = sdate + "[sshd] error: PAM: Authentication failure"
|
||||
# exclude
|
||||
|
||||
# TODO (Yarik is confused): figure out why for above it is
|
||||
# "1" as day of the week which would be Tue, although it
|
||||
# was Sun
|
||||
# yoh: on [:6] see in above test
|
||||
self.assertEqual(self.__datedetector.getTime(log)[:6], date[:6])
|
||||
self.assertEqual(self.__datedetector.getUnixTime(log), dateUnix)
|
||||
|
||||
|
@ -89,6 +103,27 @@ class DateDetectorTest(unittest.TestCase):
|
|||
self.assertRaises(ValueError, self.__datedetector._appendTemplate,
|
||||
self.__datedetector.getTemplates()[0])
|
||||
|
||||
def testFullYearMatch_gh130(self):
|
||||
# see https://github.com/fail2ban/fail2ban/pull/130
|
||||
# yoh: unfortunately this test is not really effective to reproduce the
|
||||
# situation but left in place to assure consistent behavior
|
||||
m1 = [2012, 10, 11, 2, 37, 17]
|
||||
self.assertEqual(
|
||||
self.__datedetector.getTime('2012/10/11 02:37:17 [error] 18434#0')[:6],
|
||||
m1)
|
||||
self.__datedetector.sortTemplate()
|
||||
# confuse it with year being at the end
|
||||
for i in xrange(10):
|
||||
self.assertEqual(
|
||||
self.__datedetector.getTime('11/10/2012 02:37:17 [error] 18434#0')[:6],
|
||||
m1)
|
||||
self.__datedetector.sortTemplate()
|
||||
# and now back to the original
|
||||
self.assertEqual(
|
||||
self.__datedetector.getTime('2012/10/11 02:37:17 [error] 18434#0')[:6],
|
||||
m1)
|
||||
|
||||
|
||||
# def testDefaultTempate(self):
|
||||
# self.__datedetector.setDefaultRegex("^\S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}")
|
||||
# self.__datedetector.setDefaultPattern("%b %d %H:%M:%S")
|
||||
|
|
|
@ -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=
|
|
@ -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
|
|
@ -38,11 +38,17 @@ from server.failmanager import FailManagerEmpty
|
|||
# Useful helpers
|
||||
#
|
||||
|
||||
# yoh: per Steven Hiscocks's insight while troubleshooting
|
||||
# https://github.com/fail2ban/fail2ban/issues/103#issuecomment-15542836
|
||||
# adding a sufficiently large buffer might help to guarantee that
|
||||
# writes happen atomically.
|
||||
# Overload also for python3 to use utf-8 encoding by default
|
||||
if sys.version_info >= (3,):
|
||||
def open_(filename, mode):
|
||||
return open(filename, mode, encoding='utf-8', errors='ignore')
|
||||
return open(filename, mode, 50000, encoding='utf-8', errors='ignore')
|
||||
else:
|
||||
open_ = open
|
||||
def open_(filename, mode):
|
||||
return open(filename, mode, 50000)
|
||||
|
||||
def _killfile(f, name):
|
||||
try:
|
||||
|
@ -54,6 +60,11 @@ def _killfile(f, name):
|
|||
except:
|
||||
pass
|
||||
|
||||
# there might as well be the .bak file
|
||||
if os.path.exists(name + '.bak'):
|
||||
_killfile(None, name + '.bak')
|
||||
|
||||
|
||||
def _sleep_4_poll():
|
||||
"""PollFilter relies on file timestamps - so we might need to
|
||||
sleep to guarantee that they differ
|
||||
|
@ -105,26 +116,29 @@ def _copy_lines_between_files(fin, fout, n=None, skip=0, mode='a', terminal_line
|
|||
|
||||
Returns open fout
|
||||
"""
|
||||
if sys.version_info[:2] <= (2,4):
|
||||
if sys.version_info[:2] <= (2,4): # pragma: no cover
|
||||
# on old Python st_mtime is int, so we should give at least 1 sec so
|
||||
# polling filter could detect the change
|
||||
time.sleep(1)
|
||||
if isinstance(fin, str):
|
||||
if isinstance(fin, str): # pragma: no branch - only used with str in test cases
|
||||
fin = open_(fin, 'r')
|
||||
if isinstance(fout, str):
|
||||
fout = open_(fout, mode)
|
||||
# Skip
|
||||
for i in xrange(skip):
|
||||
_ = fin.readline()
|
||||
# Read/Write
|
||||
# Read
|
||||
i = 0
|
||||
lines = []
|
||||
while n is None or i < n:
|
||||
l = fin.readline()
|
||||
if terminal_line is not None and l == terminal_line:
|
||||
break
|
||||
fout.write(l)
|
||||
fout.flush()
|
||||
lines.append(l)
|
||||
i += 1
|
||||
# Write: all at once and flush
|
||||
if isinstance(fout, str):
|
||||
fout = open_(fout, mode)
|
||||
fout.write('\n'.join(lines))
|
||||
fout.flush()
|
||||
# to give other threads possibly some time to crunch
|
||||
time.sleep(0.1)
|
||||
return fout
|
||||
|
@ -330,11 +344,17 @@ def get_monitor_failures_testcase(Filter_):
|
|||
"""Generator of TestCase's for different filters/backends
|
||||
"""
|
||||
|
||||
# add Filter_'s name so we could easily identify bad cows
|
||||
testclass_name = tempfile.mktemp(
|
||||
'fail2ban', 'monitorfailures_%s' % (Filter_.__name__,))
|
||||
|
||||
class MonitorFailures(unittest.TestCase):
|
||||
count = 0
|
||||
def setUp(self):
|
||||
"""Call before every test case."""
|
||||
self.filter = self.name = 'NA'
|
||||
_, self.name = tempfile.mkstemp('fail2ban', 'monitorfailures')
|
||||
self.name = '%s-%d' % (testclass_name, self.count)
|
||||
MonitorFailures.count += 1 # so we have unique filenames across tests
|
||||
self.file = open_(self.name, 'a')
|
||||
self.jail = DummyJail()
|
||||
self.filter = Filter_(self.jail)
|
||||
|
@ -357,12 +377,9 @@ def get_monitor_failures_testcase(Filter_):
|
|||
self.filter.join() # wait for the thread to terminate
|
||||
#print "D: KILLING THE FILE"
|
||||
_killfile(self.file, self.name)
|
||||
#time.sleep(0.2) # Give FS time to ack the removal
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
return "MonitorFailures%s(%s)" \
|
||||
% (Filter_, hasattr(self, 'name') and self.name or 'tempfile')
|
||||
|
||||
def isFilled(self, delay=2.):
|
||||
"""Wait up to `delay` sec to assure that it was modified or not
|
||||
"""
|
||||
|
@ -385,7 +402,7 @@ def get_monitor_failures_testcase(Filter_):
|
|||
return not self.isFilled(delay)
|
||||
|
||||
def assert_correct_last_attempt(self, failures, count=None):
|
||||
self.assertTrue(self.isFilled(10)) # give Filter a chance to react
|
||||
self.assertTrue(self.isFilled(20)) # give Filter a chance to react
|
||||
_assert_correct_last_attempt(self, self.jail, failures, count=count)
|
||||
|
||||
|
||||
|
@ -437,9 +454,10 @@ def get_monitor_failures_testcase(Filter_):
|
|||
# if we move file into a new location while it has been open already
|
||||
self.file = _copy_lines_between_files(GetFailures.FILENAME_01, self.name,
|
||||
n=14, mode='w')
|
||||
self.assertTrue(self.isEmpty(2))
|
||||
# Poll might need more time
|
||||
self.assertTrue(self.isEmpty(4 + int(isinstance(self.filter, FilterPoll))*2))
|
||||
self.assertRaises(FailManagerEmpty, self.filter.failManager.toBan)
|
||||
self.assertEqual(self.filter.failManager.getFailTotal(), 2) # Fails with Poll from time to time
|
||||
self.assertEqual(self.filter.failManager.getFailTotal(), 2)
|
||||
|
||||
# move aside, but leaving the handle still open...
|
||||
os.rename(self.name, self.name + '.bak')
|
||||
|
@ -494,7 +512,8 @@ def get_monitor_failures_testcase(Filter_):
|
|||
# yoh: not sure why count here is not 9... TODO
|
||||
self.assert_correct_last_attempt(GetFailures.FAILURES_01)#, count=9)
|
||||
|
||||
|
||||
MonitorFailures.__name__ = "MonitorFailures<%s>(%s)" \
|
||||
% (Filter_.__name__, testclass_name) # 'tempfile')
|
||||
return MonitorFailures
|
||||
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ __license__ = "GPL"
|
|||
|
||||
import unittest, socket, time, tempfile, os, locale
|
||||
from server.server import Server
|
||||
from common.exceptions import UnknownJailException
|
||||
|
||||
class StartStop(unittest.TestCase):
|
||||
|
||||
|
@ -49,27 +50,29 @@ class StartStop(unittest.TestCase):
|
|||
time.sleep(1)
|
||||
self.__server.stopJail(name)
|
||||
|
||||
class TestServer(Server):
|
||||
def setLogLevel(self, *args, **kwargs):
|
||||
pass
|
||||
def setLogTarget(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
class Transmitter(unittest.TestCase):
|
||||
class TransmitterBase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Call before every test case."""
|
||||
self.__server = Server()
|
||||
self.__transm = self.__server._Server__transm
|
||||
self.__server.setLogTarget("/dev/null")
|
||||
self.__server.setLogLevel(0)
|
||||
self.transm = self.server._Server__transm
|
||||
sock_fd, sock_name = tempfile.mkstemp('fail2ban.sock', 'transmitter')
|
||||
os.close(sock_fd)
|
||||
pidfile_fd, pidfile_name = tempfile.mkstemp(
|
||||
'fail2ban.pid', 'transmitter')
|
||||
os.close(pidfile_fd)
|
||||
self.__server.start(sock_name, pidfile_name, force=False)
|
||||
self.server.start(sock_name, pidfile_name, force=False)
|
||||
self.jailName = "TestJail1"
|
||||
self.__server.addJail(self.jailName, "auto")
|
||||
self.server.addJail(self.jailName, "auto")
|
||||
|
||||
def tearDown(self):
|
||||
"""Call after every test case."""
|
||||
self.__server.quit()
|
||||
self.server.quit()
|
||||
|
||||
def setGetTest(self, cmd, inValue, outValue=None, jail=None):
|
||||
setCmd = ["set", cmd, inValue]
|
||||
|
@ -80,8 +83,8 @@ class Transmitter(unittest.TestCase):
|
|||
if outValue is None:
|
||||
outValue = inValue
|
||||
|
||||
self.assertEqual(self.__transm.proceed(setCmd), (0, outValue))
|
||||
self.assertEqual(self.__transm.proceed(getCmd), (0, outValue))
|
||||
self.assertEqual(self.transm.proceed(setCmd), (0, outValue))
|
||||
self.assertEqual(self.transm.proceed(getCmd), (0, outValue))
|
||||
|
||||
def setGetTestNOK(self, cmd, inValue, jail=None):
|
||||
setCmd = ["set", cmd, inValue]
|
||||
|
@ -91,30 +94,30 @@ class Transmitter(unittest.TestCase):
|
|||
getCmd.insert(1, jail)
|
||||
|
||||
# Get initial value before trying invalid value
|
||||
initValue = self.__transm.proceed(getCmd)[1]
|
||||
self.assertEqual(self.__transm.proceed(setCmd)[0], 1)
|
||||
initValue = self.transm.proceed(getCmd)[1]
|
||||
self.assertEqual(self.transm.proceed(setCmd)[0], 1)
|
||||
# Check after failed set that value is same as previous
|
||||
self.assertEqual(self.__transm.proceed(getCmd), (0, initValue))
|
||||
self.assertEqual(self.transm.proceed(getCmd), (0, initValue))
|
||||
|
||||
def jailAddDelTest(self, cmd, values, jail):
|
||||
cmdAdd = "add" + cmd
|
||||
cmdDel = "del" + cmd
|
||||
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["get", jail, cmd]), (0, []))
|
||||
self.transm.proceed(["get", jail, cmd]), (0, []))
|
||||
for n, value in enumerate(values):
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", jail, cmdAdd, value]),
|
||||
self.transm.proceed(["set", jail, cmdAdd, value]),
|
||||
(0, values[:n+1]))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["get", jail, cmd]),
|
||||
self.transm.proceed(["get", jail, cmd]),
|
||||
(0, values[:n+1]))
|
||||
for n, value in enumerate(values):
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", jail, cmdDel, value]),
|
||||
self.transm.proceed(["set", jail, cmdDel, value]),
|
||||
(0, values[n+1:]))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["get", jail, cmd]),
|
||||
self.transm.proceed(["get", jail, cmd]),
|
||||
(0, values[n+1:]))
|
||||
|
||||
def jailAddDelRegexTest(self, cmd, inValues, outValues, jail):
|
||||
|
@ -125,87 +128,95 @@ class Transmitter(unittest.TestCase):
|
|||
outValues = inValues
|
||||
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["get", jail, cmd]), (0, []))
|
||||
self.transm.proceed(["get", jail, cmd]), (0, []))
|
||||
for n, value in enumerate(inValues):
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", jail, cmdAdd, value]),
|
||||
self.transm.proceed(["set", jail, cmdAdd, value]),
|
||||
(0, outValues[:n+1]))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["get", jail, cmd]),
|
||||
self.transm.proceed(["get", jail, cmd]),
|
||||
(0, outValues[:n+1]))
|
||||
for n, value in enumerate(inValues):
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", jail, cmdDel, 0]), # First item
|
||||
self.transm.proceed(["set", jail, cmdDel, 0]), # First item
|
||||
(0, outValues[n+1:]))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["get", jail, cmd]),
|
||||
self.transm.proceed(["get", jail, cmd]),
|
||||
(0, outValues[n+1:]))
|
||||
|
||||
class Transmitter(TransmitterBase):
|
||||
|
||||
def setUp(self):
|
||||
self.server = TestServer()
|
||||
super(Transmitter, self).setUp()
|
||||
|
||||
def testStopServer(self):
|
||||
self.assertEqual(self.transm.proceed(["stop"]), (0, None))
|
||||
|
||||
def testPing(self):
|
||||
self.assertEqual(self.__transm.proceed(["ping"]), (0, "pong"))
|
||||
self.assertEqual(self.transm.proceed(["ping"]), (0, "pong"))
|
||||
|
||||
def testSleep(self):
|
||||
t0 = time.time()
|
||||
self.assertEqual(self.__transm.proceed(["sleep", "1"]), (0, None))
|
||||
self.assertEqual(self.transm.proceed(["sleep", "1"]), (0, None))
|
||||
t1 = time.time()
|
||||
# Approx 1 second delay
|
||||
self.assertAlmostEqual(t1 - t0, 1, places=2)
|
||||
|
||||
def testLogTarget(self):
|
||||
logTargets = []
|
||||
for _ in xrange(3):
|
||||
tmpFile = tempfile.mkstemp("fail2ban", "transmitter")
|
||||
logTargets.append(tmpFile[1])
|
||||
os.close(tmpFile[0])
|
||||
for logTarget in logTargets:
|
||||
self.setGetTest("logtarget", logTarget)
|
||||
|
||||
# If path is invalid, do not change logtarget
|
||||
value = "/this/path/should/not/exist"
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", "logtarget", value]),
|
||||
(0, logTarget)) #NOTE: Shouldn't this return 1
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["get", "logtarget"]), (0, logTargets[-1]))
|
||||
|
||||
self.__transm.proceed(["set", "/dev/null"])
|
||||
for logTarget in logTargets:
|
||||
os.remove(logTarget)
|
||||
|
||||
def testLogLevel(self):
|
||||
self.setGetTest("loglevel", "4", 4)
|
||||
self.setGetTest("loglevel", "2", 2)
|
||||
self.setGetTest("loglevel", "-1", -1)
|
||||
self.setGetTestNOK("loglevel", "Bird")
|
||||
|
||||
def testAddJail(self):
|
||||
jail2 = "TestJail2"
|
||||
jail3 = "TestJail3"
|
||||
jail4 = "TestJail4"
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["add", jail2, "polling"]), (0, jail2))
|
||||
self.assertEqual(self.__transm.proceed(["add", jail3]), (0, jail3))
|
||||
self.transm.proceed(["add", jail2, "polling"]), (0, jail2))
|
||||
self.assertEqual(self.transm.proceed(["add", jail3]), (0, jail3))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["add", jail4, "invalid backend"])[0], 1)
|
||||
self.transm.proceed(["add", jail4, "invalid backend"])[0], 1)
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["add", jail4, "auto"]), (0, jail4))
|
||||
self.transm.proceed(["add", jail4, "auto"]), (0, jail4))
|
||||
# Duplicate Jail
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["add", self.jailName, "polling"])[0], 1)
|
||||
self.transm.proceed(["add", self.jailName, "polling"])[0], 1)
|
||||
# All name is reserved
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["add", "all", "polling"])[0], 1)
|
||||
self.transm.proceed(["add", "all", "polling"])[0], 1)
|
||||
|
||||
def testStartStopJail(self):
|
||||
self.assertEqual(
|
||||
self.transm.proceed(["start", self.jailName]), (0, None))
|
||||
time.sleep(1)
|
||||
self.assertEqual(
|
||||
self.transm.proceed(["stop", self.jailName]), (0, None))
|
||||
self.assertRaises(
|
||||
UnknownJailException, self.server.isAlive, self.jailName)
|
||||
|
||||
def testStartStopAllJail(self):
|
||||
self.server.addJail("TestJail2", "auto")
|
||||
self.assertEqual(
|
||||
self.transm.proceed(["start", self.jailName]), (0, None))
|
||||
self.assertEqual(
|
||||
self.transm.proceed(["start", "TestJail2"]), (0, None))
|
||||
# yoh: workaround for gh-146. I still think that there is some
|
||||
# race condition and missing locking somewhere, but for now
|
||||
# giving it a small delay reliably helps to proceed with tests
|
||||
time.sleep(0.1)
|
||||
self.assertEqual(self.transm.proceed(["stop", "all"]), (0, None))
|
||||
time.sleep(1)
|
||||
self.assertRaises(
|
||||
UnknownJailException, self.server.isAlive, self.jailName)
|
||||
self.assertRaises(
|
||||
UnknownJailException, self.server.isAlive, "TestJail2")
|
||||
|
||||
def testJailIdle(self):
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", self.jailName, "idle", "on"]),
|
||||
self.transm.proceed(["set", self.jailName, "idle", "on"]),
|
||||
(0, True))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", self.jailName, "idle", "off"]),
|
||||
self.transm.proceed(["set", self.jailName, "idle", "off"]),
|
||||
(0, False))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", self.jailName, "idle", "CAT"])[0],
|
||||
0) #NOTE: Should this return 1
|
||||
self.transm.proceed(["set", self.jailName, "idle", "CAT"])[0],
|
||||
1)
|
||||
|
||||
def testJailFindTime(self):
|
||||
self.setGetTest("findtime", "120", 120, jail=self.jailName)
|
||||
|
@ -227,30 +238,29 @@ class Transmitter(unittest.TestCase):
|
|||
# Safe default should be "no"
|
||||
value = "Fish"
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", self.jailName, "usedns", value]),
|
||||
self.transm.proceed(["set", self.jailName, "usedns", value]),
|
||||
(0, "no"))
|
||||
|
||||
def testJailBanIP(self):
|
||||
self.__server.startJail(self.jailName) # Jail must be started
|
||||
self.server.startJail(self.jailName) # Jail must be started
|
||||
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", self.jailName, "banip", "127.0.0.1"]),
|
||||
self.transm.proceed(["set", self.jailName, "banip", "127.0.0.1"]),
|
||||
(0, "127.0.0.1"))
|
||||
time.sleep(1) # Give chance to ban
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", self.jailName, "banip", "Badger"]),
|
||||
self.transm.proceed(["set", self.jailName, "banip", "Badger"]),
|
||||
(0, "Badger")) #NOTE: Is IP address validated? Is DNS Lookup done?
|
||||
time.sleep(1) # Give chance to ban
|
||||
# Unban IP
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(
|
||||
self.transm.proceed(
|
||||
["set", self.jailName, "unbanip", "127.0.0.1"]),
|
||||
(0, "127.0.0.1"))
|
||||
# Unban IP which isn't banned
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(
|
||||
["set", self.jailName, "unbanip", "192.168.1.1"]),
|
||||
(0, "None")) #NOTE: Should this return 1?
|
||||
self.transm.proceed(
|
||||
["set", self.jailName, "unbanip", "192.168.1.1"])[0],1)
|
||||
|
||||
def testJailMaxRetry(self):
|
||||
self.setGetTest("maxretry", "5", 5, jail=self.jailName)
|
||||
|
@ -278,22 +288,22 @@ class Transmitter(unittest.TestCase):
|
|||
# Try duplicates
|
||||
value = "testcases/files/testcase04.log"
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", self.jailName, "addlogpath", value]),
|
||||
self.transm.proceed(["set", self.jailName, "addlogpath", value]),
|
||||
(0, [value]))
|
||||
# Will silently ignore duplicate
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", self.jailName, "addlogpath", value]),
|
||||
self.transm.proceed(["set", self.jailName, "addlogpath", value]),
|
||||
(0, [value]))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["get", self.jailName, "logpath"]),
|
||||
self.transm.proceed(["get", self.jailName, "logpath"]),
|
||||
(0, [value]))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", self.jailName, "dellogpath", value]),
|
||||
self.transm.proceed(["set", self.jailName, "dellogpath", value]),
|
||||
(0, []))
|
||||
|
||||
# Invalid file
|
||||
value = "this_file_shouldn't_exist"
|
||||
result = self.__transm.proceed(
|
||||
result = self.transm.proceed(
|
||||
["set", self.jailName, "addlogpath", value])
|
||||
self.assertTrue(isinstance(result[1], IOError))
|
||||
|
||||
|
@ -311,18 +321,18 @@ class Transmitter(unittest.TestCase):
|
|||
# Try duplicates
|
||||
value = "127.0.0.1"
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", self.jailName, "addignoreip", value]),
|
||||
self.transm.proceed(["set", self.jailName, "addignoreip", value]),
|
||||
(0, [value]))
|
||||
# Will allow duplicate
|
||||
#NOTE: Should duplicates be allowed, or silent ignore like logpath?
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", self.jailName, "addignoreip", value]),
|
||||
self.transm.proceed(["set", self.jailName, "addignoreip", value]),
|
||||
(0, [value, value]))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["get", self.jailName, "ignoreip"]),
|
||||
self.transm.proceed(["get", self.jailName, "ignoreip"]),
|
||||
(0, [value, value]))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", self.jailName, "delignoreip", value]),
|
||||
self.transm.proceed(["set", self.jailName, "delignoreip", value]),
|
||||
(0, [value]))
|
||||
|
||||
def testJailRegex(self):
|
||||
|
@ -341,11 +351,11 @@ class Transmitter(unittest.TestCase):
|
|||
)
|
||||
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(
|
||||
["set", self.jailName, "addfailregex", "No host regex"]),
|
||||
(0, [])) #NOTE: Shouldn't this return 1?
|
||||
self.transm.proceed(
|
||||
["set", self.jailName, "addfailregex", "No host regex"])[0],
|
||||
1)
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(
|
||||
self.transm.proceed(
|
||||
["set", self.jailName, "addfailregex", 654])[0],
|
||||
1)
|
||||
|
||||
|
@ -365,21 +375,25 @@ class Transmitter(unittest.TestCase):
|
|||
)
|
||||
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(
|
||||
self.transm.proceed(
|
||||
["set", self.jailName, "addignoreregex", "Invalid [regex"])[0],
|
||||
1)
|
||||
self.assertEqual(
|
||||
self.transm.proceed(
|
||||
["set", self.jailName, "addignoreregex", 50])[0],
|
||||
1)
|
||||
|
||||
def testStatus(self):
|
||||
jails = [self.jailName]
|
||||
self.assertEqual(self.__transm.proceed(["status"]),
|
||||
self.assertEqual(self.transm.proceed(["status"]),
|
||||
(0, [('Number of jail', len(jails)), ('Jail list', ", ".join(jails))]))
|
||||
self.__server.addJail("TestJail2", "auto")
|
||||
self.server.addJail("TestJail2", "auto")
|
||||
jails.append("TestJail2")
|
||||
self.assertEqual(self.__transm.proceed(["status"]),
|
||||
self.assertEqual(self.transm.proceed(["status"]),
|
||||
(0, [('Number of jail', len(jails)), ('Jail list', ", ".join(jails))]))
|
||||
|
||||
def testJailStatus(self):
|
||||
self.assertEqual(self.__transm.proceed(["status", self.jailName]),
|
||||
self.assertEqual(self.transm.proceed(["status", self.jailName]),
|
||||
(0,
|
||||
[
|
||||
('filter', [
|
||||
|
@ -414,37 +428,90 @@ class Transmitter(unittest.TestCase):
|
|||
]
|
||||
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", self.jailName, "addaction", action]),
|
||||
self.transm.proceed(["set", self.jailName, "addaction", action]),
|
||||
(0, action))
|
||||
self.assertEqual(
|
||||
self.transm.proceed(["get", self.jailName, "addaction", action]),
|
||||
(0, action))
|
||||
for cmd, value in zip(cmdList, cmdValueList):
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(
|
||||
self.transm.proceed(
|
||||
["set", self.jailName, cmd, action, value]),
|
||||
(0, value))
|
||||
for cmd, value in zip(cmdList, cmdValueList):
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["get", self.jailName, cmd, action]),
|
||||
self.transm.proceed(["get", self.jailName, cmd, action]),
|
||||
(0, value))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(
|
||||
self.transm.proceed(
|
||||
["set", self.jailName, "setcinfo", action, "KEY", "VALUE"]),
|
||||
(0, "VALUE"))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(
|
||||
self.transm.proceed(
|
||||
["get", self.jailName, "cinfo", action, "KEY"]),
|
||||
(0, "VALUE"))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(
|
||||
self.transm.proceed(
|
||||
["get", self.jailName, "cinfo", action, "InvalidKey"])[0],
|
||||
1)
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(
|
||||
self.transm.proceed(
|
||||
["set", self.jailName, "delcinfo", action, "KEY"]),
|
||||
(0, None))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", self.jailName, "delaction", action]),
|
||||
self.transm.proceed(["set", self.jailName, "delaction", action]),
|
||||
(0, None))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(
|
||||
["set", self.jailName, "delaction", "Doesn't exist"]),
|
||||
(0, None)) #NOTE: Should this return 1?
|
||||
self.transm.proceed(
|
||||
["set", self.jailName, "delaction", "Doesn't exist"])[0],1)
|
||||
|
||||
def testNOK(self):
|
||||
self.assertEqual(self.transm.proceed(["INVALID", "COMMAND"])[0],1)
|
||||
|
||||
def testSetNOK(self):
|
||||
self.assertEqual(
|
||||
self.transm.proceed(["set", "INVALID", "COMMAND"])[0],1)
|
||||
|
||||
def testGetNOK(self):
|
||||
self.assertEqual(
|
||||
self.transm.proceed(["get", "INVALID", "COMMAND"])[0],1)
|
||||
|
||||
def testStatusNOK(self):
|
||||
self.assertEqual(
|
||||
self.transm.proceed(["status", "INVALID", "COMMAND"])[0],1)
|
||||
|
||||
class TransmitterLogging(TransmitterBase):
|
||||
|
||||
def setUp(self):
|
||||
self.server = Server()
|
||||
self.server.setLogTarget("/dev/null")
|
||||
self.server.setLogLevel(0)
|
||||
super(TransmitterLogging, self).setUp()
|
||||
|
||||
def testLogTarget(self):
|
||||
logTargets = []
|
||||
for _ in xrange(3):
|
||||
tmpFile = tempfile.mkstemp("fail2ban", "transmitter")
|
||||
logTargets.append(tmpFile[1])
|
||||
os.close(tmpFile[0])
|
||||
for logTarget in logTargets:
|
||||
self.setGetTest("logtarget", logTarget)
|
||||
|
||||
# If path is invalid, do not change logtarget
|
||||
value = "/this/path/should/not/exist"
|
||||
self.setGetTestNOK("logtarget", value)
|
||||
|
||||
self.transm.proceed(["set", "/dev/null"])
|
||||
for logTarget in logTargets:
|
||||
os.remove(logTarget)
|
||||
|
||||
self.setGetTest("logtarget", "STDOUT")
|
||||
self.setGetTest("logtarget", "STDERR")
|
||||
self.setGetTest("logtarget", "SYSLOG")
|
||||
|
||||
def testLogLevel(self):
|
||||
self.setGetTest("loglevel", "4", 4)
|
||||
self.setGetTest("loglevel", "2", 2)
|
||||
self.setGetTest("loglevel", "-1", -1)
|
||||
self.setGetTest("loglevel", "0", 0)
|
||||
self.setGetTestNOK("loglevel", "Bird")
|
||||
|
|
|
@ -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))
|
|
@ -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)
|
Loading…
Reference in New Issue