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]
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
# travis-ci.org definition for Fail2Ban build
# https://travis-ci.org/fail2ban/fail2ban/
language: python
python:
- "2.6"
- "2.7"
- "3.2"
- "3.3"
- "3.4"
- "pypy"
- "pypy3"
- 2.6
- 2.7
- pypy
- 3.2
- 3.3
- 3.4
- pypy3
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 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
- if [[ $TRAVIS_PYTHON_VERSION == 2* || $TRAVIS_PYTHON_VERSION == 'pypy' ]]; then travis_retry pip install dnspython; fi
- if [[ $TRAVIS_PYTHON_VERSION == 3* || $TRAVIS_PYTHON_VERSION == 'pypy3' ]]; then travis_retry pip install dnspython3; fi
- 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 [[ $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
before_script:
# Manually execute 2to3 for now
- if [[ "$F2B_PY_3" ]]; then ./fail2ban-2to3; fi
script:
- 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
# Keep the legacy setup.py test approach of checking coverage for python2
- if [[ "$F2B_PY_2" ]]; then coverage run setup.py test; fi
# 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:
# 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 coveralls; fi
- coveralls
matrix:
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
===================
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
----------

View File

@ -56,9 +56,12 @@ following (note: on Debian-based systems, the script is called
`python-coverage`)::
coverage run bin/fail2ban-testcases
coverage report
Optionally:
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
complete. Try to ensure tests cover as many independent paths through the
code.
@ -88,7 +91,7 @@ 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.
- 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
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
@ -37,8 +37,8 @@ Optional:
To install, just do:
tar xvfj fail2ban-0.9.2.tar.bz2
cd fail2ban-0.9.2
tar xvfj fail2ban-0.9.3.tar.bz2
cd fail2ban-0.9.3
python setup.py install
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::
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::
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.
* 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
* 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 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
release ChangeLog entry as tag annotation::
git tag -s 0.9.2
git tag -s 0.9.3
Pre Release
===========
@ -185,7 +185,7 @@ Post Release
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:
@ -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
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.

1
THANKS
View File

@ -109,6 +109,7 @@ Stefan Tatschner
Stephen Gildea
Steven Hiscocks
TESTOVIK
Thomas Mayer
Tom Pike
Tomas Pihl
Tony Lawrence

View File

@ -22,8 +22,17 @@ __author__ = "Cyril Jaquier"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import sys, string, os, pickle, re, logging, signal
import getopt, time, shlex, socket
import getopt
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.protocol import printFormatted
@ -144,11 +153,14 @@ class Fail2banClient:
return self.__processCmd([["ping"]], False)
def __processCmd(self, cmd, showRet = True):
client = None
try:
beautifier = Beautifier()
streamRet = True
for c in cmd:
beautifier.setInputCmd(c)
try:
if not client:
client = CSocket(self.__conf["socket"])
ret = client.send(c)
if ret[0] == 0:
@ -162,14 +174,37 @@ class Fail2banClient:
streamRet = False
except socket.error:
if showRet:
logSys.error("Unable to contact server. Is it running?")
self.__logSocketError()
return False
except Exception, e:
if showRet:
logSys.error(e)
return False
finally:
if client:
client.close()
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.
#

View File

@ -29,7 +29,15 @@ __author__ = "Fail2Ban Developers"
__copyright__ = "Copyright (c) 2004-2008 Cyril Jaquier, 2012-2014 Yaroslav Halchenko"
__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 ConfigParser import NoOptionError, NoSectionError, MissingSectionHeaderError
@ -297,8 +305,8 @@ class Fail2banRegex(object):
"read from %(value)s" % locals()
return False
elif command[2] == 'addjournalmatch':
journalmatch = command[3]
self.setJournalMatch(shlex.split(journalmatch))
journalmatch = command[3:]
self.setJournalMatch(journalmatch)
elif command[2] == 'datepattern':
datepattern = command[3]
self.setDatePattern(datepattern)

View File

@ -22,7 +22,9 @@ __author__ = "Cyril Jaquier"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import getopt, sys, os
import getopt
import os
import sys
from fail2ban.version import version
from fail2ban.server.server import Server

View File

@ -25,7 +25,10 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2012- Yaroslav Halchenko"
__license__ = "GPL"
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
# 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.version import version as f2bVersion
class BadIPsAction(ActionBase):
"""Fail2Ban action which reports bans to badips.com, and also
blacklist bad IPs listed on badips.com by using another action's

View File

@ -1,10 +1,14 @@
#
# 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]
@ -34,7 +38,8 @@ actioncheck =
# <time> unix timestamp of the ban time
# 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
# Notes.: command executed when unbanning an IP. Take care that the
# 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
# 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]
# 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 =
# Default Cloudflare username
cfuser =

View File

@ -17,23 +17,23 @@ before = iptables-common.conf
# Notes.: command executed once at the start of Fail2Ban.
# Values: CMD
#
actionstart = iptables -N f2b-<name>
iptables -A f2b-<name> -j RETURN
iptables -I <chain> -p <protocol> -j f2b-<name>
actionstart = <iptables> -N f2b-<name>
<iptables> -A f2b-<name> -j <returntype>
<iptables> -I <chain> -p <protocol> -j f2b-<name>
# Option: actionstop
# Notes.: command executed once at the end of Fail2Ban
# Values: CMD
#
actionstop = iptables -D <chain> -p <protocol> -j f2b-<name>
iptables -F f2b-<name>
iptables -X f2b-<name>
actionstop = <iptables> -D <chain> -p <protocol> -j f2b-<name>
<iptables> -F f2b-<name>
<iptables> -X f2b-<name>
# Option: actioncheck
# Notes.: command executed once before each actionban command
# Values: CMD
#
actioncheck = iptables -n -L <chain> | grep -q 'f2b-<name>[ \t]'
actioncheck = <iptables> -n -L <chain> | grep -q 'f2b-<name>[ \t]'
# Option: actionban
# 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
# Values: CMD
#
actionban = iptables -I f2b-<name> 1 -s <ip> -j <blocktype>
actionban = <iptables> -I f2b-<name> 1 -s <ip> -j <blocktype>
# Option: actionunban
# 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
# Values: CMD
#
actionunban = iptables -D f2b-<name> -s <ip> -j <blocktype>
actionunban = <iptables> -D f2b-<name> -s <ip> -j <blocktype>
[Init]

View File

@ -43,3 +43,22 @@ protocol = tcp
# REJECT, REJECT --reject-with icmp-port-unreachable
# Values: STRING
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
#
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
# Notes.: command executed once at the end of Fail2Ban
# 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 --destroy f2b-<name>

View File

@ -24,13 +24,13 @@ before = iptables-common.conf
# Values: CMD
#
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
# Notes.: command executed once at the end of Fail2Ban
# 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 destroy f2b-<name>

View File

@ -24,13 +24,13 @@ before = iptables-common.conf
# Values: CMD
#
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
# Notes.: command executed once at the end of Fail2Ban
# 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 destroy f2b-<name>

View File

@ -19,28 +19,28 @@ before = iptables-common.conf
# Notes.: command executed once at the start of Fail2Ban.
# Values: CMD
#
actionstart = iptables -N f2b-<name>
iptables -A f2b-<name> -j RETURN
iptables -I <chain> 1 -p <protocol> -m multiport --dports <port> -j f2b-<name>
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 -A f2b-<name>-log -j <blocktype>
actionstart = <iptables> -N f2b-<name>
<iptables> -A f2b-<name> -j <returntype>
<iptables> -I <chain> 1 -p <protocol> -m multiport --dports <port> -j f2b-<name>
<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> -A f2b-<name>-log -j <blocktype>
# Option: actionstop
# Notes.: command executed once at the end of Fail2Ban
# Values: CMD
#
actionstop = iptables -D <chain> -p <protocol> -m multiport --dports <port> -j f2b-<name>
iptables -F f2b-<name>
iptables -F f2b-<name>-log
iptables -X f2b-<name>
iptables -X f2b-<name>-log
actionstop = <iptables> -D <chain> -p <protocol> -m multiport --dports <port> -j f2b-<name>
<iptables> -F f2b-<name>
<iptables> -F f2b-<name>-log
<iptables> -X f2b-<name>
<iptables> -X f2b-<name>-log
# Option: actioncheck
# Notes.: command executed once before each actionban command
# Values: CMD
#
actioncheck = iptables -n -L f2b-<name>-log >/dev/null
actioncheck = <iptables> -n -L f2b-<name>-log >/dev/null
# Option: actionban
# 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
# 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
# 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
# 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]

View File

@ -14,23 +14,23 @@ before = iptables-common.conf
# Notes.: command executed once at the start of Fail2Ban.
# Values: CMD
#
actionstart = iptables -N f2b-<name>
iptables -A f2b-<name> -j RETURN
iptables -I <chain> -p <protocol> -m multiport --dports <port> -j f2b-<name>
actionstart = <iptables> -N f2b-<name>
<iptables> -A f2b-<name> -j <returntype>
<iptables> -I <chain> -p <protocol> -m multiport --dports <port> -j f2b-<name>
# Option: actionstop
# Notes.: command executed once at the end of Fail2Ban
# Values: CMD
#
actionstop = iptables -D <chain> -p <protocol> -m multiport --dports <port> -j f2b-<name>
iptables -F f2b-<name>
iptables -X f2b-<name>
actionstop = <iptables> -D <chain> -p <protocol> -m multiport --dports <port> -j f2b-<name>
<iptables> -F f2b-<name>
<iptables> -X f2b-<name>
# Option: actioncheck
# Notes.: command executed once before each actionban command
# Values: CMD
#
actioncheck = iptables -n -L <chain> | grep -q 'f2b-<name>[ \t]'
actioncheck = <iptables> -n -L <chain> | grep -q 'f2b-<name>[ \t]'
# Option: actionban
# 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
# Values: CMD
#
actionban = iptables -I f2b-<name> 1 -s <ip> -j <blocktype>
actionban = <iptables> -I f2b-<name> 1 -s <ip> -j <blocktype>
# Option: actionunban
# 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
# Values: CMD
#
actionunban = iptables -D f2b-<name> -s <ip> -j <blocktype>
actionunban = <iptables> -D f2b-<name> -s <ip> -j <blocktype>
[Init]

View File

@ -16,23 +16,23 @@ before = iptables-common.conf
# Notes.: command executed once at the start of Fail2Ban.
# Values: CMD
#
actionstart = iptables -N f2b-<name>
iptables -A f2b-<name> -j RETURN
iptables -I <chain> -m state --state NEW -p <protocol> --dport <port> -j f2b-<name>
actionstart = <iptables> -N f2b-<name>
<iptables> -A f2b-<name> -j <returntype>
<iptables> -I <chain> -m state --state NEW -p <protocol> --dport <port> -j f2b-<name>
# Option: actionstop
# Notes.: command executed once at the end of Fail2Ban
# Values: CMD
#
actionstop = iptables -D <chain> -m state --state NEW -p <protocol> --dport <port> -j f2b-<name>
iptables -F f2b-<name>
iptables -X f2b-<name>
actionstop = <iptables> -D <chain> -m state --state NEW -p <protocol> --dport <port> -j f2b-<name>
<iptables> -F f2b-<name>
<iptables> -X f2b-<name>
# Option: actioncheck
# Notes.: command executed once before each actionban command
# Values: CMD
#
actioncheck = iptables -n -L <chain> | grep -q 'f2b-<name>[ \t]'
actioncheck = <iptables> -n -L <chain> | grep -q 'f2b-<name>[ \t]'
# Option: actionban
# 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
# Values: CMD
#
actionban = iptables -I f2b-<name> 1 -s <ip> -j <blocktype>
actionban = <iptables> -I f2b-<name> 1 -s <ip> -j <blocktype>
# Option: actionunban
# 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
# Values: CMD
#
actionunban = iptables -D f2b-<name> -s <ip> -j <blocktype>
actionunban = <iptables> -D f2b-<name> -s <ip> -j <blocktype>
[Init]

View File

@ -32,14 +32,14 @@ before = iptables-common.conf
# own rules. The 3600 second timeout is independent and acts as a
# safeguard in case the fail2ban process dies unexpectedly. The
# 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
# Notes.: command executed once at the end of Fail2Ban
# Values: CMD
#
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
# 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.
# Values: CMD
#
actionstart = iptables -N f2b-<name>
iptables -A f2b-<name> -j RETURN
iptables -I <chain> -p <protocol> --dport <port> -j f2b-<name>
actionstart = <iptables> -N f2b-<name>
<iptables> -A f2b-<name> -j <returntype>
<iptables> -I <chain> -p <protocol> --dport <port> -j f2b-<name>
# Option: actionstop
# Notes.: command executed once at the end of Fail2Ban
# Values: CMD
#
actionstop = iptables -D <chain> -p <protocol> --dport <port> -j f2b-<name>
iptables -F f2b-<name>
iptables -X f2b-<name>
actionstop = <iptables> -D <chain> -p <protocol> --dport <port> -j f2b-<name>
<iptables> -F f2b-<name>
<iptables> -X f2b-<name>
# Option: actioncheck
# Notes.: command executed once before each actionban command
# Values: CMD
#
actioncheck = iptables -n -L <chain> | grep -q 'f2b-<name>[ \t]'
actioncheck = <iptables> -n -L <chain> | grep -q 'f2b-<name>[ \t]'
# Option: actionban
# 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
# Values: CMD
#
actionban = iptables -I f2b-<name> 1 -s <ip> -j <blocktype>
actionban = <iptables> -I f2b-<name> 1 -s <ip> -j <blocktype>
# Option: actionunban
# 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
# Values: CMD
#
actionunban = iptables -D f2b-<name> -s <ip> -j <blocktype>
actionunban = <iptables> -D f2b-<name> -s <ip> -j <blocktype>
[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
#
[INCLUDES]
before = mail-whois-common.conf
[Definition]
# Option: actionstart
@ -40,9 +44,9 @@ actionban = printf %%b "Hi,\n
The IP <ip> has just been banned by Fail2Ban after
<failures> attempts against <name>.\n\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
`grep -E '(^|[^0-9])<ip>([^0-9]|$)' <logpath>`\n\n
`grep -E <grepopts> '(^|[^0-9])<ip>([^0-9]|$)' <logpath>`\n\n
Regards,\n
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
#
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]
# Option: actionstart
@ -40,7 +44,7 @@ actionban = printf %%b "Hi,\n
The IP <ip> has just been banned by Fail2Ban after
<failures> attempts against <name>.\n\n
Here is more information about <ip> :\n
`whois <ip> || echo missing whois program`\n
`%(_whois_command)s`\n
Regards,\n
Fail2Ban"|mail -s "[Fail2Ban] <name>: banned <ip> from `uname -n`" <dest>

View File

@ -15,7 +15,7 @@ after = sendmail-common.local
# Values: CMD
#
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>>
To: <dest>\n
Hi,\n
@ -28,7 +28,7 @@ actionstart = printf %%b "Subject: [Fail2Ban] <name>: started on `uname -n`
# Values: CMD
#
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>>
To: <dest>\n
Hi,\n

View File

@ -20,7 +20,7 @@ before = sendmail-common.conf
# Values: CMD
#
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>>
To: <dest>\n
Hi,\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-`
hostname: `host -t A <ip> 2>&1`\n\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
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
#
logpath = /dev/null
# Number of log lines to include in the email
#
grepopts = -m 1000

View File

@ -17,7 +17,7 @@ before = sendmail-common.conf
# Values: CMD
#
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>>
To: <dest>\n
Hi,\n

View File

@ -17,7 +17,7 @@ before = sendmail-common.conf
# Values: CMD
#
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>>
To: <dest>\n
Hi,\n

View File

@ -17,7 +17,7 @@ before = sendmail-common.conf
# Values: CMD
#
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>>
To: <dest>\n
Hi,\n
@ -26,7 +26,7 @@ actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
Here is more information about <ip> :\n
`/usr/bin/whois <ip> || echo missing whois program`\n\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
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
@ -40,3 +40,6 @@ name = default
#
logpath = /dev/null
# Number of log lines to include in the email
#
grepopts = -m 1000

View File

@ -17,7 +17,7 @@ before = sendmail-common.conf
# Values: CMD
#
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>>
To: <dest>\n
Hi,\n

View File

@ -17,7 +17,7 @@ before = sendmail-common.conf
# Values: CMD
#
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>>
To: <dest>\n
Hi,\n

View File

@ -17,7 +17,7 @@ before = sendmail-common.conf
# Values: CMD
#
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>>
To: <dest>\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
"""
class SMTPAction(ActionBase):
"""Fail2Ban action which sends emails to inform on jail starting,
stopping and bans.

View File

@ -3,6 +3,9 @@
# Author: Yaroslav Halchenko
#
[INCLUDES]
before = iptables-common.conf
[Definition]
@ -22,21 +25,21 @@ actionstop =
# Notes.: command executed once before each actionban command
# Values: CMD
#
actioncheck = iptables -n -L <chain>
actioncheck = <iptables> -n -L <chain>
# Option: actionban
# Notes.: command executed when banning an IP.
# Values: CMD
#
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
# Notes.: command executed when unbanning an IP.
# Values: CMD
#
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]

View File

@ -46,7 +46,7 @@ actionban = oifs=${IFS}; IFS=.;SEP_IP=( <ip> ); set -- ${SEP_IP}; ADDRESSES=$(di
REPORTID=<time>@`uname -n`
TLP=<tlp>
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
(printf -- %%b "<header>\n<message>\n<report>\n";
date '+Note: Local timezone is %%z (%%Z)';

View File

@ -10,7 +10,7 @@
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
failregex = ^<HOST> -.*"(GET|POST).*HTTP.*"(?:%(badbots)s|%(badbotscustom)s)"$
failregex = ^<HOST> -.*"(GET|POST|HEAD).*HTTP.*"(?:%(badbots)s|%(badbotscustom)s)"$
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+\])
iso8601 = \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+[+-]\d{4}
# All Asterisk log messages begin like this:
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 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 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>"$
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*$
^%(__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)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 =

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]
failregex = ^<HOST> \- \S+ \[\] \"(GET|POST) \/<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+\"\, .*?$
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|HEAD) \/<block> \S+\"\, .*?$
ignoreregex =

View File

@ -2,6 +2,9 @@
#
# Set "UseReverseDNS off" in proftpd.conf to avoid the need for DNS.
# 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]

View File

@ -1,6 +1,10 @@
# 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]
@ -9,7 +13,8 @@ before = common.conf
[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 =
# DEV Notes:
@ -26,4 +31,4 @@ ignoreregex =
# arbitrary user input and IMAP response doesn't inject the wrong IP for
# 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"]
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
#
@ -344,7 +348,7 @@ logpath = %(lighttpd_error_log)s
[roundcube-auth]
port = http,https
logpath = /var/log/roundcube/userlogins
logpath = logpath = %(roundcube_errors_log)s
[openwebmail]
@ -408,6 +412,12 @@ port = 10000
logpath = %(syslog_authpriv)s
[froxlor-auth]
port = http,https
logpath = %(syslog_authpriv)s
#
# HTTP Proxy servers
#
@ -424,6 +434,7 @@ logpath = /var/log/squid/access.log
port = 3128
logpath = /var/log/3proxy.log
#
# FTP servers
#
@ -756,3 +767,16 @@ port = 2222
enabled = false
logpath = /var/lib/portsentry/portsentry.history
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
roundcube_errors_log = /var/log/roundcube/errors
# Directory with ignorecommand scripts
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
# /etc/proftpd/proftpd.conf (SystemLog)
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
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.addLevelName(logging.NOTICE, 'NOTICE')
# define a new logger function for notice
# this is exactly like existing info, critical, debug...etc
def _Logger_notice(self, msg, *args, **kwargs):
@ -53,6 +54,7 @@ def _Logger_notice(self, msg, *args, **kwargs):
logging.Logger.notice = _Logger_notice
# define a new root level notice function
# this is exactly like existing info, critical, debug...etc
def _root_notice(msg, *args, **kwargs):

View File

@ -32,6 +32,7 @@ from ..helpers import getLogger
# Gets the instance of the logger.
logSys = getLogger(__name__)
class ActionReader(DefinitionInitConfigReader):
_configOpts = [

View File

@ -27,6 +27,7 @@ from ..helpers import getLogger
# Gets the instance of the logger.
logSys = getLogger(__name__)
##
# Beautify the output of the client.
#
@ -45,7 +46,8 @@ class Beautifier:
return self.__inputCmd
def beautify(self, response):
logSys.debug("Beautify " + `response` + " with " + `self.__inputCmd`)
logSys.debug(
"Beautify " + repr(response) + " with " + repr(self.__inputCmd))
inC = self.__inputCmd
msg = response
try:
@ -102,7 +104,7 @@ class Beautifier:
elif response == 4:
msg = msg + "DEBUG"
else:
msg = msg + `response`
msg = msg + repr(response)
elif inC[1] == "dbfile":
if response is None:
msg = "Database currently disabled"
@ -183,13 +185,14 @@ class Beautifier:
msg += ", ".join(response)
except Exception:
logSys.warning("Beautifier error. Please report the error")
logSys.error("Beautify " + `response` + " with " + `self.__inputCmd` +
" failed")
msg = msg + `response`
logSys.error("Beautify " + repr(response) + " with "
+ repr(self.__inputCmd) + " failed")
msg = msg + repr(response)
return msg
def beautifyError(self, response):
logSys.debug("Beautify (error) " + `response` + " with " + `self.__inputCmd`)
logSys.debug("Beautify (error) " + repr(response) + " with "
+ repr(self.__inputCmd))
msg = response
if isinstance(response, UnknownJailException):
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'
__license__ = 'GPL'
import os, sys
import os
import sys
from ..helpers import getLogger
if sys.version_info >= (3,2): # pragma: no cover
@ -66,6 +67,7 @@ logLevel = 7
__all__ = ['SafeConfigParserWithIncludes']
class SafeConfigParserWithIncludes(SafeConfigParser):
"""
Class adds functionality to SafeConfigParser to handle included

View File

@ -24,7 +24,8 @@ __author__ = "Cyril Jaquier"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import glob, os
import glob
import os
from ConfigParser import NoOptionError, NoSectionError
from .configparserinc import SafeConfigParserWithIncludes, logLevel
@ -33,6 +34,7 @@ from ..helpers import getLogger
# Gets the instance of the logger.
logSys = getLogger(__name__)
class ConfigReader():
"""Generic config reader class.
@ -135,6 +137,7 @@ class ConfigReader():
return self._cfg.getOptions(*args, **kwargs)
return {}
class ConfigReaderUnshared(SafeConfigParserWithIncludes):
"""Unshared config reader (previously ConfigReader).
@ -186,7 +189,7 @@ class ConfigReaderUnshared(SafeConfigParserWithIncludes):
if config_files_read:
return True
logSys.error("Found no accessible config files for %r under %s",
( filename, self.getBaseDir() ))
filename, self.getBaseDir())
return False
else:
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)
except ValueError:
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]
return values
class DefinitionInitConfigReader(ConfigReader):
"""Config reader for files with options grouped in [Definition] and
[Init] sections.
@ -281,7 +285,7 @@ class DefinitionInitConfigReader(ConfigReader):
if self.has_section("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)
def convert(self):

View File

@ -31,6 +31,7 @@ from ..helpers import getLogger
# Gets the instance of the logger.
logSys = getLogger(__name__)
class Configurator:
def __init__(self, force_enable=False, share_config=None):

View File

@ -26,22 +26,12 @@ __license__ = "GPL"
#from cPickle import dumps, loads, HIGHEST_PROTOCOL
from pickle import dumps, loads, HIGHEST_PROTOCOL
import socket, sys
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 = ""
from ..protocol import CSPROTO
import socket
import sys
class CSocket:
if sys.version_info >= (3,):
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
#self.csock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@ -49,20 +39,29 @@ class CSocket:
#self.csock.connect(("localhost", 2222))
self.__csock.connect(sock)
def __del__(self):
self.close(False)
def send(self, msg):
# Convert every list member to string
obj = dumps([str(m) for m in msg], HIGHEST_PROTOCOL)
self.__csock.send(obj + CSocket.END_STRING)
ret = self.receive(self.__csock)
self.__csock.send(obj + CSPROTO.END)
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()
return ret
self.__csock = None
@staticmethod
def receive(sock):
msg = EMPTY_BYTES
while msg.rfind(CSocket.END_STRING) == -1:
msg = CSPROTO.EMPTY
while msg.rfind(CSPROTO.END) == -1:
chunk = sock.recv(6)
if chunk == '':
raise RuntimeError, "socket connection broken"
raise RuntimeError("socket connection broken")
msg = msg + chunk
return loads(msg)

View File

@ -30,6 +30,7 @@ from ..helpers import getLogger
# Gets the instance of the logger.
logSys = getLogger(__name__)
class Fail2banReader(ConfigReader):
def __init__(self, **kwargs):
@ -52,18 +53,14 @@ class Fail2banReader(ConfigReader):
self.__opts = ConfigReader.getOptions(self, "Definition", opts)
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()
for opt in self.__opts:
if opt == "loglevel":
stream.append(["set", "loglevel", self.__opts[opt]])
elif opt == "logtarget":
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)
if opt in order:
stream.append((order[opt], ["set", opt, self.__opts[opt]]))
return [opt[1] for opt in sorted(stream)]

View File

@ -24,7 +24,8 @@ __author__ = "Cyril Jaquier"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import os, shlex
import os
import shlex
from .configreader import DefinitionInitConfigReader
from ..server.action import CommandAction
@ -33,6 +34,7 @@ from ..helpers import getLogger
# Gets the instance of the logger.
logSys = getLogger(__name__)
class FilterReader(DefinitionInitConfigReader):
_configOpts = [

View File

@ -24,8 +24,10 @@ __author__ = "Cyril Jaquier"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import re, glob, os.path
import glob
import json
import os.path
import re
from .configreader import ConfigReaderUnshared, ConfigReader
from .filterreader import FilterReader
@ -35,6 +37,7 @@ from ..helpers import getLogger
# Gets the instance of the logger.
logSys = getLogger(__name__)
class JailReader(ConfigReader):
optionCRE = re.compile("^((?:\w|-|_|\.)+)(?:\[(.*)\])?$")

View File

@ -31,6 +31,7 @@ from ..helpers import getLogger
# Gets the instance of the logger.
logSys = getLogger(__name__)
class JailsReader(ConfigReader):
def __init__(self, force_enable=False, **kwargs):
@ -50,6 +51,7 @@ class JailsReader(ConfigReader):
return self.__jails
def read(self):
self.__jails = list()
return ConfigReader.read(self, "jail")
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"
__license__ = "GPL"
#
# Jails
#
class DuplicateJailException(Exception):
pass
class UnknownJailException(KeyError):
pass

View File

@ -26,11 +26,13 @@ import traceback
import re
import logging
def formatExceptionInfo():
""" Consistently format exception information """
cla, exc = sys.exc_info()[:2]
return (cla.__name__, str(exc))
#
# Following "traceback" functions are adopted from PyMVPA distributed
# 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
return base
class TraceBack(object):
"""Customized traceback to be included in debug messages
"""
@ -94,6 +97,7 @@ class TraceBack(object):
return sftb
class FormatterWithTraceBack(logging.Formatter):
"""Custom formatter which expands %(tb) and %(tbc) with tracebacks
@ -108,6 +112,7 @@ class FormatterWithTraceBack(logging.Formatter):
record.tbc = record.tb = self._tb()
return logging.Formatter.format(self, record)
def getLogger(name):
"""Get logging.Logger instance with Fail2Ban logger name convention
"""
@ -115,6 +120,7 @@ def getLogger(name):
name = "fail2ban.%s" % name.rpartition(".")[-1]
return logging.getLogger(name)
def excepthook(exctype, value, traceback):
"""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.
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 = [
['', "BASIC", ""],
["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>"],
]
##
# Prints the protocol in a "man" format. This is used for the
# "-h" output of fail2ban-client.
@ -143,6 +154,7 @@ def printFormatted():
line = ' ' * (INDENT + MARGIN) + n.strip()
print line
##
# 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 "|}"
def __printWikiHeader(section, desc):
print
print "=== " + section + " ==="

View File

@ -21,8 +21,14 @@ __author__ = "Cyril Jaquier and Fail2Ban Contributors"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2012 Yaroslav Halchenko"
__license__ = "GPL"
import logging, os, subprocess, time, signal, tempfile
import threading, re
import logging
import os
import re
import signal
import subprocess
import tempfile
import threading
import time
from abc import ABCMeta
from collections import MutableMapping
@ -49,6 +55,7 @@ _RETCODE_HINTS = {
signame = dict((num, name)
for name, num in signal.__dict__.iteritems() if name.startswith("SIG"))
class CallingMap(MutableMapping):
"""A Mapping type which returns the result of callable values.
@ -94,6 +101,7 @@ class CallingMap(MutableMapping):
def copy(self):
return self.__class__(self.data.copy())
class ActionBase(object):
"""An abstract base class for actions in Fail2Ban.
@ -176,6 +184,7 @@ class ActionBase(object):
"""
pass
class CommandAction(ActionBase):
"""A action which executes OS shell commands.
@ -400,7 +409,7 @@ class CommandAction(ActionBase):
# recursive definitions are bad
#logSys.log(5, 'recursion fail tag: %s value: %s' % (tag, value) )
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
# Missing tags are ok - cInfo can contain aInfo elements like <HOST> and valid shell
# constructs like <STDIN>.
@ -556,7 +565,9 @@ class CommandAction(ActionBase):
stderr = tempfile.TemporaryFile(suffix=".stderr", prefix="fai2ban_")
try:
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()
retcode = popen.poll()
while time.time() - stime <= timeout and retcode is None:
@ -565,11 +576,12 @@ class CommandAction(ActionBase):
if retcode is None:
logSys.error("%s -- timed out after %i seconds." %
(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)
retcode = popen.poll()
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)
retcode = popen.poll()
except OSError, e:

View File

@ -24,9 +24,10 @@ __author__ = "Cyril Jaquier"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import time, logging
import logging
import os
import sys
import time
if sys.version_info >= (3, 3):
import importlib.machinery
else:
@ -46,6 +47,7 @@ from ..helpers import getLogger
# Gets the instance of the logger.
logSys = getLogger(__name__)
class Actions(JailThread, Mapping):
"""Handles jail actions.
@ -192,13 +194,14 @@ class Actions(JailThread, Mapping):
ValueError
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.
ticket = self.__banManager.getTicketByIP(ip)
if ticket is not None:
# Unban the IP.
self.__unBan(ticket)
if self._jail.database is not None:
self._jail.database.delBan(self._jail, ticket)
else:
raise ValueError("IP %s is not banned" % ip)
@ -294,7 +297,7 @@ class Actions(JailThread, Mapping):
True if an IP address get banned.
"""
ticket = self._jail.getFailTicket()
if ticket != False:
if ticket:
aInfo = CallingMap()
bTicket = BanManager.createBanTicket(ticket)
ip = bTicket.getIP()

View File

@ -25,20 +25,20 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
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
# Gets the instance of the logger.
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.
#
@ -47,17 +47,12 @@ else:
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):
asynchat.async_chat.__init__(self, conn)
self.__transmitter = transmitter
self.__buffer = []
# Sets the terminator.
self.set_terminator(RequestHandler.END_STRING)
self.set_terminator(CSPROTO.END)
def collect_incoming_data(self, 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.
def found_terminator(self):
# Pop whole buffer
message = self.__buffer
self.__buffer = []
# 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.
message = self.__transmitter.proceed(message)
# Serializes the response.
message = dumps(message, HIGHEST_PROTOCOL)
# Sends the response to the client.
self.push(message + RequestHandler.END_STRING)
# Closes the channel.
self.close_when_done()
self.push(message + CSPROTO.END)
def handle_error(self):
e1, e2 = formatExceptionInfo()
@ -86,6 +88,7 @@ class RequestHandler(asynchat.async_chat):
logSys.error(traceback.format_exc().splitlines())
self.close()
##
# Asynchronous server class.
#
@ -181,6 +184,7 @@ class AsyncServer(asyncore.dispatcher):
flags = fcntl.fcntl(fd, fcntl.F_GETFD)
fcntl.fcntl(fd, fcntl.F_SETFD, flags|fcntl.FD_CLOEXEC)
##
# AsyncServerException is used to wrap communication exceptions.

View File

@ -33,6 +33,7 @@ from ..helpers import getLogger
# Gets the instance of the logger.
logSys = getLogger(__name__)
##
# Banning Manager.
#
@ -271,7 +272,6 @@ class BanManager:
finally:
self.__lock.release()
##
# Get the size of the ban list.
#

View File

@ -21,11 +21,12 @@ __author__ = "Steven Hiscocks"
__copyright__ = "Copyright (c) 2013 Steven Hiscocks"
__license__ = "GPL"
import sys
import shutil, time
import sqlite3
import json
import locale
import shutil
import sqlite3
import sys
import time
from functools import wraps
from threading import RLock
@ -45,6 +46,7 @@ if sys.version_info >= (3,):
logSys.error('json dumps failed: %s', e)
x = '{}'
return x
def _json_loads_safe(x):
try:
x = json.loads(x.decode(
@ -63,6 +65,7 @@ else:
return x.encode(locale.getpreferredencoding())
else:
return x
def _json_dumps_safe(x):
try:
x = json.dumps(_normalize(x), ensure_ascii=False).decode(
@ -71,6 +74,7 @@ else:
logSys.error('json dumps failed: %s', e)
x = '{}'
return x
def _json_loads_safe(x):
try:
x = _normalize(json.loads(x.decode(
@ -83,6 +87,7 @@ else:
sqlite3.register_adapter(dict, _json_dumps_safe)
sqlite3.register_converter("JSON", _json_loads_safe)
def commitandrollback(f):
@wraps(f)
def wrapper(self, *args, **kwargs):
@ -91,6 +96,7 @@ def commitandrollback(f):
return f(self, self._db.cursor(), *args, **kwargs)
return wrapper
class Fail2BanDb(object):
"""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_ip ON bans(ip);" \
def __init__(self, filename, purgeAge=24*60*60):
try:
self._lock = RLock()
@ -411,19 +418,20 @@ class Fail2BanDb(object):
"failures": ticket.getAttempt()}))
@commitandrollback
def delBan(self, cur, jail, ticket):
def delBan(self, cur, jail, ip):
"""Delete a ban from the database.
Parameters
----------
jail : Jail
Jail in which the ban has occurred.
ticket : BanTicket
Ticket of the ban to be removed.
ip : str
IP to be removed.
"""
queryArgs = (jail.name, ip);
cur.execute(
"DELETE FROM bans WHERE jail = ? AND ip = ? AND timeofban = ?",
(jail.name, ticket.getIP(), int(round(ticket.getTime()))))
"DELETE FROM bans WHERE jail = ? AND ip = ?",
queryArgs);
@commitandrollback
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.
logSys = getLogger(__name__)
class DateDetector(object):
"""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 None
class DatePatternRegex(DateTemplate):
"""Date template, with regex/pattern
@ -236,6 +237,7 @@ class DatePatternRegex(DateTemplate):
if value is not None)
return reGroupDictStrptime(groupdict), dateMatch
class DateTai64n(DateTemplate):
"""A date template which matches TAI64N formate timestamps.

View File

@ -29,6 +29,7 @@ from ..helpers import getLogger
# Gets the instance of the logger.
logSys = getLogger(__name__)
class FailData:
def __init__(self):

View File

@ -34,6 +34,7 @@ from ..helpers import getLogger
# Gets the instance of the logger.
logSys = getLogger(__name__)
class FailManager:
def __init__(self):
@ -91,7 +92,7 @@ class FailManager:
ip = ticket.getIP()
unixTime = ticket.getTime()
matches = ticket.getMatches()
if self.__failList.has_key(ip):
if ip in self.__failList:
fData = self.__failList[ip]
if fData.getLastReset() < unixTime - self.__maxTime:
fData.setLastReset(unixTime)
@ -136,7 +137,7 @@ class FailManager:
self.__lock.release()
def __delFailure(self, ip):
if self.__failList.has_key(ip):
if ip in self.__failList:
del self.__failList[ip]
def toBan(self):
@ -154,5 +155,6 @@ class FailManager:
finally:
self.__lock.release()
class FailManagerEmpty(Exception):
pass

View File

@ -21,7 +21,10 @@ __author__ = "Cyril Jaquier"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import re, sre_constants, sys
import re
import sre_constants
import sys
##
# Regular expression class.
@ -55,6 +58,7 @@ class Regex:
except sre_constants.error:
raise RegexException("Unable to compile regular expression '%s'" %
regex)
def __str__(self):
return "%s(%r)" % (self.__class__.__name__, self._regex)
##
@ -91,7 +95,6 @@ class Regex:
except ValueError:
self._matchLineEnd = len(self._matchCache.string)
lineCount1 = self._matchCache.string.count(
"\n", 0, self._matchLineStart)
lineCount2 = self._matchCache.string.count(
@ -182,6 +185,7 @@ class Regex:
else:
return ["".join(line) for line in self._matchedTupleLines]
##
# 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"
__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 .ticket import FailTicket
@ -43,6 +48,7 @@ logSys = getLogger(__name__)
# that matches a given regular expression. This class is instantiated by
# a Jail object.
class Filter(JailThread):
##
@ -81,7 +87,6 @@ class Filter(JailThread):
self.dateDetector.addDefaultTemplate()
logSys.debug("Created %s" % self)
def __repr__(self):
return "%s(%r)" % (self.__class__.__name__, self.jail)
@ -104,7 +109,6 @@ class Filter(JailThread):
logSys.error(e)
raise e
def delFailRegex(self, index):
try:
del self.__failRegex[index]
@ -390,7 +394,6 @@ class Filter(JailThread):
return False
def processLine(self, line, date=None, returnRawHost=False,
checkAllRegex=False):
"""Split the time portion from log msg and return findFailures on them
@ -576,7 +579,6 @@ class FileFilter(Filter):
# to be overridden by backends
pass
##
# Delete a log path
#
@ -716,6 +718,7 @@ except ImportError: # pragma: no cover
import md5
md5sum = md5.new
class FileContainer:
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
# addresses.
import socket, struct
import socket
import struct
class DNSUtils:

View File

@ -23,7 +23,8 @@ __author__ = "Cyril Jaquier, Yaroslav Halchenko"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2012 Yaroslav Halchenko"
__license__ = "GPL"
import time, fcntl
import fcntl
import time
import gamin
@ -35,6 +36,7 @@ from ..helpers import getLogger
# Gets the instance of the logger.
logSys = getLogger(__name__)
##
# Log reader class.
#
@ -60,16 +62,14 @@ class FilterGamin(FileFilter):
fcntl.fcntl(fd, fcntl.F_SETFD, flags|fcntl.FD_CLOEXEC)
logSys.debug("Created FilterGamin")
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):
logSys.debug("File changed: " + path)
self.__modified = True
self._process_file(path)
def _process_file(self, path):
"""Process a given file
@ -121,7 +121,6 @@ class FilterGamin(FileFilter):
logSys.debug(self.jail.name + ": filter terminated")
return True
def stop(self):
super(FilterGamin, self).stop()
self.__cleanup()

View File

@ -24,7 +24,8 @@ __author__ = "Cyril Jaquier, Yaroslav Halchenko"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier; 2012 Yaroslav Halchenko"
__license__ = "GPL"
import time, os
import os
import time
from .failmanager import FailManagerEmpty
from .filter import FileFilter
@ -34,6 +35,7 @@ from ..helpers import getLogger
# Gets the instance of the logger.
logSys = getLogger(__name__)
##
# Log reader class.
#

View File

@ -51,6 +51,7 @@ except Exception, e:
# Gets the instance of the logger.
logSys = getLogger(__name__)
##
# Log reader class.
#
@ -73,7 +74,6 @@ class FilterPyinotify(FileFilter):
self.__watches = dict()
logSys.debug("Created FilterPyinotify")
def callback(self, event, origin=''):
logSys.debug("%sCallback for Event: %s", origin, event)
path = event.pathname
@ -95,7 +95,6 @@ class FilterPyinotify(FileFilter):
self._process_file(path)
def _process_file(self, path):
"""Process a given file
@ -112,7 +111,6 @@ class FilterPyinotify(FileFilter):
self.dateDetector.sortTemplate()
self.__modified = False
def _addFileWatcher(self, path):
wd = self.__monitor.add_watch(path, pyinotify.IN_MODIFY)
self.__watches.update(wd)
@ -144,7 +142,6 @@ class FilterPyinotify(FileFilter):
self._addFileWatcher(path)
self._process_file(path)
##
# Delete a log path
#
@ -163,7 +160,6 @@ class FilterPyinotify(FileFilter):
self.__monitor.rm_watch(wdInt)
logSys.debug("Removed monitor for the parent directory %s", path_dir)
##
# Main loop.
#

View File

@ -22,7 +22,8 @@ __author__ = "Steven Hiscocks"
__copyright__ = "Copyright (c) 2013 Steven Hiscocks"
__license__ = "GPL"
import datetime, time
import datetime
import time
from distutils.version import LooseVersion
from systemd import journal
@ -37,6 +38,7 @@ from ..helpers import getLogger
# Gets the instance of the logger.
logSys = getLogger(__name__)
##
# Journal reader class.
#
@ -60,7 +62,6 @@ class FilterSystemd(JournalFilter): # pragma: systemd no cover
self.setDatePattern(None)
logSys.debug("Created FilterSystemd")
##
# 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"
__license__ = "GPL"
import Queue, logging
import logging
import Queue
from .actions import Actions
from ..helpers import getLogger
@ -31,6 +32,7 @@ from ..helpers import getLogger
# Gets the instance of the logger.
logSys = getLogger(__name__)
class Jail:
"""Fail2Ban jail, which manages a filter and associated actions.
@ -115,7 +117,6 @@ class Jail:
raise RuntimeError(
"Failed to initialize any backend for Jail %r" % self.name)
def _initPolling(self):
from filterpoll import FilterPoll
logSys.info("Jail '%s' uses poller" % self.name)

View File

@ -30,6 +30,7 @@ from abc import abstractmethod
from ..helpers import excepthook
class JailThread(Thread):
"""Abstract class for threading elements in Fail2Ban.
@ -59,6 +60,7 @@ class JailThread(Thread):
# excepthook workaround for threads, derived from:
# http://bugs.python.org/issue1230540#msg91244
run = self.run
def run_with_except_hook(*args, **kwargs):
try:
run(*args, **kwargs)

