mirror of https://github.com/fail2ban/fail2ban
ver. 0.9.3 (2015/08/01) - lets-all-stay-friends
---------- - IMPORTANT incompatible changes: * filter.d/roundcube-auth.conf - Changed logpath to 'errors' log (was 'userlogins') * action.d/iptables-common.conf - All calls to iptables command now use -w switch introduced in iptables 1.4.20 (some distribution could have patched their earlier base version as well) to provide this locking mechanism useful under heavy load to avoid contesting on iptables calls. If you need to disable, define 'action.d/iptables-common.local' with empty value for 'lockingopt' in `[Init]` section. * mail-whois-lines, sendmail-geoip-lines and sendmail-whois-lines actions now include by default only the first 1000 log lines in the emails. Adjust <grepopts> to augment the behavior. - Fixes: * reload in interactive mode appends all the jails twice (gh-825) * reload server/jail failed if database used (but was not changed) and some jail active (gh-1072) * filter.d/dovecot.conf - also match unknown user in passwd-file. Thanks Anton Shestakov * Fix fail2ban-regex not parsing journalmatch correctly from filter config * filter.d/asterisk.conf - fix security log support for Asterisk 12+ * filter.d/roundcube-auth.conf - Updated regex to work with 'errors' log (1.0.5 and 1.1.1) - Added regex to work with 'userlogins' log * action.d/sendmail*.conf - use LC_ALL (superseeding LC_TIME) to override locale on systems with customized LC_ALL * performance fix: minimizes connection overhead, close socket only at communication end (gh-1099) * unbanip always deletes ip from database (independent of bantime, also if currently not banned or persistent) * guarantee order of dbfile to be before dbpurgeage (gh-1048) * always set 'dbfile' before other database options (gh-1050) * kill the entire process group of the child process upon timeout (gh-1129). Otherwise could lead to resource exhaustion due to hanging whois processes. * resolve /var/run/fail2ban path in setup.py to help installation on platforms with /var/run -> /run symlink (gh-1142) - New Features: * RETURN iptables target is now a variable: <returntype> * New type of operation: pass2allow, use fail2ban for "knocking", opening a closed port by swapping blocktype and returntype * New filters: - froxlor-auth - Thanks Joern Muehlencord - apache-pass - filter Apache access log for successful authentication * New actions: - shorewall-ipset-proto6 - using proto feature of the Shorewall. Still requires manual pre-configuration of the shorewall. See the action file for detail. * New jails: - pass2allow-ftp - allows FTP traffic after successful HTTP authentication - Enhancements: * action.d/cloudflare.conf - improved documentation on how to allow multiple CF accounts, and jail.conf got new compound action definition action_cf_mwl to submit cloudflare report. * Check access to socket for more detailed logging on error (gh-595) * fail2ban-testcases man page * filter.d/apache-badbots.conf, filter.d/nginx-botsearch.conf - add HEAD method verb * Revamp of Travis and coverage automated testing * Added a space between IP address and the following colon in notification emails for easier text selection * Character detection heuristics for whois output via optional setting in mail-whois*.conf. Thanks Thomas Mayer. Not enabled by default, if _whois_command is set to be %(_whois_convert_charset)s (e.g. in action.d/mail-whois-common.local), it - detects character set of whois output (which is undefined by RFC 3912) via heuristics of the file command - converts whois data to UTF-8 character set with iconv - sends the whois output in UTF-8 character set to mail program - avoids that heirloom mailx creates binary attachment for input with unknown character set -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iEYEABECAAYFAlW8IeUACgkQjRFFY3XAJMh0agCfXXoSyOQJpf3j0hA052Yxyhr9 bSIAnA56k7DdZaqT//EvPvCugAEYPWvp =Vo7B -----END PGP SIGNATURE----- Merge tag '0.9.3' into debian ver. 0.9.3 (2015/08/01) - lets-all-stay-friends ---------- - IMPORTANT incompatible changes: * filter.d/roundcube-auth.conf - Changed logpath to 'errors' log (was 'userlogins') * action.d/iptables-common.conf - All calls to iptables command now use -w switch introduced in iptables 1.4.20 (some distribution could have patched their earlier base version as well) to provide this locking mechanism useful under heavy load to avoid contesting on iptables calls. If you need to disable, define 'action.d/iptables-common.local' with empty value for 'lockingopt' in `[Init]` section. * mail-whois-lines, sendmail-geoip-lines and sendmail-whois-lines actions now include by default only the first 1000 log lines in the emails. Adjust <grepopts> to augment the behavior. - Fixes: * reload in interactive mode appends all the jails twice (gh-825) * reload server/jail failed if database used (but was not changed) and some jail active (gh-1072) * filter.d/dovecot.conf - also match unknown user in passwd-file. Thanks Anton Shestakov * Fix fail2ban-regex not parsing journalmatch correctly from filter config * filter.d/asterisk.conf - fix security log support for Asterisk 12+ * filter.d/roundcube-auth.conf - Updated regex to work with 'errors' log (1.0.5 and 1.1.1) - Added regex to work with 'userlogins' log * action.d/sendmail*.conf - use LC_ALL (superseeding LC_TIME) to override locale on systems with customized LC_ALL * performance fix: minimizes connection overhead, close socket only at communication end (gh-1099) * unbanip always deletes ip from database (independent of bantime, also if currently not banned or persistent) * guarantee order of dbfile to be before dbpurgeage (gh-1048) * always set 'dbfile' before other database options (gh-1050) * kill the entire process group of the child process upon timeout (gh-1129). Otherwise could lead to resource exhaustion due to hanging whois processes. * resolve /var/run/fail2ban path in setup.py to help installation on platforms with /var/run -> /run symlink (gh-1142) - New Features: * RETURN iptables target is now a variable: <returntype> * New type of operation: pass2allow, use fail2ban for "knocking", opening a closed port by swapping blocktype and returntype * New filters: - froxlor-auth - Thanks Joern Muehlencord - apache-pass - filter Apache access log for successful authentication * New actions: - shorewall-ipset-proto6 - using proto feature of the Shorewall. Still requires manual pre-configuration of the shorewall. See the action file for detail. * New jails: - pass2allow-ftp - allows FTP traffic after successful HTTP authentication - Enhancements: * action.d/cloudflare.conf - improved documentation on how to allow multiple CF accounts, and jail.conf got new compound action definition action_cf_mwl to submit cloudflare report. * Check access to socket for more detailed logging on error (gh-595) * fail2ban-testcases man page * filter.d/apache-badbots.conf, filter.d/nginx-botsearch.conf - add HEAD method verb * Revamp of Travis and coverage automated testing * Added a space between IP address and the following colon in notification emails for easier text selection * Character detection heuristics for whois output via optional setting in mail-whois*.conf. Thanks Thomas Mayer. Not enabled by default, if _whois_command is set to be %(_whois_convert_charset)s (e.g. in action.d/mail-whois-common.local), it - detects character set of whois output (which is undefined by RFC 3912) via heuristics of the file command - converts whois data to UTF-8 character set with iconv - sends the whois output in UTF-8 character set to mail program - avoids that heirloom mailx creates binary attachment for input with unknown character set * tag '0.9.3': (99 commits) Release changes (too much of manual "labor"! ;)) BF: realpath for /var/run/fail2ban Closes #1142 Changelog entry for killpg fix Changelog entries for Serge's fixes bug fix: option 'dbpurgeage' was never set (always default) by start of fail2ban, because of invalid sorting of options ('dbfile' should be always set before other database options) / closes #1048, closes #1050 BF: guarantee order of dbfile to be before dbpurgeage (Closes #1048) DOC: Changelog for shorewall-ipset-proto6.conf + adjusted its description DOC: moved and adjusted changelog entry from 0.9.2 within 0.9.3 to come TST: test to verify killing stuck children processes BF: kill the entire process group upon timeout (Close #1129) Limit the number of log lines in *-lines.conf actions ipjailmatches is on one line with its description in man jail.conf DOC: Changelog for iptables -w change Remove self.printlog() call Remove literal "TODO" from method's name BF: do not wrap iptables into itself. Thanks Lee Added a space between IP address and the following colon BF: symbiosis-blacklist-allports now also requires iptables-common.conf RF: use <iptables> to take effect of it being a parameter ENH: added lockingopt option for iptables actions, made iptables cmd itself a parameter ...pull/1858/head
commit
bceb35ab34
|
@ -1,4 +1,11 @@
|
||||||
|
|
||||||
[run]
|
[run]
|
||||||
branch = True
|
branch = True
|
||||||
omit = /usr*
|
source =
|
||||||
|
config
|
||||||
|
fail2ban
|
||||||
|
|
||||||
|
[report]
|
||||||
|
exclude_lines =
|
||||||
|
pragma: no cover
|
||||||
|
pragma: systemd no cover
|
||||||
|
|
67
.travis.yml
67
.travis.yml
|
@ -1,29 +1,56 @@
|
||||||
# vim ft=yaml
|
# vim ft=yaml
|
||||||
# travis-ci.org definition for Fail2Ban build
|
# travis-ci.org definition for Fail2Ban build
|
||||||
|
# https://travis-ci.org/fail2ban/fail2ban/
|
||||||
language: python
|
language: python
|
||||||
python:
|
python:
|
||||||
- "2.6"
|
- 2.6
|
||||||
- "2.7"
|
- 2.7
|
||||||
- "3.2"
|
- pypy
|
||||||
- "3.3"
|
- 3.2
|
||||||
- "3.4"
|
- 3.3
|
||||||
- "pypy"
|
- 3.4
|
||||||
- "pypy3"
|
- pypy3
|
||||||
before_install:
|
before_install:
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then travis_retry sudo apt-get update -qq; fi
|
- if [[ $TRAVIS_PYTHON_VERSION == 2* || $TRAVIS_PYTHON_VERSION == 'pypy' ]]; then export F2B_PY_2=true && echo "Set F2B_PY_2"; fi
|
||||||
|
- if [[ $TRAVIS_PYTHON_VERSION == 3* || $TRAVIS_PYTHON_VERSION == 'pypy3' ]]; then export F2B_PY_3=true && echo "Set F2B_PY_3"; fi
|
||||||
|
- travis_retry sudo apt-get update -qq
|
||||||
|
# Set this so sudo executes the correct python binary
|
||||||
|
# Anything not using sudo will already have the correct environment
|
||||||
|
- export VENV_BIN="$VIRTUAL_ENV/bin" && echo "VENV_BIN set to $VENV_BIN"
|
||||||
install:
|
install:
|
||||||
|
# Install Python packages / dependencies
|
||||||
|
# coverage
|
||||||
|
- travis_retry pip install coverage
|
||||||
|
# coveralls
|
||||||
|
- travis_retry pip install coveralls
|
||||||
|
# dnspython or dnspython3
|
||||||
|
- if [[ "$F2B_PY_2" ]]; then travis_retry pip install dnspython; fi
|
||||||
|
- if [[ "$F2B_PY_3" ]]; then travis_retry pip install dnspython3; fi
|
||||||
|
# gamin - install manually (not in PyPI) - travis-ci system Python is 2.7
|
||||||
|
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then travis_retry sudo apt-get install -qq python-gamin && cp /usr/share/pyshared/gamin.py /usr/lib/pyshared/python2.7/_gamin.so $VIRTUAL_ENV/lib/python2.7/site-packages/; fi
|
||||||
|
# pyinotify
|
||||||
- travis_retry pip install pyinotify
|
- travis_retry pip install pyinotify
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == 2* || $TRAVIS_PYTHON_VERSION == 'pypy' ]]; then travis_retry pip install dnspython; fi
|
before_script:
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == 3* || $TRAVIS_PYTHON_VERSION == 'pypy3' ]]; then travis_retry pip install dnspython3; fi
|
# Manually execute 2to3 for now
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then travis_retry sudo apt-get install -qq python-gamin; cp /usr/share/pyshared/gamin.py /usr/lib/pyshared/python2.7/_gamin.so $VIRTUAL_ENV/lib/python2.7/site-packages/; fi
|
- if [[ "$F2B_PY_3" ]]; then ./fail2ban-2to3; fi
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then cd ..; travis_retry pip install -q coveralls; cd -; fi
|
|
||||||
# overcome buggy pypy
|
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == pypy ]] ; then dpkg --compare-versions $(pypy --version 2>&1 | awk '/PyPy/{print $2;}') ge 2.5.1 || { d=$PWD; cd /tmp; wget http://buildbot.pypy.org/nightly/trunk/pypy-c-jit-latest-linux64.tar.bz2; tar -xjvf pypy*bz2; cd pypy-*/bin/; export PATH=$PWD:$PATH; cd $d; } ; fi
|
|
||||||
script:
|
script:
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then coverage run --rcfile=.travis_coveragerc setup.py test; else python setup.py test; fi
|
# Keep the legacy setup.py test approach of checking coverage for python2
|
||||||
# test installation
|
- if [[ "$F2B_PY_2" ]]; then coverage run setup.py test; fi
|
||||||
- sudo python setup.py install
|
# Coverage doesn't pick up setup.py test with python3, so run it directly
|
||||||
|
- if [[ "$F2B_PY_3" ]]; then coverage run bin/fail2ban-testcases; fi
|
||||||
|
# Use $VENV_BIN (not python) or else sudo will always run the system's python (2.7)
|
||||||
|
- sudo $VENV_BIN/pip install .
|
||||||
after_success:
|
after_success:
|
||||||
# Coverage config file must be .coveragerc for coveralls
|
- coveralls
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then cp -v .travis_coveragerc .coveragerc; fi
|
matrix:
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then coveralls; fi
|
fast_finish: true
|
||||||
|
# Might be worth looking into
|
||||||
|
#notifications:
|
||||||
|
# email: true
|
||||||
|
# irc:
|
||||||
|
# channels: "irc.freenode.org#fail2ban"
|
||||||
|
# template:
|
||||||
|
# - "%{repository}@%{branch}: %{message} (%{build_url})"
|
||||||
|
# on_success: change
|
||||||
|
# on_failure: change
|
||||||
|
# skip_join: true
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
|
|
||||||
[run]
|
|
||||||
branch = True
|
|
||||||
omit =
|
|
||||||
/usr/*
|
|
||||||
/home/travis/virtualenv/*
|
|
||||||
fail2ban/server/filtersystemd.py
|
|
79
ChangeLog
79
ChangeLog
|
@ -6,6 +6,85 @@
|
||||||
Fail2Ban: Changelog
|
Fail2Ban: Changelog
|
||||||
===================
|
===================
|
||||||
|
|
||||||
|
ver. 0.9.3 (2015/08/01) - lets-all-stay-friends
|
||||||
|
----------
|
||||||
|
|
||||||
|
- IMPORTANT incompatible changes:
|
||||||
|
* filter.d/roundcube-auth.conf
|
||||||
|
- Changed logpath to 'errors' log (was 'userlogins')
|
||||||
|
* action.d/iptables-common.conf
|
||||||
|
- All calls to iptables command now use -w switch introduced in
|
||||||
|
iptables 1.4.20 (some distribution could have patched their
|
||||||
|
earlier base version as well) to provide this locking mechanism
|
||||||
|
useful under heavy load to avoid contesting on iptables calls.
|
||||||
|
If you need to disable, define 'action.d/iptables-common.local'
|
||||||
|
with empty value for 'lockingopt' in `[Init]` section.
|
||||||
|
* mail-whois-lines, sendmail-geoip-lines and sendmail-whois-lines
|
||||||
|
actions now include by default only the first 1000 log lines in
|
||||||
|
the emails. Adjust <grepopts> to augment the behavior.
|
||||||
|
|
||||||
|
- Fixes:
|
||||||
|
* reload in interactive mode appends all the jails twice (gh-825)
|
||||||
|
* reload server/jail failed if database used (but was not changed) and
|
||||||
|
some jail active (gh-1072)
|
||||||
|
* filter.d/dovecot.conf - also match unknown user in passwd-file.
|
||||||
|
Thanks Anton Shestakov
|
||||||
|
* Fix fail2ban-regex not parsing journalmatch correctly from filter config
|
||||||
|
* filter.d/asterisk.conf - fix security log support for Asterisk 12+
|
||||||
|
* filter.d/roundcube-auth.conf
|
||||||
|
- Updated regex to work with 'errors' log (1.0.5 and 1.1.1)
|
||||||
|
- Added regex to work with 'userlogins' log
|
||||||
|
* action.d/sendmail*.conf - use LC_ALL (superseeding LC_TIME) to override
|
||||||
|
locale on systems with customized LC_ALL
|
||||||
|
* performance fix: minimizes connection overhead, close socket only at
|
||||||
|
communication end (gh-1099)
|
||||||
|
* unbanip always deletes ip from database (independent of bantime, also if
|
||||||
|
currently not banned or persistent)
|
||||||
|
* guarantee order of dbfile to be before dbpurgeage (gh-1048)
|
||||||
|
* always set 'dbfile' before other database options (gh-1050)
|
||||||
|
* kill the entire process group of the child process upon timeout (gh-1129).
|
||||||
|
Otherwise could lead to resource exhaustion due to hanging whois
|
||||||
|
processes.
|
||||||
|
* resolve /var/run/fail2ban path in setup.py to help installation
|
||||||
|
on platforms with /var/run -> /run symlink (gh-1142)
|
||||||
|
|
||||||
|
- New Features:
|
||||||
|
* RETURN iptables target is now a variable: <returntype>
|
||||||
|
* New type of operation: pass2allow, use fail2ban for "knocking",
|
||||||
|
opening a closed port by swapping blocktype and returntype
|
||||||
|
* New filters:
|
||||||
|
- froxlor-auth - Thanks Joern Muehlencord
|
||||||
|
- apache-pass - filter Apache access log for successful authentication
|
||||||
|
* New actions:
|
||||||
|
- shorewall-ipset-proto6 - using proto feature of the Shorewall. Still requires
|
||||||
|
manual pre-configuration of the shorewall. See the action file for detail.
|
||||||
|
* New jails:
|
||||||
|
- pass2allow-ftp - allows FTP traffic after successful HTTP authentication
|
||||||
|
|
||||||
|
- Enhancements:
|
||||||
|
* action.d/cloudflare.conf - improved documentation on how to allow
|
||||||
|
multiple CF accounts, and jail.conf got new compound action
|
||||||
|
definition action_cf_mwl to submit cloudflare report.
|
||||||
|
* Check access to socket for more detailed logging on error (gh-595)
|
||||||
|
* fail2ban-testcases man page
|
||||||
|
* filter.d/apache-badbots.conf, filter.d/nginx-botsearch.conf - add
|
||||||
|
HEAD method verb
|
||||||
|
* Revamp of Travis and coverage automated testing
|
||||||
|
* Added a space between IP address and the following colon
|
||||||
|
in notification emails for easier text selection
|
||||||
|
* Character detection heuristics for whois output via optional setting
|
||||||
|
in mail-whois*.conf. Thanks Thomas Mayer.
|
||||||
|
Not enabled by default, if _whois_command is set to be
|
||||||
|
%(_whois_convert_charset)s (e.g. in action.d/mail-whois-common.local),
|
||||||
|
it
|
||||||
|
- detects character set of whois output (which is undefined by
|
||||||
|
RFC 3912) via heuristics of the file command
|
||||||
|
- converts whois data to UTF-8 character set with iconv
|
||||||
|
- sends the whois output in UTF-8 character set to mail program
|
||||||
|
- avoids that heirloom mailx creates binary attachment for input with
|
||||||
|
unknown character set
|
||||||
|
|
||||||
|
|
||||||
ver. 0.9.2 (2015/04/29) - better-quick-now-than-later
|
ver. 0.9.2 (2015/04/29) - better-quick-now-than-later
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
|
7
DEVELOP
7
DEVELOP
|
@ -56,9 +56,12 @@ following (note: on Debian-based systems, the script is called
|
||||||
`python-coverage`)::
|
`python-coverage`)::
|
||||||
|
|
||||||
coverage run bin/fail2ban-testcases
|
coverage run bin/fail2ban-testcases
|
||||||
|
coverage report
|
||||||
|
|
||||||
|
Optionally:
|
||||||
coverage html
|
coverage html
|
||||||
|
|
||||||
Then look at htmlcov/index.html and see how much coverage your test cases
|
And then browse htmlcov/index.html and see how much coverage your test cases
|
||||||
exert over the code base. Full coverage is a good thing however it may not be
|
exert over the code base. Full coverage is a good thing however it may not be
|
||||||
complete. Try to ensure tests cover as many independent paths through the
|
complete. Try to ensure tests cover as many independent paths through the
|
||||||
code.
|
code.
|
||||||
|
@ -88,7 +91,7 @@ Testing can now be done inside a vagrant VM. Vagrantfile provided in
|
||||||
source code repository established two VMs:
|
source code repository established two VMs:
|
||||||
|
|
||||||
- VM "secure" which can be used for testing fail2ban code.
|
- VM "secure" which can be used for testing fail2ban code.
|
||||||
- VM "attacker" which hcan be used to perform attack against our "secure" VM.
|
- VM "attacker" which can be used to perform attack against our "secure" VM.
|
||||||
|
|
||||||
Both VMs are sharing the 192.168.200/24 network. If you are using this network
|
Both VMs are sharing the 192.168.200/24 network. If you are using this network
|
||||||
take a look into the Vagrantfile and change the IP.
|
take a look into the Vagrantfile and change the IP.
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
/ _|__ _(_) |_ ) |__ __ _ _ _
|
/ _|__ _(_) |_ ) |__ __ _ _ _
|
||||||
| _/ _` | | |/ /| '_ \/ _` | ' \
|
| _/ _` | | |/ /| '_ \/ _` | ' \
|
||||||
|_| \__,_|_|_/___|_.__/\__,_|_||_|
|
|_| \__,_|_|_/___|_.__/\__,_|_||_|
|
||||||
v0.9.2 2015/04/29
|
v0.9.3 2015/08/01
|
||||||
|
|
||||||
## Fail2Ban: ban hosts that cause multiple authentication errors
|
## Fail2Ban: ban hosts that cause multiple authentication errors
|
||||||
|
|
||||||
|
@ -37,8 +37,8 @@ Optional:
|
||||||
|
|
||||||
To install, just do:
|
To install, just do:
|
||||||
|
|
||||||
tar xvfj fail2ban-0.9.2.tar.bz2
|
tar xvfj fail2ban-0.9.3.tar.bz2
|
||||||
cd fail2ban-0.9.2
|
cd fail2ban-0.9.3
|
||||||
python setup.py install
|
python setup.py install
|
||||||
|
|
||||||
This will install Fail2Ban into the python library directory. The executable
|
This will install Fail2Ban into the python library directory. The executable
|
||||||
|
|
14
RELEASE
14
RELEASE
|
@ -61,24 +61,24 @@ Preparation
|
||||||
|
|
||||||
* Which indicates that testcases/files/logs/mysqld.log has been moved or is a directory::
|
* Which indicates that testcases/files/logs/mysqld.log has been moved or is a directory::
|
||||||
|
|
||||||
tar -C /tmp -jxf dist/fail2ban-0.9.2.tar.bz2
|
tar -C /tmp -jxf dist/fail2ban-0.9.3.tar.bz2
|
||||||
|
|
||||||
* clean up current direcory::
|
* clean up current direcory::
|
||||||
|
|
||||||
diff -rul --exclude \*.pyc . /tmp/fail2ban-0.9.2/
|
diff -rul --exclude \*.pyc . /tmp/fail2ban-0.9.3/
|
||||||
|
|
||||||
* Only differences should be files that you don't want distributed.
|
* Only differences should be files that you don't want distributed.
|
||||||
|
|
||||||
|
|
||||||
* Ensure the tests work from the tarball::
|
* Ensure the tests work from the tarball::
|
||||||
|
|
||||||
cd /tmp/fail2ban-0.9.2/ && bin/fail2ban-testcases
|
cd /tmp/fail2ban-0.9.3/ && bin/fail2ban-testcases
|
||||||
|
|
||||||
* Add/finalize the corresponding entry in the ChangeLog
|
* Add/finalize the corresponding entry in the ChangeLog
|
||||||
|
|
||||||
* To generate a list of committers use e.g.::
|
* To generate a list of committers use e.g.::
|
||||||
|
|
||||||
git shortlog -sn 0.9.2.. | sed -e 's,^[ 0-9\t]*,,g' | tr '\n' '\|' | sed -e 's:|:, :g'
|
git shortlog -sn 0.9.3.. | sed -e 's,^[ 0-9\t]*,,g' | tr '\n' '\|' | sed -e 's:|:, :g'
|
||||||
|
|
||||||
* Ensure the top of the ChangeLog has the right version and current date.
|
* Ensure the top of the ChangeLog has the right version and current date.
|
||||||
* Ensure the top entry of the ChangeLog has the right version and current date.
|
* Ensure the top entry of the ChangeLog has the right version and current date.
|
||||||
|
@ -101,7 +101,7 @@ Preparation
|
||||||
* Tag the release by using a signed (and annotated) tag. Cut/paste
|
* Tag the release by using a signed (and annotated) tag. Cut/paste
|
||||||
release ChangeLog entry as tag annotation::
|
release ChangeLog entry as tag annotation::
|
||||||
|
|
||||||
git tag -s 0.9.2
|
git tag -s 0.9.3
|
||||||
|
|
||||||
Pre Release
|
Pre Release
|
||||||
===========
|
===========
|
||||||
|
@ -185,7 +185,7 @@ Post Release
|
||||||
|
|
||||||
Add the following to the top of the ChangeLog::
|
Add the following to the top of the ChangeLog::
|
||||||
|
|
||||||
ver. 0.9.3 (2014/XX/XXX) - wanna-be-released
|
ver. 0.9.4 (2014/XX/XXX) - wanna-be-released
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
- Fixes:
|
- Fixes:
|
||||||
|
@ -197,5 +197,5 @@ Add the following to the top of the ChangeLog::
|
||||||
Alter the git shortlog command in the previous section to refer to the just
|
Alter the git shortlog command in the previous section to refer to the just
|
||||||
released version.
|
released version.
|
||||||
|
|
||||||
and adjust fail2ban/version.py to carry .dev suffix to signal
|
and adjust fail2ban/version.py to carry .dev0 suffix to signal
|
||||||
a version under development.
|
a version under development.
|
||||||
|
|
1
THANKS
1
THANKS
|
@ -109,6 +109,7 @@ Stefan Tatschner
|
||||||
Stephen Gildea
|
Stephen Gildea
|
||||||
Steven Hiscocks
|
Steven Hiscocks
|
||||||
TESTOVIK
|
TESTOVIK
|
||||||
|
Thomas Mayer
|
||||||
Tom Pike
|
Tom Pike
|
||||||
Tomas Pihl
|
Tomas Pihl
|
||||||
Tony Lawrence
|
Tony Lawrence
|
||||||
|
|
|
@ -22,8 +22,17 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import sys, string, os, pickle, re, logging, signal
|
import getopt
|
||||||
import getopt, time, shlex, socket
|
import logging
|
||||||
|
import os
|
||||||
|
import pickle
|
||||||
|
import re
|
||||||
|
import shlex
|
||||||
|
import signal
|
||||||
|
import socket
|
||||||
|
import string
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
from fail2ban.version import version
|
from fail2ban.version import version
|
||||||
from fail2ban.protocol import printFormatted
|
from fail2ban.protocol import printFormatted
|
||||||
|
@ -144,11 +153,14 @@ class Fail2banClient:
|
||||||
return self.__processCmd([["ping"]], False)
|
return self.__processCmd([["ping"]], False)
|
||||||
|
|
||||||
def __processCmd(self, cmd, showRet = True):
|
def __processCmd(self, cmd, showRet = True):
|
||||||
|
client = None
|
||||||
|
try:
|
||||||
beautifier = Beautifier()
|
beautifier = Beautifier()
|
||||||
streamRet = True
|
streamRet = True
|
||||||
for c in cmd:
|
for c in cmd:
|
||||||
beautifier.setInputCmd(c)
|
beautifier.setInputCmd(c)
|
||||||
try:
|
try:
|
||||||
|
if not client:
|
||||||
client = CSocket(self.__conf["socket"])
|
client = CSocket(self.__conf["socket"])
|
||||||
ret = client.send(c)
|
ret = client.send(c)
|
||||||
if ret[0] == 0:
|
if ret[0] == 0:
|
||||||
|
@ -162,14 +174,37 @@ class Fail2banClient:
|
||||||
streamRet = False
|
streamRet = False
|
||||||
except socket.error:
|
except socket.error:
|
||||||
if showRet:
|
if showRet:
|
||||||
logSys.error("Unable to contact server. Is it running?")
|
self.__logSocketError()
|
||||||
return False
|
return False
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
if showRet:
|
if showRet:
|
||||||
logSys.error(e)
|
logSys.error(e)
|
||||||
return False
|
return False
|
||||||
|
finally:
|
||||||
|
if client:
|
||||||
|
client.close()
|
||||||
return streamRet
|
return streamRet
|
||||||
|
|
||||||
|
def __logSocketError(self):
|
||||||
|
try:
|
||||||
|
if os.access(self.__conf["socket"], os.F_OK):
|
||||||
|
# This doesn't check if path is a socket,
|
||||||
|
# but socket.error should be raised
|
||||||
|
if os.access(self.__conf["socket"], os.W_OK):
|
||||||
|
# Permissions look good, but socket.error was raised
|
||||||
|
logSys.error("Unable to contact server. Is it running?")
|
||||||
|
else:
|
||||||
|
logSys.error("Permission denied to socket: %s,"
|
||||||
|
" (you must be root)", self.__conf["socket"])
|
||||||
|
else:
|
||||||
|
logSys.error("Failed to access socket path: %s."
|
||||||
|
" Is fail2ban running?",
|
||||||
|
self.__conf["socket"])
|
||||||
|
except Exception as e:
|
||||||
|
logSys.error("Exception while checking socket access: %s",
|
||||||
|
self.__conf["socket"])
|
||||||
|
logSys.error(e)
|
||||||
|
|
||||||
##
|
##
|
||||||
# Process a command line.
|
# Process a command line.
|
||||||
#
|
#
|
||||||
|
|
|
@ -29,7 +29,15 @@ __author__ = "Fail2Ban Developers"
|
||||||
__copyright__ = "Copyright (c) 2004-2008 Cyril Jaquier, 2012-2014 Yaroslav Halchenko"
|
__copyright__ = "Copyright (c) 2004-2008 Cyril Jaquier, 2012-2014 Yaroslav Halchenko"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import getopt, sys, time, logging, os, locale, shlex, time, urllib
|
import getopt
|
||||||
|
import locale
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import shlex
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import time
|
||||||
|
import urllib
|
||||||
from optparse import OptionParser, Option
|
from optparse import OptionParser, Option
|
||||||
|
|
||||||
from ConfigParser import NoOptionError, NoSectionError, MissingSectionHeaderError
|
from ConfigParser import NoOptionError, NoSectionError, MissingSectionHeaderError
|
||||||
|
@ -297,8 +305,8 @@ class Fail2banRegex(object):
|
||||||
"read from %(value)s" % locals()
|
"read from %(value)s" % locals()
|
||||||
return False
|
return False
|
||||||
elif command[2] == 'addjournalmatch':
|
elif command[2] == 'addjournalmatch':
|
||||||
journalmatch = command[3]
|
journalmatch = command[3:]
|
||||||
self.setJournalMatch(shlex.split(journalmatch))
|
self.setJournalMatch(journalmatch)
|
||||||
elif command[2] == 'datepattern':
|
elif command[2] == 'datepattern':
|
||||||
datepattern = command[3]
|
datepattern = command[3]
|
||||||
self.setDatePattern(datepattern)
|
self.setDatePattern(datepattern)
|
||||||
|
|
|
@ -22,7 +22,9 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import getopt, sys, os
|
import getopt
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
from fail2ban.version import version
|
from fail2ban.version import version
|
||||||
from fail2ban.server.server import Server
|
from fail2ban.server.server import Server
|
||||||
|
|
|
@ -25,7 +25,10 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2012- Yaroslav Halchenko"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import unittest, sys, time, os
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import unittest
|
||||||
|
|
||||||
# Check if local fail2ban module exists, and use if it exists by
|
# Check if local fail2ban module exists, and use if it exists by
|
||||||
# modifying the path. This is such that tests can be used in dev
|
# modifying the path. This is such that tests can be used in dev
|
||||||
|
|
|
@ -35,6 +35,7 @@ else:
|
||||||
from fail2ban.server.actions import ActionBase
|
from fail2ban.server.actions import ActionBase
|
||||||
from fail2ban.version import version as f2bVersion
|
from fail2ban.version import version as f2bVersion
|
||||||
|
|
||||||
|
|
||||||
class BadIPsAction(ActionBase):
|
class BadIPsAction(ActionBase):
|
||||||
"""Fail2Ban action which reports bans to badips.com, and also
|
"""Fail2Ban action which reports bans to badips.com, and also
|
||||||
blacklist bad IPs listed on badips.com by using another action's
|
blacklist bad IPs listed on badips.com by using another action's
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
#
|
#
|
||||||
# Author: Mike Rushton
|
# Author: Mike Rushton
|
||||||
#
|
#
|
||||||
# Referenced from from http://www.normyee.net/blog/2012/02/02/adding-cloudflare-support-to-fail2ban by NORM YEE
|
# IMPORTANT
|
||||||
#
|
#
|
||||||
# To get your Cloudflare API key: https://www.cloudflare.com/my-account
|
# Please set jail.local's permission to 640 because it contains your CF API key.
|
||||||
#
|
#
|
||||||
|
# This action depends on curl.
|
||||||
|
# Referenced from http://www.normyee.net/blog/2012/02/02/adding-cloudflare-support-to-fail2ban by NORM YEE
|
||||||
|
#
|
||||||
|
# To get your CloudFlare API Key: https://www.cloudflare.com/a/account/my-account
|
||||||
|
|
||||||
[Definition]
|
[Definition]
|
||||||
|
|
||||||
|
@ -34,7 +38,8 @@ actioncheck =
|
||||||
# <time> unix timestamp of the ban time
|
# <time> unix timestamp of the ban time
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionban = curl https://www.cloudflare.com/api_json.html -d 'a=ban' -d 'tkn=<cftoken>' -d 'email=<cfuser>' -d 'key=<ip>'
|
actionban = curl -s -o /dev/null https://www.cloudflare.com/api_json.html -d 'a=ban' -d 'tkn=<cftoken>' -d 'email=<cfuser>' -d 'key=<ip>'
|
||||||
|
|
||||||
# Option: actionunban
|
# Option: actionunban
|
||||||
# Notes.: command executed when unbanning an IP. Take care that the
|
# Notes.: command executed when unbanning an IP. Take care that the
|
||||||
# command is executed with Fail2Ban user rights.
|
# command is executed with Fail2Ban user rights.
|
||||||
|
@ -43,13 +48,19 @@ actionban = curl https://www.cloudflare.com/api_json.html -d 'a=ban' -d 'tkn=<cf
|
||||||
# <time> unix timestamp of the ban time
|
# <time> unix timestamp of the ban time
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionunban = curl https://www.cloudflare.com/api_json.html -d 'a=nul' -d 'tkn=<cftoken>' -d 'email=<cfuser>' -d 'key=<ip>'
|
actionunban = curl -s -o /dev/null https://www.cloudflare.com/api_json.html -d 'a=nul' -d 'tkn=<cftoken>' -d 'email=<cfuser>' -d 'key=<ip>'
|
||||||
|
|
||||||
|
|
||||||
[Init]
|
[Init]
|
||||||
|
|
||||||
# Default Cloudflare API token
|
# If you like to use this action with mailing whois lines, you could use the composite action
|
||||||
|
# action_cf_mwl predefined in jail.conf, just define in your jail:
|
||||||
|
#
|
||||||
|
# action = %(action_cf_mwl)s
|
||||||
|
# # Your CF account e-mail
|
||||||
|
# cfemail =
|
||||||
|
# # Your CF API Key
|
||||||
|
# cfapikey =
|
||||||
|
|
||||||
cftoken =
|
cftoken =
|
||||||
|
|
||||||
# Default Cloudflare username
|
|
||||||
cfuser =
|
cfuser =
|
||||||
|
|
|
@ -17,23 +17,23 @@ before = iptables-common.conf
|
||||||
# Notes.: command executed once at the start of Fail2Ban.
|
# Notes.: command executed once at the start of Fail2Ban.
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstart = iptables -N f2b-<name>
|
actionstart = <iptables> -N f2b-<name>
|
||||||
iptables -A f2b-<name> -j RETURN
|
<iptables> -A f2b-<name> -j <returntype>
|
||||||
iptables -I <chain> -p <protocol> -j f2b-<name>
|
<iptables> -I <chain> -p <protocol> -j f2b-<name>
|
||||||
|
|
||||||
# Option: actionstop
|
# Option: actionstop
|
||||||
# Notes.: command executed once at the end of Fail2Ban
|
# Notes.: command executed once at the end of Fail2Ban
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstop = iptables -D <chain> -p <protocol> -j f2b-<name>
|
actionstop = <iptables> -D <chain> -p <protocol> -j f2b-<name>
|
||||||
iptables -F f2b-<name>
|
<iptables> -F f2b-<name>
|
||||||
iptables -X f2b-<name>
|
<iptables> -X f2b-<name>
|
||||||
|
|
||||||
# Option: actioncheck
|
# Option: actioncheck
|
||||||
# Notes.: command executed once before each actionban command
|
# Notes.: command executed once before each actionban command
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actioncheck = iptables -n -L <chain> | grep -q 'f2b-<name>[ \t]'
|
actioncheck = <iptables> -n -L <chain> | grep -q 'f2b-<name>[ \t]'
|
||||||
|
|
||||||
# Option: actionban
|
# Option: actionban
|
||||||
# Notes.: command executed when banning an IP. Take care that the
|
# Notes.: command executed when banning an IP. Take care that the
|
||||||
|
@ -41,7 +41,7 @@ actioncheck = iptables -n -L <chain> | grep -q 'f2b-<name>[ \t]'
|
||||||
# Tags: See jail.conf(5) man page
|
# Tags: See jail.conf(5) man page
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionban = iptables -I f2b-<name> 1 -s <ip> -j <blocktype>
|
actionban = <iptables> -I f2b-<name> 1 -s <ip> -j <blocktype>
|
||||||
|
|
||||||
# Option: actionunban
|
# Option: actionunban
|
||||||
# Notes.: command executed when unbanning an IP. Take care that the
|
# Notes.: command executed when unbanning an IP. Take care that the
|
||||||
|
@ -49,7 +49,7 @@ actionban = iptables -I f2b-<name> 1 -s <ip> -j <blocktype>
|
||||||
# Tags: See jail.conf(5) man page
|
# Tags: See jail.conf(5) man page
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionunban = iptables -D f2b-<name> -s <ip> -j <blocktype>
|
actionunban = <iptables> -D f2b-<name> -s <ip> -j <blocktype>
|
||||||
|
|
||||||
[Init]
|
[Init]
|
||||||
|
|
||||||
|
|
|
@ -43,3 +43,22 @@ protocol = tcp
|
||||||
# REJECT, REJECT --reject-with icmp-port-unreachable
|
# REJECT, REJECT --reject-with icmp-port-unreachable
|
||||||
# Values: STRING
|
# Values: STRING
|
||||||
blocktype = REJECT --reject-with icmp-port-unreachable
|
blocktype = REJECT --reject-with icmp-port-unreachable
|
||||||
|
|
||||||
|
# Option: returntype
|
||||||
|
# Note: This is the default rule on "actionstart". This should be RETURN
|
||||||
|
# in all (blocking) actions, except REJECT in allowing actions.
|
||||||
|
# Values: STRING
|
||||||
|
returntype = RETURN
|
||||||
|
|
||||||
|
# Option: lockingopt
|
||||||
|
# Notes.: Option was introduced to iptables to prevent multiple instances from
|
||||||
|
# running concurrently and causing irratic behavior. -w was introduced
|
||||||
|
# in iptables 1.4.20, so might be absent on older systems
|
||||||
|
# See https://github.com/fail2ban/fail2ban/issues/1122
|
||||||
|
# Values: STRING
|
||||||
|
lockingopt = -w
|
||||||
|
|
||||||
|
# Option: iptables
|
||||||
|
# Notes.: Actual command to be executed, including common to all calls options
|
||||||
|
# Values: STRING
|
||||||
|
iptables = iptables <lockingopt>
|
||||||
|
|
|
@ -28,13 +28,13 @@ before = iptables-common.conf
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstart = ipset --create f2b-<name> iphash
|
actionstart = ipset --create f2b-<name> iphash
|
||||||
iptables -I <chain> -p <protocol> -m multiport --dports <port> -m set --match-set f2b-<name> src -j <blocktype>
|
<iptables> -I <chain> -p <protocol> -m multiport --dports <port> -m set --match-set f2b-<name> src -j <blocktype>
|
||||||
|
|
||||||
# Option: actionstop
|
# Option: actionstop
|
||||||
# Notes.: command executed once at the end of Fail2Ban
|
# Notes.: command executed once at the end of Fail2Ban
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstop = iptables -D <chain> -p <protocol> -m multiport --dports <port> -m set --match-set f2b-<name> src -j <blocktype>
|
actionstop = <iptables> -D <chain> -p <protocol> -m multiport --dports <port> -m set --match-set f2b-<name> src -j <blocktype>
|
||||||
ipset --flush f2b-<name>
|
ipset --flush f2b-<name>
|
||||||
ipset --destroy f2b-<name>
|
ipset --destroy f2b-<name>
|
||||||
|
|
||||||
|
|
|
@ -24,13 +24,13 @@ before = iptables-common.conf
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstart = ipset create f2b-<name> hash:ip timeout <bantime>
|
actionstart = ipset create f2b-<name> hash:ip timeout <bantime>
|
||||||
iptables -I <chain> -m set --match-set f2b-<name> src -j <blocktype>
|
<iptables> -I <chain> -m set --match-set f2b-<name> src -j <blocktype>
|
||||||
|
|
||||||
# Option: actionstop
|
# Option: actionstop
|
||||||
# Notes.: command executed once at the end of Fail2Ban
|
# Notes.: command executed once at the end of Fail2Ban
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstop = iptables -D <chain> -m set --match-set f2b-<name> src -j <blocktype>
|
actionstop = <iptables> -D <chain> -m set --match-set f2b-<name> src -j <blocktype>
|
||||||
ipset flush f2b-<name>
|
ipset flush f2b-<name>
|
||||||
ipset destroy f2b-<name>
|
ipset destroy f2b-<name>
|
||||||
|
|
||||||
|
|
|
@ -24,13 +24,13 @@ before = iptables-common.conf
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstart = ipset create f2b-<name> hash:ip timeout <bantime>
|
actionstart = ipset create f2b-<name> hash:ip timeout <bantime>
|
||||||
iptables -I <chain> -p <protocol> -m multiport --dports <port> -m set --match-set f2b-<name> src -j <blocktype>
|
<iptables> -I <chain> -p <protocol> -m multiport --dports <port> -m set --match-set f2b-<name> src -j <blocktype>
|
||||||
|
|
||||||
# Option: actionstop
|
# Option: actionstop
|
||||||
# Notes.: command executed once at the end of Fail2Ban
|
# Notes.: command executed once at the end of Fail2Ban
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstop = iptables -D <chain> -p <protocol> -m multiport --dports <port> -m set --match-set f2b-<name> src -j <blocktype>
|
actionstop = <iptables> -D <chain> -p <protocol> -m multiport --dports <port> -m set --match-set f2b-<name> src -j <blocktype>
|
||||||
ipset flush f2b-<name>
|
ipset flush f2b-<name>
|
||||||
ipset destroy f2b-<name>
|
ipset destroy f2b-<name>
|
||||||
|
|
||||||
|
|
|
@ -19,28 +19,28 @@ before = iptables-common.conf
|
||||||
# Notes.: command executed once at the start of Fail2Ban.
|
# Notes.: command executed once at the start of Fail2Ban.
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstart = iptables -N f2b-<name>
|
actionstart = <iptables> -N f2b-<name>
|
||||||
iptables -A f2b-<name> -j RETURN
|
<iptables> -A f2b-<name> -j <returntype>
|
||||||
iptables -I <chain> 1 -p <protocol> -m multiport --dports <port> -j f2b-<name>
|
<iptables> -I <chain> 1 -p <protocol> -m multiport --dports <port> -j f2b-<name>
|
||||||
iptables -N f2b-<name>-log
|
<iptables> -N f2b-<name>-log
|
||||||
iptables -I f2b-<name>-log -j LOG --log-prefix "$(expr f2b-<name> : '\(.\{1,23\}\)'):DROP " --log-level warning -m limit --limit 6/m --limit-burst 2
|
<iptables> -I f2b-<name>-log -j LOG --log-prefix "$(expr f2b-<name> : '\(.\{1,23\}\)'):DROP " --log-level warning -m limit --limit 6/m --limit-burst 2
|
||||||
iptables -A f2b-<name>-log -j <blocktype>
|
<iptables> -A f2b-<name>-log -j <blocktype>
|
||||||
|
|
||||||
# Option: actionstop
|
# Option: actionstop
|
||||||
# Notes.: command executed once at the end of Fail2Ban
|
# Notes.: command executed once at the end of Fail2Ban
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstop = iptables -D <chain> -p <protocol> -m multiport --dports <port> -j f2b-<name>
|
actionstop = <iptables> -D <chain> -p <protocol> -m multiport --dports <port> -j f2b-<name>
|
||||||
iptables -F f2b-<name>
|
<iptables> -F f2b-<name>
|
||||||
iptables -F f2b-<name>-log
|
<iptables> -F f2b-<name>-log
|
||||||
iptables -X f2b-<name>
|
<iptables> -X f2b-<name>
|
||||||
iptables -X f2b-<name>-log
|
<iptables> -X f2b-<name>-log
|
||||||
|
|
||||||
# Option: actioncheck
|
# Option: actioncheck
|
||||||
# Notes.: command executed once before each actionban command
|
# Notes.: command executed once before each actionban command
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actioncheck = iptables -n -L f2b-<name>-log >/dev/null
|
actioncheck = <iptables> -n -L f2b-<name>-log >/dev/null
|
||||||
|
|
||||||
# Option: actionban
|
# Option: actionban
|
||||||
# Notes.: command executed when banning an IP. Take care that the
|
# Notes.: command executed when banning an IP. Take care that the
|
||||||
|
@ -48,7 +48,7 @@ actioncheck = iptables -n -L f2b-<name>-log >/dev/null
|
||||||
# Tags: See jail.conf(5) man page
|
# Tags: See jail.conf(5) man page
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionban = iptables -I f2b-<name> 1 -s <ip> -j f2b-<name>-log
|
actionban = <iptables> -I f2b-<name> 1 -s <ip> -j f2b-<name>-log
|
||||||
|
|
||||||
# Option: actionunban
|
# Option: actionunban
|
||||||
# Notes.: command executed when unbanning an IP. Take care that the
|
# Notes.: command executed when unbanning an IP. Take care that the
|
||||||
|
@ -56,7 +56,7 @@ actionban = iptables -I f2b-<name> 1 -s <ip> -j f2b-<name>-log
|
||||||
# Tags: See jail.conf(5) man page
|
# Tags: See jail.conf(5) man page
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionunban = iptables -D f2b-<name> -s <ip> -j f2b-<name>-log
|
actionunban = <iptables> -D f2b-<name> -s <ip> -j f2b-<name>-log
|
||||||
|
|
||||||
[Init]
|
[Init]
|
||||||
|
|
||||||
|
|
|
@ -14,23 +14,23 @@ before = iptables-common.conf
|
||||||
# Notes.: command executed once at the start of Fail2Ban.
|
# Notes.: command executed once at the start of Fail2Ban.
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstart = iptables -N f2b-<name>
|
actionstart = <iptables> -N f2b-<name>
|
||||||
iptables -A f2b-<name> -j RETURN
|
<iptables> -A f2b-<name> -j <returntype>
|
||||||
iptables -I <chain> -p <protocol> -m multiport --dports <port> -j f2b-<name>
|
<iptables> -I <chain> -p <protocol> -m multiport --dports <port> -j f2b-<name>
|
||||||
|
|
||||||
# Option: actionstop
|
# Option: actionstop
|
||||||
# Notes.: command executed once at the end of Fail2Ban
|
# Notes.: command executed once at the end of Fail2Ban
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstop = iptables -D <chain> -p <protocol> -m multiport --dports <port> -j f2b-<name>
|
actionstop = <iptables> -D <chain> -p <protocol> -m multiport --dports <port> -j f2b-<name>
|
||||||
iptables -F f2b-<name>
|
<iptables> -F f2b-<name>
|
||||||
iptables -X f2b-<name>
|
<iptables> -X f2b-<name>
|
||||||
|
|
||||||
# Option: actioncheck
|
# Option: actioncheck
|
||||||
# Notes.: command executed once before each actionban command
|
# Notes.: command executed once before each actionban command
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actioncheck = iptables -n -L <chain> | grep -q 'f2b-<name>[ \t]'
|
actioncheck = <iptables> -n -L <chain> | grep -q 'f2b-<name>[ \t]'
|
||||||
|
|
||||||
# Option: actionban
|
# Option: actionban
|
||||||
# Notes.: command executed when banning an IP. Take care that the
|
# Notes.: command executed when banning an IP. Take care that the
|
||||||
|
@ -38,7 +38,7 @@ actioncheck = iptables -n -L <chain> | grep -q 'f2b-<name>[ \t]'
|
||||||
# Tags: See jail.conf(5) man page
|
# Tags: See jail.conf(5) man page
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionban = iptables -I f2b-<name> 1 -s <ip> -j <blocktype>
|
actionban = <iptables> -I f2b-<name> 1 -s <ip> -j <blocktype>
|
||||||
|
|
||||||
# Option: actionunban
|
# Option: actionunban
|
||||||
# Notes.: command executed when unbanning an IP. Take care that the
|
# Notes.: command executed when unbanning an IP. Take care that the
|
||||||
|
@ -46,7 +46,7 @@ actionban = iptables -I f2b-<name> 1 -s <ip> -j <blocktype>
|
||||||
# Tags: See jail.conf(5) man page
|
# Tags: See jail.conf(5) man page
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionunban = iptables -D f2b-<name> -s <ip> -j <blocktype>
|
actionunban = <iptables> -D f2b-<name> -s <ip> -j <blocktype>
|
||||||
|
|
||||||
[Init]
|
[Init]
|
||||||
|
|
||||||
|
|
|
@ -16,23 +16,23 @@ before = iptables-common.conf
|
||||||
# Notes.: command executed once at the start of Fail2Ban.
|
# Notes.: command executed once at the start of Fail2Ban.
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstart = iptables -N f2b-<name>
|
actionstart = <iptables> -N f2b-<name>
|
||||||
iptables -A f2b-<name> -j RETURN
|
<iptables> -A f2b-<name> -j <returntype>
|
||||||
iptables -I <chain> -m state --state NEW -p <protocol> --dport <port> -j f2b-<name>
|
<iptables> -I <chain> -m state --state NEW -p <protocol> --dport <port> -j f2b-<name>
|
||||||
|
|
||||||
# Option: actionstop
|
# Option: actionstop
|
||||||
# Notes.: command executed once at the end of Fail2Ban
|
# Notes.: command executed once at the end of Fail2Ban
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstop = iptables -D <chain> -m state --state NEW -p <protocol> --dport <port> -j f2b-<name>
|
actionstop = <iptables> -D <chain> -m state --state NEW -p <protocol> --dport <port> -j f2b-<name>
|
||||||
iptables -F f2b-<name>
|
<iptables> -F f2b-<name>
|
||||||
iptables -X f2b-<name>
|
<iptables> -X f2b-<name>
|
||||||
|
|
||||||
# Option: actioncheck
|
# Option: actioncheck
|
||||||
# Notes.: command executed once before each actionban command
|
# Notes.: command executed once before each actionban command
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actioncheck = iptables -n -L <chain> | grep -q 'f2b-<name>[ \t]'
|
actioncheck = <iptables> -n -L <chain> | grep -q 'f2b-<name>[ \t]'
|
||||||
|
|
||||||
# Option: actionban
|
# Option: actionban
|
||||||
# Notes.: command executed when banning an IP. Take care that the
|
# Notes.: command executed when banning an IP. Take care that the
|
||||||
|
@ -40,7 +40,7 @@ actioncheck = iptables -n -L <chain> | grep -q 'f2b-<name>[ \t]'
|
||||||
# Tags: See jail.conf(5) man page
|
# Tags: See jail.conf(5) man page
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionban = iptables -I f2b-<name> 1 -s <ip> -j <blocktype>
|
actionban = <iptables> -I f2b-<name> 1 -s <ip> -j <blocktype>
|
||||||
|
|
||||||
# Option: actionunban
|
# Option: actionunban
|
||||||
# Notes.: command executed when unbanning an IP. Take care that the
|
# Notes.: command executed when unbanning an IP. Take care that the
|
||||||
|
@ -48,7 +48,7 @@ actionban = iptables -I f2b-<name> 1 -s <ip> -j <blocktype>
|
||||||
# Tags: See jail.conf(5) man page
|
# Tags: See jail.conf(5) man page
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionunban = iptables -D f2b-<name> -s <ip> -j <blocktype>
|
actionunban = <iptables> -D f2b-<name> -s <ip> -j <blocktype>
|
||||||
|
|
||||||
[Init]
|
[Init]
|
||||||
|
|
||||||
|
|
|
@ -32,14 +32,14 @@ before = iptables-common.conf
|
||||||
# own rules. The 3600 second timeout is independent and acts as a
|
# own rules. The 3600 second timeout is independent and acts as a
|
||||||
# safeguard in case the fail2ban process dies unexpectedly. The
|
# safeguard in case the fail2ban process dies unexpectedly. The
|
||||||
# shorter of the two timeouts actually matters.
|
# shorter of the two timeouts actually matters.
|
||||||
actionstart = if [ `id -u` -eq 0 ];then iptables -I <chain> -m recent --update --seconds 3600 --name f2b-<name> -j <blocktype>;fi
|
actionstart = if [ `id -u` -eq 0 ];then <iptables> -I <chain> -m recent --update --seconds 3600 --name f2b-<name> -j <blocktype>;fi
|
||||||
|
|
||||||
# Option: actionstop
|
# Option: actionstop
|
||||||
# Notes.: command executed once at the end of Fail2Ban
|
# Notes.: command executed once at the end of Fail2Ban
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstop = echo / > /proc/net/xt_recent/f2b-<name>
|
actionstop = echo / > /proc/net/xt_recent/f2b-<name>
|
||||||
if [ `id -u` -eq 0 ];then iptables -D <chain> -m recent --update --seconds 3600 --name f2b-<name> -j <blocktype>;fi
|
if [ `id -u` -eq 0 ];then <iptables> -D <chain> -m recent --update --seconds 3600 --name f2b-<name> -j <blocktype>;fi
|
||||||
|
|
||||||
# Option: actioncheck
|
# Option: actioncheck
|
||||||
# Notes.: command executed once before each actionban command
|
# Notes.: command executed once before each actionban command
|
||||||
|
|
|
@ -14,23 +14,23 @@ before = iptables-common.conf
|
||||||
# Notes.: command executed once at the start of Fail2Ban.
|
# Notes.: command executed once at the start of Fail2Ban.
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstart = iptables -N f2b-<name>
|
actionstart = <iptables> -N f2b-<name>
|
||||||
iptables -A f2b-<name> -j RETURN
|
<iptables> -A f2b-<name> -j <returntype>
|
||||||
iptables -I <chain> -p <protocol> --dport <port> -j f2b-<name>
|
<iptables> -I <chain> -p <protocol> --dport <port> -j f2b-<name>
|
||||||
|
|
||||||
# Option: actionstop
|
# Option: actionstop
|
||||||
# Notes.: command executed once at the end of Fail2Ban
|
# Notes.: command executed once at the end of Fail2Ban
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstop = iptables -D <chain> -p <protocol> --dport <port> -j f2b-<name>
|
actionstop = <iptables> -D <chain> -p <protocol> --dport <port> -j f2b-<name>
|
||||||
iptables -F f2b-<name>
|
<iptables> -F f2b-<name>
|
||||||
iptables -X f2b-<name>
|
<iptables> -X f2b-<name>
|
||||||
|
|
||||||
# Option: actioncheck
|
# Option: actioncheck
|
||||||
# Notes.: command executed once before each actionban command
|
# Notes.: command executed once before each actionban command
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actioncheck = iptables -n -L <chain> | grep -q 'f2b-<name>[ \t]'
|
actioncheck = <iptables> -n -L <chain> | grep -q 'f2b-<name>[ \t]'
|
||||||
|
|
||||||
# Option: actionban
|
# Option: actionban
|
||||||
# Notes.: command executed when banning an IP. Take care that the
|
# Notes.: command executed when banning an IP. Take care that the
|
||||||
|
@ -38,7 +38,7 @@ actioncheck = iptables -n -L <chain> | grep -q 'f2b-<name>[ \t]'
|
||||||
# Tags: See jail.conf(5) man page
|
# Tags: See jail.conf(5) man page
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionban = iptables -I f2b-<name> 1 -s <ip> -j <blocktype>
|
actionban = <iptables> -I f2b-<name> 1 -s <ip> -j <blocktype>
|
||||||
|
|
||||||
# Option: actionunban
|
# Option: actionunban
|
||||||
# Notes.: command executed when unbanning an IP. Take care that the
|
# Notes.: command executed when unbanning an IP. Take care that the
|
||||||
|
@ -46,7 +46,7 @@ actionban = iptables -I f2b-<name> 1 -s <ip> -j <blocktype>
|
||||||
# Tags: See jail.conf(5) man page
|
# Tags: See jail.conf(5) man page
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionunban = iptables -D f2b-<name> -s <ip> -j <blocktype>
|
actionunban = <iptables> -D f2b-<name> -s <ip> -j <blocktype>
|
||||||
|
|
||||||
[Init]
|
[Init]
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Fail2Ban configuration file
|
||||||
|
#
|
||||||
|
# Common settings for mail actions
|
||||||
|
#
|
||||||
|
# Users can override the defaults in mail-whois-common.local
|
||||||
|
|
||||||
|
[INCLUDES]
|
||||||
|
|
||||||
|
# Load customizations if any available
|
||||||
|
after = mail-whois-common.local
|
||||||
|
|
||||||
|
[DEFAULT]
|
||||||
|
#original character set of whois output will be sent to mail program
|
||||||
|
_whois = whois <ip> || echo "missing whois program"
|
||||||
|
|
||||||
|
# use heuristics to convert charset of whois output to a target
|
||||||
|
# character set before sending it to a mail program
|
||||||
|
# make sure you have 'file' and 'iconv' commands installed when opting for that
|
||||||
|
_whois_target_charset = UTF-8
|
||||||
|
_whois_convert_charset = whois <ip> |
|
||||||
|
{ WHOIS_OUTPUT=$(cat) ; WHOIS_CHARSET=$(printf %%b "$WHOIS_OUTPUT" | file -b --mime-encoding -) ; printf %%b "$WHOIS_OUTPUT" | iconv -f $WHOIS_CHARSET -t %(_whois_target_charset)s//TRANSLIT - ; }
|
||||||
|
|
||||||
|
# choose between _whois and _whois_convert_charset in mail-whois-common.local
|
||||||
|
# or other *.local which include mail-whois-common.conf.
|
||||||
|
_whois_command = %(_whois)s
|
||||||
|
#_whois_command = %(_whois_convert_charset)s
|
||||||
|
|
||||||
|
[Init]
|
|
@ -4,6 +4,10 @@
|
||||||
# Modified-By: Yaroslav Halchenko to include grepping on IP over log files
|
# Modified-By: Yaroslav Halchenko to include grepping on IP over log files
|
||||||
#
|
#
|
||||||
|
|
||||||
|
[INCLUDES]
|
||||||
|
|
||||||
|
before = mail-whois-common.conf
|
||||||
|
|
||||||
[Definition]
|
[Definition]
|
||||||
|
|
||||||
# Option: actionstart
|
# Option: actionstart
|
||||||
|
@ -39,10 +43,10 @@ actioncheck =
|
||||||
actionban = printf %%b "Hi,\n
|
actionban = printf %%b "Hi,\n
|
||||||
The IP <ip> has just been banned by Fail2Ban after
|
The IP <ip> has just been banned by Fail2Ban after
|
||||||
<failures> attempts against <name>.\n\n
|
<failures> attempts against <name>.\n\n
|
||||||
Here is more information about <ip>:\n
|
Here is more information about <ip> :\n
|
||||||
`whois <ip> || echo missing whois program`\n\n
|
`%(_whois_command)s`\n\n
|
||||||
Lines containing IP:<ip> in <logpath>\n
|
Lines containing IP:<ip> in <logpath>\n
|
||||||
`grep -E '(^|[^0-9])<ip>([^0-9]|$)' <logpath>`\n\n
|
`grep -E <grepopts> '(^|[^0-9])<ip>([^0-9]|$)' <logpath>`\n\n
|
||||||
Regards,\n
|
Regards,\n
|
||||||
Fail2Ban"|mail -s "[Fail2Ban] <name>: banned <ip> from `uname -n`" <dest>
|
Fail2Ban"|mail -s "[Fail2Ban] <name>: banned <ip> from `uname -n`" <dest>
|
||||||
|
|
||||||
|
@ -67,3 +71,7 @@ dest = root
|
||||||
# Path to the log files which contain relevant lines for the abuser IP
|
# Path to the log files which contain relevant lines for the abuser IP
|
||||||
#
|
#
|
||||||
logpath = /dev/null
|
logpath = /dev/null
|
||||||
|
|
||||||
|
# Number of log lines to include in the email
|
||||||
|
#
|
||||||
|
grepopts = -m 1000
|
||||||
|
|
|
@ -4,6 +4,10 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
|
[INCLUDES]
|
||||||
|
|
||||||
|
before = mail-whois-common.conf
|
||||||
|
|
||||||
[Definition]
|
[Definition]
|
||||||
|
|
||||||
# Option: actionstart
|
# Option: actionstart
|
||||||
|
@ -39,8 +43,8 @@ actioncheck =
|
||||||
actionban = printf %%b "Hi,\n
|
actionban = printf %%b "Hi,\n
|
||||||
The IP <ip> has just been banned by Fail2Ban after
|
The IP <ip> has just been banned by Fail2Ban after
|
||||||
<failures> attempts against <name>.\n\n
|
<failures> attempts against <name>.\n\n
|
||||||
Here is more information about <ip>:\n
|
Here is more information about <ip> :\n
|
||||||
`whois <ip> || echo missing whois program`\n
|
`%(_whois_command)s`\n
|
||||||
Regards,\n
|
Regards,\n
|
||||||
Fail2Ban"|mail -s "[Fail2Ban] <name>: banned <ip> from `uname -n`" <dest>
|
Fail2Ban"|mail -s "[Fail2Ban] <name>: banned <ip> from `uname -n`" <dest>
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ after = sendmail-common.local
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstart = printf %%b "Subject: [Fail2Ban] <name>: started on `uname -n`
|
actionstart = printf %%b "Subject: [Fail2Ban] <name>: started on `uname -n`
|
||||||
Date: `LC_TIME=C date +"%%a, %%d %%h %%Y %%T %%z"`
|
Date: `LC_ALL=C date +"%%a, %%d %%h %%Y %%T %%z"`
|
||||||
From: <sendername> <<sender>>
|
From: <sendername> <<sender>>
|
||||||
To: <dest>\n
|
To: <dest>\n
|
||||||
Hi,\n
|
Hi,\n
|
||||||
|
@ -28,7 +28,7 @@ actionstart = printf %%b "Subject: [Fail2Ban] <name>: started on `uname -n`
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstop = printf %%b "Subject: [Fail2Ban] <name>: stopped on `uname -n`
|
actionstop = printf %%b "Subject: [Fail2Ban] <name>: stopped on `uname -n`
|
||||||
Date: `LC_TIME=C date +"%%a, %%d %%h %%Y %%T %%z"`
|
Date: `LC_ALL=C date +"%%a, %%d %%h %%Y %%T %%z"`
|
||||||
From: <sendername> <<sender>>
|
From: <sendername> <<sender>>
|
||||||
To: <dest>\n
|
To: <dest>\n
|
||||||
Hi,\n
|
Hi,\n
|
||||||
|
|
|
@ -20,13 +20,13 @@ before = sendmail-common.conf
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
||||||
Date: `LC_TIME=C date +"%%a, %%d %%h %%Y %%T %%z"`
|
Date: `LC_ALL=C date +"%%a, %%d %%h %%Y %%T %%z"`
|
||||||
From: <sendername> <<sender>>
|
From: <sendername> <<sender>>
|
||||||
To: <dest>\n
|
To: <dest>\n
|
||||||
Hi,\n
|
Hi,\n
|
||||||
The IP <ip> has just been banned by Fail2Ban after
|
The IP <ip> has just been banned by Fail2Ban after
|
||||||
<failures> attempts against <name>.\n\n
|
<failures> attempts against <name>.\n\n
|
||||||
Here is more information about <ip>:\n
|
Here is more information about <ip> :\n
|
||||||
http://bgp.he.net/ip/<ip>
|
http://bgp.he.net/ip/<ip>
|
||||||
http://www.projecthoneypot.org/ip_<ip>
|
http://www.projecthoneypot.org/ip_<ip>
|
||||||
http://whois.domaintools.com/<ip>\n\n
|
http://whois.domaintools.com/<ip>\n\n
|
||||||
|
@ -34,7 +34,7 @@ actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
||||||
AS:`geoiplookup -f /usr/share/GeoIP/GeoIPASNum.dat "<ip>" | cut -d':' -f2-`
|
AS:`geoiplookup -f /usr/share/GeoIP/GeoIPASNum.dat "<ip>" | cut -d':' -f2-`
|
||||||
hostname: `host -t A <ip> 2>&1`\n\n
|
hostname: `host -t A <ip> 2>&1`\n\n
|
||||||
Lines containing IP:<ip> in <logpath>\n
|
Lines containing IP:<ip> in <logpath>\n
|
||||||
`grep -E '(^|[^0-9])<ip>([^0-9]|$)' <logpath>`\n\n
|
`grep -E <grepopts> '(^|[^0-9])<ip>([^0-9]|$)' <logpath>`\n\n
|
||||||
Regards,\n
|
Regards,\n
|
||||||
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
|
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
|
||||||
|
|
||||||
|
@ -47,3 +47,7 @@ name = default
|
||||||
# Path to the log files which contain relevant lines for the abuser IP
|
# Path to the log files which contain relevant lines for the abuser IP
|
||||||
#
|
#
|
||||||
logpath = /dev/null
|
logpath = /dev/null
|
||||||
|
|
||||||
|
# Number of log lines to include in the email
|
||||||
|
#
|
||||||
|
grepopts = -m 1000
|
||||||
|
|
|
@ -17,13 +17,13 @@ before = sendmail-common.conf
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
||||||
Date: `LC_TIME=C date +"%%a, %%d %%h %%Y %%T %%z"`
|
Date: `LC_ALL=C date +"%%a, %%d %%h %%Y %%T %%z"`
|
||||||
From: <sendername> <<sender>>
|
From: <sendername> <<sender>>
|
||||||
To: <dest>\n
|
To: <dest>\n
|
||||||
Hi,\n
|
Hi,\n
|
||||||
The IP <ip> has just been banned by Fail2Ban after
|
The IP <ip> has just been banned by Fail2Ban after
|
||||||
<failures> attempts against <name>.\n\n
|
<failures> attempts against <name>.\n\n
|
||||||
Here is more information about <ip>:\n
|
Here is more information about <ip> :\n
|
||||||
`/usr/bin/whois <ip>`\n\n
|
`/usr/bin/whois <ip>`\n\n
|
||||||
Matches for <name> with <ipjailfailures> failures IP:<ip>\n
|
Matches for <name> with <ipjailfailures> failures IP:<ip>\n
|
||||||
<ipjailmatches>\n\n
|
<ipjailmatches>\n\n
|
||||||
|
|
|
@ -17,13 +17,13 @@ before = sendmail-common.conf
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
||||||
Date: `LC_TIME=C date +"%%a, %%d %%h %%Y %%T %%z"`
|
Date: `LC_ALL=C date +"%%a, %%d %%h %%Y %%T %%z"`
|
||||||
From: <sendername> <<sender>>
|
From: <sendername> <<sender>>
|
||||||
To: <dest>\n
|
To: <dest>\n
|
||||||
Hi,\n
|
Hi,\n
|
||||||
The IP <ip> has just been banned by Fail2Ban after
|
The IP <ip> has just been banned by Fail2Ban after
|
||||||
<failures> attempts against <name>.\n\n
|
<failures> attempts against <name>.\n\n
|
||||||
Here is more information about <ip>:\n
|
Here is more information about <ip> :\n
|
||||||
`/usr/bin/whois <ip>`\n\n
|
`/usr/bin/whois <ip>`\n\n
|
||||||
Matches with <ipfailures> failures IP:<ip>\n
|
Matches with <ipfailures> failures IP:<ip>\n
|
||||||
<ipmatches>\n\n
|
<ipmatches>\n\n
|
||||||
|
|
|
@ -17,16 +17,16 @@ before = sendmail-common.conf
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
||||||
Date: `LC_TIME=C date +"%%a, %%d %%h %%Y %%T %%z"`
|
Date: `LC_ALL=C date +"%%a, %%d %%h %%Y %%T %%z"`
|
||||||
From: <sendername> <<sender>>
|
From: <sendername> <<sender>>
|
||||||
To: <dest>\n
|
To: <dest>\n
|
||||||
Hi,\n
|
Hi,\n
|
||||||
The IP <ip> has just been banned by Fail2Ban after
|
The IP <ip> has just been banned by Fail2Ban after
|
||||||
<failures> attempts against <name>.\n\n
|
<failures> attempts against <name>.\n\n
|
||||||
Here is more information about <ip>:\n
|
Here is more information about <ip> :\n
|
||||||
`/usr/bin/whois <ip> || echo missing whois program`\n\n
|
`/usr/bin/whois <ip> || echo missing whois program`\n\n
|
||||||
Lines containing IP:<ip> in <logpath>\n
|
Lines containing IP:<ip> in <logpath>\n
|
||||||
`grep -E '(^|[^0-9])<ip>([^0-9]|$)' <logpath>`\n\n
|
`grep -E <grepopts> '(^|[^0-9])<ip>([^0-9]|$)' <logpath>`\n\n
|
||||||
Regards,\n
|
Regards,\n
|
||||||
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
|
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
|
||||||
|
|
||||||
|
@ -40,3 +40,6 @@ name = default
|
||||||
#
|
#
|
||||||
logpath = /dev/null
|
logpath = /dev/null
|
||||||
|
|
||||||
|
# Number of log lines to include in the email
|
||||||
|
#
|
||||||
|
grepopts = -m 1000
|
||||||
|
|
|
@ -17,13 +17,13 @@ before = sendmail-common.conf
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
||||||
Date: `LC_TIME=C date +"%%a, %%d %%h %%Y %%T %%z"`
|
Date: `LC_ALL=C date +"%%a, %%d %%h %%Y %%T %%z"`
|
||||||
From: <sendername> <<sender>>
|
From: <sendername> <<sender>>
|
||||||
To: <dest>\n
|
To: <dest>\n
|
||||||
Hi,\n
|
Hi,\n
|
||||||
The IP <ip> has just been banned by Fail2Ban after
|
The IP <ip> has just been banned by Fail2Ban after
|
||||||
<failures> attempts against <name>.\n\n
|
<failures> attempts against <name>.\n\n
|
||||||
Here is more information about <ip>:\n
|
Here is more information about <ip> :\n
|
||||||
`/usr/bin/whois <ip>`\n\n
|
`/usr/bin/whois <ip>`\n\n
|
||||||
Matches:\n
|
Matches:\n
|
||||||
<matches>\n\n
|
<matches>\n\n
|
||||||
|
|
|
@ -17,13 +17,13 @@ before = sendmail-common.conf
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
||||||
Date: `LC_TIME=C date +"%%a, %%d %%h %%Y %%T %%z"`
|
Date: `LC_ALL=C date +"%%a, %%d %%h %%Y %%T %%z"`
|
||||||
From: <sendername> <<sender>>
|
From: <sendername> <<sender>>
|
||||||
To: <dest>\n
|
To: <dest>\n
|
||||||
Hi,\n
|
Hi,\n
|
||||||
The IP <ip> has just been banned by Fail2Ban after
|
The IP <ip> has just been banned by Fail2Ban after
|
||||||
<failures> attempts against <name>.\n\n
|
<failures> attempts against <name>.\n\n
|
||||||
Here is more information about <ip>:\n
|
Here is more information about <ip> :\n
|
||||||
`/usr/bin/whois <ip> || echo missing whois program`\n
|
`/usr/bin/whois <ip> || echo missing whois program`\n
|
||||||
Regards,\n
|
Regards,\n
|
||||||
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
|
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
|
||||||
|
|
|
@ -17,7 +17,7 @@ before = sendmail-common.conf
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
||||||
Date: `LC_TIME=C date +"%%a, %%d %%h %%Y %%T %%z"`
|
Date: `LC_ALL=C date +"%%a, %%d %%h %%Y %%T %%z"`
|
||||||
From: <sendername> <<sender>>
|
From: <sendername> <<sender>>
|
||||||
To: <dest>\n
|
To: <dest>\n
|
||||||
Hi,\n
|
Hi,\n
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
# Fail2Ban configuration file
|
||||||
|
#
|
||||||
|
# Author: Eduardo Diaz
|
||||||
|
#
|
||||||
|
# This is for ipset protocol 6 (and hopefully later) (ipset v6.14).
|
||||||
|
# for shorewall
|
||||||
|
#
|
||||||
|
# Use this setting in jail.conf to modify use this action instead of a
|
||||||
|
# default one
|
||||||
|
#
|
||||||
|
# banaction = shorewall-ipset-proto6
|
||||||
|
#
|
||||||
|
# This requires the program ipset which is normally in package called ipset.
|
||||||
|
#
|
||||||
|
# IPset was a feature introduced in the linux kernel 2.6.39 and 3.0.0
|
||||||
|
# kernels, and you need Shorewall >= 4.5.5 to use this action.
|
||||||
|
#
|
||||||
|
# The default Shorewall configuration is with "BLACKLISTNEWONLY=Yes" (see
|
||||||
|
# file /etc/shorewall/shorewall.conf). This means that when Fail2ban adds a
|
||||||
|
# new shorewall rule to ban an IP address, that rule will affect only new
|
||||||
|
# connections. So if the attacker goes on trying using the same connection
|
||||||
|
# he could even log in. In order to get the same behavior of the iptable
|
||||||
|
# action (so that the ban is immediate) the /etc/shorewall/shorewall.conf
|
||||||
|
# file should me modified with "BLACKLISTNEWONLY=No".
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Enable shorewall to use a blacklist using iptables creating a file
|
||||||
|
# /etc/shorewall/blrules and adding "DROP net:+f2b-ssh all" and
|
||||||
|
# similar lines for every jail. To enable restoring you ipset you
|
||||||
|
# must set SAVE_IPSETS=Yes in shorewall.conf . You can read more
|
||||||
|
# about ipsets handling in Shorewall at http://shorewall.net/ipsets.html
|
||||||
|
#
|
||||||
|
# To force creation of the ipset in the case that somebody deletes the
|
||||||
|
# ipset create a file /etc/shorewall/initdone and add one line for
|
||||||
|
# every ipset (this files are in Perl) and add 1 at the end of the file.
|
||||||
|
# The example:
|
||||||
|
# system("/usr/sbin/ipset -quiet -exist create f2b-ssh hash:ip timeout 600 ");
|
||||||
|
# 1;
|
||||||
|
#
|
||||||
|
# To destroy the ipset in shorewall you must add to the file /etc/shorewall/stopped
|
||||||
|
# # One line of every ipset
|
||||||
|
# system("/usr/sbin/ipset -quiet destroy f2b-ssh ");
|
||||||
|
# 1; # This must go to the end of the file if not shorewall compilation fails
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
|
||||||
|
# Option: actionstart
|
||||||
|
# Notes.: command executed once at the start of Fail2Ban.
|
||||||
|
# Values: CMD
|
||||||
|
#
|
||||||
|
actionstart = if ! ipset -quiet -name list f2b-<name> >/dev/null;
|
||||||
|
then ipset -quiet -exist create f2b-<name> hash:ip timeout <bantime>;
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Option: actionstop
|
||||||
|
# Notes.: command executed once at the end of Fail2Ban
|
||||||
|
# Values: CMD
|
||||||
|
#
|
||||||
|
actionstop = ipset flush f2b-<name>
|
||||||
|
|
||||||
|
# Option: actionban
|
||||||
|
# Notes.: command executed when banning an IP. Take care that the
|
||||||
|
# command is executed with Fail2Ban user rights.
|
||||||
|
# Tags: See jail.conf(5) man page
|
||||||
|
# Values: CMD
|
||||||
|
#
|
||||||
|
actionban = ipset add f2b-<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: See jail.conf(5) man page
|
||||||
|
# Values: CMD
|
||||||
|
#
|
||||||
|
actionunban = ipset del f2b-<name> <ip> -exist
|
||||||
|
|
||||||
|
[Init]
|
||||||
|
|
||||||
|
# Option: bantime
|
||||||
|
# Notes: specifies the bantime in seconds (handled internally rather than by fail2ban)
|
||||||
|
# Values: [ NUM ] Default: 600
|
||||||
|
#
|
||||||
|
bantime = 600
|
|
@ -68,6 +68,7 @@ Matches for %(ip)s for jail %(jailname)s:
|
||||||
%(ipjailmatches)s
|
%(ipjailmatches)s
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class SMTPAction(ActionBase):
|
class SMTPAction(ActionBase):
|
||||||
"""Fail2Ban action which sends emails to inform on jail starting,
|
"""Fail2Ban action which sends emails to inform on jail starting,
|
||||||
stopping and bans.
|
stopping and bans.
|
||||||
|
|
|
@ -3,6 +3,9 @@
|
||||||
# Author: Yaroslav Halchenko
|
# Author: Yaroslav Halchenko
|
||||||
#
|
#
|
||||||
|
|
||||||
|
[INCLUDES]
|
||||||
|
|
||||||
|
before = iptables-common.conf
|
||||||
|
|
||||||
[Definition]
|
[Definition]
|
||||||
|
|
||||||
|
@ -22,21 +25,21 @@ actionstop =
|
||||||
# Notes.: command executed once before each actionban command
|
# Notes.: command executed once before each actionban command
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actioncheck = iptables -n -L <chain>
|
actioncheck = <iptables> -n -L <chain>
|
||||||
|
|
||||||
# Option: actionban
|
# Option: actionban
|
||||||
# Notes.: command executed when banning an IP.
|
# Notes.: command executed when banning an IP.
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionban = echo 'all' >| /etc/symbiosis/firewall/blacklist.d/<ip>.auto
|
actionban = echo 'all' >| /etc/symbiosis/firewall/blacklist.d/<ip>.auto
|
||||||
iptables -I <chain> 1 -s <ip> -j <blocktype>
|
<iptables> -I <chain> 1 -s <ip> -j <blocktype>
|
||||||
|
|
||||||
# Option: actionunban
|
# Option: actionunban
|
||||||
# Notes.: command executed when unbanning an IP.
|
# Notes.: command executed when unbanning an IP.
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionunban = rm -f /etc/symbiosis/firewall/blacklist.d/<ip>.auto
|
actionunban = rm -f /etc/symbiosis/firewall/blacklist.d/<ip>.auto
|
||||||
iptables -D <chain> -s <ip> -j <blocktype> || :
|
<iptables> -D <chain> -s <ip> -j <blocktype> || :
|
||||||
|
|
||||||
[Init]
|
[Init]
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ actionban = oifs=${IFS}; IFS=.;SEP_IP=( <ip> ); set -- ${SEP_IP}; ADDRESSES=$(di
|
||||||
REPORTID=<time>@`uname -n`
|
REPORTID=<time>@`uname -n`
|
||||||
TLP=<tlp>
|
TLP=<tlp>
|
||||||
PORT=<port>
|
PORT=<port>
|
||||||
DATE=`LC_TIME=C date --date=@<time> +"%%a, %%d %%h %%Y %%T %%z"`
|
DATE=`LC_ALL=C date --date=@<time> +"%%a, %%d %%h %%Y %%T %%z"`
|
||||||
if [ ! -z "$ADDRESSES" ]; then
|
if [ ! -z "$ADDRESSES" ]; then
|
||||||
(printf -- %%b "<header>\n<message>\n<report>\n";
|
(printf -- %%b "<header>\n<message>\n<report>\n";
|
||||||
date '+Note: Local timezone is %%z (%%Z)';
|
date '+Note: Local timezone is %%z (%%Z)';
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
badbotscustom = EmailCollector|WebEMailExtrac|TrackBack/1\.02|sogou music spider
|
badbotscustom = EmailCollector|WebEMailExtrac|TrackBack/1\.02|sogou music spider
|
||||||
badbots = Atomic_Email_Hunter/4\.0|atSpider/1\.0|autoemailspider|bwh3_user_agent|China Local Browse 2\.6|ContactBot/0\.2|ContentSmartz|DataCha0s/2\.0|DBrowse 1\.4b|DBrowse 1\.4d|Demo Bot DOT 16b|Demo Bot Z 16b|DSurf15a 01|DSurf15a 71|DSurf15a 81|DSurf15a VA|EBrowse 1\.4b|Educate Search VxB|EmailSiphon|EmailSpider|EmailWolf 1\.00|ESurf15a 15|ExtractorPro|Franklin Locator 1\.8|FSurf15a 01|Full Web Bot 0416B|Full Web Bot 0516B|Full Web Bot 2816B|Guestbook Auto Submitter|Industry Program 1\.0\.x|ISC Systems iRc Search 2\.1|IUPUI Research Bot v 1\.9a|LARBIN-EXPERIMENTAL \(efp@gmx\.net\)|LetsCrawl\.com/1\.0 +http\://letscrawl\.com/|Lincoln State Web Browser|LMQueueBot/0\.2|LWP\:\:Simple/5\.803|Mac Finder 1\.0\.xx|MFC Foundation Class Library 4\.0|Microsoft URL Control - 6\.00\.8xxx|Missauga Locate 1\.0\.0|Missigua Locator 1\.9|Missouri College Browse|Mizzu Labs 2\.2|Mo College 1\.9|MVAClient|Mozilla/2\.0 \(compatible; NEWT ActiveX; Win32\)|Mozilla/3\.0 \(compatible; Indy Library\)|Mozilla/3\.0 \(compatible; scan4mail \(advanced version\) http\://www\.peterspages\.net/?scan4mail\)|Mozilla/4\.0 \(compatible; Advanced Email Extractor v2\.xx\)|Mozilla/4\.0 \(compatible; Iplexx Spider/1\.0 http\://www\.iplexx\.at\)|Mozilla/4\.0 \(compatible; MSIE 5\.0; Windows NT; DigExt; DTS Agent|Mozilla/4\.0 efp@gmx\.net|Mozilla/5\.0 \(Version\: xxxx Type\:xx\)|NameOfAgent \(CMS Spider\)|NASA Search 1\.0|Nsauditor/1\.x|PBrowse 1\.4b|PEval 1\.4b|Poirot|Port Huron Labs|Production Bot 0116B|Production Bot 2016B|Production Bot DOT 3016B|Program Shareware 1\.0\.2|PSurf15a 11|PSurf15a 51|PSurf15a VA|psycheclone|RSurf15a 41|RSurf15a 51|RSurf15a 81|searchbot admin@google\.com|ShablastBot 1\.0|snap\.com beta crawler v0|Snapbot/1\.0|Snapbot/1\.0 \(Snap Shots, +http\://www\.snap\.com\)|sogou develop spider|Sogou Orion spider/3\.0\(+http\://www\.sogou\.com/docs/help/webmasters\.htm#07\)|sogou spider|Sogou web spider/3\.0\(+http\://www\.sogou\.com/docs/help/webmasters\.htm#07\)|sohu agent|SSurf15a 11 |TSurf15a 11|Under the Rainbow 2\.2|User-Agent\: Mozilla/4\.0 \(compatible; MSIE 6\.0; Windows NT 5\.1\)|VadixBot|WebVulnCrawl\.unknown/1\.0 libwww-perl/5\.803|Wells Search II|WEP Search 00
|
badbots = Atomic_Email_Hunter/4\.0|atSpider/1\.0|autoemailspider|bwh3_user_agent|China Local Browse 2\.6|ContactBot/0\.2|ContentSmartz|DataCha0s/2\.0|DBrowse 1\.4b|DBrowse 1\.4d|Demo Bot DOT 16b|Demo Bot Z 16b|DSurf15a 01|DSurf15a 71|DSurf15a 81|DSurf15a VA|EBrowse 1\.4b|Educate Search VxB|EmailSiphon|EmailSpider|EmailWolf 1\.00|ESurf15a 15|ExtractorPro|Franklin Locator 1\.8|FSurf15a 01|Full Web Bot 0416B|Full Web Bot 0516B|Full Web Bot 2816B|Guestbook Auto Submitter|Industry Program 1\.0\.x|ISC Systems iRc Search 2\.1|IUPUI Research Bot v 1\.9a|LARBIN-EXPERIMENTAL \(efp@gmx\.net\)|LetsCrawl\.com/1\.0 +http\://letscrawl\.com/|Lincoln State Web Browser|LMQueueBot/0\.2|LWP\:\:Simple/5\.803|Mac Finder 1\.0\.xx|MFC Foundation Class Library 4\.0|Microsoft URL Control - 6\.00\.8xxx|Missauga Locate 1\.0\.0|Missigua Locator 1\.9|Missouri College Browse|Mizzu Labs 2\.2|Mo College 1\.9|MVAClient|Mozilla/2\.0 \(compatible; NEWT ActiveX; Win32\)|Mozilla/3\.0 \(compatible; Indy Library\)|Mozilla/3\.0 \(compatible; scan4mail \(advanced version\) http\://www\.peterspages\.net/?scan4mail\)|Mozilla/4\.0 \(compatible; Advanced Email Extractor v2\.xx\)|Mozilla/4\.0 \(compatible; Iplexx Spider/1\.0 http\://www\.iplexx\.at\)|Mozilla/4\.0 \(compatible; MSIE 5\.0; Windows NT; DigExt; DTS Agent|Mozilla/4\.0 efp@gmx\.net|Mozilla/5\.0 \(Version\: xxxx Type\:xx\)|NameOfAgent \(CMS Spider\)|NASA Search 1\.0|Nsauditor/1\.x|PBrowse 1\.4b|PEval 1\.4b|Poirot|Port Huron Labs|Production Bot 0116B|Production Bot 2016B|Production Bot DOT 3016B|Program Shareware 1\.0\.2|PSurf15a 11|PSurf15a 51|PSurf15a VA|psycheclone|RSurf15a 41|RSurf15a 51|RSurf15a 81|searchbot admin@google\.com|ShablastBot 1\.0|snap\.com beta crawler v0|Snapbot/1\.0|Snapbot/1\.0 \(Snap Shots, +http\://www\.snap\.com\)|sogou develop spider|Sogou Orion spider/3\.0\(+http\://www\.sogou\.com/docs/help/webmasters\.htm#07\)|sogou spider|Sogou web spider/3\.0\(+http\://www\.sogou\.com/docs/help/webmasters\.htm#07\)|sohu agent|SSurf15a 11 |TSurf15a 11|Under the Rainbow 2\.2|User-Agent\: Mozilla/4\.0 \(compatible; MSIE 6\.0; Windows NT 5\.1\)|VadixBot|WebVulnCrawl\.unknown/1\.0 libwww-perl/5\.803|Wells Search II|WEP Search 00
|
||||||
|
|
||||||
failregex = ^<HOST> -.*"(GET|POST).*HTTP.*"(?:%(badbots)s|%(badbotscustom)s)"$
|
failregex = ^<HOST> -.*"(GET|POST|HEAD).*HTTP.*"(?:%(badbots)s|%(badbotscustom)s)"$
|
||||||
|
|
||||||
ignoreregex =
|
ignoreregex =
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
# Fail2Ban Apache pass filter
|
||||||
|
# This filter is for access.log, NOT for error.log
|
||||||
|
#
|
||||||
|
# The knocking request must have a referer.
|
||||||
|
|
||||||
|
[INCLUDES]
|
||||||
|
|
||||||
|
before = apache-common.conf
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
|
||||||
|
failregex = ^<HOST> - \w+ \[\] "GET <knocking_url> HTTP/1\.[01]" 200 \d+ ".*" "[^-].*"$
|
||||||
|
|
||||||
|
ignoreregex =
|
||||||
|
|
||||||
|
[Init]
|
||||||
|
|
||||||
|
knocking_url = /knocking/
|
||||||
|
|
||||||
|
# Author: Viktor Szépe
|
|
@ -13,6 +13,8 @@ _daemon = asterisk
|
||||||
|
|
||||||
__pid_re = (?:\[\d+\])
|
__pid_re = (?:\[\d+\])
|
||||||
|
|
||||||
|
iso8601 = \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+[+-]\d{4}
|
||||||
|
|
||||||
# All Asterisk log messages begin like this:
|
# All Asterisk log messages begin like this:
|
||||||
log_prefix= (?:NOTICE|SECURITY)%(__pid_re)s:?(?:\[C-[\da-f]*\])? \S+:\d*( in \w+:)?
|
log_prefix= (?:NOTICE|SECURITY)%(__pid_re)s:?(?:\[C-[\da-f]*\])? \S+:\d*( in \w+:)?
|
||||||
|
|
||||||
|
@ -23,7 +25,7 @@ failregex = ^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s Registration from '[^']*'
|
||||||
^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s Host <HOST> failed MD5 authentication for '[^']*' \([^)]+\)$
|
^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s Host <HOST> failed MD5 authentication for '[^']*' \([^)]+\)$
|
||||||
^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s Failed to authenticate (user|device) [^@]+@<HOST>\S*$
|
^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s Failed to authenticate (user|device) [^@]+@<HOST>\S*$
|
||||||
^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s hacking attempt detected '<HOST>'$
|
^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s hacking attempt detected '<HOST>'$
|
||||||
^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s SecurityEvent="(FailedACL|InvalidAccountID|ChallengeResponseFailed|InvalidPassword)",EventTV="[\d-]+",Severity="[\w]+",Service="[\w]+",EventVersion="\d+",AccountID="\d*",SessionID="0x[\da-f]+",LocalAddress="IPV[46]/(UD|TC)P/[\da-fA-F:.]+/\d+",RemoteAddress="IPV[46]/(UD|TC)P/<HOST>/\d+"(,Challenge="\w+",ReceivedChallenge="\w+")?(,ReceivedHash="[\da-f]+")?(,ACLName="\w+")?$
|
^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s SecurityEvent="(FailedACL|InvalidAccountID|ChallengeResponseFailed|InvalidPassword)",EventTV="([\d-]+|%(iso8601)s)",Severity="[\w]+",Service="[\w]+",EventVersion="\d+",AccountID="(\d*|<unknown>)",SessionID=".+",LocalAddress="IPV[46]/(UDP|TCP|WS)/[\da-fA-F:.]+/\d+",RemoteAddress="IPV[46]/(UDP|TCP|WS)/<HOST>/\d+"(,Challenge="[\w/]+")?(,ReceivedChallenge="\w+")?(,Response="\w+",ExpectedResponse="\w*")?(,ReceivedHash="[\da-f]+")?(,ACLName="\w+")?$
|
||||||
^(%(__prefix_line)s|\[\]\s*WARNING%(__pid_re)s:?(?:\[C-[\da-f]*\])? )Ext\. s: "Rejecting unknown SIP connection from <HOST>"$
|
^(%(__prefix_line)s|\[\]\s*WARNING%(__pid_re)s:?(?:\[C-[\da-f]*\])? )Ext\. s: "Rejecting unknown SIP connection from <HOST>"$
|
||||||
|
|
||||||
ignoreregex =
|
ignoreregex =
|
||||||
|
|
|
@ -12,7 +12,7 @@ _daemon = (auth|dovecot(-auth)?|auth-worker)
|
||||||
failregex = ^%(__prefix_line)s(%(__pam_auth)s(\(dovecot:auth\))?:)?\s+authentication failure; logname=\S* uid=\S* euid=\S* tty=dovecot ruser=\S* rhost=<HOST>(\s+user=\S*)?\s*$
|
failregex = ^%(__prefix_line)s(%(__pam_auth)s(\(dovecot:auth\))?:)?\s+authentication failure; logname=\S* uid=\S* euid=\S* tty=dovecot ruser=\S* rhost=<HOST>(\s+user=\S*)?\s*$
|
||||||
^%(__prefix_line)s(pop3|imap)-login: (Info: )?(Aborted login|Disconnected)(: Inactivity)? \(((auth failed, \d+ attempts)( in \d+ secs)?|tried to use (disabled|disallowed) \S+ auth)\):( user=<\S*>,)?( method=\S+,)? rip=<HOST>(, lip=(\d{1,3}\.){3}\d{1,3})?(, TLS( handshaking(: SSL_accept\(\) failed: error:[\dA-F]+:SSL routines:[TLS\d]+_GET_CLIENT_HELLO:unknown protocol)?)?(: Disconnected)?)?(, session=<\S+>)?\s*$
|
^%(__prefix_line)s(pop3|imap)-login: (Info: )?(Aborted login|Disconnected)(: Inactivity)? \(((auth failed, \d+ attempts)( in \d+ secs)?|tried to use (disabled|disallowed) \S+ auth)\):( user=<\S*>,)?( method=\S+,)? rip=<HOST>(, lip=(\d{1,3}\.){3}\d{1,3})?(, TLS( handshaking(: SSL_accept\(\) failed: error:[\dA-F]+:SSL routines:[TLS\d]+_GET_CLIENT_HELLO:unknown protocol)?)?(: Disconnected)?)?(, session=<\S+>)?\s*$
|
||||||
^%(__prefix_line)s(Info|dovecot: auth\(default\)|auth-worker\(\d+\)): pam\(\S+,<HOST>\): pam_authenticate\(\) failed: (User not known to the underlying authentication module: \d+ Time\(s\)|Authentication failure \(password mismatch\?\))\s*$
|
^%(__prefix_line)s(Info|dovecot: auth\(default\)|auth-worker\(\d+\)): pam\(\S+,<HOST>\): pam_authenticate\(\) failed: (User not known to the underlying authentication module: \d+ Time\(s\)|Authentication failure \(password mismatch\?\))\s*$
|
||||||
^%(__prefix_line)sauth-worker\(\d+\): pam\(\S+,<HOST>\): unknown user\s*$
|
^%(__prefix_line)s(auth|auth-worker\(\d+\)): (pam|passwd-file)\(\S+,<HOST>\): unknown user\s*$
|
||||||
|
|
||||||
ignoreregex =
|
ignoreregex =
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
# Fail2Ban configuration file to block repeated failed login attempts to Frolor installation(s)
|
||||||
|
#
|
||||||
|
# Froxlor needs to log to Syslog User (e.g. /var/log/user.log) with one of the following messages
|
||||||
|
# <syslog prefix> Froxlor: [Login Action <HOST>] Unknown user '<USER>' tried to login.
|
||||||
|
# <syslog prefix> Froxlor: [Login Action <HOST>] User '<USER>' tried to login with wrong password.
|
||||||
|
#
|
||||||
|
# Author: Joern Muehlencord
|
||||||
|
#
|
||||||
|
|
||||||
|
[INCLUDES]
|
||||||
|
|
||||||
|
# Read common prefixes. If any customizations available -- read them from
|
||||||
|
# common.local
|
||||||
|
before = common.conf
|
||||||
|
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
|
||||||
|
_daemon = Froxlor
|
||||||
|
|
||||||
|
# Option: failregex
|
||||||
|
# Notes.: regex to match the password failures messages in the logfile. The
|
||||||
|
# host must be matched by a group named "host". The tag "<HOST>" can
|
||||||
|
# be used for standard IP/hostname matching and is only an alias for
|
||||||
|
# (?:::f{4,6}:)?(?P<host>[\w\-.^_]+)
|
||||||
|
# Values: TEXT
|
||||||
|
#
|
||||||
|
failregex = ^%(__prefix_line)s\[Login Action <HOST>\] Unknown user \S* tried to login.$
|
||||||
|
^%(__prefix_line)s\[Login Action <HOST>\] User \S* tried to login with wrong password.$
|
||||||
|
|
||||||
|
|
||||||
|
# Option: ignoreregex
|
||||||
|
# Notes.: regex to ignore. If this regex matches, the line is ignored.
|
||||||
|
# Values: TEXT
|
||||||
|
#
|
||||||
|
ignoreregex =
|
||||||
|
|
|
@ -8,8 +8,8 @@ before = botsearch-common.conf
|
||||||
|
|
||||||
[Definition]
|
[Definition]
|
||||||
|
|
||||||
failregex = ^<HOST> \- \S+ \[\] \"(GET|POST) \/<block> \S+\" 404 .+$
|
failregex = ^<HOST> \- \S+ \[\] \"(GET|POST|HEAD) \/<block> \S+\" 404 .+$
|
||||||
^ \[error\] \d+#\d+: \*\d+ (\S+ )?\"\S+\" (failed|is not found) \(2\: No such file or directory\), client\: <HOST>\, server\: \S*\, request: \"(GET|POST) \/<block> \S+\"\, .*?$
|
^ \[error\] \d+#\d+: \*\d+ (\S+ )?\"\S+\" (failed|is not found) \(2\: No such file or directory\), client\: <HOST>\, server\: \S*\, request: \"(GET|POST|HEAD) \/<block> \S+\"\, .*?$
|
||||||
|
|
||||||
ignoreregex =
|
ignoreregex =
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
#
|
#
|
||||||
# Set "UseReverseDNS off" in proftpd.conf to avoid the need for DNS.
|
# Set "UseReverseDNS off" in proftpd.conf to avoid the need for DNS.
|
||||||
# See: http://www.proftpd.org/docs/howto/DNS.html
|
# See: http://www.proftpd.org/docs/howto/DNS.html
|
||||||
|
# When the default locale for your system is not en_US.UTF-8
|
||||||
|
# on Debian-based systems be sure to add this to /etc/default/proftpd
|
||||||
|
# export LC_TIME="en_US.UTF-8"
|
||||||
|
|
||||||
[INCLUDES]
|
[INCLUDES]
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
# Fail2Ban configuration file for roundcube web server
|
# Fail2Ban configuration file for roundcube web server
|
||||||
#
|
#
|
||||||
|
# By default failed logins are printed to 'errors'. The first regex matches those
|
||||||
|
# The second regex matches those printed to 'userlogins'
|
||||||
|
# The userlogins log file can be enabled by setting $config['log_logins'] = true; in config.inc.php
|
||||||
#
|
#
|
||||||
|
# The logpath in your jail can be updated to userlogins if you wish
|
||||||
#
|
#
|
||||||
|
|
||||||
[INCLUDES]
|
[INCLUDES]
|
||||||
|
@ -9,7 +13,8 @@ before = common.conf
|
||||||
|
|
||||||
[Definition]
|
[Definition]
|
||||||
|
|
||||||
failregex = ^\s*(\[\])?(%(__hostname)s roundcube: IMAP Error)?: (FAILED login|Login failed) for .*? from <HOST>(\. .* in .*?/rcube_imap\.php on line \d+ \(\S+ \S+\))?$
|
failregex = ^\s*(\[\])?(%(__hostname)s\s*(roundcube:)?\s*(<[\w]+>)? IMAP Error)?: (FAILED login|Login failed) for .*? from <HOST>(\. .* in .*?/rcube_imap\.php on line \d+ \(\S+ \S+\))?$
|
||||||
|
^\[\]:\s*(<[\w]+>)? Failed login for [\w\-\.\+]+(@[\w\-\.\+]+\.[a-zA-Z]{2,6})? from <HOST> in session \w+( \(error: \d\))?$
|
||||||
|
|
||||||
ignoreregex =
|
ignoreregex =
|
||||||
# DEV Notes:
|
# DEV Notes:
|
||||||
|
@ -26,4 +31,4 @@ ignoreregex =
|
||||||
# arbitrary user input and IMAP response doesn't inject the wrong IP for
|
# arbitrary user input and IMAP response doesn't inject the wrong IP for
|
||||||
# fail2ban
|
# fail2ban
|
||||||
#
|
#
|
||||||
# Author: Teodor Micu & Yaroslav Halchenko & terence namusonge & Daniel Black
|
# Author: Teodor Micu & Yaroslav Halchenko & terence namusonge & Daniel Black & Lee Clemens
|
||||||
|
|
|
@ -174,6 +174,10 @@ action_mwl = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(por
|
||||||
action_xarf = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
|
action_xarf = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
|
||||||
xarf-login-attack[service=%(__name__)s, sender="%(sender)s", logpath=%(logpath)s, port="%(port)s"]
|
xarf-login-attack[service=%(__name__)s, sender="%(sender)s", logpath=%(logpath)s, port="%(port)s"]
|
||||||
|
|
||||||
|
# ban IP on CloudFlare & send an e-mail with whois report and relevant log lines
|
||||||
|
# to the destemail.
|
||||||
|
action_cf_mwl = cloudflare[cfuser="%(cfemail)s", cftoken="%(cfapikey)s"]
|
||||||
|
%(mta)s-whois-lines[name=%(__name__)s, dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s"]
|
||||||
|
|
||||||
# Report block via blocklist.de fail2ban reporting service API
|
# Report block via blocklist.de fail2ban reporting service API
|
||||||
#
|
#
|
||||||
|
@ -344,7 +348,7 @@ logpath = %(lighttpd_error_log)s
|
||||||
[roundcube-auth]
|
[roundcube-auth]
|
||||||
|
|
||||||
port = http,https
|
port = http,https
|
||||||
logpath = /var/log/roundcube/userlogins
|
logpath = logpath = %(roundcube_errors_log)s
|
||||||
|
|
||||||
|
|
||||||
[openwebmail]
|
[openwebmail]
|
||||||
|
@ -408,6 +412,12 @@ port = 10000
|
||||||
logpath = %(syslog_authpriv)s
|
logpath = %(syslog_authpriv)s
|
||||||
|
|
||||||
|
|
||||||
|
[froxlor-auth]
|
||||||
|
|
||||||
|
port = http,https
|
||||||
|
logpath = %(syslog_authpriv)s
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# HTTP Proxy servers
|
# HTTP Proxy servers
|
||||||
#
|
#
|
||||||
|
@ -424,6 +434,7 @@ logpath = /var/log/squid/access.log
|
||||||
port = 3128
|
port = 3128
|
||||||
logpath = /var/log/3proxy.log
|
logpath = /var/log/3proxy.log
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# FTP servers
|
# FTP servers
|
||||||
#
|
#
|
||||||
|
@ -756,3 +767,16 @@ port = 2222
|
||||||
enabled = false
|
enabled = false
|
||||||
logpath = /var/lib/portsentry/portsentry.history
|
logpath = /var/lib/portsentry/portsentry.history
|
||||||
maxretry = 1
|
maxretry = 1
|
||||||
|
|
||||||
|
[pass2allow-ftp]
|
||||||
|
# this pass2allow example allows FTP traffic after successful HTTP authentication
|
||||||
|
port = ftp,ftp-data,ftps,ftps-data
|
||||||
|
# knocking_url variable must be overridden to some secret value in filter.d/apache-pass.local
|
||||||
|
filter = apache-pass
|
||||||
|
# access log of the website with HTTP auth
|
||||||
|
logpath = %(apache_access_log)s
|
||||||
|
blocktype = RETURN
|
||||||
|
returntype = DROP
|
||||||
|
bantime = 3600
|
||||||
|
maxretry = 1
|
||||||
|
findtime = 1
|
||||||
|
|
|
@ -62,5 +62,7 @@ solidpop3d_log = %(syslog_local0)s
|
||||||
|
|
||||||
mysql_log = %(syslog_daemon)s
|
mysql_log = %(syslog_daemon)s
|
||||||
|
|
||||||
|
roundcube_errors_log = /var/log/roundcube/errors
|
||||||
|
|
||||||
# Directory with ignorecommand scripts
|
# Directory with ignorecommand scripts
|
||||||
ignorecommands_dir = /etc/fail2ban/filter.d/ignorecommands
|
ignorecommands_dir = /etc/fail2ban/filter.d/ignorecommands
|
||||||
|
|
|
@ -35,6 +35,3 @@ exim_main_log = /var/log/exim4/mainlog
|
||||||
# was in debian squeezy but not in wheezy
|
# was in debian squeezy but not in wheezy
|
||||||
# /etc/proftpd/proftpd.conf (SystemLog)
|
# /etc/proftpd/proftpd.conf (SystemLog)
|
||||||
proftpd_log = /var/log/proftpd/proftpd.log
|
proftpd_log = /var/log/proftpd/proftpd.log
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -35,3 +35,5 @@ apache_access_log = /var/log/httpd/*access_log
|
||||||
exim_main_log = /var/log/exim/main.log
|
exim_main_log = /var/log/exim/main.log
|
||||||
|
|
||||||
mysql_log = /var/lib/mysql/mysqld.log
|
mysql_log = /var/lib/mysql/mysqld.log
|
||||||
|
|
||||||
|
roundcube_errors_log = /var/log/roundcubemail/errors
|
||||||
|
|
|
@ -37,6 +37,7 @@ Below derived from:
|
||||||
logging.NOTICE = logging.INFO + 5
|
logging.NOTICE = logging.INFO + 5
|
||||||
logging.addLevelName(logging.NOTICE, 'NOTICE')
|
logging.addLevelName(logging.NOTICE, 'NOTICE')
|
||||||
|
|
||||||
|
|
||||||
# define a new logger function for notice
|
# define a new logger function for notice
|
||||||
# this is exactly like existing info, critical, debug...etc
|
# this is exactly like existing info, critical, debug...etc
|
||||||
def _Logger_notice(self, msg, *args, **kwargs):
|
def _Logger_notice(self, msg, *args, **kwargs):
|
||||||
|
@ -53,6 +54,7 @@ def _Logger_notice(self, msg, *args, **kwargs):
|
||||||
|
|
||||||
logging.Logger.notice = _Logger_notice
|
logging.Logger.notice = _Logger_notice
|
||||||
|
|
||||||
|
|
||||||
# define a new root level notice function
|
# define a new root level notice function
|
||||||
# this is exactly like existing info, critical, debug...etc
|
# this is exactly like existing info, critical, debug...etc
|
||||||
def _root_notice(msg, *args, **kwargs):
|
def _root_notice(msg, *args, **kwargs):
|
||||||
|
|
|
@ -32,6 +32,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ActionReader(DefinitionInitConfigReader):
|
class ActionReader(DefinitionInitConfigReader):
|
||||||
|
|
||||||
_configOpts = [
|
_configOpts = [
|
||||||
|
|
|
@ -27,6 +27,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Beautify the output of the client.
|
# Beautify the output of the client.
|
||||||
#
|
#
|
||||||
|
@ -45,7 +46,8 @@ class Beautifier:
|
||||||
return self.__inputCmd
|
return self.__inputCmd
|
||||||
|
|
||||||
def beautify(self, response):
|
def beautify(self, response):
|
||||||
logSys.debug("Beautify " + `response` + " with " + `self.__inputCmd`)
|
logSys.debug(
|
||||||
|
"Beautify " + repr(response) + " with " + repr(self.__inputCmd))
|
||||||
inC = self.__inputCmd
|
inC = self.__inputCmd
|
||||||
msg = response
|
msg = response
|
||||||
try:
|
try:
|
||||||
|
@ -102,7 +104,7 @@ class Beautifier:
|
||||||
elif response == 4:
|
elif response == 4:
|
||||||
msg = msg + "DEBUG"
|
msg = msg + "DEBUG"
|
||||||
else:
|
else:
|
||||||
msg = msg + `response`
|
msg = msg + repr(response)
|
||||||
elif inC[1] == "dbfile":
|
elif inC[1] == "dbfile":
|
||||||
if response is None:
|
if response is None:
|
||||||
msg = "Database currently disabled"
|
msg = "Database currently disabled"
|
||||||
|
@ -183,13 +185,14 @@ class Beautifier:
|
||||||
msg += ", ".join(response)
|
msg += ", ".join(response)
|
||||||
except Exception:
|
except Exception:
|
||||||
logSys.warning("Beautifier error. Please report the error")
|
logSys.warning("Beautifier error. Please report the error")
|
||||||
logSys.error("Beautify " + `response` + " with " + `self.__inputCmd` +
|
logSys.error("Beautify " + repr(response) + " with "
|
||||||
" failed")
|
+ repr(self.__inputCmd) + " failed")
|
||||||
msg = msg + `response`
|
msg = msg + repr(response)
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
def beautifyError(self, response):
|
def beautifyError(self, response):
|
||||||
logSys.debug("Beautify (error) " + `response` + " with " + `self.__inputCmd`)
|
logSys.debug("Beautify (error) " + repr(response) + " with "
|
||||||
|
+ repr(self.__inputCmd))
|
||||||
msg = response
|
msg = response
|
||||||
if isinstance(response, UnknownJailException):
|
if isinstance(response, UnknownJailException):
|
||||||
msg = "Sorry but the jail '" + response.args[0] + "' does not exist"
|
msg = "Sorry but the jail '" + response.args[0] + "' does not exist"
|
||||||
|
|
|
@ -24,7 +24,8 @@ __author__ = 'Yaroslav Halhenko'
|
||||||
__copyright__ = 'Copyright (c) 2007 Yaroslav Halchenko'
|
__copyright__ = 'Copyright (c) 2007 Yaroslav Halchenko'
|
||||||
__license__ = 'GPL'
|
__license__ = 'GPL'
|
||||||
|
|
||||||
import os, sys
|
import os
|
||||||
|
import sys
|
||||||
from ..helpers import getLogger
|
from ..helpers import getLogger
|
||||||
|
|
||||||
if sys.version_info >= (3,2): # pragma: no cover
|
if sys.version_info >= (3,2): # pragma: no cover
|
||||||
|
@ -66,6 +67,7 @@ logLevel = 7
|
||||||
|
|
||||||
__all__ = ['SafeConfigParserWithIncludes']
|
__all__ = ['SafeConfigParserWithIncludes']
|
||||||
|
|
||||||
|
|
||||||
class SafeConfigParserWithIncludes(SafeConfigParser):
|
class SafeConfigParserWithIncludes(SafeConfigParser):
|
||||||
"""
|
"""
|
||||||
Class adds functionality to SafeConfigParser to handle included
|
Class adds functionality to SafeConfigParser to handle included
|
||||||
|
|
|
@ -24,7 +24,8 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import glob, os
|
import glob
|
||||||
|
import os
|
||||||
from ConfigParser import NoOptionError, NoSectionError
|
from ConfigParser import NoOptionError, NoSectionError
|
||||||
|
|
||||||
from .configparserinc import SafeConfigParserWithIncludes, logLevel
|
from .configparserinc import SafeConfigParserWithIncludes, logLevel
|
||||||
|
@ -33,6 +34,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ConfigReader():
|
class ConfigReader():
|
||||||
"""Generic config reader class.
|
"""Generic config reader class.
|
||||||
|
|
||||||
|
@ -135,6 +137,7 @@ class ConfigReader():
|
||||||
return self._cfg.getOptions(*args, **kwargs)
|
return self._cfg.getOptions(*args, **kwargs)
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
class ConfigReaderUnshared(SafeConfigParserWithIncludes):
|
class ConfigReaderUnshared(SafeConfigParserWithIncludes):
|
||||||
"""Unshared config reader (previously ConfigReader).
|
"""Unshared config reader (previously ConfigReader).
|
||||||
|
|
||||||
|
@ -186,7 +189,7 @@ class ConfigReaderUnshared(SafeConfigParserWithIncludes):
|
||||||
if config_files_read:
|
if config_files_read:
|
||||||
return True
|
return True
|
||||||
logSys.error("Found no accessible config files for %r under %s",
|
logSys.error("Found no accessible config files for %r under %s",
|
||||||
( filename, self.getBaseDir() ))
|
filename, self.getBaseDir())
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
logSys.error("Found no accessible config files for %r " % filename
|
logSys.error("Found no accessible config files for %r " % filename
|
||||||
|
@ -232,10 +235,11 @@ class ConfigReaderUnshared(SafeConfigParserWithIncludes):
|
||||||
logSys.log(logLevel, "Non essential option '%s' not defined in '%s'.", option[1], sec)
|
logSys.log(logLevel, "Non essential option '%s' not defined in '%s'.", option[1], sec)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
logSys.warning("Wrong value for '" + option[1] + "' in '" + sec +
|
logSys.warning("Wrong value for '" + option[1] + "' in '" + sec +
|
||||||
"'. Using default one: '" + `option[2]` + "'")
|
"'. Using default one: '" + repr(option[2]) + "'")
|
||||||
values[option[1]] = option[2]
|
values[option[1]] = option[2]
|
||||||
return values
|
return values
|
||||||
|
|
||||||
|
|
||||||
class DefinitionInitConfigReader(ConfigReader):
|
class DefinitionInitConfigReader(ConfigReader):
|
||||||
"""Config reader for files with options grouped in [Definition] and
|
"""Config reader for files with options grouped in [Definition] and
|
||||||
[Init] sections.
|
[Init] sections.
|
||||||
|
@ -281,7 +285,7 @@ class DefinitionInitConfigReader(ConfigReader):
|
||||||
|
|
||||||
if self.has_section("Init"):
|
if self.has_section("Init"):
|
||||||
for opt in self.options("Init"):
|
for opt in self.options("Init"):
|
||||||
if not self._initOpts.has_key(opt):
|
if not opt in self._initOpts:
|
||||||
self._initOpts[opt] = self.get("Init", opt)
|
self._initOpts[opt] = self.get("Init", opt)
|
||||||
|
|
||||||
def convert(self):
|
def convert(self):
|
||||||
|
|
|
@ -31,6 +31,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Configurator:
|
class Configurator:
|
||||||
|
|
||||||
def __init__(self, force_enable=False, share_config=None):
|
def __init__(self, force_enable=False, share_config=None):
|
||||||
|
|
|
@ -26,43 +26,42 @@ __license__ = "GPL"
|
||||||
|
|
||||||
#from cPickle import dumps, loads, HIGHEST_PROTOCOL
|
#from cPickle import dumps, loads, HIGHEST_PROTOCOL
|
||||||
from pickle import dumps, loads, HIGHEST_PROTOCOL
|
from pickle import dumps, loads, HIGHEST_PROTOCOL
|
||||||
import socket, sys
|
from ..protocol import CSPROTO
|
||||||
|
import socket
|
||||||
if sys.version_info >= (3,):
|
import sys
|
||||||
# b"" causes SyntaxError in python <= 2.5, so below implements equivalent
|
|
||||||
EMPTY_BYTES = bytes("", encoding="ascii")
|
|
||||||
else:
|
|
||||||
# python 2.x, string type is equivalent to bytes.
|
|
||||||
EMPTY_BYTES = ""
|
|
||||||
|
|
||||||
class CSocket:
|
class CSocket:
|
||||||
|
|
||||||
if sys.version_info >= (3,):
|
def __init__(self, sock="/var/run/fail2ban/fail2ban.sock"):
|
||||||
END_STRING = bytes("<F2B_END_COMMAND>", encoding='ascii')
|
|
||||||
else:
|
|
||||||
END_STRING = "<F2B_END_COMMAND>"
|
|
||||||
|
|
||||||
def __init__(self, sock = "/var/run/fail2ban/fail2ban.sock"):
|
|
||||||
# Create an INET, STREAMing socket
|
# Create an INET, STREAMing socket
|
||||||
#self.csock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
#self.csock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
self.__csock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
self.__csock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||||
#self.csock.connect(("localhost", 2222))
|
#self.csock.connect(("localhost", 2222))
|
||||||
self.__csock.connect(sock)
|
self.__csock.connect(sock)
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.close(False)
|
||||||
|
|
||||||
def send(self, msg):
|
def send(self, msg):
|
||||||
# Convert every list member to string
|
# Convert every list member to string
|
||||||
obj = dumps([str(m) for m in msg], HIGHEST_PROTOCOL)
|
obj = dumps([str(m) for m in msg], HIGHEST_PROTOCOL)
|
||||||
self.__csock.send(obj + CSocket.END_STRING)
|
self.__csock.send(obj + CSPROTO.END)
|
||||||
ret = self.receive(self.__csock)
|
return self.receive(self.__csock)
|
||||||
|
|
||||||
|
def close(self, sendEnd=True):
|
||||||
|
if not self.__csock:
|
||||||
|
return
|
||||||
|
if sendEnd:
|
||||||
|
self.__csock.sendall(CSPROTO.CLOSE + CSPROTO.END)
|
||||||
self.__csock.close()
|
self.__csock.close()
|
||||||
return ret
|
self.__csock = None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def receive(sock):
|
def receive(sock):
|
||||||
msg = EMPTY_BYTES
|
msg = CSPROTO.EMPTY
|
||||||
while msg.rfind(CSocket.END_STRING) == -1:
|
while msg.rfind(CSPROTO.END) == -1:
|
||||||
chunk = sock.recv(6)
|
chunk = sock.recv(6)
|
||||||
if chunk == '':
|
if chunk == '':
|
||||||
raise RuntimeError, "socket connection broken"
|
raise RuntimeError("socket connection broken")
|
||||||
msg = msg + chunk
|
msg = msg + chunk
|
||||||
return loads(msg)
|
return loads(msg)
|
||||||
|
|
|
@ -30,6 +30,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Fail2banReader(ConfigReader):
|
class Fail2banReader(ConfigReader):
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
@ -52,18 +53,14 @@ class Fail2banReader(ConfigReader):
|
||||||
self.__opts = ConfigReader.getOptions(self, "Definition", opts)
|
self.__opts = ConfigReader.getOptions(self, "Definition", opts)
|
||||||
|
|
||||||
def convert(self):
|
def convert(self):
|
||||||
|
# Ensure logtarget/level set first so any db errors are captured
|
||||||
|
# Also dbfile should be set before all other database options.
|
||||||
|
# So adding order indices into items, to be stripped after sorting, upon return
|
||||||
|
order = {"syslogsocket":0, "loglevel":1, "logtarget":2,
|
||||||
|
"dbfile":50, "dbpurgeage":51}
|
||||||
stream = list()
|
stream = list()
|
||||||
for opt in self.__opts:
|
for opt in self.__opts:
|
||||||
if opt == "loglevel":
|
if opt in order:
|
||||||
stream.append(["set", "loglevel", self.__opts[opt]])
|
stream.append((order[opt], ["set", opt, self.__opts[opt]]))
|
||||||
elif opt == "logtarget":
|
return [opt[1] for opt in sorted(stream)]
|
||||||
stream.append(["set", "logtarget", self.__opts[opt]])
|
|
||||||
elif opt == "syslogsocket":
|
|
||||||
stream.append(["set", "syslogsocket", self.__opts[opt]])
|
|
||||||
elif opt == "dbfile":
|
|
||||||
stream.append(["set", "dbfile", self.__opts[opt]])
|
|
||||||
elif opt == "dbpurgeage":
|
|
||||||
stream.append(["set", "dbpurgeage", self.__opts[opt]])
|
|
||||||
# Ensure logtarget/level set first so any db errors are captured
|
|
||||||
return sorted(stream, reverse=True)
|
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,8 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import os, shlex
|
import os
|
||||||
|
import shlex
|
||||||
|
|
||||||
from .configreader import DefinitionInitConfigReader
|
from .configreader import DefinitionInitConfigReader
|
||||||
from ..server.action import CommandAction
|
from ..server.action import CommandAction
|
||||||
|
@ -33,6 +34,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class FilterReader(DefinitionInitConfigReader):
|
class FilterReader(DefinitionInitConfigReader):
|
||||||
|
|
||||||
_configOpts = [
|
_configOpts = [
|
||||||
|
|
|
@ -24,8 +24,10 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import re, glob, os.path
|
import glob
|
||||||
import json
|
import json
|
||||||
|
import os.path
|
||||||
|
import re
|
||||||
|
|
||||||
from .configreader import ConfigReaderUnshared, ConfigReader
|
from .configreader import ConfigReaderUnshared, ConfigReader
|
||||||
from .filterreader import FilterReader
|
from .filterreader import FilterReader
|
||||||
|
@ -35,6 +37,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class JailReader(ConfigReader):
|
class JailReader(ConfigReader):
|
||||||
|
|
||||||
optionCRE = re.compile("^((?:\w|-|_|\.)+)(?:\[(.*)\])?$")
|
optionCRE = re.compile("^((?:\w|-|_|\.)+)(?:\[(.*)\])?$")
|
||||||
|
|
|
@ -31,6 +31,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class JailsReader(ConfigReader):
|
class JailsReader(ConfigReader):
|
||||||
|
|
||||||
def __init__(self, force_enable=False, **kwargs):
|
def __init__(self, force_enable=False, **kwargs):
|
||||||
|
@ -50,6 +51,7 @@ class JailsReader(ConfigReader):
|
||||||
return self.__jails
|
return self.__jails
|
||||||
|
|
||||||
def read(self):
|
def read(self):
|
||||||
|
self.__jails = list()
|
||||||
return ConfigReader.read(self, "jail")
|
return ConfigReader.read(self, "jail")
|
||||||
|
|
||||||
def getOptions(self, section=None):
|
def getOptions(self, section=None):
|
||||||
|
|
|
@ -23,12 +23,14 @@ __author__ = "Cyril Jaquier, Yaroslav Halchenko"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2012 Yaroslav Halchenko"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2012 Yaroslav Halchenko"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Jails
|
# Jails
|
||||||
#
|
#
|
||||||
class DuplicateJailException(Exception):
|
class DuplicateJailException(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class UnknownJailException(KeyError):
|
class UnknownJailException(KeyError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -26,11 +26,13 @@ import traceback
|
||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|
||||||
def formatExceptionInfo():
|
def formatExceptionInfo():
|
||||||
""" Consistently format exception information """
|
""" Consistently format exception information """
|
||||||
cla, exc = sys.exc_info()[:2]
|
cla, exc = sys.exc_info()[:2]
|
||||||
return (cla.__name__, str(exc))
|
return (cla.__name__, str(exc))
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Following "traceback" functions are adopted from PyMVPA distributed
|
# Following "traceback" functions are adopted from PyMVPA distributed
|
||||||
# under MIT/Expat and copyright by PyMVPA developers (i.e. me and
|
# under MIT/Expat and copyright by PyMVPA developers (i.e. me and
|
||||||
|
@ -49,6 +51,7 @@ def mbasename(s):
|
||||||
base = os.path.basename(os.path.dirname(s)) + '.' + base
|
base = os.path.basename(os.path.dirname(s)) + '.' + base
|
||||||
return base
|
return base
|
||||||
|
|
||||||
|
|
||||||
class TraceBack(object):
|
class TraceBack(object):
|
||||||
"""Customized traceback to be included in debug messages
|
"""Customized traceback to be included in debug messages
|
||||||
"""
|
"""
|
||||||
|
@ -94,6 +97,7 @@ class TraceBack(object):
|
||||||
|
|
||||||
return sftb
|
return sftb
|
||||||
|
|
||||||
|
|
||||||
class FormatterWithTraceBack(logging.Formatter):
|
class FormatterWithTraceBack(logging.Formatter):
|
||||||
"""Custom formatter which expands %(tb) and %(tbc) with tracebacks
|
"""Custom formatter which expands %(tb) and %(tbc) with tracebacks
|
||||||
|
|
||||||
|
@ -108,6 +112,7 @@ class FormatterWithTraceBack(logging.Formatter):
|
||||||
record.tbc = record.tb = self._tb()
|
record.tbc = record.tb = self._tb()
|
||||||
return logging.Formatter.format(self, record)
|
return logging.Formatter.format(self, record)
|
||||||
|
|
||||||
|
|
||||||
def getLogger(name):
|
def getLogger(name):
|
||||||
"""Get logging.Logger instance with Fail2Ban logger name convention
|
"""Get logging.Logger instance with Fail2Ban logger name convention
|
||||||
"""
|
"""
|
||||||
|
@ -115,6 +120,7 @@ def getLogger(name):
|
||||||
name = "fail2ban.%s" % name.rpartition(".")[-1]
|
name = "fail2ban.%s" % name.rpartition(".")[-1]
|
||||||
return logging.getLogger(name)
|
return logging.getLogger(name)
|
||||||
|
|
||||||
|
|
||||||
def excepthook(exctype, value, traceback):
|
def excepthook(exctype, value, traceback):
|
||||||
"""Except hook used to log unhandled exceptions to Fail2Ban log
|
"""Except hook used to log unhandled exceptions to Fail2Ban log
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -29,6 +29,16 @@ import textwrap
|
||||||
##
|
##
|
||||||
# Describes the protocol used to communicate with the server.
|
# Describes the protocol used to communicate with the server.
|
||||||
|
|
||||||
|
class dotdict(dict):
|
||||||
|
def __getattr__(self, name):
|
||||||
|
return self[name]
|
||||||
|
|
||||||
|
CSPROTO = dotdict({
|
||||||
|
"EMPTY": b"",
|
||||||
|
"END": b"<F2B_END_COMMAND>",
|
||||||
|
"CLOSE": b"<F2B_CLOSE_COMMAND>"
|
||||||
|
})
|
||||||
|
|
||||||
protocol = [
|
protocol = [
|
||||||
['', "BASIC", ""],
|
['', "BASIC", ""],
|
||||||
["start", "starts the server and the jails"],
|
["start", "starts the server and the jails"],
|
||||||
|
@ -119,6 +129,7 @@ protocol = [
|
||||||
["get <JAIL> action <ACT> <PROPERTY>", "gets the value of <PROPERTY> for the action <ACT> for <JAIL>"],
|
["get <JAIL> action <ACT> <PROPERTY>", "gets the value of <PROPERTY> for the action <ACT> for <JAIL>"],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Prints the protocol in a "man" format. This is used for the
|
# Prints the protocol in a "man" format. This is used for the
|
||||||
# "-h" output of fail2ban-client.
|
# "-h" output of fail2ban-client.
|
||||||
|
@ -143,6 +154,7 @@ def printFormatted():
|
||||||
line = ' ' * (INDENT + MARGIN) + n.strip()
|
line = ' ' * (INDENT + MARGIN) + n.strip()
|
||||||
print line
|
print line
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Prints the protocol in a "mediawiki" format.
|
# Prints the protocol in a "mediawiki" format.
|
||||||
|
|
||||||
|
@ -159,6 +171,7 @@ def printWiki():
|
||||||
print "| <span style=\"white-space:nowrap;\"><tt>" + m[0] + "</tt></span> || || " + m[1]
|
print "| <span style=\"white-space:nowrap;\"><tt>" + m[0] + "</tt></span> || || " + m[1]
|
||||||
print "|}"
|
print "|}"
|
||||||
|
|
||||||
|
|
||||||
def __printWikiHeader(section, desc):
|
def __printWikiHeader(section, desc):
|
||||||
print
|
print
|
||||||
print "=== " + section + " ==="
|
print "=== " + section + " ==="
|
||||||
|
|
|
@ -21,8 +21,14 @@ __author__ = "Cyril Jaquier and Fail2Ban Contributors"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2012 Yaroslav Halchenko"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2012 Yaroslav Halchenko"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import logging, os, subprocess, time, signal, tempfile
|
import logging
|
||||||
import threading, re
|
import os
|
||||||
|
import re
|
||||||
|
import signal
|
||||||
|
import subprocess
|
||||||
|
import tempfile
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
from abc import ABCMeta
|
from abc import ABCMeta
|
||||||
from collections import MutableMapping
|
from collections import MutableMapping
|
||||||
|
|
||||||
|
@ -49,6 +55,7 @@ _RETCODE_HINTS = {
|
||||||
signame = dict((num, name)
|
signame = dict((num, name)
|
||||||
for name, num in signal.__dict__.iteritems() if name.startswith("SIG"))
|
for name, num in signal.__dict__.iteritems() if name.startswith("SIG"))
|
||||||
|
|
||||||
|
|
||||||
class CallingMap(MutableMapping):
|
class CallingMap(MutableMapping):
|
||||||
"""A Mapping type which returns the result of callable values.
|
"""A Mapping type which returns the result of callable values.
|
||||||
|
|
||||||
|
@ -94,6 +101,7 @@ class CallingMap(MutableMapping):
|
||||||
def copy(self):
|
def copy(self):
|
||||||
return self.__class__(self.data.copy())
|
return self.__class__(self.data.copy())
|
||||||
|
|
||||||
|
|
||||||
class ActionBase(object):
|
class ActionBase(object):
|
||||||
"""An abstract base class for actions in Fail2Ban.
|
"""An abstract base class for actions in Fail2Ban.
|
||||||
|
|
||||||
|
@ -176,6 +184,7 @@ class ActionBase(object):
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CommandAction(ActionBase):
|
class CommandAction(ActionBase):
|
||||||
"""A action which executes OS shell commands.
|
"""A action which executes OS shell commands.
|
||||||
|
|
||||||
|
@ -400,7 +409,7 @@ class CommandAction(ActionBase):
|
||||||
# recursive definitions are bad
|
# recursive definitions are bad
|
||||||
#logSys.log(5, 'recursion fail tag: %s value: %s' % (tag, value) )
|
#logSys.log(5, 'recursion fail tag: %s value: %s' % (tag, value) )
|
||||||
return False
|
return False
|
||||||
if found_tag in cls._escapedTags or not tags.has_key(found_tag):
|
if found_tag in cls._escapedTags or not found_tag in tags:
|
||||||
# Escaped or missing tags - just continue on searching after end of match
|
# Escaped or missing tags - just continue on searching after end of match
|
||||||
# Missing tags are ok - cInfo can contain aInfo elements like <HOST> and valid shell
|
# Missing tags are ok - cInfo can contain aInfo elements like <HOST> and valid shell
|
||||||
# constructs like <STDIN>.
|
# constructs like <STDIN>.
|
||||||
|
@ -556,7 +565,9 @@ class CommandAction(ActionBase):
|
||||||
stderr = tempfile.TemporaryFile(suffix=".stderr", prefix="fai2ban_")
|
stderr = tempfile.TemporaryFile(suffix=".stderr", prefix="fai2ban_")
|
||||||
try:
|
try:
|
||||||
popen = subprocess.Popen(
|
popen = subprocess.Popen(
|
||||||
realCmd, stdout=stdout, stderr=stderr, shell=True)
|
realCmd, stdout=stdout, stderr=stderr, shell=True,
|
||||||
|
preexec_fn=os.setsid # so that killpg does not kill our process
|
||||||
|
)
|
||||||
stime = time.time()
|
stime = time.time()
|
||||||
retcode = popen.poll()
|
retcode = popen.poll()
|
||||||
while time.time() - stime <= timeout and retcode is None:
|
while time.time() - stime <= timeout and retcode is None:
|
||||||
|
@ -565,11 +576,12 @@ class CommandAction(ActionBase):
|
||||||
if retcode is None:
|
if retcode is None:
|
||||||
logSys.error("%s -- timed out after %i seconds." %
|
logSys.error("%s -- timed out after %i seconds." %
|
||||||
(realCmd, timeout))
|
(realCmd, timeout))
|
||||||
os.kill(popen.pid, signal.SIGTERM) # Terminate the process
|
pgid = os.getpgid(popen.pid)
|
||||||
|
os.killpg(pgid, signal.SIGTERM) # Terminate the process
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
retcode = popen.poll()
|
retcode = popen.poll()
|
||||||
if retcode is None: # Still going...
|
if retcode is None: # Still going...
|
||||||
os.kill(popen.pid, signal.SIGKILL) # Kill the process
|
os.killpg(pgid, signal.SIGKILL) # Kill the process
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
retcode = popen.poll()
|
retcode = popen.poll()
|
||||||
except OSError, e:
|
except OSError, e:
|
||||||
|
|
|
@ -24,9 +24,10 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import time, logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
if sys.version_info >= (3, 3):
|
if sys.version_info >= (3, 3):
|
||||||
import importlib.machinery
|
import importlib.machinery
|
||||||
else:
|
else:
|
||||||
|
@ -46,6 +47,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Actions(JailThread, Mapping):
|
class Actions(JailThread, Mapping):
|
||||||
"""Handles jail actions.
|
"""Handles jail actions.
|
||||||
|
|
||||||
|
@ -192,13 +194,14 @@ class Actions(JailThread, Mapping):
|
||||||
ValueError
|
ValueError
|
||||||
If `ip` is not banned
|
If `ip` is not banned
|
||||||
"""
|
"""
|
||||||
|
# Always delete ip from database (also if currently not banned)
|
||||||
|
if self._jail.database is not None:
|
||||||
|
self._jail.database.delBan(self._jail, ip)
|
||||||
# Find the ticket with the IP.
|
# Find the ticket with the IP.
|
||||||
ticket = self.__banManager.getTicketByIP(ip)
|
ticket = self.__banManager.getTicketByIP(ip)
|
||||||
if ticket is not None:
|
if ticket is not None:
|
||||||
# Unban the IP.
|
# Unban the IP.
|
||||||
self.__unBan(ticket)
|
self.__unBan(ticket)
|
||||||
if self._jail.database is not None:
|
|
||||||
self._jail.database.delBan(self._jail, ticket)
|
|
||||||
else:
|
else:
|
||||||
raise ValueError("IP %s is not banned" % ip)
|
raise ValueError("IP %s is not banned" % ip)
|
||||||
|
|
||||||
|
@ -294,7 +297,7 @@ class Actions(JailThread, Mapping):
|
||||||
True if an IP address get banned.
|
True if an IP address get banned.
|
||||||
"""
|
"""
|
||||||
ticket = self._jail.getFailTicket()
|
ticket = self._jail.getFailTicket()
|
||||||
if ticket != False:
|
if ticket:
|
||||||
aInfo = CallingMap()
|
aInfo = CallingMap()
|
||||||
bTicket = BanManager.createBanTicket(ticket)
|
bTicket = BanManager.createBanTicket(ticket)
|
||||||
ip = bTicket.getIP()
|
ip = bTicket.getIP()
|
||||||
|
|
|
@ -25,20 +25,20 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
from pickle import dumps, loads, HIGHEST_PROTOCOL
|
from pickle import dumps, loads, HIGHEST_PROTOCOL
|
||||||
import asyncore, asynchat, socket, os, sys, traceback, fcntl
|
import asynchat
|
||||||
|
import asyncore
|
||||||
|
import fcntl
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
from ..protocol import CSPROTO
|
||||||
from ..helpers import getLogger,formatExceptionInfo
|
from ..helpers import getLogger,formatExceptionInfo
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
if sys.version_info >= (3,):
|
|
||||||
# b"" causes SyntaxError in python <= 2.5, so below implements equivalent
|
|
||||||
EMPTY_BYTES = bytes("", encoding="ascii")
|
|
||||||
else:
|
|
||||||
# python 2.x, string type is equivalent to bytes.
|
|
||||||
EMPTY_BYTES = ""
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Request handler class.
|
# Request handler class.
|
||||||
#
|
#
|
||||||
|
@ -47,17 +47,12 @@ else:
|
||||||
|
|
||||||
class RequestHandler(asynchat.async_chat):
|
class RequestHandler(asynchat.async_chat):
|
||||||
|
|
||||||
if sys.version_info >= (3,):
|
|
||||||
END_STRING = bytes("<F2B_END_COMMAND>", encoding="ascii")
|
|
||||||
else:
|
|
||||||
END_STRING = "<F2B_END_COMMAND>"
|
|
||||||
|
|
||||||
def __init__(self, conn, transmitter):
|
def __init__(self, conn, transmitter):
|
||||||
asynchat.async_chat.__init__(self, conn)
|
asynchat.async_chat.__init__(self, conn)
|
||||||
self.__transmitter = transmitter
|
self.__transmitter = transmitter
|
||||||
self.__buffer = []
|
self.__buffer = []
|
||||||
# Sets the terminator.
|
# Sets the terminator.
|
||||||
self.set_terminator(RequestHandler.END_STRING)
|
self.set_terminator(CSPROTO.END)
|
||||||
|
|
||||||
def collect_incoming_data(self, data):
|
def collect_incoming_data(self, data):
|
||||||
#logSys.debug("Received raw data: " + str(data))
|
#logSys.debug("Received raw data: " + str(data))
|
||||||
|
@ -69,16 +64,23 @@ class RequestHandler(asynchat.async_chat):
|
||||||
# This method is called once we have a complete request.
|
# This method is called once we have a complete request.
|
||||||
|
|
||||||
def found_terminator(self):
|
def found_terminator(self):
|
||||||
|
# Pop whole buffer
|
||||||
|
message = self.__buffer
|
||||||
|
self.__buffer = []
|
||||||
# Joins the buffer items.
|
# Joins the buffer items.
|
||||||
message = loads(EMPTY_BYTES.join(self.__buffer))
|
message = CSPROTO.EMPTY.join(message)
|
||||||
|
# Closes the channel if close was received
|
||||||
|
if message == CSPROTO.CLOSE:
|
||||||
|
self.close_when_done()
|
||||||
|
return
|
||||||
|
# Deserialize
|
||||||
|
message = loads(message)
|
||||||
# Gives the message to the transmitter.
|
# Gives the message to the transmitter.
|
||||||
message = self.__transmitter.proceed(message)
|
message = self.__transmitter.proceed(message)
|
||||||
# Serializes the response.
|
# Serializes the response.
|
||||||
message = dumps(message, HIGHEST_PROTOCOL)
|
message = dumps(message, HIGHEST_PROTOCOL)
|
||||||
# Sends the response to the client.
|
# Sends the response to the client.
|
||||||
self.push(message + RequestHandler.END_STRING)
|
self.push(message + CSPROTO.END)
|
||||||
# Closes the channel.
|
|
||||||
self.close_when_done()
|
|
||||||
|
|
||||||
def handle_error(self):
|
def handle_error(self):
|
||||||
e1, e2 = formatExceptionInfo()
|
e1, e2 = formatExceptionInfo()
|
||||||
|
@ -86,6 +88,7 @@ class RequestHandler(asynchat.async_chat):
|
||||||
logSys.error(traceback.format_exc().splitlines())
|
logSys.error(traceback.format_exc().splitlines())
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Asynchronous server class.
|
# Asynchronous server class.
|
||||||
#
|
#
|
||||||
|
@ -181,6 +184,7 @@ class AsyncServer(asyncore.dispatcher):
|
||||||
flags = fcntl.fcntl(fd, fcntl.F_GETFD)
|
flags = fcntl.fcntl(fd, fcntl.F_GETFD)
|
||||||
fcntl.fcntl(fd, fcntl.F_SETFD, flags|fcntl.FD_CLOEXEC)
|
fcntl.fcntl(fd, fcntl.F_SETFD, flags|fcntl.FD_CLOEXEC)
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# AsyncServerException is used to wrap communication exceptions.
|
# AsyncServerException is used to wrap communication exceptions.
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Banning Manager.
|
# Banning Manager.
|
||||||
#
|
#
|
||||||
|
@ -271,7 +272,6 @@ class BanManager:
|
||||||
finally:
|
finally:
|
||||||
self.__lock.release()
|
self.__lock.release()
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Get the size of the ban list.
|
# Get the size of the ban list.
|
||||||
#
|
#
|
||||||
|
|
|
@ -21,11 +21,12 @@ __author__ = "Steven Hiscocks"
|
||||||
__copyright__ = "Copyright (c) 2013 Steven Hiscocks"
|
__copyright__ = "Copyright (c) 2013 Steven Hiscocks"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import sys
|
|
||||||
import shutil, time
|
|
||||||
import sqlite3
|
|
||||||
import json
|
import json
|
||||||
import locale
|
import locale
|
||||||
|
import shutil
|
||||||
|
import sqlite3
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from threading import RLock
|
from threading import RLock
|
||||||
|
|
||||||
|
@ -45,6 +46,7 @@ if sys.version_info >= (3,):
|
||||||
logSys.error('json dumps failed: %s', e)
|
logSys.error('json dumps failed: %s', e)
|
||||||
x = '{}'
|
x = '{}'
|
||||||
return x
|
return x
|
||||||
|
|
||||||
def _json_loads_safe(x):
|
def _json_loads_safe(x):
|
||||||
try:
|
try:
|
||||||
x = json.loads(x.decode(
|
x = json.loads(x.decode(
|
||||||
|
@ -63,6 +65,7 @@ else:
|
||||||
return x.encode(locale.getpreferredencoding())
|
return x.encode(locale.getpreferredencoding())
|
||||||
else:
|
else:
|
||||||
return x
|
return x
|
||||||
|
|
||||||
def _json_dumps_safe(x):
|
def _json_dumps_safe(x):
|
||||||
try:
|
try:
|
||||||
x = json.dumps(_normalize(x), ensure_ascii=False).decode(
|
x = json.dumps(_normalize(x), ensure_ascii=False).decode(
|
||||||
|
@ -71,6 +74,7 @@ else:
|
||||||
logSys.error('json dumps failed: %s', e)
|
logSys.error('json dumps failed: %s', e)
|
||||||
x = '{}'
|
x = '{}'
|
||||||
return x
|
return x
|
||||||
|
|
||||||
def _json_loads_safe(x):
|
def _json_loads_safe(x):
|
||||||
try:
|
try:
|
||||||
x = _normalize(json.loads(x.decode(
|
x = _normalize(json.loads(x.decode(
|
||||||
|
@ -83,6 +87,7 @@ else:
|
||||||
sqlite3.register_adapter(dict, _json_dumps_safe)
|
sqlite3.register_adapter(dict, _json_dumps_safe)
|
||||||
sqlite3.register_converter("JSON", _json_loads_safe)
|
sqlite3.register_converter("JSON", _json_loads_safe)
|
||||||
|
|
||||||
|
|
||||||
def commitandrollback(f):
|
def commitandrollback(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def wrapper(self, *args, **kwargs):
|
def wrapper(self, *args, **kwargs):
|
||||||
|
@ -91,6 +96,7 @@ def commitandrollback(f):
|
||||||
return f(self, self._db.cursor(), *args, **kwargs)
|
return f(self, self._db.cursor(), *args, **kwargs)
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
class Fail2BanDb(object):
|
class Fail2BanDb(object):
|
||||||
"""Fail2Ban database for storing persistent data.
|
"""Fail2Ban database for storing persistent data.
|
||||||
|
|
||||||
|
@ -155,6 +161,7 @@ class Fail2BanDb(object):
|
||||||
"CREATE INDEX bans_jail_ip ON bans(jail, ip);" \
|
"CREATE INDEX bans_jail_ip ON bans(jail, ip);" \
|
||||||
"CREATE INDEX bans_ip ON bans(ip);" \
|
"CREATE INDEX bans_ip ON bans(ip);" \
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, filename, purgeAge=24*60*60):
|
def __init__(self, filename, purgeAge=24*60*60):
|
||||||
try:
|
try:
|
||||||
self._lock = RLock()
|
self._lock = RLock()
|
||||||
|
@ -411,19 +418,20 @@ class Fail2BanDb(object):
|
||||||
"failures": ticket.getAttempt()}))
|
"failures": ticket.getAttempt()}))
|
||||||
|
|
||||||
@commitandrollback
|
@commitandrollback
|
||||||
def delBan(self, cur, jail, ticket):
|
def delBan(self, cur, jail, ip):
|
||||||
"""Delete a ban from the database.
|
"""Delete a ban from the database.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
jail : Jail
|
jail : Jail
|
||||||
Jail in which the ban has occurred.
|
Jail in which the ban has occurred.
|
||||||
ticket : BanTicket
|
ip : str
|
||||||
Ticket of the ban to be removed.
|
IP to be removed.
|
||||||
"""
|
"""
|
||||||
|
queryArgs = (jail.name, ip);
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"DELETE FROM bans WHERE jail = ? AND ip = ? AND timeofban = ?",
|
"DELETE FROM bans WHERE jail = ? AND ip = ?",
|
||||||
(jail.name, ticket.getIP(), int(round(ticket.getTime()))))
|
queryArgs);
|
||||||
|
|
||||||
@commitandrollback
|
@commitandrollback
|
||||||
def _getBans(self, cur, jail=None, bantime=None, ip=None):
|
def _getBans(self, cur, jail=None, bantime=None, ip=None):
|
||||||
|
|
|
@ -29,6 +29,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class DateDetector(object):
|
class DateDetector(object):
|
||||||
"""Manages one or more date templates to find a date within a log line.
|
"""Manages one or more date templates to find a date within a log line.
|
||||||
|
|
||||||
|
|
|
@ -154,6 +154,7 @@ class DateEpoch(DateTemplate):
|
||||||
return (float(dateMatch.group()), dateMatch)
|
return (float(dateMatch.group()), dateMatch)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
class DatePatternRegex(DateTemplate):
|
class DatePatternRegex(DateTemplate):
|
||||||
"""Date template, with regex/pattern
|
"""Date template, with regex/pattern
|
||||||
|
|
||||||
|
@ -236,6 +237,7 @@ class DatePatternRegex(DateTemplate):
|
||||||
if value is not None)
|
if value is not None)
|
||||||
return reGroupDictStrptime(groupdict), dateMatch
|
return reGroupDictStrptime(groupdict), dateMatch
|
||||||
|
|
||||||
|
|
||||||
class DateTai64n(DateTemplate):
|
class DateTai64n(DateTemplate):
|
||||||
"""A date template which matches TAI64N formate timestamps.
|
"""A date template which matches TAI64N formate timestamps.
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class FailData:
|
class FailData:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
|
@ -34,6 +34,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class FailManager:
|
class FailManager:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -91,7 +92,7 @@ class FailManager:
|
||||||
ip = ticket.getIP()
|
ip = ticket.getIP()
|
||||||
unixTime = ticket.getTime()
|
unixTime = ticket.getTime()
|
||||||
matches = ticket.getMatches()
|
matches = ticket.getMatches()
|
||||||
if self.__failList.has_key(ip):
|
if ip in self.__failList:
|
||||||
fData = self.__failList[ip]
|
fData = self.__failList[ip]
|
||||||
if fData.getLastReset() < unixTime - self.__maxTime:
|
if fData.getLastReset() < unixTime - self.__maxTime:
|
||||||
fData.setLastReset(unixTime)
|
fData.setLastReset(unixTime)
|
||||||
|
@ -136,7 +137,7 @@ class FailManager:
|
||||||
self.__lock.release()
|
self.__lock.release()
|
||||||
|
|
||||||
def __delFailure(self, ip):
|
def __delFailure(self, ip):
|
||||||
if self.__failList.has_key(ip):
|
if ip in self.__failList:
|
||||||
del self.__failList[ip]
|
del self.__failList[ip]
|
||||||
|
|
||||||
def toBan(self):
|
def toBan(self):
|
||||||
|
@ -154,5 +155,6 @@ class FailManager:
|
||||||
finally:
|
finally:
|
||||||
self.__lock.release()
|
self.__lock.release()
|
||||||
|
|
||||||
|
|
||||||
class FailManagerEmpty(Exception):
|
class FailManagerEmpty(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -21,7 +21,10 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import re, sre_constants, sys
|
import re
|
||||||
|
import sre_constants
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Regular expression class.
|
# Regular expression class.
|
||||||
|
@ -55,6 +58,7 @@ class Regex:
|
||||||
except sre_constants.error:
|
except sre_constants.error:
|
||||||
raise RegexException("Unable to compile regular expression '%s'" %
|
raise RegexException("Unable to compile regular expression '%s'" %
|
||||||
regex)
|
regex)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s(%r)" % (self.__class__.__name__, self._regex)
|
return "%s(%r)" % (self.__class__.__name__, self._regex)
|
||||||
##
|
##
|
||||||
|
@ -91,7 +95,6 @@ class Regex:
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self._matchLineEnd = len(self._matchCache.string)
|
self._matchLineEnd = len(self._matchCache.string)
|
||||||
|
|
||||||
|
|
||||||
lineCount1 = self._matchCache.string.count(
|
lineCount1 = self._matchCache.string.count(
|
||||||
"\n", 0, self._matchLineStart)
|
"\n", 0, self._matchLineStart)
|
||||||
lineCount2 = self._matchCache.string.count(
|
lineCount2 = self._matchCache.string.count(
|
||||||
|
@ -182,6 +185,7 @@ class Regex:
|
||||||
else:
|
else:
|
||||||
return ["".join(line) for line in self._matchedTupleLines]
|
return ["".join(line) for line in self._matchedTupleLines]
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Exception dedicated to the class Regex.
|
# Exception dedicated to the class Regex.
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,12 @@ __author__ = "Cyril Jaquier and Fail2Ban Contributors"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2013 Yaroslav Halchenko"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2013 Yaroslav Halchenko"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import re, os, fcntl, sys, locale, codecs
|
import codecs
|
||||||
|
import fcntl
|
||||||
|
import locale
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
from .failmanager import FailManagerEmpty, FailManager
|
from .failmanager import FailManagerEmpty, FailManager
|
||||||
from .ticket import FailTicket
|
from .ticket import FailTicket
|
||||||
|
@ -43,6 +48,7 @@ logSys = getLogger(__name__)
|
||||||
# that matches a given regular expression. This class is instantiated by
|
# that matches a given regular expression. This class is instantiated by
|
||||||
# a Jail object.
|
# a Jail object.
|
||||||
|
|
||||||
|
|
||||||
class Filter(JailThread):
|
class Filter(JailThread):
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -81,7 +87,6 @@ class Filter(JailThread):
|
||||||
self.dateDetector.addDefaultTemplate()
|
self.dateDetector.addDefaultTemplate()
|
||||||
logSys.debug("Created %s" % self)
|
logSys.debug("Created %s" % self)
|
||||||
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%s(%r)" % (self.__class__.__name__, self.jail)
|
return "%s(%r)" % (self.__class__.__name__, self.jail)
|
||||||
|
|
||||||
|
@ -104,7 +109,6 @@ class Filter(JailThread):
|
||||||
logSys.error(e)
|
logSys.error(e)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
|
||||||
def delFailRegex(self, index):
|
def delFailRegex(self, index):
|
||||||
try:
|
try:
|
||||||
del self.__failRegex[index]
|
del self.__failRegex[index]
|
||||||
|
@ -390,7 +394,6 @@ class Filter(JailThread):
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def processLine(self, line, date=None, returnRawHost=False,
|
def processLine(self, line, date=None, returnRawHost=False,
|
||||||
checkAllRegex=False):
|
checkAllRegex=False):
|
||||||
"""Split the time portion from log msg and return findFailures on them
|
"""Split the time portion from log msg and return findFailures on them
|
||||||
|
@ -576,7 +579,6 @@ class FileFilter(Filter):
|
||||||
# to be overridden by backends
|
# to be overridden by backends
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Delete a log path
|
# Delete a log path
|
||||||
#
|
#
|
||||||
|
@ -716,6 +718,7 @@ except ImportError: # pragma: no cover
|
||||||
import md5
|
import md5
|
||||||
md5sum = md5.new
|
md5sum = md5.new
|
||||||
|
|
||||||
|
|
||||||
class FileContainer:
|
class FileContainer:
|
||||||
|
|
||||||
def __init__(self, filename, encoding, tail = False):
|
def __init__(self, filename, encoding, tail = False):
|
||||||
|
@ -839,7 +842,9 @@ class JournalFilter(Filter): # pragma: systemd no cover
|
||||||
# This class contains only static methods used to handle DNS and IP
|
# This class contains only static methods used to handle DNS and IP
|
||||||
# addresses.
|
# addresses.
|
||||||
|
|
||||||
import socket, struct
|
import socket
|
||||||
|
import struct
|
||||||
|
|
||||||
|
|
||||||
class DNSUtils:
|
class DNSUtils:
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,8 @@ __author__ = "Cyril Jaquier, Yaroslav Halchenko"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2012 Yaroslav Halchenko"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2012 Yaroslav Halchenko"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import time, fcntl
|
import fcntl
|
||||||
|
import time
|
||||||
|
|
||||||
import gamin
|
import gamin
|
||||||
|
|
||||||
|
@ -35,6 +36,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Log reader class.
|
# Log reader class.
|
||||||
#
|
#
|
||||||
|
@ -60,16 +62,14 @@ class FilterGamin(FileFilter):
|
||||||
fcntl.fcntl(fd, fcntl.F_SETFD, flags|fcntl.FD_CLOEXEC)
|
fcntl.fcntl(fd, fcntl.F_SETFD, flags|fcntl.FD_CLOEXEC)
|
||||||
logSys.debug("Created FilterGamin")
|
logSys.debug("Created FilterGamin")
|
||||||
|
|
||||||
|
|
||||||
def callback(self, path, event):
|
def callback(self, path, event):
|
||||||
logSys.debug("Got event: " + `event` + " for " + path)
|
logSys.debug("Got event: " + repr(event) + " for " + path)
|
||||||
if event in (gamin.GAMCreated, gamin.GAMChanged, gamin.GAMExists):
|
if event in (gamin.GAMCreated, gamin.GAMChanged, gamin.GAMExists):
|
||||||
logSys.debug("File changed: " + path)
|
logSys.debug("File changed: " + path)
|
||||||
self.__modified = True
|
self.__modified = True
|
||||||
|
|
||||||
self._process_file(path)
|
self._process_file(path)
|
||||||
|
|
||||||
|
|
||||||
def _process_file(self, path):
|
def _process_file(self, path):
|
||||||
"""Process a given file
|
"""Process a given file
|
||||||
|
|
||||||
|
@ -121,7 +121,6 @@ class FilterGamin(FileFilter):
|
||||||
logSys.debug(self.jail.name + ": filter terminated")
|
logSys.debug(self.jail.name + ": filter terminated")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
super(FilterGamin, self).stop()
|
super(FilterGamin, self).stop()
|
||||||
self.__cleanup()
|
self.__cleanup()
|
||||||
|
|
|
@ -24,7 +24,8 @@ __author__ = "Cyril Jaquier, Yaroslav Halchenko"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier; 2012 Yaroslav Halchenko"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier; 2012 Yaroslav Halchenko"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import time, os
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
from .failmanager import FailManagerEmpty
|
from .failmanager import FailManagerEmpty
|
||||||
from .filter import FileFilter
|
from .filter import FileFilter
|
||||||
|
@ -34,6 +35,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Log reader class.
|
# Log reader class.
|
||||||
#
|
#
|
||||||
|
|
|
@ -51,6 +51,7 @@ except Exception, e:
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Log reader class.
|
# Log reader class.
|
||||||
#
|
#
|
||||||
|
@ -73,7 +74,6 @@ class FilterPyinotify(FileFilter):
|
||||||
self.__watches = dict()
|
self.__watches = dict()
|
||||||
logSys.debug("Created FilterPyinotify")
|
logSys.debug("Created FilterPyinotify")
|
||||||
|
|
||||||
|
|
||||||
def callback(self, event, origin=''):
|
def callback(self, event, origin=''):
|
||||||
logSys.debug("%sCallback for Event: %s", origin, event)
|
logSys.debug("%sCallback for Event: %s", origin, event)
|
||||||
path = event.pathname
|
path = event.pathname
|
||||||
|
@ -95,7 +95,6 @@ class FilterPyinotify(FileFilter):
|
||||||
|
|
||||||
self._process_file(path)
|
self._process_file(path)
|
||||||
|
|
||||||
|
|
||||||
def _process_file(self, path):
|
def _process_file(self, path):
|
||||||
"""Process a given file
|
"""Process a given file
|
||||||
|
|
||||||
|
@ -112,7 +111,6 @@ class FilterPyinotify(FileFilter):
|
||||||
self.dateDetector.sortTemplate()
|
self.dateDetector.sortTemplate()
|
||||||
self.__modified = False
|
self.__modified = False
|
||||||
|
|
||||||
|
|
||||||
def _addFileWatcher(self, path):
|
def _addFileWatcher(self, path):
|
||||||
wd = self.__monitor.add_watch(path, pyinotify.IN_MODIFY)
|
wd = self.__monitor.add_watch(path, pyinotify.IN_MODIFY)
|
||||||
self.__watches.update(wd)
|
self.__watches.update(wd)
|
||||||
|
@ -144,7 +142,6 @@ class FilterPyinotify(FileFilter):
|
||||||
self._addFileWatcher(path)
|
self._addFileWatcher(path)
|
||||||
self._process_file(path)
|
self._process_file(path)
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Delete a log path
|
# Delete a log path
|
||||||
#
|
#
|
||||||
|
@ -163,7 +160,6 @@ class FilterPyinotify(FileFilter):
|
||||||
self.__monitor.rm_watch(wdInt)
|
self.__monitor.rm_watch(wdInt)
|
||||||
logSys.debug("Removed monitor for the parent directory %s", path_dir)
|
logSys.debug("Removed monitor for the parent directory %s", path_dir)
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Main loop.
|
# Main loop.
|
||||||
#
|
#
|
||||||
|
|
|
@ -22,7 +22,8 @@ __author__ = "Steven Hiscocks"
|
||||||
__copyright__ = "Copyright (c) 2013 Steven Hiscocks"
|
__copyright__ = "Copyright (c) 2013 Steven Hiscocks"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import datetime, time
|
import datetime
|
||||||
|
import time
|
||||||
from distutils.version import LooseVersion
|
from distutils.version import LooseVersion
|
||||||
|
|
||||||
from systemd import journal
|
from systemd import journal
|
||||||
|
@ -37,6 +38,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Journal reader class.
|
# Journal reader class.
|
||||||
#
|
#
|
||||||
|
@ -60,7 +62,6 @@ class FilterSystemd(JournalFilter): # pragma: systemd no cover
|
||||||
self.setDatePattern(None)
|
self.setDatePattern(None)
|
||||||
logSys.debug("Created FilterSystemd")
|
logSys.debug("Created FilterSystemd")
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Add a journal match filters from list structure
|
# Add a journal match filters from list structure
|
||||||
#
|
#
|
||||||
|
|
|
@ -23,7 +23,8 @@ __author__ = "Cyril Jaquier, Lee Clemens, Yaroslav Halchenko"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2012 Lee Clemens, 2012 Yaroslav Halchenko"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2012 Lee Clemens, 2012 Yaroslav Halchenko"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import Queue, logging
|
import logging
|
||||||
|
import Queue
|
||||||
|
|
||||||
from .actions import Actions
|
from .actions import Actions
|
||||||
from ..helpers import getLogger
|
from ..helpers import getLogger
|
||||||
|
@ -31,6 +32,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Jail:
|
class Jail:
|
||||||
"""Fail2Ban jail, which manages a filter and associated actions.
|
"""Fail2Ban jail, which manages a filter and associated actions.
|
||||||
|
|
||||||
|
@ -115,7 +117,6 @@ class Jail:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"Failed to initialize any backend for Jail %r" % self.name)
|
"Failed to initialize any backend for Jail %r" % self.name)
|
||||||
|
|
||||||
|
|
||||||
def _initPolling(self):
|
def _initPolling(self):
|
||||||
from filterpoll import FilterPoll
|
from filterpoll import FilterPoll
|
||||||
logSys.info("Jail '%s' uses poller" % self.name)
|
logSys.info("Jail '%s' uses poller" % self.name)
|
||||||
|
|
|
@ -30,6 +30,7 @@ from abc import abstractmethod
|
||||||
|
|
||||||
from ..helpers import excepthook
|
from ..helpers import excepthook
|
||||||
|
|
||||||
|
|
||||||
class JailThread(Thread):
|
class JailThread(Thread):
|
||||||
"""Abstract class for threading elements in Fail2Ban.
|
"""Abstract class for threading elements in Fail2Ban.
|
||||||
|
|
||||||
|
@ -59,6 +60,7 @@ class JailThread(Thread):
|
||||||
# excepthook workaround for threads, derived from:
|
# excepthook workaround for threads, derived from:
|
||||||
# http://bugs.python.org/issue1230540#msg91244
|
# http://bugs.python.org/issue1230540#msg91244
|
||||||
run = self.run
|
run = self.run
|
||||||
|
|
||||||
def run_with_except_hook(*args, **kwargs):
|
def run_with_except_hook(*args, **kwargs):
|
||||||
try:
|
try:
|
||||||
run(*args, **kwargs)
|
run(*args, **kwargs)
|
||||||
|
|
|
@ -21,7 +21,9 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import time, datetime
|
import datetime
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# MyTime class.
|
# MyTime class.
|
||||||
|
|
|
@ -25,7 +25,12 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
from threading import Lock, RLock
|
from threading import Lock, RLock
|
||||||
import logging, logging.handlers, sys, os, signal, stat
|
import logging
|
||||||
|
import logging.handlers
|
||||||
|
import os
|
||||||
|
import signal
|
||||||
|
import stat
|
||||||
|
import sys
|
||||||
|
|
||||||
from .jails import Jails
|
from .jails import Jails
|
||||||
from .filter import FileFilter, JournalFilter
|
from .filter import FileFilter, JournalFilter
|
||||||
|
@ -43,6 +48,7 @@ except ImportError:
|
||||||
# Dont print error here, as database may not even be used
|
# Dont print error here, as database may not even be used
|
||||||
Fail2BanDb = None
|
Fail2BanDb = None
|
||||||
|
|
||||||
|
|
||||||
class Server:
|
class Server:
|
||||||
|
|
||||||
def __init__(self, daemon = False):
|
def __init__(self, daemon = False):
|
||||||
|
@ -61,11 +67,10 @@ class Server:
|
||||||
'FreeBSD': '/var/run/log',
|
'FreeBSD': '/var/run/log',
|
||||||
'Linux': '/dev/log',
|
'Linux': '/dev/log',
|
||||||
}
|
}
|
||||||
|
self.setSyslogSocket("auto")
|
||||||
# Set logging level
|
# Set logging level
|
||||||
self.setLogLevel("INFO")
|
self.setLogLevel("INFO")
|
||||||
self.setLogTarget("STDOUT")
|
self.setLogTarget("STDOUT")
|
||||||
self.setSyslogSocket("auto")
|
|
||||||
|
|
||||||
|
|
||||||
def __sigTERMhandler(self, signum, frame):
|
def __sigTERMhandler(self, signum, frame):
|
||||||
logSys.debug("Caught signal %d. Exiting" % signum)
|
logSys.debug("Caught signal %d. Exiting" % signum)
|
||||||
|
@ -139,7 +144,6 @@ class Server:
|
||||||
finally:
|
finally:
|
||||||
self.__loggingLock.release()
|
self.__loggingLock.release()
|
||||||
|
|
||||||
|
|
||||||
def addJail(self, name, backend):
|
def addJail(self, name, backend):
|
||||||
self.__jails.add(name, backend, self.__db)
|
self.__jails.add(name, backend, self.__db)
|
||||||
if self.__db is not None:
|
if self.__db is not None:
|
||||||
|
@ -494,7 +498,14 @@ class Server:
|
||||||
return "flushed"
|
return "flushed"
|
||||||
|
|
||||||
def setDatabase(self, filename):
|
def setDatabase(self, filename):
|
||||||
if len(self.__jails) == 0:
|
# if not changed - nothing to do
|
||||||
|
if self.__db and self.__db.filename == filename:
|
||||||
|
return
|
||||||
|
if not self.__db and filename.lower() == 'none':
|
||||||
|
return
|
||||||
|
if len(self.__jails) != 0:
|
||||||
|
raise RuntimeError(
|
||||||
|
"Cannot change database when there are jails present")
|
||||||
if filename.lower() == "none":
|
if filename.lower() == "none":
|
||||||
self.__db = None
|
self.__db = None
|
||||||
else:
|
else:
|
||||||
|
@ -505,14 +516,10 @@ class Server:
|
||||||
logSys.error(
|
logSys.error(
|
||||||
"Unable to import fail2ban database module as sqlite "
|
"Unable to import fail2ban database module as sqlite "
|
||||||
"is not available.")
|
"is not available.")
|
||||||
else:
|
|
||||||
raise RuntimeError(
|
|
||||||
"Cannot change database when there are jails present")
|
|
||||||
|
|
||||||
def getDatabase(self):
|
def getDatabase(self):
|
||||||
return self.__db
|
return self.__db
|
||||||
|
|
||||||
|
|
||||||
def __createDaemon(self): # pragma: no cover
|
def __createDaemon(self): # pragma: no cover
|
||||||
""" Detach a process from the controlling terminal and run it in the
|
""" Detach a process from the controlling terminal and run it in the
|
||||||
background as a daemon.
|
background as a daemon.
|
||||||
|
|
|
@ -28,6 +28,7 @@ locale_time = LocaleTime()
|
||||||
timeRE = TimeRE()
|
timeRE = TimeRE()
|
||||||
timeRE['z'] = r"(?P<z>Z|[+-]\d{2}(?::?[0-5]\d)?)"
|
timeRE['z'] = r"(?P<z>Z|[+-]\d{2}(?::?[0-5]\d)?)"
|
||||||
|
|
||||||
|
|
||||||
def reGroupDictStrptime(found_dict):
|
def reGroupDictStrptime(found_dict):
|
||||||
"""Return time from dictionary of strptime fields
|
"""Return time from dictionary of strptime fields
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Ticket:
|
class Ticket:
|
||||||
|
|
||||||
def __init__(self, ip, time, matches=None):
|
def __init__(self, ip, time, matches=None):
|
||||||
|
|
|
@ -33,6 +33,7 @@ from .. import version
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Transmitter:
|
class Transmitter:
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -51,7 +52,7 @@ class Transmitter:
|
||||||
|
|
||||||
def proceed(self, command):
|
def proceed(self, command):
|
||||||
# Deserialize object
|
# Deserialize object
|
||||||
logSys.debug("Command: " + `command`)
|
logSys.debug("Command: " + repr(command))
|
||||||
try:
|
try:
|
||||||
ret = self.__commandHandler(command)
|
ret = self.__commandHandler(command)
|
||||||
ack = 0, ret
|
ack = 0, ret
|
||||||
|
@ -138,6 +139,7 @@ class Transmitter:
|
||||||
elif name == "dbpurgeage":
|
elif name == "dbpurgeage":
|
||||||
db = self.__server.getDatabase()
|
db = self.__server.getDatabase()
|
||||||
if db is None:
|
if db is None:
|
||||||
|
logSys.warning("dbpurgeage setting was not in effect since no db yet")
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
db.purgeage = command[1]
|
db.purgeage = command[1]
|
||||||
|
|
|
@ -32,6 +32,7 @@ from ..dummyjail import DummyJail
|
||||||
|
|
||||||
from ..utils import CONFIG_DIR
|
from ..utils import CONFIG_DIR
|
||||||
|
|
||||||
|
|
||||||
class TestSMTPServer(smtpd.SMTPServer):
|
class TestSMTPServer(smtpd.SMTPServer):
|
||||||
|
|
||||||
def process_message(self, peer, mailfrom, rcpttos, data):
|
def process_message(self, peer, mailfrom, rcpttos, data):
|
||||||
|
@ -40,6 +41,7 @@ class TestSMTPServer(smtpd.SMTPServer):
|
||||||
self.rcpttos = rcpttos
|
self.rcpttos = rcpttos
|
||||||
self.data = data
|
self.data = data
|
||||||
|
|
||||||
|
|
||||||
class SMTPActionTest(unittest.TestCase):
|
class SMTPActionTest(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
|
@ -35,6 +35,7 @@ from .utils import LogCaptureTestCase
|
||||||
|
|
||||||
TEST_FILES_DIR = os.path.join(os.path.dirname(__file__), "files")
|
TEST_FILES_DIR = os.path.join(os.path.dirname(__file__), "files")
|
||||||
|
|
||||||
|
|
||||||
class ExecuteActions(LogCaptureTestCase):
|
class ExecuteActions(LogCaptureTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -76,7 +77,6 @@ class ExecuteActions(LogCaptureTestCase):
|
||||||
self.assertEqual(self.__actions.getBanTime(),127)
|
self.assertEqual(self.__actions.getBanTime(),127)
|
||||||
self.assertRaises(ValueError, self.__actions.removeBannedIP, '127.0.0.1')
|
self.assertRaises(ValueError, self.__actions.removeBannedIP, '127.0.0.1')
|
||||||
|
|
||||||
|
|
||||||
def testActionsOutput(self):
|
def testActionsOutput(self):
|
||||||
self.defaultActions()
|
self.defaultActions()
|
||||||
self.__actions.start()
|
self.__actions.start()
|
||||||
|
@ -89,7 +89,6 @@ class ExecuteActions(LogCaptureTestCase):
|
||||||
self.assertEqual(self.__actions.status(),[("Currently banned", 0 ),
|
self.assertEqual(self.__actions.status(),[("Currently banned", 0 ),
|
||||||
("Total banned", 0 ), ("Banned IP list", [] )])
|
("Total banned", 0 ), ("Banned IP list", [] )])
|
||||||
|
|
||||||
|
|
||||||
def testAddActionPython(self):
|
def testAddActionPython(self):
|
||||||
self.__actions.add(
|
self.__actions.add(
|
||||||
"Action", os.path.join(TEST_FILES_DIR, "action.d/action.py"),
|
"Action", os.path.join(TEST_FILES_DIR, "action.d/action.py"),
|
||||||
|
|
|
@ -24,11 +24,14 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
import os
|
||||||
import time
|
import time
|
||||||
|
import tempfile
|
||||||
|
|
||||||
from ..server.action import CommandAction, CallingMap
|
from ..server.action import CommandAction, CallingMap
|
||||||
|
|
||||||
from .utils import LogCaptureTestCase
|
from .utils import LogCaptureTestCase
|
||||||
|
from .utils import pid_exists
|
||||||
|
|
||||||
class CommandActionTest(LogCaptureTestCase):
|
class CommandActionTest(LogCaptureTestCase):
|
||||||
|
|
||||||
|
@ -110,7 +113,6 @@ class CommandActionTest(LogCaptureTestCase):
|
||||||
{'ipjailmatches': "some >char< should \< be[ escap}ed&\n"}),
|
{'ipjailmatches': "some >char< should \< be[ escap}ed&\n"}),
|
||||||
"some \\>char\\< should \\\\\\< be\\[ escap\\}ed\\&\n")
|
"some \\>char\\< should \\\\\\< be\\[ escap\\}ed\\&\n")
|
||||||
|
|
||||||
|
|
||||||
# Recursive
|
# Recursive
|
||||||
aInfo["ABC"] = "<xyz>"
|
aInfo["ABC"] = "<xyz>"
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
@ -202,6 +204,44 @@ class CommandActionTest(LogCaptureTestCase):
|
||||||
or self._is_logged('sleep 60 -- timed out after 3 seconds'))
|
or self._is_logged('sleep 60 -- timed out after 3 seconds'))
|
||||||
self.assertTrue(self._is_logged('sleep 60 -- killed with SIGTERM'))
|
self.assertTrue(self._is_logged('sleep 60 -- killed with SIGTERM'))
|
||||||
|
|
||||||
|
def testExecuteTimeoutWithNastyChildren(self):
|
||||||
|
# temporary file for a nasty kid shell script
|
||||||
|
tmpFilename = tempfile.mktemp(".sh", "fail2ban_")
|
||||||
|
# Create a nasty script which would hang there for a while
|
||||||
|
with open(tmpFilename, 'w') as f:
|
||||||
|
f.write("""#!/bin/bash
|
||||||
|
trap : HUP EXIT TERM
|
||||||
|
|
||||||
|
echo "$$" > %s.pid
|
||||||
|
echo "my pid $$ . sleeping lo-o-o-ong"
|
||||||
|
sleep 10000
|
||||||
|
""" % tmpFilename)
|
||||||
|
|
||||||
|
def getnastypid():
|
||||||
|
with open(tmpFilename + '.pid') as f:
|
||||||
|
return int(f.read())
|
||||||
|
|
||||||
|
# First test if can kill the bastard
|
||||||
|
self.assertRaises(
|
||||||
|
RuntimeError, CommandAction.executeCmd, 'bash %s' % tmpFilename, timeout=.1)
|
||||||
|
# Verify that the proccess itself got killed
|
||||||
|
self.assertFalse(pid_exists(getnastypid())) # process should have been killed
|
||||||
|
self.assertTrue(self._is_logged('timed out'))
|
||||||
|
self.assertTrue(self._is_logged('killed with SIGTERM'))
|
||||||
|
|
||||||
|
# A bit evolved case even though, previous test already tests killing children processes
|
||||||
|
self.assertRaises(
|
||||||
|
RuntimeError, CommandAction.executeCmd, 'out=`bash %s`; echo ALRIGHT' % tmpFilename,
|
||||||
|
timeout=.2)
|
||||||
|
# Verify that the proccess itself got killed
|
||||||
|
self.assertFalse(pid_exists(getnastypid()))
|
||||||
|
self.assertTrue(self._is_logged('timed out'))
|
||||||
|
self.assertTrue(self._is_logged('killed with SIGTERM'))
|
||||||
|
|
||||||
|
os.unlink(tmpFilename)
|
||||||
|
os.unlink(tmpFilename + '.pid')
|
||||||
|
|
||||||
|
|
||||||
def testCaptureStdOutErr(self):
|
def testCaptureStdOutErr(self):
|
||||||
CommandAction.executeCmd('echo "How now brown cow"')
|
CommandAction.executeCmd('echo "How now brown cow"')
|
||||||
self.assertTrue(self._is_logged("'How now brown cow\\n'"))
|
self.assertTrue(self._is_logged("'How now brown cow\\n'"))
|
||||||
|
|
|
@ -29,6 +29,7 @@ import unittest
|
||||||
from ..server.banmanager import BanManager
|
from ..server.banmanager import BanManager
|
||||||
from ..server.ticket import BanTicket
|
from ..server.ticket import BanTicket
|
||||||
|
|
||||||
|
|
||||||
class AddFailure(unittest.TestCase):
|
class AddFailure(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""Call before every test case."""
|
"""Call before every test case."""
|
||||||
|
|
|
@ -21,7 +21,13 @@ __author__ = "Cyril Jaquier, Yaroslav Halchenko"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2013 Yaroslav Halchenko"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2013 Yaroslav Halchenko"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import os, glob, shutil, tempfile, unittest, re, logging
|
import glob
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
import tempfile
|
||||||
|
import unittest
|
||||||
from ..client.configreader import ConfigReaderUnshared
|
from ..client.configreader import ConfigReaderUnshared
|
||||||
from ..client import configparserinc
|
from ..client import configparserinc
|
||||||
from ..client.jailreader import JailReader
|
from ..client.jailreader import JailReader
|
||||||
|
@ -39,6 +45,7 @@ STOCK = os.path.exists(os.path.join('config','fail2ban.conf'))
|
||||||
|
|
||||||
IMPERFECT_CONFIG = os.path.join(os.path.dirname(__file__), 'config')
|
IMPERFECT_CONFIG = os.path.join(os.path.dirname(__file__), 'config')
|
||||||
|
|
||||||
|
|
||||||
class ConfigReaderTest(unittest.TestCase):
|
class ConfigReaderTest(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -71,12 +78,10 @@ option = %s
|
||||||
os.unlink("%s/%s" % (self.d, fname))
|
os.unlink("%s/%s" % (self.d, fname))
|
||||||
self.assertTrue(self.c.read('c')) # we still should have some
|
self.assertTrue(self.c.read('c')) # we still should have some
|
||||||
|
|
||||||
|
|
||||||
def _getoption(self, f='c'):
|
def _getoption(self, f='c'):
|
||||||
self.assertTrue(self.c.read(f)) # we got some now
|
self.assertTrue(self.c.read(f)) # we got some now
|
||||||
return self.c.getOptions('section', [("int", 'option')])['option']
|
return self.c.getOptions('section', [("int", 'option')])['option']
|
||||||
|
|
||||||
|
|
||||||
def testInaccessibleFile(self):
|
def testInaccessibleFile(self):
|
||||||
f = os.path.join(self.d, "d.conf") # inaccessible file
|
f = os.path.join(self.d, "d.conf") # inaccessible file
|
||||||
self._write('d.conf', 0)
|
self._write('d.conf', 0)
|
||||||
|
@ -91,7 +96,6 @@ option = %s
|
||||||
# raise unittest.SkipTest("Skipping on %s -- access rights are not enforced" % platform)
|
# raise unittest.SkipTest("Skipping on %s -- access rights are not enforced" % platform)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def testOptionalDotDDir(self):
|
def testOptionalDotDDir(self):
|
||||||
self.assertFalse(self.c.read('c')) # nothing is there yet
|
self.assertFalse(self.c.read('c')) # nothing is there yet
|
||||||
self._write("c.conf", "1")
|
self._write("c.conf", "1")
|
||||||
|
@ -153,6 +157,7 @@ c = d ;in line comment
|
||||||
self.assertEqual(self.c.get('DEFAULT', 'b'), 'a')
|
self.assertEqual(self.c.get('DEFAULT', 'b'), 'a')
|
||||||
self.assertEqual(self.c.get('DEFAULT', 'c'), 'd')
|
self.assertEqual(self.c.get('DEFAULT', 'c'), 'd')
|
||||||
|
|
||||||
|
|
||||||
class JailReaderTest(LogCaptureTestCase):
|
class JailReaderTest(LogCaptureTestCase):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -179,15 +184,19 @@ class JailReaderTest(LogCaptureTestCase):
|
||||||
self.assertTrue(self._is_logged("Found no accessible config files for 'filter.d/catchallthebadies' under %s" % IMPERFECT_CONFIG))
|
self.assertTrue(self._is_logged("Found no accessible config files for 'filter.d/catchallthebadies' under %s" % IMPERFECT_CONFIG))
|
||||||
self.assertTrue(self._is_logged('Unable to read the filter'))
|
self.assertTrue(self._is_logged('Unable to read the filter'))
|
||||||
|
|
||||||
def TODOtestJailActionBrokenDef(self):
|
def testJailActionBrokenDef(self):
|
||||||
jail = JailReader('brokenactiondef', basedir=IMPERFECT_CONFIG, share_config = self.__share_cfg)
|
jail = JailReader('brokenactiondef', basedir=IMPERFECT_CONFIG,
|
||||||
|
share_config=self.__share_cfg)
|
||||||
self.assertTrue(jail.read())
|
self.assertTrue(jail.read())
|
||||||
self.assertFalse(jail.getOptions())
|
self.assertFalse(jail.getOptions())
|
||||||
self.assertTrue(jail.isEnabled())
|
self.assertTrue(jail.isEnabled())
|
||||||
self.printLog()
|
|
||||||
self.assertTrue(self._is_logged('Error in action definition joho[foo'))
|
self.assertTrue(self._is_logged('Error in action definition joho[foo'))
|
||||||
self.assertTrue(self._is_logged('Caught exception: While reading action joho[foo we should have got 1 or 2 groups. Got: 0'))
|
# This unittest has been deactivated for some time...
|
||||||
|
# self.assertTrue(self._is_logged(
|
||||||
|
# 'Caught exception: While reading action joho[foo we should have got 1 or 2 groups. Got: 0'))
|
||||||
|
# let's test for what is actually logged and handle changes in the future
|
||||||
|
self.assertTrue(self._is_logged(
|
||||||
|
"Caught exception: 'NoneType' object has no attribute 'endswith'"))
|
||||||
|
|
||||||
if STOCK:
|
if STOCK:
|
||||||
def testStockSSHJail(self):
|
def testStockSSHJail(self):
|
||||||
|
@ -218,7 +227,6 @@ class JailReaderTest(LogCaptureTestCase):
|
||||||
|
|
||||||
#self.assertRaises(ValueError, JailReader.extractOptions ,'mail-how[')
|
#self.assertRaises(ValueError, JailReader.extractOptions ,'mail-how[')
|
||||||
|
|
||||||
|
|
||||||
# Empty option
|
# Empty option
|
||||||
option = "abc[]"
|
option = "abc[]"
|
||||||
expected = ('abc', {})
|
expected = ('abc', {})
|
||||||
|
@ -312,7 +320,6 @@ class FilterReaderTest(unittest.TestCase):
|
||||||
output[-1][-1] = "5"
|
output[-1][-1] = "5"
|
||||||
self.assertEqual(sorted(filterReader.convert()), sorted(output))
|
self.assertEqual(sorted(filterReader.convert()), sorted(output))
|
||||||
|
|
||||||
|
|
||||||
def testFilterReaderSubstitionDefault(self):
|
def testFilterReaderSubstitionDefault(self):
|
||||||
output = [['set', 'jailname', 'addfailregex', 'to=sweet@example.com fromip=<IP>']]
|
output = [['set', 'jailname', 'addfailregex', 'to=sweet@example.com fromip=<IP>']]
|
||||||
filterReader = FilterReader('substition', "jailname", {})
|
filterReader = FilterReader('substition', "jailname", {})
|
||||||
|
@ -355,6 +362,7 @@ class FilterReaderTest(unittest.TestCase):
|
||||||
except Exception, e: # pragma: no cover - failed if reachable
|
except Exception, e: # pragma: no cover - failed if reachable
|
||||||
self.fail('unexpected options after readexplicit: %s' % (e))
|
self.fail('unexpected options after readexplicit: %s' % (e))
|
||||||
|
|
||||||
|
|
||||||
class JailsReaderTestCache(LogCaptureTestCase):
|
class JailsReaderTestCache(LogCaptureTestCase):
|
||||||
|
|
||||||
def _readWholeConf(self, basedir, force_enable=False, share_config=None):
|
def _readWholeConf(self, basedir, force_enable=False, share_config=None):
|
||||||
|
@ -474,7 +482,6 @@ class JailsReaderTest(LogCaptureTestCase):
|
||||||
self.assertTrue('Init' in actionReader.sections(),
|
self.assertTrue('Init' in actionReader.sections(),
|
||||||
msg="Action file %r is lacking [Init] section" % actionConfig)
|
msg="Action file %r is lacking [Init] section" % actionConfig)
|
||||||
|
|
||||||
|
|
||||||
def testReadStockJailConf(self):
|
def testReadStockJailConf(self):
|
||||||
jails = JailsReader(basedir=CONFIG_DIR, share_config=self.__share_cfg) # we are running tests from root project dir atm
|
jails = JailsReader(basedir=CONFIG_DIR, share_config=self.__share_cfg) # we are running tests from root project dir atm
|
||||||
self.assertTrue(jails.read()) # opens fine
|
self.assertTrue(jails.read()) # opens fine
|
||||||
|
@ -601,7 +608,6 @@ class JailsReaderTest(LogCaptureTestCase):
|
||||||
msg="Found no %s command among %s"
|
msg="Found no %s command among %s"
|
||||||
% (target_command, str(commands)) )
|
% (target_command, str(commands)) )
|
||||||
|
|
||||||
|
|
||||||
def testStockConfigurator(self):
|
def testStockConfigurator(self):
|
||||||
configurator = Configurator()
|
configurator = Configurator()
|
||||||
configurator.setBaseDir(CONFIG_DIR)
|
configurator.setBaseDir(CONFIG_DIR)
|
||||||
|
@ -616,6 +622,22 @@ class JailsReaderTest(LogCaptureTestCase):
|
||||||
configurator.getOptions()
|
configurator.getOptions()
|
||||||
configurator.convertToProtocol()
|
configurator.convertToProtocol()
|
||||||
commands = configurator.getConfigStream()
|
commands = configurator.getConfigStream()
|
||||||
|
|
||||||
|
# verify that dbfile comes before dbpurgeage
|
||||||
|
def find_set(option):
|
||||||
|
for i, e in enumerate(commands):
|
||||||
|
if e[0] == 'set' and e[1] == option:
|
||||||
|
return i
|
||||||
|
raise ValueError("Did not find command 'set %s' among commands %s"
|
||||||
|
% (option, commands))
|
||||||
|
|
||||||
|
# Set up of logging should come first
|
||||||
|
self.assertEqual(find_set('syslogsocket'), 0)
|
||||||
|
self.assertEqual(find_set('loglevel'), 1)
|
||||||
|
self.assertEqual(find_set('logtarget'), 2)
|
||||||
|
# then dbfile should be before dbpurgeage
|
||||||
|
self.assertTrue(find_set('dbpurgeage') > find_set('dbfile'))
|
||||||
|
|
||||||
# and there is logging information left to be passed into the
|
# and there is logging information left to be passed into the
|
||||||
# server
|
# server
|
||||||
self.assertEqual(sorted(commands),
|
self.assertEqual(sorted(commands),
|
||||||
|
|
|
@ -42,6 +42,7 @@ from .utils import LogCaptureTestCase
|
||||||
|
|
||||||
TEST_FILES_DIR = os.path.join(os.path.dirname(__file__), "files")
|
TEST_FILES_DIR = os.path.join(os.path.dirname(__file__), "files")
|
||||||
|
|
||||||
|
|
||||||
class DatabaseTest(LogCaptureTestCase):
|
class DatabaseTest(LogCaptureTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -211,7 +212,7 @@ class DatabaseTest(LogCaptureTestCase):
|
||||||
def testDelBan(self):
|
def testDelBan(self):
|
||||||
self.testAddBan()
|
self.testAddBan()
|
||||||
ticket = self.db.getBans(jail=self.jail)[0]
|
ticket = self.db.getBans(jail=self.jail)[0]
|
||||||
self.db.delBan(self.jail, ticket)
|
self.db.delBan(self.jail, ticket.getIP())
|
||||||
self.assertEqual(len(self.db.getBans(jail=self.jail)), 0)
|
self.assertEqual(len(self.db.getBans(jail=self.jail)), 0)
|
||||||
|
|
||||||
def testGetBansWithTime(self):
|
def testGetBansWithTime(self):
|
||||||
|
@ -305,7 +306,7 @@ class DatabaseTest(LogCaptureTestCase):
|
||||||
def testActionWithDB(self):
|
def testActionWithDB(self):
|
||||||
# test action together with database functionality
|
# test action together with database functionality
|
||||||
self.testAddJail() # Jail required
|
self.testAddJail() # Jail required
|
||||||
self.jail.database = self.db;
|
self.jail.database = self.db
|
||||||
actions = Actions(self.jail)
|
actions = Actions(self.jail)
|
||||||
actions.add(
|
actions.add(
|
||||||
"action_checkainfo",
|
"action_checkainfo",
|
||||||
|
@ -317,7 +318,6 @@ class DatabaseTest(LogCaptureTestCase):
|
||||||
actions._Actions__checkBan()
|
actions._Actions__checkBan()
|
||||||
self.assertTrue(self._is_logged("ban ainfo %s, %s, %s, %s" % (True, True, True, True)))
|
self.assertTrue(self._is_logged("ban ainfo %s, %s, %s, %s" % (True, True, True, True)))
|
||||||
|
|
||||||
|
|
||||||
def testPurge(self):
|
def testPurge(self):
|
||||||
if Fail2BanDb is None: # pragma: no cover
|
if Fail2BanDb is None: # pragma: no cover
|
||||||
return
|
return
|
||||||
|
|
|
@ -32,6 +32,7 @@ from ..server.datedetector import DateDetector
|
||||||
from ..server.datetemplate import DateTemplate
|
from ..server.datetemplate import DateTemplate
|
||||||
from .utils import setUpMyTime, tearDownMyTime
|
from .utils import setUpMyTime, tearDownMyTime
|
||||||
|
|
||||||
|
|
||||||
class DateDetectorTest(unittest.TestCase):
|
class DateDetectorTest(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
|
@ -26,6 +26,7 @@ from threading import Lock
|
||||||
|
|
||||||
from ..server.actions import Actions
|
from ..server.actions import Actions
|
||||||
|
|
||||||
|
|
||||||
class DummyJail(object):
|
class DummyJail(object):
|
||||||
"""A simple 'jail' to suck in all the tickets generated by Filter's
|
"""A simple 'jail' to suck in all the tickets generated by Filter's
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -29,6 +29,7 @@ import unittest
|
||||||
from ..server.failmanager import FailManager, FailManagerEmpty
|
from ..server.failmanager import FailManager, FailManagerEmpty
|
||||||
from ..server.ticket import FailTicket
|
from ..server.ticket import FailTicket
|
||||||
|
|
||||||
|
|
||||||
class AddFailure(unittest.TestCase):
|
class AddFailure(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -100,7 +101,7 @@ class AddFailure(unittest.TestCase):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
ticket_repr,
|
ticket_repr,
|
||||||
'FailTicket: ip=193.168.0.128 time=1167605999.0 #attempts=5 matches=[]')
|
'FailTicket: ip=193.168.0.128 time=1167605999.0 #attempts=5 matches=[]')
|
||||||
self.assertFalse(ticket == False)
|
self.assertFalse(not ticket)
|
||||||
# and some get/set-ers otherwise not tested
|
# and some get/set-ers otherwise not tested
|
||||||
ticket.setTime(1000002000.0)
|
ticket.setTime(1000002000.0)
|
||||||
self.assertEqual(ticket.getTime(), 1000002000.0)
|
self.assertEqual(ticket.getTime(), 1000002000.0)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
|
||||||
from fail2ban.server.action import ActionBase
|
from fail2ban.server.action import ActionBase
|
||||||
|
|
||||||
|
|
||||||
class TestAction(ActionBase):
|
class TestAction(ActionBase):
|
||||||
|
|
||||||
def __init__(self, jail, name, opt1, opt2=None):
|
def __init__(self, jail, name, opt1, opt2=None):
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
|
||||||
from fail2ban.server.action import ActionBase
|
from fail2ban.server.action import ActionBase
|
||||||
|
|
||||||
|
|
||||||
class TestAction(ActionBase):
|
class TestAction(ActionBase):
|
||||||
|
|
||||||
def ban(self, aInfo):
|
def ban(self, aInfo):
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
|
||||||
from fail2ban.server.action import ActionBase
|
from fail2ban.server.action import ActionBase
|
||||||
|
|
||||||
|
|
||||||
class TestAction(ActionBase):
|
class TestAction(ActionBase):
|
||||||
|
|
||||||
def __init__(self, jail, name):
|
def __init__(self, jail, name):
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
|
||||||
from fail2ban.server.action import ActionBase
|
from fail2ban.server.action import ActionBase
|
||||||
|
|
||||||
|
|
||||||
class TestAction(ActionBase):
|
class TestAction(ActionBase):
|
||||||
|
|
||||||
def ban(self, aInfo):
|
def ban(self, aInfo):
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue