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
Yaroslav Halchenko 2015-07-31 21:34:06 -04:00
commit bceb35ab34
124 changed files with 1174 additions and 373 deletions

View File

@ -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

View File

@ -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

View File

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

View File

@ -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
---------- ----------

View File

@ -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.

View File

@ -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
View File

@ -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
View File

@ -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

View File

@ -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.
# #

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 =

View File

@ -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]

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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]

View File

@ -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]

View File

@ -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]

View File

@ -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

View File

@ -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]

View File

@ -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]

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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]

View File

@ -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)';

View File

@ -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&#44; +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&#44; +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 =

View File

@ -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

View File

@ -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 =

View File

@ -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 =

View File

@ -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 =

View File

@ -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 =

View File

@ -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]

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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):

View File

@ -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 = [

View File

@ -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"

View File

@ -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

View File

@ -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):

View File

@ -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):

View File

@ -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)

View File

@ -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)

View File

@ -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 = [

View File

@ -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|-|_|\.)+)(?:\[(.*)\])?$")

View File

@ -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):

View File

@ -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

View File

@ -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
""" """

View File

@ -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 + " ==="

View File

@ -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:

View File

@ -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()

View File

@ -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.

View File

@ -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.
# #

View File

@ -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):

View File

@ -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.

View File

@ -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.

View File

@ -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):

View File

@ -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

View File

@ -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.

View File

@ -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:

View File

@ -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()

View File

@ -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.
# #

View File

@ -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.
# #

View File

@ -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
# #

View File

@ -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)

View File

@ -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)

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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):

View File

@ -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]

View File

@ -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):

View File

@ -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"),

View File

@ -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'"))

View File

@ -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."""

View File

@ -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),

View File

@ -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

View File

@ -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):

View File

@ -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
""" """

View File

@ -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)

View File

@ -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):

View File

@ -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):

View File

@ -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):

View File

@ -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