View File

@ -21,7 +21,9 @@ __author__ = "Cyril Jaquier"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import time, datetime
import datetime
import time
##
# MyTime class.

View File

@ -25,7 +25,12 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
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 .filter import FileFilter, JournalFilter
@ -43,6 +48,7 @@ except ImportError:
# Dont print error here, as database may not even be used
Fail2BanDb = None
class Server:
def __init__(self, daemon = False):
@ -61,11 +67,10 @@ class Server:
'FreeBSD': '/var/run/log',
'Linux': '/dev/log',
}
self.setSyslogSocket("auto")
# Set logging level
self.setLogLevel("INFO")
self.setLogTarget("STDOUT")
self.setSyslogSocket("auto")
def __sigTERMhandler(self, signum, frame):
logSys.debug("Caught signal %d. Exiting" % signum)
@ -139,7 +144,6 @@ class Server:
finally:
self.__loggingLock.release()
def addJail(self, name, backend):
self.__jails.add(name, backend, self.__db)
if self.__db is not None:
@ -494,7 +498,14 @@ class Server:
return "flushed"
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":
self.__db = None
else:
@ -505,14 +516,10 @@ class Server:
logSys.error(
"Unable to import fail2ban database module as sqlite "
"is not available.")
else:
raise RuntimeError(
"Cannot change database when there are jails present")
def getDatabase(self):
return self.__db
def __createDaemon(self): # pragma: no cover
""" Detach a process from the controlling terminal and run it in the
background as a daemon.

View File

@ -28,6 +28,7 @@ locale_time = LocaleTime()
timeRE = TimeRE()
timeRE['z'] = r"(?P<z>Z|[+-]\d{2}(?::?[0-5]\d)?)"
def reGroupDictStrptime(found_dict):
"""Return time from dictionary of strptime fields

