What aught to be a bugfix release delayed into a featured release 0.9.1

ver. 0.9.1 (2014/10/29) - better, faster, stronger
 ----------
 
 - Refactoring (IMPORTANT -- Please review your setup and configuration):
    * iptables-common.conf replaced iptables-blocktype.conf
      (iptables-blocktype.local should still be read) and now also
      provides defaults for the chain, port, protocol and name tags
 
 - Fixes:
    * start of file2ban aborted (on slow hosts, systemd considers the server has
      been timed out and kills him), see gh-824
    * UTF-8 fixes in pure-ftp thanks to Johannes Weberhofer. Closes gh-806.
    * systemd backend error on bad utf-8 in python3
    * badips.py action error when logging HTTP error raised with badips request
    * fail2ban-regex failed to work in python3 due to space/tab mix
    * recidive regex samples incorrect log level
    * journalmatch for recidive incorrect PRIORITY
    * loglevel couldn't be changed in fail2ban.conf
    * Handle case when no sqlite library is available for persistent database
    * Only reban once per IP from database on fail2ban restart
    * Nginx filter to support missing server_name. Closes gh-676
    * fail2ban-regex assertion error caused by miscount missed lines with
      multiline regex
    * Fix actions failing to execute for Python 3.4.0. Workaround for
      http://bugs.python.org/issue21207
    * Database now returns persistent bans on restart (bantime < 0)
    * Recursive action tags now fully processed. Fixes issue with bsd-ipfw
      action
    * Fixed TypeError with "ipfailures" and "ipjailfailures" action tags.
      Thanks Serg G. Brester
    * Correct times for non-timezone date times formats during DST
    * Pass a copy of, not original, aInfo into actions to avoid side-effects
    * Per-distribution paths to the exim's main log
    * Ignored IPs are no longer banned when being restored from persistent
      database
    * Manually unbanned IPs are now removed from persistent database, such they
      wont be banned again when Fail2Ban is restarted
    * Pass "bantime" parameter to the actions in default jail's action
      definition(s)
    * filters.d/sieve.conf - fixed typo in _daemon.  Thanks Jisoo Park
    * cyrus-imap -- also catch also failed logins via secured (imaps/pop3s).
      Regression was introduced while strengthening failregex in 0.8.11 (bd175f)
      Debian bug #755173
    * postfix-sasl - added journalmatch.  Thanks Luc Maisonobe
    * postfix* - match with a new daemon string (postfix/submission/smtpd).
      Closes gh-804 .  Thanks Paul Traina
    * apache - added filter for AH01630 client denied by server configuration.
 
 - New features:
    - New filters:
      - monit  Thanks Jason H Martin
      - directadmin  Thanks niorg
      - apache-shellshock  Thanks Eugene Hopkinson (SlowRiot)
    - New actions:
      - symbiosis-blacklist-allports  for Bytemark symbiosis firewall
    - fail2ban-client can fetch the running server version
    - Added Cloudflare API action
 
 - Enhancements
    * Start performance of fail2ban-client (and tests) increased, start time
      and cpu usage rapidly reduced. Introduced a shared storage logic, to
      bypass reading lots of config files (see gh-824).
      Thanks to Joost Molenaar for good catch (reported gh-820).
    * Fail2ban-regex - add print-all-matched option. Closes gh-652
    * Suppress fail2ban-client warnings for non-critical config options
    * Match non "Bye Bye" disconnect messages for sshd locked account regex
    * courier-smtp filter:
      - match lines with user names
      - match lines containing "535 Authentication failed" attempts
    * Add <chain> tag to iptables-ipsets
    * Realign fail2ban log output with white space to improve readability. Does
      not affect SYSLOG output
    * Log unhandled exceptions
    * cyrus-imap: catch "user not found" attempts
    * Add support for Portsentry
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iEYEABECAAYFAlRO9sEACgkQjRFFY3XAJMi/5wCgktRXWZyyjT/vBWPqYGbpjT0x
 29UAnAxPZaUBBuzenJ5ROMNA7Xbrmzoq
 =Fd3J
 -----END PGP SIGNATURE-----

Merge tag '0.9.1' into debian

What aught to be a bugfix release delayed into a featured release 0.9.1

ver. 0.9.1 (2014/10/29) - better, faster, stronger
----------

- Refactoring (IMPORTANT -- Please review your setup and configuration):
   * iptables-common.conf replaced iptables-blocktype.conf
     (iptables-blocktype.local should still be read) and now also
     provides defaults for the chain, port, protocol and name tags

- Fixes:
   * start of file2ban aborted (on slow hosts, systemd considers the server has
     been timed out and kills him), see gh-824
   * UTF-8 fixes in pure-ftp thanks to Johannes Weberhofer. Closes gh-806.
   * systemd backend error on bad utf-8 in python3
   * badips.py action error when logging HTTP error raised with badips request
   * fail2ban-regex failed to work in python3 due to space/tab mix
   * recidive regex samples incorrect log level
   * journalmatch for recidive incorrect PRIORITY
   * loglevel couldn't be changed in fail2ban.conf
   * Handle case when no sqlite library is available for persistent database
   * Only reban once per IP from database on fail2ban restart
   * Nginx filter to support missing server_name. Closes gh-676
   * fail2ban-regex assertion error caused by miscount missed lines with
     multiline regex
   * Fix actions failing to execute for Python 3.4.0. Workaround for
     http://bugs.python.org/issue21207
   * Database now returns persistent bans on restart (bantime < 0)
   * Recursive action tags now fully processed. Fixes issue with bsd-ipfw
     action
   * Fixed TypeError with "ipfailures" and "ipjailfailures" action tags.
     Thanks Serg G. Brester
   * Correct times for non-timezone date times formats during DST
   * Pass a copy of, not original, aInfo into actions to avoid side-effects
   * Per-distribution paths to the exim's main log
   * Ignored IPs are no longer banned when being restored from persistent
     database
   * Manually unbanned IPs are now removed from persistent database, such they
     wont be banned again when Fail2Ban is restarted
   * Pass "bantime" parameter to the actions in default jail's action
     definition(s)
   * filters.d/sieve.conf - fixed typo in _daemon.  Thanks Jisoo Park
   * cyrus-imap -- also catch also failed logins via secured (imaps/pop3s).
     Regression was introduced while strengthening failregex in 0.8.11 (bd175f)
     Debian bug #755173
   * postfix-sasl - added journalmatch.  Thanks Luc Maisonobe
   * postfix* - match with a new daemon string (postfix/submission/smtpd).
     Closes gh-804 .  Thanks Paul Traina
   * apache - added filter for AH01630 client denied by server configuration.

- New features:
   - New filters:
     - monit  Thanks Jason H Martin
     - directadmin  Thanks niorg
     - apache-shellshock  Thanks Eugene Hopkinson (SlowRiot)
   - New actions:
     - symbiosis-blacklist-allports  for Bytemark symbiosis firewall
   - fail2ban-client can fetch the running server version
   - Added Cloudflare API action

- Enhancements
   * Start performance of fail2ban-client (and tests) increased, start time
     and cpu usage rapidly reduced. Introduced a shared storage logic, to
     bypass reading lots of config files (see gh-824).
     Thanks to Joost Molenaar for good catch (reported gh-820).
   * Fail2ban-regex - add print-all-matched option. Closes gh-652
   * Suppress fail2ban-client warnings for non-critical config options
   * Match non "Bye Bye" disconnect messages for sshd locked account regex
   * courier-smtp filter:
     - match lines with user names
     - match lines containing "535 Authentication failed" attempts
   * Add <chain> tag to iptables-ipsets
   * Realign fail2ban log output with white space to improve readability. Does
     not affect SYSLOG output
   * Log unhandled exceptions
   * cyrus-imap: catch "user not found" attempts
   * Add support for Portsentry

* tag '0.9.1': (36 commits)
  ENH: additional versioning changes
  Refreshed manpages
  ENH: fail early in generate-man + provide PYTHONPATH upstairs
  Changes for the 0.9.1 release versioning
  Populated MANIFEST with more entries which were preiously missed or duplicated. Sorted within each "section"
  Add portsentry to changelog
  ConfigReader.touch renamed into protected _create_unshared
  DOC: documentation about available vagrantfile setup
  Added myself into THANKS
  DOC: adjust docs in mytime to place docs into docstrings
  ENH: do use @staticmethod (we are well beyond support of 2.4 now)
  testExecuteTimeout fixed: give a test still 1 second, because system could be too busy
  coverage: no cover (for failed except)
  fix: fail2ban-regex with filter file failed (after merging #824, because test case missing); test case for 'readexplicit' added;
  ENH:  remove obsolete code for python < 2.6 (we support >= 2.6)
  DOC: very minor (tabs/spaces)
  We better check that installation doesn't cause any errors as well
  code review, change log entries added;
  reset share/cache storage (if we use 'reload' in client with interactive mode)
  normalize tabs/spaces in docstrings;
  ...
pull/856/head
Yaroslav Halchenko 2014-10-27 21:52:39 -04:00
commit b1d7c3f3ff
34 changed files with 697 additions and 348 deletions

View File

@ -16,6 +16,8 @@ install:
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then cd ..; pip install -q coveralls; cd -; fi - if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then cd ..; pip install -q coveralls; cd -; fi
script: script:
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then coverage run --rcfile=.travis_coveragerc setup.py test; else python setup.py test; fi - if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then coverage run --rcfile=.travis_coveragerc setup.py test; else python setup.py test; fi
# test installation
- sudo python setup.py install
after_success: after_success:
# Coverage config file must be .coveragerc for coveralls # Coverage config file must be .coveragerc for coveralls
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then cp -v .travis_coveragerc .coveragerc; fi - if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then cp -v .travis_coveragerc .coveragerc; fi

View File

@ -4,10 +4,10 @@
|_| \__,_|_|_/___|_.__/\__,_|_||_| |_| \__,_|_|_/___|_.__/\__,_|_||_|
================================================================================ ================================================================================
Fail2Ban (version 0.9.0.dev) 2014/xx/xx Fail2Ban (version 0.9.1) 2014/10/29
================================================================================ ================================================================================
ver. 0.9.1 (2014/xx/xx) - better, faster, stronger ver. 0.9.1 (2014/10/29) - better, faster, stronger
---------- ----------
- Refactoring (IMPORTANT -- Please review your setup and configuration): - Refactoring (IMPORTANT -- Please review your setup and configuration):
@ -16,6 +16,8 @@ ver. 0.9.1 (2014/xx/xx) - better, faster, stronger
provides defaults for the chain, port, protocol and name tags provides defaults for the chain, port, protocol and name tags
- Fixes: - Fixes:
* start of file2ban aborted (on slow hosts, systemd considers the server has
been timed out and kills him), see gh-824
* UTF-8 fixes in pure-ftp thanks to Johannes Weberhofer. Closes gh-806. * UTF-8 fixes in pure-ftp thanks to Johannes Weberhofer. Closes gh-806.
* systemd backend error on bad utf-8 in python3 * systemd backend error on bad utf-8 in python3
* badips.py action error when logging HTTP error raised with badips request * badips.py action error when logging HTTP error raised with badips request
@ -64,6 +66,10 @@ ver. 0.9.1 (2014/xx/xx) - better, faster, stronger
- Added Cloudflare API action - Added Cloudflare API action
- Enhancements - Enhancements
* Start performance of fail2ban-client (and tests) increased, start time
and cpu usage rapidly reduced. Introduced a shared storage logic, to
bypass reading lots of config files (see gh-824).
Thanks to Joost Molenaar for good catch (reported gh-820).
* Fail2ban-regex - add print-all-matched option. Closes gh-652 * Fail2ban-regex - add print-all-matched option. Closes gh-652
* Suppress fail2ban-client warnings for non-critical config options * Suppress fail2ban-client warnings for non-critical config options
* Match non "Bye Bye" disconnect messages for sshd locked account regex * Match non "Bye Bye" disconnect messages for sshd locked account regex
@ -75,6 +81,7 @@ ver. 0.9.1 (2014/xx/xx) - better, faster, stronger
not affect SYSLOG output not affect SYSLOG output
* Log unhandled exceptions * Log unhandled exceptions
* cyrus-imap: catch "user not found" attempts * cyrus-imap: catch "user not found" attempts
* Add support for Portsentry
ver. 0.9.0 (2014/03/14) - beta ver. 0.9.0 (2014/03/14) - beta
---------- ----------

12
DEVELOP
View File

@ -81,6 +81,18 @@ some quick commands::
status test status test
Testing with vagrant
--------------------
Testing can now be done inside a vagrant VM. Vagrantfile provided in
source code repository established two VMs:
- VM "secure" which can be used for testing fail2ban code.
- VM "attacker" which hcan 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
take a look into the Vagrantfile and change the IP.
Coding Standards Coding Standards
================ ================

348
MANIFEST
View File

@ -1,11 +1,14 @@
README.md CONTRIBUTING.md
README.Solaris
ChangeLog
TODO
THANKS
COPYING COPYING
ChangeLog
DEVELOP DEVELOP
FILTERS FILTERS
README.Solaris
README.md
RELEASE
THANKS
TODO
Vagrantfile
fail2ban-2to3 fail2ban-2to3
fail2ban-testcases-all fail2ban-testcases-all
fail2ban-testcases-all-python3 fail2ban-testcases-all-python3
@ -14,109 +17,108 @@ bin/fail2ban-server
bin/fail2ban-testcases bin/fail2ban-testcases
bin/fail2ban-regex bin/fail2ban-regex
doc/run-rootless.txt doc/run-rootless.txt
fail2ban/client/configreader.py
fail2ban/client/configparserinc.py
fail2ban/client/jailreader.py
fail2ban/client/fail2banreader.py
fail2ban/client/jailsreader.py
fail2ban/client/beautifier.py
fail2ban/client/filterreader.py
fail2ban/client/actionreader.py
fail2ban/client/__init__.py fail2ban/client/__init__.py
fail2ban/client/actionreader.py
fail2ban/client/beautifier.py
fail2ban/client/configparserinc.py
fail2ban/client/configreader.py
fail2ban/client/configurator.py fail2ban/client/configurator.py
fail2ban/client/csocket.py fail2ban/client/csocket.py
fail2ban/server/asyncserver.py fail2ban/client/fail2banreader.py
fail2ban/server/database.py fail2ban/client/filterreader.py
fail2ban/server/filter.py fail2ban/client/jailreader.py
fail2ban/server/filterpyinotify.py fail2ban/client/jailsreader.py
fail2ban/server/filtergamin.py fail2ban/server/__init__.py
fail2ban/server/filterpoll.py fail2ban/server/action.py
fail2ban/server/filtersystemd.py
fail2ban/server/iso8601.py
fail2ban/server/server.py
fail2ban/server/actions.py fail2ban/server/actions.py
fail2ban/server/asyncserver.py
fail2ban/server/banmanager.py
fail2ban/server/database.py
fail2ban/server/datedetector.py
fail2ban/server/datetemplate.py
fail2ban/server/faildata.py fail2ban/server/faildata.py
fail2ban/server/failmanager.py fail2ban/server/failmanager.py
fail2ban/server/datedetector.py fail2ban/server/failregex.py
fail2ban/server/jailthread.py fail2ban/server/filter.py
fail2ban/server/transmitter.py fail2ban/server/filtergamin.py
fail2ban/server/action.py fail2ban/server/filterpoll.py
fail2ban/server/ticket.py fail2ban/server/filterpyinotify.py
fail2ban/server/filtersystemd.py
fail2ban/server/iso8601.py
fail2ban/server/jail.py fail2ban/server/jail.py
fail2ban/server/jails.py fail2ban/server/jails.py
fail2ban/server/__init__.py fail2ban/server/jailthread.py
fail2ban/server/banmanager.py
fail2ban/server/datetemplate.py
fail2ban/server/mytime.py fail2ban/server/mytime.py
fail2ban/server/failregex.py fail2ban/server/server.py
fail2ban/server/database.py fail2ban/server/strptime.py
fail2ban/tests/banmanagertestcase.py fail2ban/server/ticket.py
fail2ban/tests/failmanagertestcase.py fail2ban/server/transmitter.py
fail2ban/tests/clientreadertestcase.py
fail2ban/tests/filtertestcase.py
fail2ban/tests/__init__.py fail2ban/tests/__init__.py
fail2ban/tests/dummyjail.py fail2ban/tests/action_d/__init__.py
fail2ban/tests/samplestestcase.py fail2ban/tests/action_d/test_badips.py
fail2ban/tests/datedetectortestcase.py fail2ban/tests/action_d/test_smtp.py
fail2ban/tests/actionstestcase.py
fail2ban/tests/actiontestcase.py fail2ban/tests/actiontestcase.py
fail2ban/tests/servertestcase.py fail2ban/tests/banmanagertestcase.py
fail2ban/tests/sockettestcase.py fail2ban/tests/clientreadertestcase.py
fail2ban/tests/utils.py fail2ban/tests/config/action.d/brokenaction.conf
fail2ban/tests/misctestcase.py
fail2ban/tests/databasetestcase.py
fail2ban/tests/config/jail.conf
fail2ban/tests/config/fail2ban.conf fail2ban/tests/config/fail2ban.conf
fail2ban/tests/config/filter.d/simple.conf
fail2ban/tests/config/jail.conf
fail2ban/tests/config/paths-common.conf fail2ban/tests/config/paths-common.conf
fail2ban/tests/config/paths-debian.conf
fail2ban/tests/config/paths-freebsd.conf fail2ban/tests/config/paths-freebsd.conf
fail2ban/tests/config/paths-osx.conf fail2ban/tests/config/paths-osx.conf
fail2ban/tests/config/paths-debian.conf fail2ban/tests/databasetestcase.py
fail2ban/tests/config/filter.d/simple.conf fail2ban/tests/datedetectortestcase.py
fail2ban/tests/config/action.d/brokenaction.conf fail2ban/tests/dummyjail.py
fail2ban/tests/files/config/apache-auth/digest/.htaccess fail2ban/tests/failmanagertestcase.py
fail2ban/tests/files/config/apache-auth/digest/.htpasswd fail2ban/tests/files/action.d/action.py
fail2ban/tests/files/config/apache-auth/digest_time/.htaccess fail2ban/tests/files/action.d/action_errors.py
fail2ban/tests/files/config/apache-auth/digest_time/.htpasswd fail2ban/tests/files/action.d/action_modifyainfo.py
fail2ban/tests/files/action.d/action_noAction.py
fail2ban/tests/files/action.d/action_nomethod.py
fail2ban/tests/files/config/apache-auth/README
fail2ban/tests/files/config/apache-auth/basic/authz_owner/.htaccess fail2ban/tests/files/config/apache-auth/basic/authz_owner/.htaccess
fail2ban/tests/files/config/apache-auth/basic/authz_owner/cant_get_me.html
fail2ban/tests/files/config/apache-auth/basic/authz_owner/.htpasswd fail2ban/tests/files/config/apache-auth/basic/authz_owner/.htpasswd
fail2ban/tests/files/config/apache-auth/basic/authz_owner/cant_get_me.html
fail2ban/tests/files/config/apache-auth/basic/file/.htaccess fail2ban/tests/files/config/apache-auth/basic/file/.htaccess
fail2ban/tests/files/config/apache-auth/basic/file/.htpasswd fail2ban/tests/files/config/apache-auth/basic/file/.htpasswd
fail2ban/tests/files/config/apache-auth/digest.py fail2ban/tests/files/config/apache-auth/digest.py
fail2ban/tests/files/config/apache-auth/digest_wrongrelm/.htaccess fail2ban/tests/files/config/apache-auth/digest/.htaccess
fail2ban/tests/files/config/apache-auth/digest_wrongrelm/.htpasswd fail2ban/tests/files/config/apache-auth/digest/.htpasswd
fail2ban/tests/files/config/apache-auth/digest_anon/.htaccess fail2ban/tests/files/config/apache-auth/digest_anon/.htaccess
fail2ban/tests/files/config/apache-auth/digest_anon/.htpasswd fail2ban/tests/files/config/apache-auth/digest_anon/.htpasswd
fail2ban/tests/files/config/apache-auth/README fail2ban/tests/files/config/apache-auth/digest_time/.htaccess
fail2ban/tests/files/config/apache-auth/digest_time/.htpasswd
fail2ban/tests/files/config/apache-auth/digest_wrongrelm/.htaccess
fail2ban/tests/files/config/apache-auth/digest_wrongrelm/.htpasswd
fail2ban/tests/files/config/apache-auth/noentry/.htaccess fail2ban/tests/files/config/apache-auth/noentry/.htaccess
fail2ban/tests/files/database_v1.db fail2ban/tests/files/database_v1.db
fail2ban/tests/files/ignorecommand.py
fail2ban/tests/files/filter.d/substition.conf fail2ban/tests/files/filter.d/substition.conf
fail2ban/tests/files/filter.d/testcase-common.conf fail2ban/tests/files/filter.d/testcase-common.conf
fail2ban/tests/files/filter.d/testcase01.conf fail2ban/tests/files/filter.d/testcase01.conf
fail2ban/tests/files/testcase01.log fail2ban/tests/files/ignorecommand.py
fail2ban/tests/files/testcase02.log
fail2ban/tests/files/testcase03.log
fail2ban/tests/files/testcase04.log
fail2ban/tests/files/testcase-usedns.log
fail2ban/tests/files/testcase-journal.log
fail2ban/tests/files/testcase-multiline.log
fail2ban/tests/files/logs/bsd/syslog-plain.txt
fail2ban/tests/files/logs/bsd/syslog-v.txt
fail2ban/tests/files/logs/bsd/syslog-vv.txt
fail2ban/tests/files/logs/3proxy fail2ban/tests/files/logs/3proxy
fail2ban/tests/files/logs/apache-auth fail2ban/tests/files/logs/apache-auth
fail2ban/tests/files/logs/apache-badbots fail2ban/tests/files/logs/apache-badbots
fail2ban/tests/files/logs/apache-botscripts fail2ban/tests/files/logs/apache-botscripts
fail2ban/tests/files/logs/apache-botsearch
fail2ban/tests/files/logs/apache-modsecurity fail2ban/tests/files/logs/apache-modsecurity
fail2ban/tests/files/logs/apache-nohome fail2ban/tests/files/logs/apache-nohome
fail2ban/tests/files/logs/apache-noscript fail2ban/tests/files/logs/apache-noscript
fail2ban/tests/files/logs/apache-overflows fail2ban/tests/files/logs/apache-overflows
fail2ban/tests/files/logs/apache-shellshock
fail2ban/tests/files/logs/assp fail2ban/tests/files/logs/assp
fail2ban/tests/files/logs/asterisk fail2ban/tests/files/logs/asterisk
fail2ban/tests/files/logs/bsd/syslog-plain.txt
fail2ban/tests/files/logs/bsd/syslog-v.txt
fail2ban/tests/files/logs/bsd/syslog-vv.txt
fail2ban/tests/files/logs/counter-strike fail2ban/tests/files/logs/counter-strike
fail2ban/tests/files/logs/courier-auth fail2ban/tests/files/logs/courier-auth
fail2ban/tests/files/logs/courier-smtp fail2ban/tests/files/logs/courier-smtp
fail2ban/tests/files/logs/cyrus-imap fail2ban/tests/files/logs/cyrus-imap
fail2ban/tests/files/logs/directadmin
fail2ban/tests/files/logs/dovecot fail2ban/tests/files/logs/dovecot
fail2ban/tests/files/logs/dropbear fail2ban/tests/files/logs/dropbear
fail2ban/tests/files/logs/ejabberd-auth fail2ban/tests/files/logs/ejabberd-auth
@ -126,42 +128,60 @@ fail2ban/tests/files/logs/freeswitch
fail2ban/tests/files/logs/groupoffice fail2ban/tests/files/logs/groupoffice
fail2ban/tests/files/logs/gssftpd fail2ban/tests/files/logs/gssftpd
fail2ban/tests/files/logs/guacamole fail2ban/tests/files/logs/guacamole
fail2ban/tests/files/logs/horde
fail2ban/tests/files/logs/kerio fail2ban/tests/files/logs/kerio
fail2ban/tests/files/logs/lighttpd-auth fail2ban/tests/files/logs/lighttpd-auth
fail2ban/tests/files/logs/monit
fail2ban/tests/files/logs/mysqld-auth fail2ban/tests/files/logs/mysqld-auth
fail2ban/tests/files/logs/nagios fail2ban/tests/files/logs/nagios
fail2ban/tests/files/logs/nsd
fail2ban/tests/files/logs/perdition
fail2ban/tests/files/logs/php-url-fopen
fail2ban/tests/files/logs/postfix-sasl
fail2ban/tests/files/logs/named-refused fail2ban/tests/files/logs/named-refused
fail2ban/tests/files/logs/nginx-http-auth fail2ban/tests/files/logs/nginx-http-auth
fail2ban/tests/files/logs/nsd
fail2ban/tests/files/logs/openwebmail
fail2ban/tests/files/logs/oracleims
fail2ban/tests/files/logs/pam-generic fail2ban/tests/files/logs/pam-generic
fail2ban/tests/files/logs/perdition
fail2ban/tests/files/logs/php-url-fopen
fail2ban/tests/files/logs/portsentry
fail2ban/tests/files/logs/postfix fail2ban/tests/files/logs/postfix
fail2ban/tests/files/logs/postfix-sasl
fail2ban/tests/files/logs/proftpd fail2ban/tests/files/logs/proftpd
fail2ban/tests/files/logs/pure-ftpd fail2ban/tests/files/logs/pure-ftpd
fail2ban/tests/files/logs/qmail fail2ban/tests/files/logs/qmail
fail2ban/tests/files/logs/recidive fail2ban/tests/files/logs/recidive
fail2ban/tests/files/logs/roundcube-auth fail2ban/tests/files/logs/roundcube-auth
fail2ban/tests/files/logs/selinux-ssh fail2ban/tests/files/logs/selinux-ssh
fail2ban/tests/files/logs/sendmail-auth
fail2ban/tests/files/logs/sendmail-reject
fail2ban/tests/files/logs/sendmail-spam fail2ban/tests/files/logs/sendmail-spam
fail2ban/tests/files/logs/sieve fail2ban/tests/files/logs/sieve
fail2ban/tests/files/logs/squid
fail2ban/tests/files/logs/stunnel
fail2ban/tests/files/logs/suhosin
fail2ban/tests/files/logs/sogo-auth fail2ban/tests/files/logs/sogo-auth
fail2ban/tests/files/logs/solid-pop3d fail2ban/tests/files/logs/solid-pop3d
fail2ban/tests/files/logs/squid
fail2ban/tests/files/logs/squirrelmail
fail2ban/tests/files/logs/sshd fail2ban/tests/files/logs/sshd
fail2ban/tests/files/logs/sshd-ddos fail2ban/tests/files/logs/sshd-ddos
fail2ban/tests/files/logs/stunnel
fail2ban/tests/files/logs/suhosin
fail2ban/tests/files/logs/tine20
fail2ban/tests/files/logs/uwimap-auth
fail2ban/tests/files/logs/vsftpd fail2ban/tests/files/logs/vsftpd
fail2ban/tests/files/logs/webmin-auth fail2ban/tests/files/logs/webmin-auth
fail2ban/tests/files/logs/wuftpd fail2ban/tests/files/logs/wuftpd
fail2ban/tests/files/logs/uwimap-auth
fail2ban/tests/files/logs/xinetd-fail fail2ban/tests/files/logs/xinetd-fail
fail2ban/tests/config/jail.conf fail2ban/tests/files/testcase-journal.log
fail2ban/tests/config/fail2ban.conf fail2ban/tests/files/testcase-multiline.log
fail2ban/tests/config/filter.d/simple.conf fail2ban/tests/files/testcase-usedns.log
fail2ban/tests/config/action.d/brokenaction.conf fail2ban/tests/files/testcase01.log
fail2ban/tests/files/testcase02.log
fail2ban/tests/files/testcase03.log
fail2ban/tests/files/testcase04.log
fail2ban/tests/filtertestcase.py
fail2ban/tests/misctestcase.py
fail2ban/tests/samplestestcase.py
fail2ban/tests/servertestcase.py
fail2ban/tests/sockettestcase.py
fail2ban/tests/utils.py
setup.py setup.py
setup.cfg setup.cfg
fail2ban/__init__.py fail2ban/__init__.py
@ -169,126 +189,136 @@ fail2ban/exceptions.py
fail2ban/helpers.py fail2ban/helpers.py
fail2ban/version.py fail2ban/version.py
fail2ban/protocol.py fail2ban/protocol.py
setup.py
setup.cfg
kill-server kill-server
config/jail.conf config/action.d/apf.conf
config/action.d/badips.conf
config/action.d/badips.py
config/action.d/blocklist_de.conf
config/action.d/bsd-ipfw.conf
config/action.d/cloudflare.conf
config/action.d/complain.conf
config/action.d/dshield.conf
config/action.d/dummy.conf
config/action.d/firewallcmd-ipset.conf
config/action.d/firewallcmd-new.conf
config/action.d/hostsdeny.conf
config/action.d/ipfilter.conf
config/action.d/ipfw.conf
config/action.d/iptables-allports.conf
config/action.d/iptables-common.conf
config/action.d/iptables-ipset-proto4.conf
config/action.d/iptables-ipset-proto6-allports.conf
config/action.d/iptables-ipset-proto6.conf
config/action.d/iptables-multiport-log.conf
config/action.d/iptables-multiport.conf
config/action.d/iptables-new.conf
config/action.d/iptables-xt_recent-echo.conf
config/action.d/iptables.conf
config/action.d/mail-buffered.conf
config/action.d/mail-whois-lines.conf
config/action.d/mail-whois.conf
config/action.d/mail.conf
config/action.d/mynetwatchman.conf
config/action.d/osx-afctl.conf
config/action.d/osx-ipfw.conf
config/action.d/pf.conf
config/action.d/route.conf
config/action.d/sendmail-buffered.conf
config/action.d/sendmail-common.conf
config/action.d/sendmail-whois-ipjailmatches.conf
config/action.d/sendmail-whois-ipmatches.conf
config/action.d/sendmail-whois-lines.conf
config/action.d/sendmail-whois-matches.conf
config/action.d/sendmail-whois.conf
config/action.d/sendmail.conf
config/action.d/shorewall.conf
config/action.d/smtp.py
config/action.d/symbiosis-blacklist-allports.conf
config/action.d/ufw.conf
config/action.d/xarf-login-attack.conf
config/fail2ban.conf config/fail2ban.conf
config/filter.d/common.conf config/filter.d/3proxy.conf
config/filter.d/apache-auth.conf config/filter.d/apache-auth.conf
config/filter.d/apache-badbots.conf config/filter.d/apache-badbots.conf
config/filter.d/apache-botsearch.conf config/filter.d/apache-botsearch.conf
config/filter.d/apache-common.conf
config/filter.d/apache-modsecurity.conf config/filter.d/apache-modsecurity.conf
config/filter.d/apache-nohome.conf config/filter.d/apache-nohome.conf
config/filter.d/apache-noscript.conf config/filter.d/apache-noscript.conf
config/filter.d/apache-overflows.conf config/filter.d/apache-overflows.conf
config/filter.d/nginx-http-auth.conf config/filter.d/apache-shellshock.conf
config/filter.d/assp.conf
config/filter.d/asterisk.conf
config/filter.d/common.conf
config/filter.d/counter-strike.conf config/filter.d/counter-strike.conf
config/filter.d/courier-auth.conf config/filter.d/courier-auth.conf
config/filter.d/courier-smtp.conf config/filter.d/courier-smtp.conf
config/filter.d/cyrus-imap.conf config/filter.d/cyrus-imap.conf
config/filter.d/directadmin.conf
config/filter.d/dovecot.conf
config/filter.d/dropbear.conf
config/filter.d/ejabberd-auth.conf config/filter.d/ejabberd-auth.conf
config/filter.d/exim-common.conf
config/filter.d/exim-spam.conf
config/filter.d/exim.conf config/filter.d/exim.conf
config/filter.d/freeswitch.conf config/filter.d/freeswitch.conf
config/filter.d/groupoffice.conf
config/filter.d/gssftpd.conf config/filter.d/gssftpd.conf
config/filter.d/kerio.conf config/filter.d/guacamole.conf
config/filter.d/horde.conf config/filter.d/horde.conf
config/filter.d/suhosin.conf config/filter.d/kerio.conf
config/filter.d/lighttpd-auth.conf
config/filter.d/monit.conf
config/filter.d/mysqld-auth.conf
config/filter.d/nagios.conf config/filter.d/nagios.conf
config/filter.d/named-refused.conf config/filter.d/named-refused.conf
config/filter.d/nginx-http-auth.conf
config/filter.d/nsd.conf config/filter.d/nsd.conf
config/filter.d/openwebmail.conf config/filter.d/openwebmail.conf
config/filter.d/oracleims.conf
config/filter.d/pam-generic.conf config/filter.d/pam-generic.conf
config/filter.d/pam-generic.conf
config/filter.d/pam-generic.conf
config/filter.d/perdition.conf
config/filter.d/php-url-fopen.conf config/filter.d/php-url-fopen.conf
config/filter.d/php-url-fopen.conf
config/filter.d/php-url-fopen.conf
config/filter.d/portsentry.conf
config/filter.d/postfix-sasl.conf
config/filter.d/postfix-sasl.conf config/filter.d/postfix-sasl.conf
config/filter.d/pam-generic.conf
config/filter.d/php-url-fopen.conf
config/filter.d/postfix-sasl.conf config/filter.d/postfix-sasl.conf
config/filter.d/postfix.conf config/filter.d/postfix.conf
config/filter.d/proftpd.conf config/filter.d/proftpd.conf
config/filter.d/pure-ftpd.conf config/filter.d/pure-ftpd.conf
config/filter.d/qmail.conf config/filter.d/qmail.conf
config/filter.d/pam-generic.conf config/filter.d/recidive.conf
config/filter.d/php-url-fopen.conf config/filter.d/roundcube-auth.conf
config/filter.d/postfix-sasl.conf config/filter.d/selinux-common.conf
config/filter.d/selinux-ssh.conf
config/filter.d/sendmail-auth.conf config/filter.d/sendmail-auth.conf
config/filter.d/sendmail-reject.conf config/filter.d/sendmail-reject.conf
config/filter.d/sendmail-spam.conf
config/filter.d/sieve.conf config/filter.d/sieve.conf
config/filter.d/sogo-auth.conf
config/filter.d/solid-pop3d.conf config/filter.d/solid-pop3d.conf
config/filter.d/squid.conf config/filter.d/squid.conf
config/filter.d/sshd.conf config/filter.d/squirrelmail.conf
config/filter.d/sshd-ddos.conf config/filter.d/sshd-ddos.conf
config/filter.d/sshd.conf
config/filter.d/stunnel.conf config/filter.d/stunnel.conf
config/filter.d/suhosin.conf
config/filter.d/tine20.conf
config/filter.d/uwimap-auth.conf
config/filter.d/vsftpd.conf config/filter.d/vsftpd.conf
config/filter.d/webmin-auth.conf config/filter.d/webmin-auth.conf
config/filter.d/wuftpd.conf config/filter.d/wuftpd.conf
config/filter.d/xinetd-fail.conf config/filter.d/xinetd-fail.conf
config/filter.d/asterisk.conf config/jail.conf
config/filter.d/dovecot.conf config/paths-common.conf
config/filter.d/dropbear.conf config/paths-debian.conf
config/filter.d/lighttpd-auth.conf config/paths-fedora.conf
config/filter.d/recidive.conf config/paths-freebsd.conf
config/filter.d/roundcube-auth.conf config/paths-osx.conf
config/filter.d/assp.conf
config/filter.d/sogo-auth.conf
config/filter.d/mysqld-auth.conf
config/filter.d/selinux-common.conf
config/filter.d/selinux-ssh.conf
config/filter.d/3proxy.conf
config/filter.d/apache-common.conf
config/filter.d/exim-common.conf
config/filter.d/exim-spam.conf
config/filter.d/freeswitch.conf
config/filter.d/groupoffice.conf
config/filter.d/perdition.conf
config/filter.d/uwimap-auth.conf
config/filter.d/courier-auth.conf
config/filter.d/courier-smtp.conf
config/filter.d/ejabberd-auth.conf
config/filter.d/guacamole.conf
config/filter.d/sendmail-spam.conf
config/action.d/apf.conf
config/action.d/blocklist_de.conf
config/action.d/osx-afctl.conf
config/action.d/osx-ipfw.conf
config/action.d/sendmail-common.conf
config/action.d/badips.conf
config/action.d/bsd-ipfw.conf
config/action.d/dummy.conf
config/action.d/firewallcmd-new.conf
config/action.d/firewallcmd-ipset.conf
config/action.d/iptables-ipset-proto6-allports.conf
config/action.d/iptables-common.conf
config/action.d/iptables-ipset-proto4.conf
config/action.d/iptables-ipset-proto6.conf
config/action.d/iptables-xt_recent-echo.conf
config/action.d/route.conf
config/action.d/complain.conf
config/action.d/dshield.conf
config/action.d/hostsdeny.conf
config/action.d/ipfw.conf
config/action.d/ipfilter.conf
config/action.d/iptables.conf
config/action.d/iptables-allports.conf
config/action.d/iptables-multiport.conf
config/action.d/iptables-multiport-log.conf
config/action.d/iptables-new.conf
config/action.d/mail.conf
config/action.d/mail-buffered.conf
config/action.d/mail-whois.conf
config/action.d/mail-whois-lines.conf
config/action.d/mynetwatchman.conf
config/action.d/pf.conf
config/action.d/sendmail.conf
config/action.d/sendmail-buffered.conf
config/action.d/sendmail-whois-ipmatches.conf
config/action.d/sendmail-whois.conf
config/action.d/sendmail-whois-lines.conf
config/action.d/shorewall.conf
config/action.d/xarf-login-attack.conf
config/action.d/ufw.conf
config/fail2ban.conf
doc/run-rootless.txt
man/fail2ban-client.1 man/fail2ban-client.1
man/fail2ban.1 man/fail2ban.1
man/jail.conf.5 man/jail.conf.5
@ -306,6 +336,8 @@ files/solaris-fail2ban.xml
files/solaris-svc-fail2ban files/solaris-svc-fail2ban
files/suse-initd files/suse-initd
files/fail2ban-logrotate files/fail2ban-logrotate
files/fail2ban.upstart
files/logwatch/fail2ban
files/cacti/fail2ban_stats.sh files/cacti/fail2ban_stats.sh
files/cacti/cacti_host_template_fail2ban.xml files/cacti/cacti_host_template_fail2ban.xml
files/cacti/README files/cacti/README

View File

@ -1,4 +1,4 @@
include ChangeLog COPYING DEVELOP FILTERS README.* THANKS TODO include ChangeLog COPYING DEVELOP FILTERS README.* THANKS TODO CONTRIBUTING* Vagrantfile
graft doc graft doc
graft files graft files
recursive-include config *.conf *.py recursive-include config *.conf *.py

View File

@ -2,7 +2,7 @@
/ _|__ _(_) |_ ) |__ __ _ _ _ / _|__ _(_) |_ ) |__ __ _ _ _
| _/ _` | | |/ /| '_ \/ _` | ' \ | _/ _` | | |/ /| '_ \/ _` | ' \
|_| \__,_|_|_/___|_.__/\__,_|_||_| |_| \__,_|_|_/___|_.__/\__,_|_||_|
v0.9.0 2014/03/14 v0.9.1 2014/10/29
## Fail2Ban: ban hosts that cause multiple authentication errors ## Fail2Ban: ban hosts that cause multiple authentication errors
@ -36,8 +36,8 @@ Optional:
To install, just do: To install, just do:
tar xvfj fail2ban-0.9.0.tar.bz2 tar xvfj fail2ban-0.9.1.tar.bz2
cd fail2ban-0.9.0 cd fail2ban-0.9.1
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

@ -47,6 +47,10 @@ Preparation
* Ensure the MANIFEST is complete * Ensure the MANIFEST is complete
ad-hoc bash script to run in a clean clone:
find -type f | grep -v -e '\.git' -e '/doc/' -e '\.travis' -e MANIFEST | sed -e 's,^\./,,g' | while read f; do grep -ne "^$f\$" MANIFEST >/dev/null || echo "$f" ; done
* Run:: * Run::
python setup.py sdist python setup.py sdist
@ -57,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.0.tar.bz2 tar -C /tmp -jxf dist/fail2ban-0.9.1.tar.bz2
* clean up current direcory:: * clean up current direcory::
diff -rul --exclude \*.pyc . /tmp/fail2ban-0.9.0/ diff -rul --exclude \*.pyc . /tmp/fail2ban-0.9.1/
* 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.0/ && export PYTHONPATH=`pwd` && bin/fail2ban-testcases cd /tmp/fail2ban-0.9.1/ && export PYTHONPATH=`pwd` && 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.8.12.. | sed -e 's,^[ 0-9\t]*,,g' | tr '\n' '\|' | sed -e 's:|:, :g' git shortlog -sn 0.9.1.. | 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.
@ -181,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.1 (2014/XX/XXX) - wanna-be-released ver. 0.9.2 (2014/XX/XXX) - wanna-be-released
----------- -----------
- Fixes: - Fixes:

1
THANKS
View File

@ -26,6 +26,7 @@ Christian Rauch
Christophe Carles Christophe Carles
Christoph Haas Christoph Haas
Christos Psonis Christos Psonis
craneworks
Cyril Jaquier Cyril Jaquier
Daniel B. Cid Daniel B. Cid
Daniel B. Daniel B.

View File

@ -409,6 +409,7 @@ class Fail2banClient:
# TODO: get away from stew of return codes and exception # TODO: get away from stew of return codes and exception
# handling -- handle via exceptions # handling -- handle via exceptions
try: try:
self.__configurator.Reload()
self.__configurator.readAll() self.__configurator.readAll()
ret = self.__configurator.getOptions(jail) ret = self.__configurator.getOptions(jail)
self.__configurator.convertToProtocol() self.__configurator.convertToProtocol()
@ -418,12 +419,11 @@ class Fail2banClient:
ret = False ret = False
return ret return ret
#@staticmethod @staticmethod
def dumpConfig(cmd): def dumpConfig(cmd):
for c in cmd: for c in cmd:
print c print c
return True return True
dumpConfig = staticmethod(dumpConfig)
class ServerExecutionException(Exception): class ServerExecutionException(Exception):

View File

@ -0,0 +1,10 @@
# Fail2Ban filter for failure attempts in Counter Strike-1.6
#
#
[Definition]
failregex = \/<HOST> Port\: [0-9]+ (TCP|UDP) Blocked$
# Author: Pacop <pacoparu@gmail.com>

View File

@ -719,3 +719,8 @@ banaction = iptables-allports
enabled = false enabled = false
logpath = /var/log/directadmin/login.log logpath = /var/log/directadmin/login.log
port = 2222 port = 2222
[portsentry]
enabled = false
logpath = /var/lib/portsentry/portsentry.history
maxretry = 1

View File

@ -26,7 +26,7 @@ __license__ = "GPL"
import os import os
from .configreader import ConfigReader, DefinitionInitConfigReader from .configreader import DefinitionInitConfigReader
from ..helpers import getLogger from ..helpers import getLogger
# Gets the instance of the logger. # Gets the instance of the logger.
@ -47,15 +47,19 @@ class ActionReader(DefinitionInitConfigReader):
DefinitionInitConfigReader.__init__( DefinitionInitConfigReader.__init__(
self, file_, jailName, initOpts, **kwargs) self, file_, jailName, initOpts, **kwargs)
def setFile(self, fileName):
self.__file = fileName
DefinitionInitConfigReader.setFile(self, os.path.join("action.d", fileName))
def getFile(self):
return self.__file
def setName(self, name): def setName(self, name):
self._name = name self._name = name
def getName(self): def getName(self):
return self._name return self._name
def read(self):
return ConfigReader.read(self, os.path.join("action.d", self._file))
def convert(self): def convert(self):
head = ["set", self._jailName] head = ["set", self._jailName]
stream = list() stream = list()

View File

@ -62,6 +62,7 @@ else: # pragma: no cover
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = getLogger(__name__) logSys = getLogger(__name__)
logLevel = 7
__all__ = ['SafeConfigParserWithIncludes'] __all__ = ['SafeConfigParserWithIncludes']
@ -98,30 +99,73 @@ after = 1.conf
if sys.version_info >= (3,2): if sys.version_info >= (3,2):
# overload constructor only for fancy new Python3's # overload constructor only for fancy new Python3's
def __init__(self, *args, **kwargs): def __init__(self, share_config=None, *args, **kwargs):
kwargs = kwargs.copy() kwargs = kwargs.copy()
kwargs['interpolation'] = BasicInterpolationWithName() kwargs['interpolation'] = BasicInterpolationWithName()
kwargs['inline_comment_prefixes'] = ";" kwargs['inline_comment_prefixes'] = ";"
super(SafeConfigParserWithIncludes, self).__init__( super(SafeConfigParserWithIncludes, self).__init__(
*args, **kwargs) *args, **kwargs)
self._cfg_share = share_config
#@staticmethod else:
def getIncludes(resource, seen = []): def __init__(self, share_config=None, *args, **kwargs):
SafeConfigParser.__init__(self, *args, **kwargs)
self._cfg_share = share_config
@property
def share_config(self):
return self._cfg_share
def _getSharedSCPWI(self, filename):
SCPWI = SafeConfigParserWithIncludes
# read single one, add to return list, use sharing if possible:
if self._cfg_share:
# cache/share each file as include (ex: filter.d/common could be included in each filter config):
hashv = 'inc:'+(filename if not isinstance(filename, list) else '\x01'.join(filename))
cfg, i = self._cfg_share.get(hashv, (None, None))
if cfg is None:
cfg = SCPWI(share_config=self._cfg_share)
i = cfg.read(filename, get_includes=False)
self._cfg_share[hashv] = (cfg, i)
elif logSys.getEffectiveLevel() <= logLevel:
logSys.log(logLevel, " Shared file: %s", filename)
else:
# don't have sharing:
cfg = SCPWI()
i = cfg.read(filename, get_includes=False)
return (cfg, i)
def _getIncludes(self, filenames, seen=[]):
if not isinstance(filenames, list):
filenames = [ filenames ]
# retrieve or cache include paths:
if self._cfg_share:
# cache/share include list:
hashv = 'inc-path:'+('\x01'.join(filenames))
fileNamesFull = self._cfg_share.get(hashv)
if fileNamesFull is None:
fileNamesFull = []
for filename in filenames:
fileNamesFull += self.__getIncludesUncached(filename, seen)
self._cfg_share[hashv] = fileNamesFull
return fileNamesFull
# don't have sharing:
fileNamesFull = []
for filename in filenames:
fileNamesFull += self.__getIncludesUncached(filename, seen)
return fileNamesFull
def __getIncludesUncached(self, resource, seen=[]):
""" """
Given 1 config resource returns list of included files Given 1 config resource returns list of included files
(recursively) with the original one as well (recursively) with the original one as well
Simple loops are taken care about Simple loops are taken care about
""" """
# Use a short class name ;)
SCPWI = SafeConfigParserWithIncludes SCPWI = SafeConfigParserWithIncludes
parser = SafeConfigParser()
try: try:
if sys.version_info >= (3,2): # pragma: no cover parser, i = self._getSharedSCPWI(resource)
parser.read(resource, encoding='utf-8') if not i:
else: return []
parser.read(resource)
except UnicodeDecodeError, e: except UnicodeDecodeError, e:
logSys.error("Error decoding config file '%s': %s" % (resource, e)) logSys.error("Error decoding config file '%s': %s" % (resource, e))
return [] return []
@ -141,22 +185,60 @@ after = 1.conf
if r in seen: if r in seen:
continue continue
s = seen + [resource] s = seen + [resource]
option_list += SCPWI.getIncludes(r, s) option_list += self._getIncludes(r, s)
# combine lists # combine lists
return newFiles[0][1] + [resource] + newFiles[1][1] return newFiles[0][1] + [resource] + newFiles[1][1]
#print "Includes list for " + resource + " is " + `resources`
getIncludes = staticmethod(getIncludes)
def get_defaults(self):
return self._defaults
def read(self, filenames): def get_sections(self):
fileNamesFull = [] return self._sections
def read(self, filenames, get_includes=True):
if not isinstance(filenames, list): if not isinstance(filenames, list):
filenames = [ filenames ] filenames = [ filenames ]
for filename in filenames: # retrieve (and cache) includes:
fileNamesFull += SafeConfigParserWithIncludes.getIncludes(filename) fileNamesFull = []
logSys.debug("Reading files: %s" % fileNamesFull) if get_includes:
fileNamesFull += self._getIncludes(filenames)
else:
fileNamesFull = filenames
if not fileNamesFull:
return []
logSys.info(" Loading files: %s", fileNamesFull)
if get_includes or len(fileNamesFull) > 1:
# read multiple configs:
ret = []
alld = self.get_defaults()
alls = self.get_sections()
for filename in fileNamesFull:
# read single one, add to return list, use sharing if possible:
cfg, i = self._getSharedSCPWI(filename)
if i:
ret += i
# merge defaults and all sections to self:
alld.update(cfg.get_defaults())
for n, s in cfg.get_sections().iteritems():
if isinstance(s, dict):
s2 = alls.get(n)
if isinstance(s2, dict):
s2.update(s)
else:
alls[n] = s.copy()
else:
alls[n] = s
return ret
# read one config :
if logSys.getEffectiveLevel() <= logLevel:
logSys.log(logLevel, " Reading file: %s", fileNamesFull[0])
# read file(s) :
if sys.version_info >= (3,2): # pragma: no cover if sys.version_info >= (3,2): # pragma: no cover
return SafeConfigParser.read(self, fileNamesFull, encoding='utf-8') return SafeConfigParser.read(self, fileNamesFull, encoding='utf-8')
else: else:
return SafeConfigParser.read(self, fileNamesFull) return SafeConfigParser.read(self, fileNamesFull)

View File

@ -27,24 +27,127 @@ __license__ = "GPL"
import glob, os import glob, os
from ConfigParser import NoOptionError, NoSectionError from ConfigParser import NoOptionError, NoSectionError
from .configparserinc import SafeConfigParserWithIncludes from .configparserinc import SafeConfigParserWithIncludes, logLevel
from ..helpers import getLogger from ..helpers import getLogger
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = getLogger(__name__) logSys = getLogger(__name__)
class ConfigReader(SafeConfigParserWithIncludes): class ConfigReader():
"""Generic config reader class.
A caching adapter which automatically reuses already shared configuration.
"""
def __init__(self, use_config=None, share_config=None, **kwargs):
# use given shared config if possible (see read):
self._cfg_share = None
self._cfg = None
if use_config is not None:
self._cfg = use_config
# share config if possible:
if share_config is not None:
self._cfg_share = share_config
self._cfg_share_kwargs = kwargs
self._cfg_share_basedir = None
elif self._cfg is None:
self._cfg = ConfigReaderUnshared(**kwargs)
def setBaseDir(self, basedir):
if self._cfg:
self._cfg.setBaseDir(basedir)
else:
self._cfg_share_basedir = basedir
def getBaseDir(self):
if self._cfg:
return self._cfg.getBaseDir()
else:
return self._cfg_share_basedir
@property
def share_config(self):
return self._cfg_share
def read(self, name, once=True):
""" Overloads a default (not shared) read of config reader.
To prevent mutiple reads of config files with it includes, reads into
the config reader, if it was not yet cached/shared by 'name'.
"""
# already shared ?
if not self._cfg:
self._create_unshared(name)
# performance feature - read once if using shared config reader:
if once and self._cfg.read_cfg_files is not None:
return self._cfg.read_cfg_files
# load:
logSys.info("Loading configs for %s under %s ", name, self._cfg.getBaseDir())
ret = self._cfg.read(name)
# save already read and return:
self._cfg.read_cfg_files = ret
return ret
def _create_unshared(self, name=''):
""" Allocates and share a config file by it name.
Automatically allocates unshared or reuses shared handle by given 'name' and
init arguments inside a given shared storage.
"""
if not self._cfg and self._cfg_share is not None:
self._cfg = self._cfg_share.get(name)
if not self._cfg:
self._cfg = ConfigReaderUnshared(share_config=self._cfg_share, **self._cfg_share_kwargs)
if self._cfg_share_basedir is not None:
self._cfg.setBaseDir(self._cfg_share_basedir)
self._cfg_share[name] = self._cfg
else:
self._cfg = ConfigReaderUnshared(**self._cfg_share_kwargs)
def sections(self):
if self._cfg is not None:
return self._cfg.sections()
return []
def has_section(self, sec):
if self._cfg is not None:
return self._cfg.has_section(sec)
return False
def options(self, *args):
if self._cfg is not None:
return self._cfg.options(*args)
return {}
def get(self, sec, opt):
if self._cfg is not None:
return self._cfg.get(sec, opt)
return None
def getOptions(self, *args, **kwargs):
if self._cfg is not None:
return self._cfg.getOptions(*args, **kwargs)
return {}
class ConfigReaderUnshared(SafeConfigParserWithIncludes):
"""Unshared config reader (previously ConfigReader).
Do not use this class (internal not shared/cached represenation).
Use ConfigReader instead.
"""
DEFAULT_BASEDIR = '/etc/fail2ban' DEFAULT_BASEDIR = '/etc/fail2ban'
def __init__(self, basedir=None): def __init__(self, basedir=None, *args, **kwargs):
SafeConfigParserWithIncludes.__init__(self) SafeConfigParserWithIncludes.__init__(self, *args, **kwargs)
self.read_cfg_files = None
self.setBaseDir(basedir) self.setBaseDir(basedir)
self.__opts = None
def setBaseDir(self, basedir): def setBaseDir(self, basedir):
if basedir is None: if basedir is None:
basedir = ConfigReader.DEFAULT_BASEDIR # stock system location basedir = ConfigReaderUnshared.DEFAULT_BASEDIR # stock system location
self._basedir = basedir.rstrip('/') self._basedir = basedir.rstrip('/')
def getBaseDir(self): def getBaseDir(self):
@ -55,7 +158,7 @@ class ConfigReader(SafeConfigParserWithIncludes):
raise ValueError("Base configuration directory %s does not exist " raise ValueError("Base configuration directory %s does not exist "
% self._basedir) % self._basedir)
basename = os.path.join(self._basedir, filename) basename = os.path.join(self._basedir, filename)
logSys.info("Reading configs for %s under %s " % (basename, self._basedir)) logSys.debug("Reading configs for %s under %s " , filename, self._basedir)
config_files = [ basename + ".conf" ] config_files = [ basename + ".conf" ]
# possible further customizations under a .conf.d directory # possible further customizations under a .conf.d directory
@ -71,14 +174,14 @@ class ConfigReader(SafeConfigParserWithIncludes):
if len(config_files): if len(config_files):
# at least one config exists and accessible # at least one config exists and accessible
logSys.debug("Reading config files: " + ', '.join(config_files)) logSys.debug("Reading config files: %s", ', '.join(config_files))
config_files_read = SafeConfigParserWithIncludes.read(self, config_files) config_files_read = SafeConfigParserWithIncludes.read(self, config_files)
missed = [ cf for cf in config_files if cf not in config_files_read ] missed = [ cf for cf in config_files if cf not in config_files_read ]
if missed: if missed:
logSys.error("Could not read config files: " + ', '.join(missed)) logSys.error("Could not read config files: %s", ', '.join(missed))
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:
@ -98,7 +201,7 @@ class ConfigReader(SafeConfigParserWithIncludes):
# 1 -> the name of the option # 1 -> the name of the option
# 2 -> the default value for the option # 2 -> the default value for the option
def getOptions(self, sec, options, pOptions = None): def getOptions(self, sec, options, pOptions=None):
values = dict() values = dict()
for option in options: for option in options:
try: try:
@ -121,10 +224,8 @@ class ConfigReader(SafeConfigParserWithIncludes):
logSys.warning("'%s' not defined in '%s'. Using default one: %r" logSys.warning("'%s' not defined in '%s'. Using default one: %r"
% (option[1], sec, option[2])) % (option[1], sec, option[2]))
values[option[1]] = option[2] values[option[1]] = option[2]
else: elif logSys.getEffectiveLevel() <= logLevel:
logSys.debug( logSys.log(logLevel, "Non essential option '%s' not defined in '%s'.", option[1], sec)
"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: '" + `option[2]` + "'")
@ -133,12 +234,12 @@ class ConfigReader(SafeConfigParserWithIncludes):
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.
Is a base class for readers of filters and actions, where definitions Is a base class for readers of filters and actions, where definitions
in jails might provide custom values for options defined in [Init] in jails might provide custom values for options defined in [Init]
section. section.
""" """
_configOpts = [] _configOpts = []
@ -166,7 +267,9 @@ class DefinitionInitConfigReader(ConfigReader):
# needed for fail2ban-regex that doesn't need fancy directories # needed for fail2ban-regex that doesn't need fancy directories
def readexplicit(self): def readexplicit(self):
return SafeConfigParserWithIncludes.read(self, self._file) if not self._cfg:
self._create_unshared(self._file)
return SafeConfigParserWithIncludes.read(self._cfg, self._file)
def getOptions(self, pOpts): def getOptions(self, pOpts):
self._opts = ConfigReader.getOptions( self._opts = ConfigReader.getOptions(

View File

@ -33,12 +33,20 @@ logSys = getLogger(__name__)
class Configurator: class Configurator:
def __init__(self): def __init__(self, force_enable=False, share_config=None):
self.__settings = dict() self.__settings = dict()
self.__streams = dict() self.__streams = dict()
self.__fail2ban = Fail2banReader() # always share all config readers:
self.__jails = JailsReader() if share_config is None:
share_config = dict()
self.__share_config = share_config
self.__fail2ban = Fail2banReader(share_config=share_config)
self.__jails = JailsReader(force_enable=force_enable, share_config=share_config)
def Reload(self):
# clear all shared handlers:
self.__share_config.clear()
def setBaseDir(self, folderName): def setBaseDir(self, folderName):
self.__fail2ban.setBaseDir(folderName) self.__fail2ban.setBaseDir(folderName)
self.__jails.setBaseDir(folderName) self.__jails.setBaseDir(folderName)

View File

@ -57,7 +57,7 @@ class CSocket:
self.__csock.close() self.__csock.close()
return ret return ret
#@staticmethod @staticmethod
def receive(sock): def receive(sock):
msg = EMPTY_BYTES msg = EMPTY_BYTES
while msg.rfind(CSocket.END_STRING) == -1: while msg.rfind(CSocket.END_STRING) == -1:
@ -66,4 +66,3 @@ class CSocket:
raise RuntimeError, "socket connection broken" raise RuntimeError, "socket connection broken"
msg = msg + chunk msg = msg + chunk
return loads(msg) return loads(msg)
receive = staticmethod(receive)

View File

@ -26,7 +26,7 @@ __license__ = "GPL"
import os, shlex import os, shlex
from .configreader import ConfigReader, DefinitionInitConfigReader from .configreader import DefinitionInitConfigReader
from ..server.action import CommandAction from ..server.action import CommandAction
from ..helpers import getLogger from ..helpers import getLogger
@ -40,8 +40,12 @@ class FilterReader(DefinitionInitConfigReader):
["string", "failregex", ""], ["string", "failregex", ""],
] ]
def read(self): def setFile(self, fileName):
return ConfigReader.read(self, os.path.join("filter.d", self._file)) self.__file = fileName
DefinitionInitConfigReader.setFile(self, os.path.join("filter.d", fileName))
def getFile(self):
return self.__file
def convert(self): def convert(self):
stream = list() stream = list()

View File

@ -27,7 +27,7 @@ __license__ = "GPL"
import re, glob, os.path import re, glob, os.path
import json import json
from .configreader import ConfigReader from .configreader import ConfigReaderUnshared, ConfigReader
from .filterreader import FilterReader from .filterreader import FilterReader
from .actionreader import ActionReader from .actionreader import ActionReader
from ..helpers import getLogger from ..helpers import getLogger
@ -111,7 +111,7 @@ class JailReader(ConfigReader):
filterName, filterOpt = JailReader.extractOptions( filterName, filterOpt = JailReader.extractOptions(
self.__opts["filter"]) self.__opts["filter"])
self.__filter = FilterReader( self.__filter = FilterReader(
filterName, self.__name, filterOpt, basedir=self.getBaseDir()) filterName, self.__name, filterOpt, share_config=self.share_config, basedir=self.getBaseDir())
ret = self.__filter.read() ret = self.__filter.read()
if ret: if ret:
self.__filter.getOptions(self.__opts) self.__filter.getOptions(self.__opts)
@ -141,7 +141,7 @@ class JailReader(ConfigReader):
else: else:
action = ActionReader( action = ActionReader(
actName, self.__name, actOpt, actName, self.__name, actOpt,
basedir=self.getBaseDir()) share_config=self.share_config, basedir=self.getBaseDir())
ret = action.read() ret = action.read()
if ret: if ret:
action.getOptions(self.__opts) action.getOptions(self.__opts)
@ -213,14 +213,14 @@ class JailReader(ConfigReader):
if self.__filter: if self.__filter:
stream.extend(self.__filter.convert()) stream.extend(self.__filter.convert())
for action in self.__actions: for action in self.__actions:
if isinstance(action, ConfigReader): if isinstance(action, (ConfigReaderUnshared, ConfigReader)):
stream.extend(action.convert()) stream.extend(action.convert())
else: else:
stream.append(action) stream.append(action)
stream.insert(0, ["add", self.__name, backend]) stream.insert(0, ["add", self.__name, backend])
return stream return stream
#@staticmethod @staticmethod
def extractOptions(option): def extractOptions(option):
match = JailReader.optionCRE.match(option) match = JailReader.optionCRE.match(option)
if not match: if not match:
@ -235,4 +235,3 @@ class JailReader(ConfigReader):
val for val in optmatch.group(2,3,4) if val is not None][0] val for val in optmatch.group(2,3,4) if val is not None][0]
option_opts[opt.strip()] = value.strip() option_opts[opt.strip()] = value.strip()
return option_name, option_opts return option_name, option_opts
extractOptions = staticmethod(extractOptions)

View File

@ -68,9 +68,10 @@ class JailsReader(ConfigReader):
for sec in sections: for sec in sections:
if sec == 'INCLUDES': if sec == 'INCLUDES':
continue continue
jail = JailReader(sec, basedir=self.getBaseDir(), # use the cfg_share for filter/action caching and the same config for all
force_enable=self.__force_enable) # jails (use_config=...), therefore don't read it here:
jail.read() jail = JailReader(sec, force_enable=self.__force_enable,
share_config=self.share_config, use_config=self._cfg)
ret = jail.getOptions() ret = jail.getOptions()
if ret: if ret:
if jail.isEnabled(): if jail.isEnabled():

View File

@ -149,12 +149,8 @@ class AsyncServer(asyncore.dispatcher):
self.__init = True self.__init = True
# TODO Add try..catch # TODO Add try..catch
# There's a bug report for Python 2.6/3.0 that use_poll=True yields some 2.5 incompatibilities: # There's a bug report for Python 2.6/3.0 that use_poll=True yields some 2.5 incompatibilities:
if sys.version_info >= (2, 6): # if python 2.6 or greater... logSys.debug("Detected Python 2.6 or greater. asyncore.loop() not using poll")
logSys.debug("Detected Python 2.6 or greater. asyncore.loop() not using poll") asyncore.loop(use_poll=False) # fixes the "Unexpected communication problem" issue on Python 2.6 and 3.0
asyncore.loop(use_poll = False) # fixes the "Unexpected communication problem" issue on Python 2.6 and 3.0
else: # pragma: no cover
logSys.debug("NOT Python 2.6/3.* - asyncore.loop() using poll")
asyncore.loop(use_poll = True)
## ##
# Stops the communication server. # Stops the communication server.
@ -175,12 +171,11 @@ class AsyncServer(asyncore.dispatcher):
# @param sock: socket file. # @param sock: socket file.
#@staticmethod @staticmethod
def __markCloseOnExec(sock): def __markCloseOnExec(sock):
fd = sock.fileno() fd = sock.fileno()
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)
__markCloseOnExec = staticmethod(__markCloseOnExec)
## ##
# AsyncServerException is used to wrap communication exceptions. # AsyncServerException is used to wrap communication exceptions.

View File

@ -126,7 +126,7 @@ class BanManager:
# @param ticket the FailTicket # @param ticket the FailTicket
# @return a BanTicket # @return a BanTicket
#@staticmethod @staticmethod
def createBanTicket(ticket): def createBanTicket(ticket):
ip = ticket.getIP() ip = ticket.getIP()
#lastTime = ticket.getTime() #lastTime = ticket.getTime()
@ -134,7 +134,6 @@ class BanManager:
banTicket = BanTicket(ip, lastTime, ticket.getMatches()) banTicket = BanTicket(ip, lastTime, ticket.getMatches())
banTicket.setAttempt(ticket.getAttempt()) banTicket.setAttempt(ticket.getAttempt())
return banTicket return banTicket
createBanTicket = staticmethod(createBanTicket)
## ##
# Add a ban ticket. # Add a ban ticket.

View File

@ -838,7 +838,7 @@ class DNSUtils:
IP_CRE = re.compile("^(?:\d{1,3}\.){3}\d{1,3}$") IP_CRE = re.compile("^(?:\d{1,3}\.){3}\d{1,3}$")
#@staticmethod @staticmethod
def dnsToIp(dns): def dnsToIp(dns):
""" Convert a DNS into an IP address using the Python socket module. """ Convert a DNS into an IP address using the Python socket module.
Thanks to Kevin Drapel. Thanks to Kevin Drapel.
@ -853,9 +853,8 @@ class DNSUtils:
logSys.warning("Socket error raised trying to resolve hostname %s: %s" logSys.warning("Socket error raised trying to resolve hostname %s: %s"
% (dns, e)) % (dns, e))
return list() return list()
dnsToIp = staticmethod(dnsToIp)
#@staticmethod @staticmethod
def searchIP(text): def searchIP(text):
""" Search if an IP address if directly available and return """ Search if an IP address if directly available and return
it. it.
@ -865,9 +864,8 @@ class DNSUtils:
return match return match
else: else:
return None return None
searchIP = staticmethod(searchIP)
#@staticmethod @staticmethod
def isValidIP(string): def isValidIP(string):
""" Return true if str is a valid IP """ Return true if str is a valid IP
""" """
@ -877,9 +875,8 @@ class DNSUtils:
return True return True
except socket.error: except socket.error:
return False return False
isValidIP = staticmethod(isValidIP)
#@staticmethod @staticmethod
def textToIp(text, useDns): def textToIp(text, useDns):
""" Return the IP of DNS found in a given text. """ Return the IP of DNS found in a given text.
""" """
@ -901,9 +898,8 @@ class DNSUtils:
text, ipList) text, ipList)
return ipList return ipList
textToIp = staticmethod(textToIp)
#@staticmethod @staticmethod
def cidr(i, n): def cidr(i, n):
""" Convert an IP address string with a CIDR mask into a 32-bit """ Convert an IP address string with a CIDR mask into a 32-bit
integer. integer.
@ -911,18 +907,15 @@ class DNSUtils:
# 32-bit IPv4 address mask # 32-bit IPv4 address mask
MASK = 0xFFFFFFFFL MASK = 0xFFFFFFFFL
return ~(MASK >> n) & MASK & DNSUtils.addr2bin(i) return ~(MASK >> n) & MASK & DNSUtils.addr2bin(i)
cidr = staticmethod(cidr)
#@staticmethod @staticmethod
def addr2bin(string): def addr2bin(string):
""" Convert a string IPv4 address into an unsigned integer. """ Convert a string IPv4 address into an unsigned integer.
""" """
return struct.unpack("!L", socket.inet_aton(string))[0] return struct.unpack("!L", socket.inet_aton(string))[0]
addr2bin = staticmethod(addr2bin)
#@staticmethod @staticmethod
def bin2addr(addr): def bin2addr(addr):
""" Convert a numeric IPv4 address into string n.n.n.n form. """ Convert a numeric IPv4 address into string n.n.n.n form.
""" """
return socket.inet_ntoa(struct.pack("!L", addr)) return socket.inet_ntoa(struct.pack("!L", addr))
bin2addr = staticmethod(bin2addr)

View File

@ -26,65 +26,71 @@ import time, datetime
## ##
# MyTime class. # MyTime class.
# #
# This class is a wrapper around time.time() and time.gmtime(). When
# performing unit test, it is very useful to get a fixed value from these
# functions.
# Thus, time.time() and time.gmtime() should never be called directly.
# This wrapper should be called instead. The API are equivalent.
class MyTime: class MyTime:
"""A wrapper around time module primarily for testing purposes
This class is a wrapper around time.time() and time.gmtime(). When
performing unit test, it is very useful to get a fixed value from
these functions. Thus, time.time() and time.gmtime() should never
be called directly. This wrapper should be called instead. The API
are equivalent.
"""
myTime = None myTime = None
## @staticmethod
# Sets the current time.
#
# Use None in order to always get the real current time.
#
# @param t the time to set or None
#@staticmethod
def setTime(t): def setTime(t):
"""Set current time.
Use None in order to always get the real current time.
@param t the time to set or None
"""
MyTime.myTime = t MyTime.myTime = t
setTime = staticmethod(setTime)
@staticmethod
##
# Equivalent to time.time()
#
# @return time.time() if setTime was called with None
#@staticmethod
def time(): def time():
"""Decorate time.time() for the purpose of testing mocking
@return time.time() if setTime was called with None
"""
if MyTime.myTime is None: if MyTime.myTime is None:
return time.time() return time.time()
else: else:
return MyTime.myTime return MyTime.myTime
time = staticmethod(time)
@staticmethod
##
# Equivalent to time.gmtime()
#
# @return time.gmtime() if setTime was called with None
#@staticmethod
def gmtime(): def gmtime():
"""Decorate time.gmtime() for the purpose of testing mocking
@return time.gmtime() if setTime was called with None
"""
if MyTime.myTime is None: if MyTime.myTime is None:
return time.gmtime() return time.gmtime()
else: else:
return time.gmtime(MyTime.myTime) return time.gmtime(MyTime.myTime)
gmtime = staticmethod(gmtime)
#@staticmethod @staticmethod
def now(): def now():
"""Decorate datetime.now() for the purpose of testing mocking
@return datetime.now() if setTime was called with None
"""
if MyTime.myTime is None: if MyTime.myTime is None:
return datetime.datetime.now() return datetime.datetime.now()
else: else:
return datetime.datetime.fromtimestamp(MyTime.myTime) return datetime.datetime.fromtimestamp(MyTime.myTime)
now = staticmethod(now)
@staticmethod
def localtime(x=None): def localtime(x=None):
"""Decorate time.localtime() for the purpose of testing mocking
@return time.localtime() if setTime was called with None
"""
if MyTime.myTime is None or x is not None: if MyTime.myTime is None or x is not None:
return time.localtime(x) return time.localtime(x)
else: else:
return time.localtime(MyTime.myTime) return time.localtime(MyTime.myTime)
localtime = staticmethod(localtime)

View File

@ -186,8 +186,10 @@ class CommandActionTest(LogCaptureTestCase):
# Should take a minute # Should take a minute
self.assertRaises( self.assertRaises(
RuntimeError, CommandAction.executeCmd, 'sleep 60', timeout=2) RuntimeError, CommandAction.executeCmd, 'sleep 60', timeout=2)
self.assertAlmostEqual(time.time() - stime, 2, places=0) # give a test still 1 second, because system could be too busy
self.assertTrue(self._is_logged('sleep 60 -- timed out after 2 seconds')) self.assertTrue(time.time() >= stime + 2 and time.time() <= stime + 3)
self.assertTrue(self._is_logged('sleep 60 -- timed out after 2 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 testCaptureStdOutErr(self): def testCaptureStdOutErr(self):

View File

@ -21,9 +21,9 @@ __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 import os, glob, shutil, tempfile, unittest, re, logging
from ..client.configreader import ConfigReaderUnshared
from ..client.configreader import ConfigReader from ..client import configparserinc
from ..client.jailreader import JailReader from ..client.jailreader import JailReader
from ..client.filterreader import FilterReader from ..client.filterreader import FilterReader
from ..client.jailsreader import JailsReader from ..client.jailsreader import JailsReader
@ -44,7 +44,7 @@ class ConfigReaderTest(unittest.TestCase):
def setUp(self): def setUp(self):
"""Call before every test case.""" """Call before every test case."""
self.d = tempfile.mkdtemp(prefix="f2b-temp") self.d = tempfile.mkdtemp(prefix="f2b-temp")
self.c = ConfigReader(basedir=self.d) self.c = ConfigReaderUnshared(basedir=self.d)
def tearDown(self): def tearDown(self):
"""Call after every test case.""" """Call after every test case."""
@ -334,6 +334,80 @@ class FilterReaderTest(unittest.TestCase):
filterReader.getOptions(None) filterReader.getOptions(None)
self.assertRaises(ValueError, FilterReader.convert, filterReader) self.assertRaises(ValueError, FilterReader.convert, filterReader)
def testFilterReaderExplicit(self):
# read explicit uses absolute path:
path_ = os.path.abspath(os.path.join(TEST_FILES_DIR, "filter.d"))
filterReader = FilterReader(os.path.join(path_, "testcase01.conf"), "testcase01", {})
self.assertEqual(filterReader.readexplicit(),
[os.path.join(path_, "testcase-common.conf"), os.path.join(path_, "testcase01.conf")]
)
try:
filterReader.getOptions(None)
# from included common
filterReader.get('Definition', '__prefix_line')
# from testcase01
filterReader.get('Definition', 'failregex')
filterReader.get('Definition', 'ignoreregex')
except Exception, e: # pragma: no cover - failed if reachable
self.fail('unexpected options after readexplicit: %s' % (e))
class JailsReaderTestCache(LogCaptureTestCase):
def _readWholeConf(self, basedir, force_enable=False, share_config=None):
# read whole configuration like a file2ban-client ...
configurator = Configurator(force_enable=force_enable, share_config=share_config)
configurator.setBaseDir(basedir)
configurator.readEarly()
configurator.getEarlyOptions()
configurator.readAll()
# from here we test a cache with all includes / before / after :
self.assertTrue(configurator.getOptions(None))
def _getLoggedReadCount(self, filematch):
cnt = 0
for s in self.getLog().rsplit('\n'):
if re.match(r"^\s*Reading files?: .*/"+filematch, s):
cnt += 1
return cnt
def testTestJailConfCache(self):
saved_ll = configparserinc.logLevel
configparserinc.logLevel = logging.DEBUG
basedir = tempfile.mkdtemp("fail2ban_conf")
try:
shutil.rmtree(basedir)
shutil.copytree(CONFIG_DIR, basedir)
shutil.copy(CONFIG_DIR + '/jail.conf', basedir + '/jail.local')
shutil.copy(CONFIG_DIR + '/fail2ban.conf', basedir + '/fail2ban.local')
# common sharing handle for this test:
share_cfg = dict()
# read whole configuration like a file2ban-client ...
self._readWholeConf(basedir, share_config=share_cfg)
# how many times jail.local was read:
cnt = self._getLoggedReadCount('jail.local')
# if cnt > 1:
# self.printLog()
self.assertTrue(cnt == 1, "Unexpected count by reading of jail files, cnt = %s" % cnt)
# read whole configuration like a file2ban-client, again ...
# but this time force enable all jails, to check filter and action cached also:
self._readWholeConf(basedir, force_enable=True, share_config=share_cfg)
cnt = self._getLoggedReadCount(r'jail\.local')
# still one (no more reads):
self.assertTrue(cnt == 1, "Unexpected count by second reading of jail files, cnt = %s" % cnt)
# same with filter:
cnt = self._getLoggedReadCount(r'filter\.d/common\.conf')
self.assertTrue(cnt == 1, "Unexpected count by reading of filter files, cnt = %s" % cnt)
# same with action:
cnt = self._getLoggedReadCount(r'action\.d/iptables-common\.conf')
self.assertTrue(cnt == 1, "Unexpected count by reading of action files, cnt = %s" % cnt)
finally:
shutil.rmtree(basedir)
configparserinc.logLevel = saved_ll
class JailsReaderTest(LogCaptureTestCase): class JailsReaderTest(LogCaptureTestCase):

View File

@ -86,6 +86,7 @@ class DateDetectorTest(unittest.TestCase):
(False, "23-Jan-2005 21:59:59.02"), (False, "23-Jan-2005 21:59:59.02"),
(False, "23-Jan-2005 21:59:59 +0100"), (False, "23-Jan-2005 21:59:59 +0100"),
(False, "23-01-2005 21:59:59"), (False, "23-01-2005 21:59:59"),
(True, "1106513999"), # Portsetry
(False, "01-23-2005 21:59:59.252"), # reported on f2b, causes Feb29 fix to break (False, "01-23-2005 21:59:59.252"), # reported on f2b, causes Feb29 fix to break
(False, "@4000000041f4104f00000000"), # TAI64N (False, "@4000000041f4104f00000000"), # TAI64N
(False, "2005-01-23T20:59:59.252Z"), #ISO 8601 (UTC) (False, "2005-01-23T20:59:59.252Z"), #ISO 8601 (UTC)

View File

@ -0,0 +1,4 @@
# failJSON: { "time": "2014-06-27T17:51:19", "match": true , "host": "192.168.56.1" }
1403884279 - 06/27/2014 17:51:19 Host: 192.168.56.1/192.168.56.1 Port: 1 TCP Blocked
# failJSON: { "time": "2014-06-27T17:51:19", "match": true , "host": "192.168.56.1" }
1403884279 - 06/27/2014 17:51:19 Host: 192.168.56.1/192.168.56.1 Port: 1 UDP Blocked

View File

@ -24,11 +24,7 @@ __license__ = "GPL"
import unittest, sys, os, fileinput, re, time, datetime, inspect import unittest, sys, os, fileinput, re, time, datetime, inspect
if sys.version_info >= (2, 6): import json
import json
else:
import simplejson as json
next = lambda x: x.next()
from ..server.filter import Filter from ..server.filter import Filter
from ..client.filterreader import FilterReader from ..client.filterreader import FilterReader

View File

@ -25,6 +25,7 @@ __license__ = "GPL"
import logging import logging
import os import os
import re import re
import sys
import time import time
import unittest import unittest
from StringIO import StringIO from StringIO import StringIO
@ -111,6 +112,7 @@ def gatherTests(regexps=None, no_network=False):
tests.addTest(unittest.makeSuite(clientreadertestcase.JailReaderTest)) tests.addTest(unittest.makeSuite(clientreadertestcase.JailReaderTest))
tests.addTest(unittest.makeSuite(clientreadertestcase.FilterReaderTest)) tests.addTest(unittest.makeSuite(clientreadertestcase.FilterReaderTest))
tests.addTest(unittest.makeSuite(clientreadertestcase.JailsReaderTest)) tests.addTest(unittest.makeSuite(clientreadertestcase.JailsReaderTest))
tests.addTest(unittest.makeSuite(clientreadertestcase.JailsReaderTestCache))
# CSocket and AsyncServer # CSocket and AsyncServer
tests.addTest(unittest.makeSuite(sockettestcase.Socket)) tests.addTest(unittest.makeSuite(sockettestcase.Socket))
# Misc helpers # Misc helpers
@ -204,6 +206,8 @@ class LogCaptureTestCase(unittest.TestCase):
# Let's log everything into a string # Let's log everything into a string
self._log = StringIO() self._log = StringIO()
logSys.handlers = [logging.StreamHandler(self._log)] logSys.handlers = [logging.StreamHandler(self._log)]
if self._old_level < logging.DEBUG: # so if HEAVYDEBUG etc -- show them!
logSys.handlers += self._old_handlers
logSys.setLevel(getattr(logging, 'DEBUG')) logSys.setLevel(getattr(logging, 'DEBUG'))
def tearDown(self): def tearDown(self):
@ -216,5 +220,8 @@ class LogCaptureTestCase(unittest.TestCase):
def _is_logged(self, s): def _is_logged(self, s):
return s in self._log.getvalue() return s in self._log.getvalue()
def getLog(self):
return self._log.getvalue()
def printLog(self): def printLog(self):
print(self._log.getvalue()) print(self._log.getvalue())

View File

@ -24,4 +24,4 @@ __author__ = "Cyril Jaquier, Yaroslav Halchenko, Steven Hiscocks, Daniel Black"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2014 Yaroslav Halchenko, 2013-2013 Steven Hiscocks, Daniel Black" __copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2014 Yaroslav Halchenko, 2013-2013 Steven Hiscocks, Daniel Black"
__license__ = "GPL-v2+" __license__ = "GPL-v2+"
version = "0.9.0.dev" version = "0.9.1"

View File

@ -1,12 +1,12 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.2. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.4.
.TH FAIL2BAN-CLIENT "1" "March 2014" "fail2ban-client v0.9.0" "User Commands" .TH FAIL2BAN-CLIENT "1" "October 2014" "fail2ban-client v0.9.1" "User Commands"
.SH NAME .SH NAME
fail2ban-client \- configure and control the server fail2ban-client \- configure and control the server
.SH SYNOPSIS .SH SYNOPSIS
.B fail2ban-client .B fail2ban-client
[\fIOPTIONS\fR] \fI<COMMAND>\fR [\fI\,OPTIONS\/\fR] \fI\,<COMMAND>\/\fR
.SH DESCRIPTION .SH DESCRIPTION
Fail2Ban v0.9.0 reads log file that contains password failure report Fail2Ban v0.9.1 reads log file that contains password failure report
and bans the corresponding IP addresses using firewall rules. and bans the corresponding IP addresses using firewall rules.
.SH OPTIONS .SH OPTIONS
.TP .TP
@ -35,10 +35,10 @@ decrease verbosity
force execution of the server (remove socket file) force execution of the server (remove socket file)
.TP .TP
\fB\-b\fR \fB\-b\fR
start the server in background mode (default) start server in background (default)
.TP .TP
\fB\-f\fR \fB\-f\fR
start the server in foreground mode (note that the client forks once itself) start server in foreground (note that the client forks once itself)
.TP .TP
\fB\-h\fR, \fB\-\-help\fR \fB\-h\fR, \fB\-\-help\fR
display this help message display this help message
@ -217,9 +217,8 @@ for <JAIL>
\fBset <JAIL> maxlines <LINES>\fR \fBset <JAIL> maxlines <LINES>\fR
sets the number of <LINES> to sets the number of <LINES> to
buffer for regex search for <JAIL> buffer for regex search for <JAIL>
.IP .TP
set <JAIL> addaction <ACT>[ <PYTHONFILE> <JSONKWARGS>] \fBset <JAIL> addaction <ACT>[ <PYTHONFILE> <JSONKWARGS>]\fR
.IP
adds a new action named <NAME> for adds a new action named <NAME> for
<JAIL>. Optionally for a Python <JAIL>. Optionally for a Python
based action, a <PYTHONFILE> and based action, a <PYTHONFILE> and
@ -231,45 +230,38 @@ removes the action <ACT> from
<JAIL> <JAIL>
.IP .IP
COMMAND ACTION CONFIGURATION COMMAND ACTION CONFIGURATION
.IP .TP
set <JAIL> action <ACT> actionstart <CMD> \fBset <JAIL> action <ACT> actionstart <CMD>\fR
.IP
sets the start command <CMD> of sets the start command <CMD> of
the action <ACT> for <JAIL> the action <ACT> for <JAIL>
.IP .TP
set <JAIL> action <ACT> actionstop <CMD> sets the stop command <CMD> of the \fBset <JAIL> action <ACT> actionstop <CMD> sets the stop command <CMD> of the\fR
.IP
action <ACT> for <JAIL> action <ACT> for <JAIL>
.IP .TP
set <JAIL> action <ACT> actioncheck <CMD> \fBset <JAIL> action <ACT> actioncheck <CMD>\fR
.IP
sets the check command <CMD> of sets the check command <CMD> of
the action <ACT> for <JAIL> the action <ACT> for <JAIL>
.TP .TP
\fBset <JAIL> action <ACT> actionban <CMD>\fR \fBset <JAIL> action <ACT> actionban <CMD>\fR
sets the ban command <CMD> of the sets the ban command <CMD> of the
action <ACT> for <JAIL> action <ACT> for <JAIL>
.IP .TP
set <JAIL> action <ACT> actionunban <CMD> \fBset <JAIL> action <ACT> actionunban <CMD>\fR
.IP
sets the unban command <CMD> of sets the unban command <CMD> of
the action <ACT> for <JAIL> the action <ACT> for <JAIL>
.IP .TP
set <JAIL> action <ACT> timeout <TIMEOUT> \fBset <JAIL> action <ACT> timeout <TIMEOUT>\fR
.IP
sets <TIMEOUT> as the command sets <TIMEOUT> as the command
timeout in seconds for the action timeout in seconds for the action
<ACT> for <JAIL> <ACT> for <JAIL>
.IP .IP
GENERAL ACTION CONFIGURATION GENERAL ACTION CONFIGURATION
.IP .TP
set <JAIL> action <ACT> <PROPERTY> <VALUE> \fBset <JAIL> action <ACT> <PROPERTY> <VALUE>\fR
.IP
sets the <VALUE> of <PROPERTY> for sets the <VALUE> of <PROPERTY> for
the action <ACT> for <JAIL> the action <ACT> for <JAIL>
.IP .TP
set <JAIL> action <ACT> <METHOD>[ <JSONKWARGS>] \fBset <JAIL> action <ACT> <METHOD>[ <JSONKWARGS>]\fR
.IP
calls the <METHOD> with calls the <METHOD> with
<JSONKWARGS> for the action <ACT> <JSONKWARGS> for the action <ACT>
for <JAIL> for <JAIL>

View File

@ -1,10 +1,10 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.2. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.4.
.TH FAIL2BAN-REGEX "1" "March 2014" "fail2ban-regex 0.9.0" "User Commands" .TH FAIL2BAN-REGEX "1" "October 2014" "fail2ban-regex 0.9.1" "User Commands"
.SH NAME .SH NAME
fail2ban-regex \- test Fail2ban "failregex" option fail2ban-regex \- test Fail2ban "failregex" option
.SH SYNOPSIS .SH SYNOPSIS
.B fail2ban-regex .B fail2ban-regex
[\fIOPTIONS\fR] \fI<LOG> <REGEX> \fR[\fIIGNOREREGEX\fR] [\fI\,OPTIONS\/\fR] \fI\,<LOG> <REGEX> \/\fR[\fI\,IGNOREREGEX\/\fR]
.SH DESCRIPTION .SH DESCRIPTION
Fail2Ban reads log file that contains password failure report Fail2Ban reads log file that contains password failure report
and bans the corresponding IP addresses using firewall rules. and bans the corresponding IP addresses using firewall rules.
@ -16,7 +16,7 @@ string
a string representing a log line a string representing a log line
.TP .TP
filename filename
path to a log file (\fI/var/log/auth.log\fP) path to a log file (\fI\,/var/log/auth.log\/\fP)
.TP .TP
"systemd\-journal" "systemd\-journal"
search systemd journal (systemd\-python required) search systemd journal (systemd\-python required)
@ -42,20 +42,20 @@ show program's version number and exit
\fB\-h\fR, \fB\-\-help\fR \fB\-h\fR, \fB\-\-help\fR
show this help message and exit show this help message and exit
.TP .TP
\fB\-d\fR DATEPATTERN, \fB\-\-datepattern\fR=\fIDATEPATTERN\fR \fB\-d\fR DATEPATTERN, \fB\-\-datepattern\fR=\fI\,DATEPATTERN\/\fR
set custom pattern used to match date/times set custom pattern used to match date/times
.TP .TP
\fB\-e\fR ENCODING, \fB\-\-encoding\fR=\fIENCODING\fR \fB\-e\fR ENCODING, \fB\-\-encoding\fR=\fI\,ENCODING\/\fR
File encoding. Default: system locale File encoding. Default: system locale
.TP .TP
\fB\-L\fR MAXLINES, \fB\-\-maxlines\fR=\fIMAXLINES\fR \fB\-L\fR MAXLINES, \fB\-\-maxlines\fR=\fI\,MAXLINES\/\fR
maxlines for multi\-line regex maxlines for multi\-line regex
.TP .TP
\fB\-m\fR JOURNALMATCH, \fB\-\-journalmatch\fR=\fIJOURNALMATCH\fR \fB\-m\fR JOURNALMATCH, \fB\-\-journalmatch\fR=\fI\,JOURNALMATCH\/\fR
journalctl style matches overriding filter file. journalctl style matches overriding filter file.
"systemd\-journal" only "systemd\-journal" only
.TP .TP
\fB\-l\fR LOG_LEVEL, \fB\-\-log\-level\fR=\fILOG_LEVEL\fR \fB\-l\fR LOG_LEVEL, \fB\-\-log\-level\fR=\fI\,LOG_LEVEL\/\fR
Log level for the Fail2Ban logger to use Log level for the Fail2Ban logger to use
.TP .TP
\fB\-v\fR, \fB\-\-verbose\fR \fB\-v\fR, \fB\-\-verbose\fR
@ -70,6 +70,9 @@ Do not print any missed lines
\fB\-\-print\-no\-ignored\fR \fB\-\-print\-no\-ignored\fR
Do not print any ignored lines Do not print any ignored lines
.TP .TP
\fB\-\-print\-all\-matched\fR
Print all matched lines
.TP
\fB\-\-print\-all\-missed\fR \fB\-\-print\-all\-missed\fR
Print all missed lines, no matter how many Print all missed lines, no matter how many
.TP .TP

View File

@ -1,12 +1,12 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.2. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.4.
.TH FAIL2BAN-SERVER "1" "March 2014" "fail2ban-server v0.9.0" "User Commands" .TH FAIL2BAN-SERVER "1" "October 2014" "fail2ban-server v0.9.1" "User Commands"
.SH NAME .SH NAME
fail2ban-server \- start the server fail2ban-server \- start the server
.SH SYNOPSIS .SH SYNOPSIS
.B fail2ban-server .B fail2ban-server
[\fIOPTIONS\fR] [\fI\,OPTIONS\/\fR]
.SH DESCRIPTION .SH DESCRIPTION
Fail2Ban v0.9.0 reads log file that contains password failure report Fail2Ban v0.9.1 reads log file that contains password failure report
and bans the corresponding IP addresses using firewall rules. and bans the corresponding IP addresses using firewall rules.
.PP .PP
Only use this command for debugging purpose. Start the server with Only use this command for debugging purpose. Start the server with

View File

@ -1,5 +1,9 @@
#!/bin/bash #!/bin/bash
set -eu
export PYTHONPATH=..
# fail2ban-client # fail2ban-client
echo -n "Generating fail2ban-client " echo -n "Generating fail2ban-client "
help2man --section=1 --no-info --include=fail2ban-client.h2m --output fail2ban-client.1 ../bin/fail2ban-client help2man --section=1 --no-info --include=fail2ban-client.h2m --output fail2ban-client.1 ../bin/fail2ban-client