View File

@ -29,6 +29,7 @@ from ..helpers import getLogger
# Gets the instance of the logger.
logSys = getLogger(__name__)
class Ticket:
def __init__(self, ip, time, matches=None):

View File

@ -33,6 +33,7 @@ from .. import version
# Gets the instance of the logger.
logSys = getLogger(__name__)
class Transmitter:
##
@ -51,7 +52,7 @@ class Transmitter:
def proceed(self, command):
# Deserialize object
logSys.debug("Command: " + `command`)
logSys.debug("Command: " + repr(command))
try:
ret = self.__commandHandler(command)
ack = 0, ret
@ -138,6 +139,7 @@ class Transmitter:
elif name == "dbpurgeage":
db = self.__server.getDatabase()
if db is None:
logSys.warning("dbpurgeage setting was not in effect since no db yet")
return None
else:
db.purgeage = command[1]

View File

@ -32,6 +32,7 @@ from ..dummyjail import DummyJail
from ..utils import CONFIG_DIR
class TestSMTPServer(smtpd.SMTPServer):
def process_message(self, peer, mailfrom, rcpttos, data):
@ -40,6 +41,7 @@ class TestSMTPServer(smtpd.SMTPServer):
self.rcpttos = rcpttos
self.data = data
class SMTPActionTest(unittest.TestCase):
def setUp(self):

View File

@ -35,6 +35,7 @@ from .utils import LogCaptureTestCase
TEST_FILES_DIR = os.path.join(os.path.dirname(__file__), "files")
class ExecuteActions(LogCaptureTestCase):
def setUp(self):
@ -76,7 +77,6 @@ class ExecuteActions(LogCaptureTestCase):
self.assertEqual(self.__actions.getBanTime(),127)
self.assertRaises(ValueError, self.__actions.removeBannedIP, '127.0.0.1')
def testActionsOutput(self):
self.defaultActions()
self.__actions.start()
@ -89,7 +89,6 @@ class ExecuteActions(LogCaptureTestCase):
self.assertEqual(self.__actions.status(),[("Currently banned", 0 ),
("Total banned", 0 ), ("Banned IP list", [] )])
def testAddActionPython(self):
self.__actions.add(
"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"
__license__ = "GPL"
import os
import time
import tempfile
from ..server.action import CommandAction, CallingMap
from .utils import LogCaptureTestCase
from .utils import pid_exists
class CommandActionTest(LogCaptureTestCase):
@ -110,7 +113,6 @@ class CommandActionTest(LogCaptureTestCase):
{'ipjailmatches': "some >char< should \< be[ escap}ed&\n"}),
"some \\>char\\< should \\\\\\< be\\[ escap\\}ed\\&\n")
# Recursive
aInfo["ABC"] = "<xyz>"
self.assertEqual(
@ -202,6 +204,44 @@ class CommandActionTest(LogCaptureTestCase):
or self._is_logged('sleep 60 -- timed out after 3 seconds'))
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):
CommandAction.executeCmd('echo "How now brown cow"')
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.ticket import BanTicket
class AddFailure(unittest.TestCase):
def setUp(self):
"""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"
__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 import configparserinc
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')
class ConfigReaderTest(unittest.TestCase):
def setUp(self):
@ -71,12 +78,10 @@ option = %s
os.unlink("%s/%s" % (self.d, fname))
self.assertTrue(self.c.read('c')) # we still should have some
def _getoption(self, f='c'):
self.assertTrue(self.c.read(f)) # we got some now
return self.c.getOptions('section', [("int", 'option')])['option']
def testInaccessibleFile(self):
f = os.path.join(self.d, "d.conf") # inaccessible file
self._write('d.conf', 0)
@ -91,7 +96,6 @@ option = %s
# raise unittest.SkipTest("Skipping on %s -- access rights are not enforced" % platform)
pass
def testOptionalDotDDir(self):
self.assertFalse(self.c.read('c')) # nothing is there yet
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', 'c'), 'd')
class JailReaderTest(LogCaptureTestCase):
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('Unable to read the filter'))
def TODOtestJailActionBrokenDef(self):
jail = JailReader('brokenactiondef', basedir=IMPERFECT_CONFIG, share_config = self.__share_cfg)
def testJailActionBrokenDef(self):
jail = JailReader('brokenactiondef', basedir=IMPERFECT_CONFIG,
share_config=self.__share_cfg)
self.assertTrue(jail.read())
self.assertFalse(jail.getOptions())
self.assertTrue(jail.isEnabled())
self.printLog()
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:
def testStockSSHJail(self):
@ -218,7 +227,6 @@ class JailReaderTest(LogCaptureTestCase):
#self.assertRaises(ValueError, JailReader.extractOptions ,'mail-how[')
# Empty option
option = "abc[]"
expected = ('abc', {})
@ -312,7 +320,6 @@ class FilterReaderTest(unittest.TestCase):
output[-1][-1] = "5"
self.assertEqual(sorted(filterReader.convert()), sorted(output))
def testFilterReaderSubstitionDefault(self):
output = [['set', 'jailname', 'addfailregex', 'to=sweet@example.com fromip=<IP>']]
filterReader = FilterReader('substition', "jailname", {})
@ -355,6 +362,7 @@ class FilterReaderTest(unittest.TestCase):
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):
@ -474,7 +482,6 @@ class JailsReaderTest(LogCaptureTestCase):
self.assertTrue('Init' in actionReader.sections(),
msg="Action file %r is lacking [Init] section" % actionConfig)
def testReadStockJailConf(self):
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
@ -601,7 +608,6 @@ class JailsReaderTest(LogCaptureTestCase):
msg="Found no %s command among %s"
% (target_command, str(commands)) )
def testStockConfigurator(self):
configurator = Configurator()
configurator.setBaseDir(CONFIG_DIR)
@ -616,6 +622,22 @@ class JailsReaderTest(LogCaptureTestCase):
configurator.getOptions()
configurator.convertToProtocol()
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
# server
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")
class DatabaseTest(LogCaptureTestCase):
def setUp(self):
@ -211,7 +212,7 @@ class DatabaseTest(LogCaptureTestCase):
def testDelBan(self):
self.testAddBan()
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)
def testGetBansWithTime(self):
@ -305,7 +306,7 @@ class DatabaseTest(LogCaptureTestCase):
def testActionWithDB(self):
# test action together with database functionality
self.testAddJail() # Jail required
self.jail.database = self.db;
self.jail.database = self.db
actions = Actions(self.jail)
actions.add(
"action_checkainfo",
@ -317,7 +318,6 @@ class DatabaseTest(LogCaptureTestCase):
actions._Actions__checkBan()
self.assertTrue(self._is_logged("ban ainfo %s, %s, %s, %s" % (True, True, True, True)))
def testPurge(self):
if Fail2BanDb is None: # pragma: no cover
return

View File

@ -32,6 +32,7 @@ from ..server.datedetector import DateDetector
from ..server.datetemplate import DateTemplate
from .utils import setUpMyTime, tearDownMyTime
class DateDetectorTest(unittest.TestCase):
def setUp(self):

View File

@ -26,6 +26,7 @@ from threading import Lock
from ..server.actions import Actions
class DummyJail(object):
"""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.ticket import FailTicket
class AddFailure(unittest.TestCase):
def setUp(self):
@ -100,7 +101,7 @@ class AddFailure(unittest.TestCase):
self.assertEqual(
ticket_repr,
'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
ticket.setTime(1000002000.0)
self.assertEqual(ticket.getTime(), 1000002000.0)

View File

@ -1,6 +1,7 @@
from fail2ban.server.action import ActionBase
class TestAction(ActionBase):
def __init__(self, jail, name, opt1, opt2=None):

View File

@ -1,6 +1,7 @@
from fail2ban.server.action import ActionBase
class TestAction(ActionBase):
def ban(self, aInfo):

View File

@ -1,6 +1,7 @@
from fail2ban.server.action import ActionBase
class TestAction(ActionBase):
def __init__(self, jail, name):

View File

@ -1,6 +1,7 @@
from fail2ban.server.action import ActionBase
class TestAction(ActionBase):
def ban(self, aInfo):

Some files were not shown because too many files have changed in this diff Show More