mirror of https://github.com/fail2ban/fail2ban
Merge remote-tracking branch 'master' into 'ban-time-incr'
commit
386da502ba
|
@ -1,4 +1,11 @@
|
||||||
|
|
||||||
[run]
|
[run]
|
||||||
branch = True
|
branch = True
|
||||||
omit = /usr*
|
source =
|
||||||
|
config
|
||||||
|
fail2ban
|
||||||
|
|
||||||
|
[report]
|
||||||
|
exclude_lines =
|
||||||
|
pragma: no cover
|
||||||
|
pragma: systemd no cover
|
||||||
|
|
64
.travis.yml
64
.travis.yml
|
@ -1,24 +1,56 @@
|
||||||
# vim ft=yaml
|
# vim ft=yaml
|
||||||
# travis-ci.org definition for Fail2Ban build
|
# travis-ci.org definition for Fail2Ban build
|
||||||
|
# https://travis-ci.org/fail2ban/fail2ban/
|
||||||
language: python
|
language: python
|
||||||
python:
|
python:
|
||||||
- "2.6"
|
- 2.6
|
||||||
- "2.7"
|
- 2.7
|
||||||
- "3.2"
|
- pypy
|
||||||
- "3.3"
|
- 3.2
|
||||||
- "3.4"
|
- 3.3
|
||||||
- "pypy"
|
- 3.4
|
||||||
|
- pypy3
|
||||||
before_install:
|
before_install:
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then 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:
|
||||||
- pip install pyinotify
|
# Install Python packages / dependencies
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then 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
|
# coverage
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then cd ..; pip install -q coveralls; cd -; fi
|
- 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
|
||||||
|
before_script:
|
||||||
|
# Manually execute 2to3 for now
|
||||||
|
- if [[ "$F2B_PY_3" ]]; then ./fail2ban-2to3; fi
|
||||||
script:
|
script:
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then coverage run --rcfile=.travis_coveragerc setup.py test; else python setup.py test; fi
|
# Keep the legacy setup.py test approach of checking coverage for python2
|
||||||
# test installation
|
- if [[ "$F2B_PY_2" ]]; then coverage run setup.py test; fi
|
||||||
- sudo python setup.py install
|
# Coverage doesn't pick up setup.py test with python3, so run it directly
|
||||||
|
- if [[ "$F2B_PY_3" ]]; then coverage run bin/fail2ban-testcases; fi
|
||||||
|
# Use $VENV_BIN (not python) or else sudo will always run the system's python (2.7)
|
||||||
|
- sudo $VENV_BIN/pip install .
|
||||||
after_success:
|
after_success:
|
||||||
# Coverage config file must be .coveragerc for coveralls
|
- coveralls
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then cp -v .travis_coveragerc .coveragerc; fi
|
matrix:
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then coveralls; fi
|
fast_finish: true
|
||||||
|
# Might be worth looking into
|
||||||
|
#notifications:
|
||||||
|
# email: true
|
||||||
|
# irc:
|
||||||
|
# channels: "irc.freenode.org#fail2ban"
|
||||||
|
# template:
|
||||||
|
# - "%{repository}@%{branch}: %{message} (%{build_url})"
|
||||||
|
# on_success: change
|
||||||
|
# on_failure: change
|
||||||
|
# skip_join: true
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
|
|
||||||
[run]
|
|
||||||
branch = True
|
|
||||||
omit =
|
|
||||||
/usr/*
|
|
||||||
/home/travis/virtualenv/*
|
|
||||||
fail2ban/server/filtersystemd.py
|
|
|
@ -15,3 +15,13 @@ join the [mailing list](https://lists.sourceforge.net/lists/listinfo/fail2ban-us
|
||||||
|
|
||||||
### You would like to contribute (new filters/actions/code/documentation)?
|
### You would like to contribute (new filters/actions/code/documentation)?
|
||||||
send a [pull request](https://github.com/fail2ban/fail2ban/pulls)
|
send a [pull request](https://github.com/fail2ban/fail2ban/pulls)
|
||||||
|
|
||||||
|
Pull requests guidelines
|
||||||
|
========================
|
||||||
|
|
||||||
|
- If there is an issue on github to be closed by the pull request, include
|
||||||
|
```Closes #ISSUE``` (where ISSUE is issue's number)
|
||||||
|
|
||||||
|
- Add a brief summary of the change to the ChangeLog file into a corresponding
|
||||||
|
section out of Fixes, New Features or Enhancements (improvements to existing
|
||||||
|
features)
|
||||||
|
|
110
ChangeLog
110
ChangeLog
|
@ -3,21 +3,67 @@
|
||||||
| _/ _` | | |/ /| '_ \/ _` | ' \
|
| _/ _` | | |/ /| '_ \/ _` | ' \
|
||||||
|_| \__,_|_|_/___|_.__/\__,_|_||_|
|
|_| \__,_|_|_/___|_.__/\__,_|_||_|
|
||||||
|
|
||||||
================================================================================
|
Fail2Ban: Changelog
|
||||||
Fail2Ban (version 0.9.2.dev) 2014/xx/xx
|
===================
|
||||||
================================================================================
|
|
||||||
|
|
||||||
ver. 0.9.2 (2014/xx/xx) - increment ban time
|
ver. 0.9.3 (2015/XX/XXX) - increment ban time
|
||||||
----------
|
-----------
|
||||||
|
|
||||||
|
- IMPORTANT incompatible changes:
|
||||||
|
* filter.d/roundcube-auth.conf
|
||||||
|
- Changed logpath to 'errors' log (was 'userlogins')
|
||||||
|
|
||||||
- Fixes:
|
- Fixes:
|
||||||
* purge database will be executed now (within observer).
|
* purge database will be executed now (within observer).
|
||||||
* database functionality extended with bad ips.
|
* database functionality extended with bad ips.
|
||||||
* restoring currently banned ip after service restart fixed
|
* restoring currently banned ip after service restart fixed
|
||||||
(now < timeofban + bantime), ignore old log failures (already banned)
|
(now < timeofban + bantime), ignore old log failures (already banned)
|
||||||
|
* 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)
|
||||||
|
|
||||||
|
- New Features:
|
||||||
|
* increment ban time (+ observer) functionality introduced.
|
||||||
|
Thanks Serg G. Brester (sebres)
|
||||||
|
* New filters:
|
||||||
|
- froxlor-auth Thanks Joern Muehlencord
|
||||||
|
|
||||||
|
- 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
|
||||||
|
|
||||||
|
|
||||||
|
ver. 0.9.2 (2015/04/29) - better-quick-now-than-later
|
||||||
|
----------
|
||||||
|
|
||||||
|
- Fixes:
|
||||||
|
* Fix ufw action commands
|
||||||
|
* infinite busy loop on _escapedTags match in substituteRecursiveTags gh-907.
|
||||||
|
Thanks TonyThompson
|
||||||
|
* port[s] typo in jail.conf/nginx-http-auth gh-913. Thanks Frederik Wagner
|
||||||
|
(fnerdwq)
|
||||||
* $ typo in jail.conf. Thanks Skibbi. Debian bug #767255
|
* $ typo in jail.conf. Thanks Skibbi. Debian bug #767255
|
||||||
* grep'ing for IP in *mail-whois-lines.conf should now match also
|
* grep'ing for IP in *mail-whois-lines.conf should now match also
|
||||||
at the begginning and EOL. Thanks Dean Lee
|
at the beginning and EOL. Thanks Dean Lee
|
||||||
* jail.conf
|
* jail.conf
|
||||||
- php-url-fopen: separate logpath entries by newline
|
- php-url-fopen: separate logpath entries by newline
|
||||||
* failregex declared direct in jail was joined to single line (specifying of
|
* failregex declared direct in jail was joined to single line (specifying of
|
||||||
|
@ -26,23 +72,67 @@ ver. 0.9.2 (2014/xx/xx) - increment ban time
|
||||||
details. Thanks bes.internal
|
details. Thanks bes.internal
|
||||||
* filter.d/postfix-sasl.conf - failregex is now case insensitive
|
* filter.d/postfix-sasl.conf - failregex is now case insensitive
|
||||||
* filters.d/postfix.conf - add 'Client host rejected error message' failregex
|
* filters.d/postfix.conf - add 'Client host rejected error message' failregex
|
||||||
|
* fail2ban/__init__.py - add strptime thread safety hack-around
|
||||||
|
* recidive uses iptables-allports banaction by default now.
|
||||||
|
Avoids problems with iptables versions not understanding 'all' for
|
||||||
|
protocols and ports
|
||||||
|
* filter.d/dovecot.conf
|
||||||
|
- match pam_authenticate line from EL7
|
||||||
|
- match unknown user line from EL7
|
||||||
|
* Use use_poll=True for Python 2.7 and >=3.4 to overcome "Bad file
|
||||||
|
descriptor" msgs issue (gh-161)
|
||||||
|
* filter.d/postfix-sasl.conf - tweak failregex and add ignoreregex to ignore
|
||||||
|
system authentication issues
|
||||||
|
* fail2ban-regex reads filter file(s) completely, incl. '.local' file etc.
|
||||||
|
(gh-954)
|
||||||
|
* firewallcmd-* actions: split output into separate lines for grepping (gh-908)
|
||||||
|
* Guard unicode encode/decode issues while storing records in the database.
|
||||||
|
Fixes "binding parameter error (unsupported type)" (gh-973), thanks to kot
|
||||||
|
for reporting
|
||||||
|
* filter.d/sshd added regex for matching openSUSE ssh authentication failure
|
||||||
|
* filter.d/asterisk.conf:
|
||||||
|
- Dropped "Sending fake auth rejection" failregex since it incorrectly
|
||||||
|
targets the asterisk server itself
|
||||||
|
- match "hacking attempt detected" logs
|
||||||
|
|
||||||
- New Features:
|
- New Features:
|
||||||
* increment ban time (+ observer) functionality introduced.
|
- New filters:
|
||||||
Thanks Serg G. Brester (sebres)
|
- postfix-rbl Thanks Lee Clemens
|
||||||
* New interpolation feature for config readers - `%(known/parameter)s`.
|
- apache-fakegooglebot.conf Thanks Lee Clemens
|
||||||
|
- nginx-botsearch Thanks Frantisek Sumsal
|
||||||
|
- drupal-auth Thanks Lee Clemens
|
||||||
|
- New recursive embedded substitution feature added:
|
||||||
|
- `<<PREF>HOST>` becomes `<IPV4HOST>` for PREF=`IPV4`;
|
||||||
|
- `<<PREF>HOST>` becomes `1.2.3.4` for PREF=`IPV4` and IPV4HOST=`1.2.3.4`;
|
||||||
|
- New interpolation feature for config readers - `%(known/parameter)s`.
|
||||||
(means last known option with name `parameter`). This interpolation makes
|
(means last known option with name `parameter`). This interpolation makes
|
||||||
possible to extend a stock filter or jail regexp in .local file
|
possible to extend a stock filter or jail regexp in .local file
|
||||||
(opposite to simply set failregex/ignoreregex that overwrites it),
|
(opposite to simply set failregex/ignoreregex that overwrites it),
|
||||||
see gh-867.
|
see gh-867.
|
||||||
- Monit config for fail2ban in /files/monit
|
- Monit config for fail2ban in files/monit/
|
||||||
- New actions:
|
- New actions:
|
||||||
- action.d/firewallcmd-multiport and action.d/firewallcmd-allports Thanks Donald Yandt
|
- action.d/firewallcmd-multiport and action.d/firewallcmd-allports Thanks Donald Yandt
|
||||||
|
- action.d/sendmail-geoip-lines.conf
|
||||||
|
- action.d/nsupdate to update DNSBL. Thanks Andrew St. Jean
|
||||||
|
- New status argument for fail2ban-client -- flavor:
|
||||||
|
fail2ban-client status <jail> [flavor]
|
||||||
|
- empty or "basic" works as-is
|
||||||
|
- "cymru" additionally prints (ASN, Country RIR) per banned IP
|
||||||
|
(requires dnspython or dnspython3)
|
||||||
|
- Flush log at USR1 signal
|
||||||
|
|
||||||
- Enhancements:
|
- Enhancements:
|
||||||
* Enable multiport for firewallcmd-new action. Closes gh-834
|
* Enable multiport for firewallcmd-new action. Closes gh-834
|
||||||
* files/debian-initd migrated from the debian branch and should be
|
* files/debian-initd migrated from the debian branch and should be
|
||||||
suitable for manual installations now (thanks Juan Karlo de Guzman)
|
suitable for manual installations now (thanks Juan Karlo de Guzman)
|
||||||
|
* Define empty ignoreregex in filters which didn't have it to avoid
|
||||||
|
warnings (gh-934)
|
||||||
|
* action.d/{sendmail-*,xarf-login-attack}.conf - report local
|
||||||
|
timezone not UTC time/zone. Closes gh-911
|
||||||
|
* Conditionally log Ignore IP with reason (dns, ip, command). Closes gh-916
|
||||||
|
* Absorbed DNSUtils.cidr into addr2bin in filter.py, added unittests
|
||||||
|
* Added syslogsocket configuration to fail2ban.conf
|
||||||
|
* Note in the jail.conf for the recidive jail to increase dbpurgeage (gh-964)
|
||||||
|
|
||||||
ver. 0.9.1 (2014/10/29) - better, faster, stronger
|
ver. 0.9.1 (2014/10/29) - better, faster, stronger
|
||||||
----------
|
----------
|
||||||
|
|
7
DEVELOP
7
DEVELOP
|
@ -56,9 +56,12 @@ following (note: on Debian-based systems, the script is called
|
||||||
`python-coverage`)::
|
`python-coverage`)::
|
||||||
|
|
||||||
coverage run bin/fail2ban-testcases
|
coverage run bin/fail2ban-testcases
|
||||||
|
coverage report
|
||||||
|
|
||||||
|
Optionally:
|
||||||
coverage html
|
coverage html
|
||||||
|
|
||||||
Then look at htmlcov/index.html and see how much coverage your test cases
|
And then browse htmlcov/index.html and see how much coverage your test cases
|
||||||
exert over the code base. Full coverage is a good thing however it may not be
|
exert over the code base. Full coverage is a good thing however it may not be
|
||||||
complete. Try to ensure tests cover as many independent paths through the
|
complete. Try to ensure tests cover as many independent paths through the
|
||||||
code.
|
code.
|
||||||
|
@ -88,7 +91,7 @@ Testing can now be done inside a vagrant VM. Vagrantfile provided in
|
||||||
source code repository established two VMs:
|
source code repository established two VMs:
|
||||||
|
|
||||||
- VM "secure" which can be used for testing fail2ban code.
|
- VM "secure" which can be used for testing fail2ban code.
|
||||||
- VM "attacker" which hcan be used to perform attack against our "secure" VM.
|
- VM "attacker" which can be used to perform attack against our "secure" VM.
|
||||||
|
|
||||||
Both VMs are sharing the 192.168.200/24 network. If you are using this network
|
Both VMs are sharing the 192.168.200/24 network. If you are using this network
|
||||||
take a look into the Vagrantfile and change the IP.
|
take a look into the Vagrantfile and change the IP.
|
||||||
|
|
348
MANIFEST
348
MANIFEST
|
@ -9,14 +9,155 @@ RELEASE
|
||||||
THANKS
|
THANKS
|
||||||
TODO
|
TODO
|
||||||
Vagrantfile
|
Vagrantfile
|
||||||
|
bin/fail2ban-client
|
||||||
|
bin/fail2ban-regex
|
||||||
|
bin/fail2ban-server
|
||||||
|
bin/fail2ban-testcases
|
||||||
|
config/action.d/apf.conf
|
||||||
|
config/action.d/badips.conf
|
||||||
|
config/action.d/badips.py
|
||||||
|
config/action.d/blocklist_de.conf
|
||||||
|
config/action.d/bsd-ipfw.conf
|
||||||
|
config/action.d/cloudflare.conf
|
||||||
|
config/action.d/complain.conf
|
||||||
|
config/action.d/dshield.conf
|
||||||
|
config/action.d/dummy.conf
|
||||||
|
config/action.d/firewallcmd-allports.conf
|
||||||
|
config/action.d/firewallcmd-ipset.conf
|
||||||
|
config/action.d/firewallcmd-multiport.conf
|
||||||
|
config/action.d/firewallcmd-new.conf
|
||||||
|
config/action.d/hostsdeny.conf
|
||||||
|
config/action.d/ipfilter.conf
|
||||||
|
config/action.d/ipfw.conf
|
||||||
|
config/action.d/iptables-allports.conf
|
||||||
|
config/action.d/iptables-common.conf
|
||||||
|
config/action.d/iptables-ipset-proto4.conf
|
||||||
|
config/action.d/iptables-ipset-proto6-allports.conf
|
||||||
|
config/action.d/iptables-ipset-proto6.conf
|
||||||
|
config/action.d/iptables-multiport-log.conf
|
||||||
|
config/action.d/iptables-multiport.conf
|
||||||
|
config/action.d/iptables-new.conf
|
||||||
|
config/action.d/iptables-xt_recent-echo.conf
|
||||||
|
config/action.d/iptables.conf
|
||||||
|
config/action.d/mail-buffered.conf
|
||||||
|
config/action.d/mail-whois-lines.conf
|
||||||
|
config/action.d/mail-whois.conf
|
||||||
|
config/action.d/mail.conf
|
||||||
|
config/action.d/mynetwatchman.conf
|
||||||
|
config/action.d/nsupdate.conf
|
||||||
|
config/action.d/nsupdate.conf
|
||||||
|
config/action.d/osx-afctl.conf
|
||||||
|
config/action.d/osx-ipfw.conf
|
||||||
|
config/action.d/pf.conf
|
||||||
|
config/action.d/route.conf
|
||||||
|
config/action.d/sendmail-buffered.conf
|
||||||
|
config/action.d/sendmail-common.conf
|
||||||
|
config/action.d/sendmail-geoip-lines.conf
|
||||||
|
config/action.d/sendmail-whois-ipjailmatches.conf
|
||||||
|
config/action.d/sendmail-whois-ipmatches.conf
|
||||||
|
config/action.d/sendmail-whois-lines.conf
|
||||||
|
config/action.d/sendmail-whois-matches.conf
|
||||||
|
config/action.d/sendmail-whois.conf
|
||||||
|
config/action.d/sendmail.conf
|
||||||
|
config/action.d/shorewall.conf
|
||||||
|
config/action.d/smtp.py
|
||||||
|
config/action.d/symbiosis-blacklist-allports.conf
|
||||||
|
config/action.d/ufw.conf
|
||||||
|
config/action.d/xarf-login-attack.conf
|
||||||
|
config/fail2ban.conf
|
||||||
|
config/filter.d/3proxy.conf
|
||||||
|
config/filter.d/apache-auth.conf
|
||||||
|
config/filter.d/apache-badbots.conf
|
||||||
|
config/filter.d/apache-botsearch.conf
|
||||||
|
config/filter.d/apache-common.conf
|
||||||
|
config/filter.d/apache-fakegooglebot.conf
|
||||||
|
config/filter.d/apache-modsecurity.conf
|
||||||
|
config/filter.d/apache-nohome.conf
|
||||||
|
config/filter.d/apache-noscript.conf
|
||||||
|
config/filter.d/apache-overflows.conf
|
||||||
|
config/filter.d/apache-shellshock.conf
|
||||||
|
config/filter.d/assp.conf
|
||||||
|
config/filter.d/asterisk.conf
|
||||||
|
config/filter.d/botsearch-common.conf
|
||||||
|
config/filter.d/common.conf
|
||||||
|
config/filter.d/counter-strike.conf
|
||||||
|
config/filter.d/courier-auth.conf
|
||||||
|
config/filter.d/courier-smtp.conf
|
||||||
|
config/filter.d/cyrus-imap.conf
|
||||||
|
config/filter.d/directadmin.conf
|
||||||
|
config/filter.d/dovecot.conf
|
||||||
|
config/filter.d/dropbear.conf
|
||||||
|
config/filter.d/ejabberd-auth.conf
|
||||||
|
config/filter.d/exim-common.conf
|
||||||
|
config/filter.d/exim-spam.conf
|
||||||
|
config/filter.d/exim.conf
|
||||||
|
config/filter.d/freeswitch.conf
|
||||||
|
config/filter.d/groupoffice.conf
|
||||||
|
config/filter.d/gssftpd.conf
|
||||||
|
config/filter.d/guacamole.conf
|
||||||
|
config/filter.d/horde.conf
|
||||||
|
config/filter.d/ignorecommands
|
||||||
|
config/filter.d/ignorecommands/apache-fakegooglebot
|
||||||
|
config/filter.d/kerio.conf
|
||||||
|
config/filter.d/lighttpd-auth.conf
|
||||||
|
config/filter.d/monit.conf
|
||||||
|
config/filter.d/mysqld-auth.conf
|
||||||
|
config/filter.d/nagios.conf
|
||||||
|
config/filter.d/named-refused.conf
|
||||||
|
config/filter.d/nginx-botsearch.conf
|
||||||
|
config/filter.d/nginx-http-auth.conf
|
||||||
|
config/filter.d/nsd.conf
|
||||||
|
config/filter.d/openwebmail.conf
|
||||||
|
config/filter.d/oracleims.conf
|
||||||
|
config/filter.d/pam-generic.conf
|
||||||
|
config/filter.d/pam-generic.conf
|
||||||
|
config/filter.d/pam-generic.conf
|
||||||
|
config/filter.d/perdition.conf
|
||||||
|
config/filter.d/php-url-fopen.conf
|
||||||
|
config/filter.d/php-url-fopen.conf
|
||||||
|
config/filter.d/php-url-fopen.conf
|
||||||
|
config/filter.d/portsentry.conf
|
||||||
|
config/filter.d/postfix-rbl.conf
|
||||||
|
config/filter.d/postfix-sasl.conf
|
||||||
|
config/filter.d/postfix-sasl.conf
|
||||||
|
config/filter.d/postfix-sasl.conf
|
||||||
|
config/filter.d/postfix.conf
|
||||||
|
config/filter.d/proftpd.conf
|
||||||
|
config/filter.d/pure-ftpd.conf
|
||||||
|
config/filter.d/qmail.conf
|
||||||
|
config/filter.d/recidive.conf
|
||||||
|
config/filter.d/roundcube-auth.conf
|
||||||
|
config/filter.d/selinux-common.conf
|
||||||
|
config/filter.d/selinux-ssh.conf
|
||||||
|
config/filter.d/sendmail-auth.conf
|
||||||
|
config/filter.d/sendmail-reject.conf
|
||||||
|
config/filter.d/sendmail-spam.conf
|
||||||
|
config/filter.d/sieve.conf
|
||||||
|
config/filter.d/sogo-auth.conf
|
||||||
|
config/filter.d/solid-pop3d.conf
|
||||||
|
config/filter.d/squid.conf
|
||||||
|
config/filter.d/squirrelmail.conf
|
||||||
|
config/filter.d/sshd-ddos.conf
|
||||||
|
config/filter.d/sshd.conf
|
||||||
|
config/filter.d/stunnel.conf
|
||||||
|
config/filter.d/suhosin.conf
|
||||||
|
config/filter.d/tine20.conf
|
||||||
|
config/filter.d/uwimap-auth.conf
|
||||||
|
config/filter.d/vsftpd.conf
|
||||||
|
config/filter.d/webmin-auth.conf
|
||||||
|
config/filter.d/wuftpd.conf
|
||||||
|
config/filter.d/xinetd-fail.conf
|
||||||
|
config/jail.conf
|
||||||
|
config/paths-common.conf
|
||||||
|
config/paths-debian.conf
|
||||||
|
config/paths-fedora.conf
|
||||||
|
config/paths-freebsd.conf
|
||||||
|
config/paths-osx.conf
|
||||||
|
doc/run-rootless.txt
|
||||||
fail2ban-2to3
|
fail2ban-2to3
|
||||||
fail2ban-testcases-all
|
fail2ban-testcases-all
|
||||||
fail2ban-testcases-all-python3
|
fail2ban-testcases-all-python3
|
||||||
bin/fail2ban-client
|
fail2ban/__init__.py
|
||||||
bin/fail2ban-server
|
|
||||||
bin/fail2ban-testcases
|
|
||||||
bin/fail2ban-regex
|
|
||||||
doc/run-rootless.txt
|
|
||||||
fail2ban/client/__init__.py
|
fail2ban/client/__init__.py
|
||||||
fail2ban/client/actionreader.py
|
fail2ban/client/actionreader.py
|
||||||
fail2ban/client/beautifier.py
|
fail2ban/client/beautifier.py
|
||||||
|
@ -28,6 +169,9 @@ fail2ban/client/fail2banreader.py
|
||||||
fail2ban/client/filterreader.py
|
fail2ban/client/filterreader.py
|
||||||
fail2ban/client/jailreader.py
|
fail2ban/client/jailreader.py
|
||||||
fail2ban/client/jailsreader.py
|
fail2ban/client/jailsreader.py
|
||||||
|
fail2ban/exceptions.py
|
||||||
|
fail2ban/helpers.py
|
||||||
|
fail2ban/protocol.py
|
||||||
fail2ban/server/__init__.py
|
fail2ban/server/__init__.py
|
||||||
fail2ban/server/action.py
|
fail2ban/server/action.py
|
||||||
fail2ban/server/actions.py
|
fail2ban/server/actions.py
|
||||||
|
@ -64,6 +208,8 @@ fail2ban/tests/clientreadertestcase.py
|
||||||
fail2ban/tests/config/action.d/brokenaction.conf
|
fail2ban/tests/config/action.d/brokenaction.conf
|
||||||
fail2ban/tests/config/fail2ban.conf
|
fail2ban/tests/config/fail2ban.conf
|
||||||
fail2ban/tests/config/filter.d/simple.conf
|
fail2ban/tests/config/filter.d/simple.conf
|
||||||
|
fail2ban/tests/config/filter.d/test.conf
|
||||||
|
fail2ban/tests/config/filter.d/test.local
|
||||||
fail2ban/tests/config/jail.conf
|
fail2ban/tests/config/jail.conf
|
||||||
fail2ban/tests/config/paths-common.conf
|
fail2ban/tests/config/paths-common.conf
|
||||||
fail2ban/tests/config/paths-debian.conf
|
fail2ban/tests/config/paths-debian.conf
|
||||||
|
@ -74,6 +220,7 @@ fail2ban/tests/datedetectortestcase.py
|
||||||
fail2ban/tests/dummyjail.py
|
fail2ban/tests/dummyjail.py
|
||||||
fail2ban/tests/failmanagertestcase.py
|
fail2ban/tests/failmanagertestcase.py
|
||||||
fail2ban/tests/files/action.d/action.py
|
fail2ban/tests/files/action.d/action.py
|
||||||
|
fail2ban/tests/files/action.d/action_checkainfo.py
|
||||||
fail2ban/tests/files/action.d/action_errors.py
|
fail2ban/tests/files/action.d/action_errors.py
|
||||||
fail2ban/tests/files/action.d/action_modifyainfo.py
|
fail2ban/tests/files/action.d/action_modifyainfo.py
|
||||||
fail2ban/tests/files/action.d/action_noAction.py
|
fail2ban/tests/files/action.d/action_noAction.py
|
||||||
|
@ -104,6 +251,7 @@ fail2ban/tests/files/logs/apache-auth
|
||||||
fail2ban/tests/files/logs/apache-badbots
|
fail2ban/tests/files/logs/apache-badbots
|
||||||
fail2ban/tests/files/logs/apache-botscripts
|
fail2ban/tests/files/logs/apache-botscripts
|
||||||
fail2ban/tests/files/logs/apache-botsearch
|
fail2ban/tests/files/logs/apache-botsearch
|
||||||
|
fail2ban/tests/files/logs/apache-fakegooglebot
|
||||||
fail2ban/tests/files/logs/apache-modsecurity
|
fail2ban/tests/files/logs/apache-modsecurity
|
||||||
fail2ban/tests/files/logs/apache-nohome
|
fail2ban/tests/files/logs/apache-nohome
|
||||||
fail2ban/tests/files/logs/apache-noscript
|
fail2ban/tests/files/logs/apache-noscript
|
||||||
|
@ -135,6 +283,7 @@ fail2ban/tests/files/logs/monit
|
||||||
fail2ban/tests/files/logs/mysqld-auth
|
fail2ban/tests/files/logs/mysqld-auth
|
||||||
fail2ban/tests/files/logs/nagios
|
fail2ban/tests/files/logs/nagios
|
||||||
fail2ban/tests/files/logs/named-refused
|
fail2ban/tests/files/logs/named-refused
|
||||||
|
fail2ban/tests/files/logs/nginx-botsearch
|
||||||
fail2ban/tests/files/logs/nginx-http-auth
|
fail2ban/tests/files/logs/nginx-http-auth
|
||||||
fail2ban/tests/files/logs/nsd
|
fail2ban/tests/files/logs/nsd
|
||||||
fail2ban/tests/files/logs/openwebmail
|
fail2ban/tests/files/logs/openwebmail
|
||||||
|
@ -144,6 +293,7 @@ fail2ban/tests/files/logs/perdition
|
||||||
fail2ban/tests/files/logs/php-url-fopen
|
fail2ban/tests/files/logs/php-url-fopen
|
||||||
fail2ban/tests/files/logs/portsentry
|
fail2ban/tests/files/logs/portsentry
|
||||||
fail2ban/tests/files/logs/postfix
|
fail2ban/tests/files/logs/postfix
|
||||||
|
fail2ban/tests/files/logs/postfix-rbl
|
||||||
fail2ban/tests/files/logs/postfix-sasl
|
fail2ban/tests/files/logs/postfix-sasl
|
||||||
fail2ban/tests/files/logs/proftpd
|
fail2ban/tests/files/logs/proftpd
|
||||||
fail2ban/tests/files/logs/pure-ftpd
|
fail2ban/tests/files/logs/pure-ftpd
|
||||||
|
@ -182,170 +332,38 @@ fail2ban/tests/samplestestcase.py
|
||||||
fail2ban/tests/servertestcase.py
|
fail2ban/tests/servertestcase.py
|
||||||
fail2ban/tests/sockettestcase.py
|
fail2ban/tests/sockettestcase.py
|
||||||
fail2ban/tests/utils.py
|
fail2ban/tests/utils.py
|
||||||
setup.py
|
|
||||||
setup.cfg
|
|
||||||
fail2ban/__init__.py
|
|
||||||
fail2ban/exceptions.py
|
|
||||||
fail2ban/helpers.py
|
|
||||||
fail2ban/version.py
|
fail2ban/version.py
|
||||||
fail2ban/protocol.py
|
files/bash-completion
|
||||||
kill-server
|
files/cacti/README
|
||||||
config/action.d/apf.conf
|
files/cacti/cacti_host_template_fail2ban.xml
|
||||||
config/action.d/badips.conf
|
files/cacti/fail2ban_stats.sh
|
||||||
config/action.d/badips.py
|
|
||||||
config/action.d/blocklist_de.conf
|
|
||||||
config/action.d/bsd-ipfw.conf
|
|
||||||
config/action.d/cloudflare.conf
|
|
||||||
config/action.d/complain.conf
|
|
||||||
config/action.d/dshield.conf
|
|
||||||
config/action.d/dummy.conf
|
|
||||||
config/action.d/firewallcmd-ipset.conf
|
|
||||||
config/action.d/firewallcmd-new.conf
|
|
||||||
config/action.d/hostsdeny.conf
|
|
||||||
config/action.d/ipfilter.conf
|
|
||||||
config/action.d/ipfw.conf
|
|
||||||
config/action.d/iptables-allports.conf
|
|
||||||
config/action.d/iptables-common.conf
|
|
||||||
config/action.d/iptables-ipset-proto4.conf
|
|
||||||
config/action.d/iptables-ipset-proto6-allports.conf
|
|
||||||
config/action.d/iptables-ipset-proto6.conf
|
|
||||||
config/action.d/iptables-multiport-log.conf
|
|
||||||
config/action.d/iptables-multiport.conf
|
|
||||||
config/action.d/iptables-new.conf
|
|
||||||
config/action.d/iptables-xt_recent-echo.conf
|
|
||||||
config/action.d/iptables.conf
|
|
||||||
config/action.d/mail-buffered.conf
|
|
||||||
config/action.d/mail-whois-lines.conf
|
|
||||||
config/action.d/mail-whois.conf
|
|
||||||
config/action.d/mail.conf
|
|
||||||
config/action.d/mynetwatchman.conf
|
|
||||||
config/action.d/osx-afctl.conf
|
|
||||||
config/action.d/osx-ipfw.conf
|
|
||||||
config/action.d/pf.conf
|
|
||||||
config/action.d/route.conf
|
|
||||||
config/action.d/sendmail-buffered.conf
|
|
||||||
config/action.d/sendmail-common.conf
|
|
||||||
config/action.d/sendmail-whois-ipjailmatches.conf
|
|
||||||
config/action.d/sendmail-whois-ipmatches.conf
|
|
||||||
config/action.d/sendmail-whois-lines.conf
|
|
||||||
config/action.d/sendmail-whois-matches.conf
|
|
||||||
config/action.d/sendmail-whois.conf
|
|
||||||
config/action.d/sendmail.conf
|
|
||||||
config/action.d/shorewall.conf
|
|
||||||
config/action.d/smtp.py
|
|
||||||
config/action.d/symbiosis-blacklist-allports.conf
|
|
||||||
config/action.d/ufw.conf
|
|
||||||
config/action.d/xarf-login-attack.conf
|
|
||||||
config/fail2ban.conf
|
|
||||||
config/filter.d/3proxy.conf
|
|
||||||
config/filter.d/apache-auth.conf
|
|
||||||
config/filter.d/apache-badbots.conf
|
|
||||||
config/filter.d/apache-botsearch.conf
|
|
||||||
config/filter.d/apache-common.conf
|
|
||||||
config/filter.d/apache-modsecurity.conf
|
|
||||||
config/filter.d/apache-nohome.conf
|
|
||||||
config/filter.d/apache-noscript.conf
|
|
||||||
config/filter.d/apache-overflows.conf
|
|
||||||
config/filter.d/apache-shellshock.conf
|
|
||||||
config/filter.d/assp.conf
|
|
||||||
config/filter.d/asterisk.conf
|
|
||||||
config/filter.d/common.conf
|
|
||||||
config/filter.d/counter-strike.conf
|
|
||||||
config/filter.d/courier-auth.conf
|
|
||||||
config/filter.d/courier-smtp.conf
|
|
||||||
config/filter.d/cyrus-imap.conf
|
|
||||||
config/filter.d/directadmin.conf
|
|
||||||
config/filter.d/dovecot.conf
|
|
||||||
config/filter.d/dropbear.conf
|
|
||||||
config/filter.d/ejabberd-auth.conf
|
|
||||||
config/filter.d/exim-common.conf
|
|
||||||
config/filter.d/exim-spam.conf
|
|
||||||
config/filter.d/exim.conf
|
|
||||||
config/filter.d/freeswitch.conf
|
|
||||||
config/filter.d/groupoffice.conf
|
|
||||||
config/filter.d/gssftpd.conf
|
|
||||||
config/filter.d/guacamole.conf
|
|
||||||
config/filter.d/horde.conf
|
|
||||||
config/filter.d/kerio.conf
|
|
||||||
config/filter.d/lighttpd-auth.conf
|
|
||||||
config/filter.d/monit.conf
|
|
||||||
config/filter.d/mysqld-auth.conf
|
|
||||||
config/filter.d/nagios.conf
|
|
||||||
config/filter.d/named-refused.conf
|
|
||||||
config/filter.d/nginx-http-auth.conf
|
|
||||||
config/filter.d/nsd.conf
|
|
||||||
config/filter.d/openwebmail.conf
|
|
||||||
config/filter.d/oracleims.conf
|
|
||||||
config/filter.d/pam-generic.conf
|
|
||||||
config/filter.d/pam-generic.conf
|
|
||||||
config/filter.d/pam-generic.conf
|
|
||||||
config/filter.d/perdition.conf
|
|
||||||
config/filter.d/php-url-fopen.conf
|
|
||||||
config/filter.d/php-url-fopen.conf
|
|
||||||
config/filter.d/php-url-fopen.conf
|
|
||||||
config/filter.d/portsentry.conf
|
|
||||||
config/filter.d/postfix-sasl.conf
|
|
||||||
config/filter.d/postfix-sasl.conf
|
|
||||||
config/filter.d/postfix-sasl.conf
|
|
||||||
config/filter.d/postfix.conf
|
|
||||||
config/filter.d/proftpd.conf
|
|
||||||
config/filter.d/pure-ftpd.conf
|
|
||||||
config/filter.d/qmail.conf
|
|
||||||
config/filter.d/recidive.conf
|
|
||||||
config/filter.d/roundcube-auth.conf
|
|
||||||
config/filter.d/selinux-common.conf
|
|
||||||
config/filter.d/selinux-ssh.conf
|
|
||||||
config/filter.d/sendmail-auth.conf
|
|
||||||
config/filter.d/sendmail-reject.conf
|
|
||||||
config/filter.d/sendmail-spam.conf
|
|
||||||
config/filter.d/sieve.conf
|
|
||||||
config/filter.d/sogo-auth.conf
|
|
||||||
config/filter.d/solid-pop3d.conf
|
|
||||||
config/filter.d/squid.conf
|
|
||||||
config/filter.d/squirrelmail.conf
|
|
||||||
config/filter.d/sshd-ddos.conf
|
|
||||||
config/filter.d/sshd.conf
|
|
||||||
config/filter.d/stunnel.conf
|
|
||||||
config/filter.d/suhosin.conf
|
|
||||||
config/filter.d/tine20.conf
|
|
||||||
config/filter.d/uwimap-auth.conf
|
|
||||||
config/filter.d/vsftpd.conf
|
|
||||||
config/filter.d/webmin-auth.conf
|
|
||||||
config/filter.d/wuftpd.conf
|
|
||||||
config/filter.d/xinetd-fail.conf
|
|
||||||
config/jail.conf
|
|
||||||
config/paths-common.conf
|
|
||||||
config/paths-debian.conf
|
|
||||||
config/paths-fedora.conf
|
|
||||||
config/paths-freebsd.conf
|
|
||||||
config/paths-osx.conf
|
|
||||||
man/fail2ban-client.1
|
|
||||||
man/fail2ban.1
|
|
||||||
man/jail.conf.5
|
|
||||||
man/fail2ban-client.h2m
|
|
||||||
man/fail2ban-server.1
|
|
||||||
man/fail2ban-server.h2m
|
|
||||||
man/fail2ban-regex.1
|
|
||||||
man/fail2ban-regex.h2m
|
|
||||||
man/generate-man
|
|
||||||
files/debian-initd
|
files/debian-initd
|
||||||
files/gentoo-initd
|
files/fail2ban-logrotate
|
||||||
|
files/fail2ban-tmpfiles.conf
|
||||||
|
files/fail2ban.service
|
||||||
|
files/fail2ban.upstart
|
||||||
|
files/gen_badbots
|
||||||
files/gentoo-confd
|
files/gentoo-confd
|
||||||
files/redhat-initd
|
files/gentoo-initd
|
||||||
|
files/ipmasq-ZZZzzz_fail2ban.rul
|
||||||
|
files/logwatch/fail2ban
|
||||||
files/macosx-initd
|
files/macosx-initd
|
||||||
|
files/monit/fail2ban
|
||||||
|
files/nagios/README
|
||||||
|
files/nagios/check_fail2ban
|
||||||
|
files/redhat-initd
|
||||||
files/solaris-fail2ban.xml
|
files/solaris-fail2ban.xml
|
||||||
files/solaris-svc-fail2ban
|
files/solaris-svc-fail2ban
|
||||||
files/suse-initd
|
files/suse-initd
|
||||||
files/fail2ban-logrotate
|
kill-server
|
||||||
files/fail2ban.upstart
|
man/fail2ban-client.1
|
||||||
files/logwatch/fail2ban
|
man/fail2ban-client.h2m
|
||||||
files/cacti/fail2ban_stats.sh
|
man/fail2ban-regex.1
|
||||||
files/cacti/cacti_host_template_fail2ban.xml
|
man/fail2ban-regex.h2m
|
||||||
files/cacti/README
|
man/fail2ban-server.1
|
||||||
files/nagios/check_fail2ban
|
man/fail2ban-server.h2m
|
||||||
files/nagios/README
|
man/fail2ban.1
|
||||||
files/bash-completion
|
man/generate-man
|
||||||
files/fail2ban-tmpfiles.conf
|
man/jail.conf.5
|
||||||
files/fail2ban.service
|
setup.cfg
|
||||||
files/ipmasq-ZZZzzz_fail2ban.rul
|
setup.py
|
||||||
files/gen_badbots
|
|
||||||
|
|
|
@ -2,3 +2,4 @@ include ChangeLog COPYING DEVELOP FILTERS README.* THANKS TODO CONTRIBUTING* Vag
|
||||||
graft doc
|
graft doc
|
||||||
graft files
|
graft files
|
||||||
recursive-include config *.conf *.py
|
recursive-include config *.conf *.py
|
||||||
|
recursive-include config/filter.d/ignorecommands *
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
/ _|__ _(_) |_ ) |__ __ _ _ _
|
/ _|__ _(_) |_ ) |__ __ _ _ _
|
||||||
| _/ _` | | |/ /| '_ \/ _` | ' \
|
| _/ _` | | |/ /| '_ \/ _` | ' \
|
||||||
|_| \__,_|_|_/___|_.__/\__,_|_||_|
|
|_| \__,_|_|_/___|_.__/\__,_|_||_|
|
||||||
v0.9.1.dev 2014/??/??
|
v0.9.2.dev0 2015/xx/xx
|
||||||
|
|
||||||
## Fail2Ban: ban hosts that cause multiple authentication errors
|
## Fail2Ban: ban hosts that cause multiple authentication errors
|
||||||
|
|
||||||
|
@ -33,11 +33,12 @@ Optional:
|
||||||
- Linux >= 2.6.13
|
- Linux >= 2.6.13
|
||||||
- [gamin >= 0.0.21](http://www.gnome.org/~veillard/gamin)
|
- [gamin >= 0.0.21](http://www.gnome.org/~veillard/gamin)
|
||||||
- [systemd >= 204](http://www.freedesktop.org/wiki/Software/systemd)
|
- [systemd >= 204](http://www.freedesktop.org/wiki/Software/systemd)
|
||||||
|
- [dnspython](http://www.dnspython.org/)
|
||||||
|
|
||||||
To install, just do:
|
To install, just do:
|
||||||
|
|
||||||
tar xvfj fail2ban-0.9.1.tar.bz2
|
tar xvfj fail2ban-0.9.2.tar.bz2
|
||||||
cd fail2ban-0.9.1
|
cd fail2ban-0.9.2
|
||||||
python setup.py install
|
python setup.py install
|
||||||
|
|
||||||
This will install Fail2Ban into the python library directory. The executable
|
This will install Fail2Ban into the python library directory. The executable
|
||||||
|
|
12
RELEASE
12
RELEASE
|
@ -61,24 +61,24 @@ Preparation
|
||||||
|
|
||||||
* Which indicates that testcases/files/logs/mysqld.log has been moved or is a directory::
|
* Which indicates that testcases/files/logs/mysqld.log has been moved or is a directory::
|
||||||
|
|
||||||
tar -C /tmp -jxf dist/fail2ban-0.9.2.tar.bz2
|
tar -C /tmp -jxf dist/fail2ban-0.9.3.tar.bz2
|
||||||
|
|
||||||
* clean up current direcory::
|
* clean up current direcory::
|
||||||
|
|
||||||
diff -rul --exclude \*.pyc . /tmp/fail2ban-0.9.2/
|
diff -rul --exclude \*.pyc . /tmp/fail2ban-0.9.3/
|
||||||
|
|
||||||
* Only differences should be files that you don't want distributed.
|
* Only differences should be files that you don't want distributed.
|
||||||
|
|
||||||
|
|
||||||
* Ensure the tests work from the tarball::
|
* Ensure the tests work from the tarball::
|
||||||
|
|
||||||
cd /tmp/fail2ban-0.9.2/ && export PYTHONPATH=`pwd` && bin/fail2ban-testcases
|
cd /tmp/fail2ban-0.9.3/ && bin/fail2ban-testcases
|
||||||
|
|
||||||
* Add/finalize the corresponding entry in the ChangeLog
|
* Add/finalize the corresponding entry in the ChangeLog
|
||||||
|
|
||||||
* To generate a list of committers use e.g.::
|
* To generate a list of committers use e.g.::
|
||||||
|
|
||||||
git shortlog -sn 0.9.2.. | sed -e 's,^[ 0-9\t]*,,g' | tr '\n' '\|' | sed -e 's:|:, :g'
|
git shortlog -sn 0.9.3.. | sed -e 's,^[ 0-9\t]*,,g' | tr '\n' '\|' | sed -e 's:|:, :g'
|
||||||
|
|
||||||
* Ensure the top of the ChangeLog has the right version and current date.
|
* Ensure the top of the ChangeLog has the right version and current date.
|
||||||
* Ensure the top entry of the ChangeLog has the right version and current date.
|
* Ensure the top entry of the ChangeLog has the right version and current date.
|
||||||
|
@ -101,7 +101,7 @@ Preparation
|
||||||
* Tag the release by using a signed (and annotated) tag. Cut/paste
|
* Tag the release by using a signed (and annotated) tag. Cut/paste
|
||||||
release ChangeLog entry as tag annotation::
|
release ChangeLog entry as tag annotation::
|
||||||
|
|
||||||
git tag -s 0.9.2
|
git tag -s 0.9.3
|
||||||
|
|
||||||
Pre Release
|
Pre Release
|
||||||
===========
|
===========
|
||||||
|
@ -197,5 +197,5 @@ Add the following to the top of the ChangeLog::
|
||||||
Alter the git shortlog command in the previous section to refer to the just
|
Alter the git shortlog command in the previous section to refer to the just
|
||||||
released version.
|
released version.
|
||||||
|
|
||||||
and adjust fail2ban/version.py to carry .dev suffix to signal
|
and adjust fail2ban/version.py to carry .dev0 suffix to signal
|
||||||
a version under development.
|
a version under development.
|
||||||
|
|
4
THANKS
4
THANKS
|
@ -6,6 +6,7 @@ the project. If you have been left off, please let us know
|
||||||
(preferably send a pull request on github with the "fix") and you will
|
(preferably send a pull request on github with the "fix") and you will
|
||||||
be added
|
be added
|
||||||
|
|
||||||
|
Aaron Brice
|
||||||
Adam Tkac
|
Adam Tkac
|
||||||
Adrien Clerc
|
Adrien Clerc
|
||||||
ache
|
ache
|
||||||
|
@ -13,6 +14,7 @@ ag4ve (Shawn)
|
||||||
Alasdair D. Campbell
|
Alasdair D. Campbell
|
||||||
Amir Caspi
|
Amir Caspi
|
||||||
Amy
|
Amy
|
||||||
|
Andrew St. Jean
|
||||||
Andrey G. Grozin
|
Andrey G. Grozin
|
||||||
Andy Fragen
|
Andy Fragen
|
||||||
Arturo 'Buanzo' Busleiman
|
Arturo 'Buanzo' Busleiman
|
||||||
|
@ -39,6 +41,7 @@ Enrico Labedzki
|
||||||
Eugene Hopkinson (SlowRiot)
|
Eugene Hopkinson (SlowRiot)
|
||||||
ftoppi
|
ftoppi
|
||||||
François Boulogne
|
François Boulogne
|
||||||
|
Frantisek Sumsal
|
||||||
Frédéric
|
Frédéric
|
||||||
Georgiy Mernov
|
Georgiy Mernov
|
||||||
Guilhem Lettron
|
Guilhem Lettron
|
||||||
|
@ -83,6 +86,7 @@ Michael Hanselmann
|
||||||
Mika (mkl)
|
Mika (mkl)
|
||||||
Nick Munger
|
Nick Munger
|
||||||
onorua
|
onorua
|
||||||
|
Orion Poplawski
|
||||||
Paul Marrapese
|
Paul Marrapese
|
||||||
Paul Traina
|
Paul Traina
|
||||||
Noel Butler
|
Noel Butler
|
||||||
|
|
|
@ -22,8 +22,17 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import sys, string, os, pickle, re, logging, signal
|
import getopt
|
||||||
import getopt, time, shlex, socket
|
import logging
|
||||||
|
import os
|
||||||
|
import pickle
|
||||||
|
import re
|
||||||
|
import shlex
|
||||||
|
import signal
|
||||||
|
import socket
|
||||||
|
import string
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
from fail2ban.version import version
|
from fail2ban.version import version
|
||||||
from fail2ban.protocol import printFormatted
|
from fail2ban.protocol import printFormatted
|
||||||
|
@ -144,11 +153,14 @@ class Fail2banClient:
|
||||||
return self.__processCmd([["ping"]], False)
|
return self.__processCmd([["ping"]], False)
|
||||||
|
|
||||||
def __processCmd(self, cmd, showRet = True):
|
def __processCmd(self, cmd, showRet = True):
|
||||||
|
client = None
|
||||||
|
try:
|
||||||
beautifier = Beautifier()
|
beautifier = Beautifier()
|
||||||
streamRet = True
|
streamRet = True
|
||||||
for c in cmd:
|
for c in cmd:
|
||||||
beautifier.setInputCmd(c)
|
beautifier.setInputCmd(c)
|
||||||
try:
|
try:
|
||||||
|
if not client:
|
||||||
client = CSocket(self.__conf["socket"])
|
client = CSocket(self.__conf["socket"])
|
||||||
ret = client.send(c)
|
ret = client.send(c)
|
||||||
if ret[0] == 0:
|
if ret[0] == 0:
|
||||||
|
@ -162,14 +174,37 @@ class Fail2banClient:
|
||||||
streamRet = False
|
streamRet = False
|
||||||
except socket.error:
|
except socket.error:
|
||||||
if showRet:
|
if showRet:
|
||||||
logSys.error("Unable to contact server. Is it running?")
|
self.__logSocketError()
|
||||||
return False
|
return False
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
if showRet:
|
if showRet:
|
||||||
logSys.error(e)
|
logSys.error(e)
|
||||||
return False
|
return False
|
||||||
|
finally:
|
||||||
|
if client:
|
||||||
|
client.close()
|
||||||
return streamRet
|
return streamRet
|
||||||
|
|
||||||
|
def __logSocketError(self):
|
||||||
|
try:
|
||||||
|
if os.access(self.__conf["socket"], os.F_OK):
|
||||||
|
# This doesn't check if path is a socket,
|
||||||
|
# but socket.error should be raised
|
||||||
|
if os.access(self.__conf["socket"], os.W_OK):
|
||||||
|
# Permissions look good, but socket.error was raised
|
||||||
|
logSys.error("Unable to contact server. Is it running?")
|
||||||
|
else:
|
||||||
|
logSys.error("Permission denied to socket: %s,"
|
||||||
|
" (you must be root)", self.__conf["socket"])
|
||||||
|
else:
|
||||||
|
logSys.error("Failed to access socket path: %s."
|
||||||
|
" Is fail2ban running?",
|
||||||
|
self.__conf["socket"])
|
||||||
|
except Exception as e:
|
||||||
|
logSys.error("Exception while checking socket access: %s",
|
||||||
|
self.__conf["socket"])
|
||||||
|
logSys.error(e)
|
||||||
|
|
||||||
##
|
##
|
||||||
# Process a command line.
|
# Process a command line.
|
||||||
#
|
#
|
||||||
|
|
|
@ -29,7 +29,15 @@ __author__ = "Fail2Ban Developers"
|
||||||
__copyright__ = "Copyright (c) 2004-2008 Cyril Jaquier, 2012-2014 Yaroslav Halchenko"
|
__copyright__ = "Copyright (c) 2004-2008 Cyril Jaquier, 2012-2014 Yaroslav Halchenko"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import getopt, sys, time, logging, os, locale, shlex, time, urllib
|
import getopt
|
||||||
|
import locale
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import shlex
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import time
|
||||||
|
import urllib
|
||||||
from optparse import OptionParser, Option
|
from optparse import OptionParser, Option
|
||||||
|
|
||||||
from ConfigParser import NoOptionError, NoSectionError, MissingSectionHeaderError
|
from ConfigParser import NoOptionError, NoSectionError, MissingSectionHeaderError
|
||||||
|
@ -220,6 +228,7 @@ class Fail2banRegex(object):
|
||||||
self._datepattern_set = False
|
self._datepattern_set = False
|
||||||
self._journalmatch = None
|
self._journalmatch = None
|
||||||
|
|
||||||
|
self.share_config=dict()
|
||||||
self._filter = Filter(None)
|
self._filter = Filter(None)
|
||||||
self._ignoreregex = list()
|
self._ignoreregex = list()
|
||||||
self._failregex = list()
|
self._failregex = list()
|
||||||
|
@ -260,12 +269,24 @@ class Fail2banRegex(object):
|
||||||
def readRegex(self, value, regextype):
|
def readRegex(self, value, regextype):
|
||||||
assert(regextype in ('fail', 'ignore'))
|
assert(regextype in ('fail', 'ignore'))
|
||||||
regex = regextype + 'regex'
|
regex = regextype + 'regex'
|
||||||
if os.path.isfile(value):
|
if os.path.isfile(value) or os.path.isfile(value + '.conf'):
|
||||||
|
if os.path.basename(os.path.dirname(value)) == 'filter.d':
|
||||||
|
## within filter.d folder - use standard loading algorithm to load filter completely (with .local etc.):
|
||||||
|
basedir = os.path.dirname(os.path.dirname(value))
|
||||||
|
value = os.path.splitext(os.path.basename(value))[0]
|
||||||
|
print "Use %11s filter file : %s, basedir: %s" % (regex, value, basedir)
|
||||||
|
reader = FilterReader(value, 'fail2ban-regex-jail', {}, share_config=self.share_config, basedir=basedir)
|
||||||
|
if not reader.read():
|
||||||
|
print "ERROR: failed to load filter %s" % value
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
## foreign file - readexplicit this file and includes if possible:
|
||||||
print "Use %11s file : %s" % (regex, value)
|
print "Use %11s file : %s" % (regex, value)
|
||||||
reader = FilterReader(value, 'fail2ban-regex-jail', {})
|
reader = FilterReader(value, 'fail2ban-regex-jail', {}, share_config=self.share_config)
|
||||||
reader.setBaseDir(None)
|
reader.setBaseDir(None)
|
||||||
|
if not reader.readexplicit():
|
||||||
if reader.readexplicit():
|
print "ERROR: failed to read %s" % value
|
||||||
|
return False
|
||||||
reader.getOptions(None)
|
reader.getOptions(None)
|
||||||
readercommands = reader.convert()
|
readercommands = reader.convert()
|
||||||
regex_values = [
|
regex_values = [
|
||||||
|
@ -284,14 +305,11 @@ class Fail2banRegex(object):
|
||||||
"read from %(value)s" % locals()
|
"read from %(value)s" % locals()
|
||||||
return False
|
return False
|
||||||
elif command[2] == 'addjournalmatch':
|
elif command[2] == 'addjournalmatch':
|
||||||
journalmatch = command[3]
|
journalmatch = command[3:]
|
||||||
self.setJournalMatch(shlex.split(journalmatch))
|
self.setJournalMatch(journalmatch)
|
||||||
elif command[2] == 'datepattern':
|
elif command[2] == 'datepattern':
|
||||||
datepattern = command[3]
|
datepattern = command[3]
|
||||||
self.setDatePattern(datepattern)
|
self.setDatePattern(datepattern)
|
||||||
else:
|
|
||||||
print "ERROR: failed to read %s" % value
|
|
||||||
return False
|
|
||||||
else:
|
else:
|
||||||
print "Use %11s line : %s" % (regex, shortstr(value))
|
print "Use %11s line : %s" % (regex, shortstr(value))
|
||||||
regex_values = [RegexStat(value)]
|
regex_values = [RegexStat(value)]
|
||||||
|
|
|
@ -22,7 +22,9 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import getopt, sys, os
|
import getopt
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
from fail2ban.version import version
|
from fail2ban.version import version
|
||||||
from fail2ban.server.server import Server
|
from fail2ban.server.server import Server
|
||||||
|
|
|
@ -25,7 +25,10 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2012- Yaroslav Halchenko"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import unittest, sys, time, os
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import unittest
|
||||||
|
|
||||||
# Check if local fail2ban module exists, and use if it exists by
|
# Check if local fail2ban module exists, and use if it exists by
|
||||||
# modifying the path. This is such that tests can be used in dev
|
# modifying the path. This is such that tests can be used in dev
|
||||||
|
|
|
@ -35,6 +35,7 @@ else:
|
||||||
from fail2ban.server.actions import ActionBase
|
from fail2ban.server.actions import ActionBase
|
||||||
from fail2ban.version import version as f2bVersion
|
from fail2ban.version import version as f2bVersion
|
||||||
|
|
||||||
|
|
||||||
class BadIPsAction(ActionBase):
|
class BadIPsAction(ActionBase):
|
||||||
"""Fail2Ban action which reports bans to badips.com, and also
|
"""Fail2Ban action which reports bans to badips.com, and also
|
||||||
blacklist bad IPs listed on badips.com by using another action's
|
blacklist bad IPs listed on badips.com by using another action's
|
||||||
|
@ -111,6 +112,8 @@ class BadIPsAction(ActionBase):
|
||||||
------
|
------
|
||||||
HTTPError
|
HTTPError
|
||||||
Any issues with badips.com request.
|
Any issues with badips.com request.
|
||||||
|
ValueError
|
||||||
|
If badips.com response didn't contain necessary information
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
response = urlopen(
|
response = urlopen(
|
||||||
|
@ -122,7 +125,13 @@ class BadIPsAction(ActionBase):
|
||||||
messages['err'])
|
messages['err'])
|
||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
categories = json.loads(response.read().decode('utf-8'))['categories']
|
response_json = json.loads(response.read().decode('utf-8'))
|
||||||
|
if not 'categories' in response_json:
|
||||||
|
err = "badips.com response lacked categories specification. Response was: %s" \
|
||||||
|
% (response_json,)
|
||||||
|
self._logSys.error(err)
|
||||||
|
raise ValueError(err)
|
||||||
|
categories = response_json['categories']
|
||||||
categories_names = set(
|
categories_names = set(
|
||||||
value['Name'] for value in categories)
|
value['Name'] for value in categories)
|
||||||
if incParents:
|
if incParents:
|
||||||
|
|
|
@ -38,7 +38,7 @@ actioncheck =
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
# requires an ipfw rule like "deny ip from table(1) to me"
|
# requires an ipfw rule like "deny ip from table(1) to me"
|
||||||
actionban = ipfw table <table> add <ip>
|
actionban = e=`ipfw table <table> add <ip> 2>&1`; x=$?; [ $x -eq 0 -o "$e" = 'ipfw: setsockopt(IP_FW_TABLE_XADD): File exists' ] || { echo "$e" 1>&2; exit $x; }
|
||||||
|
|
||||||
|
|
||||||
# Option: actionunban
|
# Option: actionunban
|
||||||
|
@ -47,7 +47,7 @@ actionban = ipfw table <table> add <ip>
|
||||||
# Tags: See jail.conf(5) man page
|
# Tags: See jail.conf(5) man page
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionunban = ipfw table <table> delete <ip>
|
actionunban = e=`ipfw table <table> delete <ip> 2>&1`; x=$?; [ $x -eq 0 -o "$e" = 'ipfw: setsockopt(IP_FW_TABLE_XDEL): No such process' ] || { echo "$e" 1>&2; exit $x; }
|
||||||
|
|
||||||
[Init]
|
[Init]
|
||||||
# Option: table
|
# Option: table
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
#
|
#
|
||||||
# Author: Mike Rushton
|
# Author: Mike Rushton
|
||||||
#
|
#
|
||||||
# Referenced from from http://www.normyee.net/blog/2012/02/02/adding-cloudflare-support-to-fail2ban by NORM YEE
|
# IMPORTANT
|
||||||
#
|
#
|
||||||
# To get your Cloudflare API key: https://www.cloudflare.com/my-account
|
# Please set jail.local's permission to 640 because it contains your CF API key.
|
||||||
#
|
#
|
||||||
|
# This action depends on curl.
|
||||||
|
# Referenced from http://www.normyee.net/blog/2012/02/02/adding-cloudflare-support-to-fail2ban by NORM YEE
|
||||||
|
#
|
||||||
|
# To get your CloudFlare API Key: https://www.cloudflare.com/a/account/my-account
|
||||||
|
|
||||||
[Definition]
|
[Definition]
|
||||||
|
|
||||||
|
@ -34,7 +38,8 @@ actioncheck =
|
||||||
# <time> unix timestamp of the ban time
|
# <time> unix timestamp of the ban time
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionban = curl https://www.cloudflare.com/api_json.html -d 'a=ban' -d 'tkn=<cftoken>' -d 'email=<cfuser>' -d 'key=<ip>'
|
actionban = curl -s -o /dev/null https://www.cloudflare.com/api_json.html -d 'a=ban' -d 'tkn=<cftoken>' -d 'email=<cfuser>' -d 'key=<ip>'
|
||||||
|
|
||||||
# Option: actionunban
|
# Option: actionunban
|
||||||
# Notes.: command executed when unbanning an IP. Take care that the
|
# Notes.: command executed when unbanning an IP. Take care that the
|
||||||
# command is executed with Fail2Ban user rights.
|
# command is executed with Fail2Ban user rights.
|
||||||
|
@ -43,13 +48,19 @@ actionban = curl https://www.cloudflare.com/api_json.html -d 'a=ban' -d 'tkn=<cf
|
||||||
# <time> unix timestamp of the ban time
|
# <time> unix timestamp of the ban time
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionunban = curl https://www.cloudflare.com/api_json.html -d 'a=nul' -d 'tkn=<cftoken>' -d 'email=<cfuser>' -d 'key=<ip>'
|
actionunban = curl -s -o /dev/null https://www.cloudflare.com/api_json.html -d 'a=nul' -d 'tkn=<cftoken>' -d 'email=<cfuser>' -d 'key=<ip>'
|
||||||
|
|
||||||
|
|
||||||
[Init]
|
[Init]
|
||||||
|
|
||||||
# Default Cloudflare API token
|
# If you like to use this action with mailing whois lines, you could use the composite action
|
||||||
|
# action_cf_mwl predefined in jail.conf, just define in your jail:
|
||||||
|
#
|
||||||
|
# action = %(action_cf_mwl)s
|
||||||
|
# # Your CF account e-mail
|
||||||
|
# cfemail =
|
||||||
|
# # Your CF API Key
|
||||||
|
# cfapikey =
|
||||||
|
|
||||||
cftoken =
|
cftoken =
|
||||||
|
|
||||||
# Default Cloudflare username
|
|
||||||
cfuser =
|
cfuser =
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
before = iptables-blocktype.conf
|
before = iptables-blocktype.conf
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
|
||||||
actionstart = firewall-cmd --direct --add-chain ipv4 filter f2b-<name>
|
actionstart = firewall-cmd --direct --add-chain ipv4 filter f2b-<name>
|
||||||
firewall-cmd --direct --add-rule ipv4 filter f2b-<name> 1000 -j RETURN
|
firewall-cmd --direct --add-rule ipv4 filter f2b-<name> 1000 -j RETURN
|
||||||
firewall-cmd --direct --add-rule ipv4 filter <chain> 0 -j f2b-<name>
|
firewall-cmd --direct --add-rule ipv4 filter <chain> 0 -j f2b-<name>
|
||||||
|
@ -17,10 +19,9 @@ actionstop = firewall-cmd --direct --remove-rule ipv4 filter <chain> 0 -j f2b-<n
|
||||||
firewall-cmd --direct --remove-chain ipv4 filter f2b-<name>
|
firewall-cmd --direct --remove-chain ipv4 filter f2b-<name>
|
||||||
|
|
||||||
|
|
||||||
# Note: uses regular expression whitespaces '\s' & end of line '$'
|
# Example actioncheck: firewall-cmd --direct --get-chains ipv4 filter | sed -e 's, ,\n,g' | grep -q '^f2b-recidive$'
|
||||||
# Example actioncheck: firewall-cmd --direct --get-chains ipv4 filter | grep -q '\sf2b-recidive$'
|
|
||||||
|
|
||||||
actioncheck = firewall-cmd --direct --get-chains ipv4 filter | grep -q '\sf2b-<name>$'
|
actioncheck = firewall-cmd --direct --get-chains ipv4 filter | sed -e 's, ,\n,g' | grep -q '^f2b-<name>$'
|
||||||
|
|
||||||
actionban = firewall-cmd --direct --add-rule ipv4 filter f2b-<name> 0 -s <ip> -j <blocktype>
|
actionban = firewall-cmd --direct --add-rule ipv4 filter f2b-<name> 0 -s <ip> -j <blocktype>
|
||||||
|
|
||||||
|
|
|
@ -17,10 +17,9 @@ actionstop = firewall-cmd --direct --remove-rule ipv4 filter <chain> 0 -m state
|
||||||
firewall-cmd --direct --remove-rules ipv4 filter f2b-<name>
|
firewall-cmd --direct --remove-rules ipv4 filter f2b-<name>
|
||||||
firewall-cmd --direct --remove-chain ipv4 filter f2b-<name>
|
firewall-cmd --direct --remove-chain ipv4 filter f2b-<name>
|
||||||
|
|
||||||
# Note: uses regular expression whitespaces '\s' & end of line '$'
|
# Example actioncheck: firewall-cmd --direct --get-chains ipv4 filter | sed -e 's, ,\n,g' | grep -q '^f2b-apache-modsecurity$'
|
||||||
# Example actioncheck: firewall-cmd --direct --get-chains ipv4 filter | grep -q '\sf2b-apache-modsecurity$'
|
|
||||||
|
|
||||||
actioncheck = firewall-cmd --direct --get-chains ipv4 filter | grep -q '\sf2b-<name>$'
|
actioncheck = firewall-cmd --direct --get-chains ipv4 filter | sed -e 's, ,\n,g' | grep -q '^f2b-<name>$'
|
||||||
|
|
||||||
actionban = firewall-cmd --direct --add-rule ipv4 filter f2b-<name> 0 -s <ip> -j <blocktype>
|
actionban = firewall-cmd --direct --add-rule ipv4 filter f2b-<name> 0 -s <ip> -j <blocktype>
|
||||||
|
|
||||||
|
@ -59,6 +58,6 @@ protocol = tcp
|
||||||
# $ sudo firewall-cmd --direct --add-rule ipv4 filter INPUT_direct 0 -m state --state NEW -p tcp -m multiport --dports 80,443 -j f2b-apache-modsecurity
|
# $ sudo firewall-cmd --direct --add-rule ipv4 filter INPUT_direct 0 -m state --state NEW -p tcp -m multiport --dports 80,443 -j f2b-apache-modsecurity
|
||||||
# success
|
# success
|
||||||
# actioncheck:
|
# actioncheck:
|
||||||
# $ firewall-cmd --direct --get-chains ipv4 filter f2b-apache-modsecurity | grep -q '\sf2b-apache-modsecurity$'
|
# $ firewall-cmd --direct --get-chains ipv4 filter f2b-apache-modsecurity | sed -e 's, ,\n,g' | grep -q '^f2b-apache-modsecurity$'
|
||||||
# f2b-apache-modsecurity
|
# f2b-apache-modsecurity
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
# Fail2Ban configuration file
|
||||||
|
#
|
||||||
|
# Author: Andrew St. Jean
|
||||||
|
#
|
||||||
|
# Use nsupdate to perform dynamic DNS updates on a BIND zone file.
|
||||||
|
# One may want to do this to update a local RBL with banned IP addresses.
|
||||||
|
#
|
||||||
|
# Options
|
||||||
|
#
|
||||||
|
# domain DNS domain that will appear in nsupdate add and delete
|
||||||
|
# commands.
|
||||||
|
#
|
||||||
|
# ttl The time to live (TTL) in seconds of the TXT resource
|
||||||
|
# record.
|
||||||
|
#
|
||||||
|
# rdata Data portion of the TXT resource record.
|
||||||
|
#
|
||||||
|
# nsupdatecmd Full path to the nsupdate command.
|
||||||
|
#
|
||||||
|
# keyfile Full path to TSIG key file used for authentication between
|
||||||
|
# nsupdate and BIND.
|
||||||
|
#
|
||||||
|
# Create an nsupdate.local to set at least the <domain> and <keyfile>
|
||||||
|
# options as they don't have default values.
|
||||||
|
#
|
||||||
|
# The ban and unban commands assume nsupdate will authenticate to the BIND
|
||||||
|
# server using a TSIG key. The full path to the key file must be specified
|
||||||
|
# in the <keyfile> parameter. Use this command to generate your TSIG key.
|
||||||
|
#
|
||||||
|
# dnssec-keygen -a HMAC-MD5 -b 256 -n HOST <key_name>
|
||||||
|
#
|
||||||
|
# Replace <key_name> with some meaningful name.
|
||||||
|
#
|
||||||
|
# This command will generate two files. Specify the .private file in the
|
||||||
|
# <keyfile> option. Note that the .key file must also be present in the same
|
||||||
|
# directory for nsupdate to use the key.
|
||||||
|
#
|
||||||
|
# Don't forget to add the key and appropriate allow-update or update-policy
|
||||||
|
# option to your named.conf file.
|
||||||
|
#
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
|
||||||
|
# Option: actionstart
|
||||||
|
# Notes.: command executed once at the start of Fail2Ban.
|
||||||
|
# Values: CMD
|
||||||
|
#
|
||||||
|
actionstart =
|
||||||
|
|
||||||
|
|
||||||
|
# Option: actionstop
|
||||||
|
# Notes.: command executed once at the end of Fail2Ban
|
||||||
|
# Values: CMD
|
||||||
|
#
|
||||||
|
actionstop =
|
||||||
|
|
||||||
|
|
||||||
|
# Option: actioncheck
|
||||||
|
# Notes.: command executed once before each actionban command
|
||||||
|
# Values: CMD
|
||||||
|
#
|
||||||
|
actioncheck =
|
||||||
|
|
||||||
|
# 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 = echo <ip> | awk -F. '{print "prereq nxrrset "$4"."$3"."$2"."$1".<domain> TXT"; print "update add "$4"."$3"."$2"."$1".<domain> <ttl> IN TXT \"<rdata>\""; print "send"}' | <nsupdatecmd> -k <keyfile>
|
||||||
|
|
||||||
|
# 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 = echo <ip> | awk -F. '{print "update delete "$4"."$3"."$2"."$1".<domain>"; print "send"}' | <nsupdatecmd> -k <keyfile>
|
||||||
|
|
||||||
|
[Init]
|
||||||
|
|
||||||
|
# Option: domain
|
||||||
|
# Notes.: DNS domain that nsupdate will update.
|
||||||
|
# Values: STRING
|
||||||
|
#
|
||||||
|
domain =
|
||||||
|
|
||||||
|
# Option: ttl
|
||||||
|
# Notes.: time to live (TTL) in seconds of TXT resource record
|
||||||
|
# added by nsupdate.
|
||||||
|
# Values: NUM
|
||||||
|
#
|
||||||
|
ttl = 60
|
||||||
|
|
||||||
|
# Option: rdata
|
||||||
|
# Notes.: data portion of the TXT resource record added by nsupdate.
|
||||||
|
# Values: STRING
|
||||||
|
#
|
||||||
|
rdata = Your IP has been banned
|
||||||
|
|
||||||
|
# Option: nsupdatecmd
|
||||||
|
# Notes.: specifies the full path to the nsupdate program that dynamically
|
||||||
|
# updates BIND zone files.
|
||||||
|
# Values: CMD
|
||||||
|
#
|
||||||
|
nsupdatecmd = /usr/bin/nsupdate
|
||||||
|
|
||||||
|
# Option: keyfile
|
||||||
|
# Notes.: specifies the full path to the file containing the
|
||||||
|
# TSIG key for communicating with BIND.
|
||||||
|
# Values: STRING
|
||||||
|
#
|
||||||
|
keyfile =
|
||||||
|
|
|
@ -15,7 +15,7 @@ after = sendmail-common.local
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstart = printf %%b "Subject: [Fail2Ban] <name>: started on `uname -n`
|
actionstart = printf %%b "Subject: [Fail2Ban] <name>: started on `uname -n`
|
||||||
Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"`
|
Date: `LC_ALL=C date +"%%a, %%d %%h %%Y %%T %%z"`
|
||||||
From: <sendername> <<sender>>
|
From: <sendername> <<sender>>
|
||||||
To: <dest>\n
|
To: <dest>\n
|
||||||
Hi,\n
|
Hi,\n
|
||||||
|
@ -28,7 +28,7 @@ actionstart = printf %%b "Subject: [Fail2Ban] <name>: started on `uname -n`
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstop = printf %%b "Subject: [Fail2Ban] <name>: stopped on `uname -n`
|
actionstop = printf %%b "Subject: [Fail2Ban] <name>: stopped on `uname -n`
|
||||||
Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"`
|
Date: `LC_ALL=C date +"%%a, %%d %%h %%Y %%T %%z"`
|
||||||
From: <sendername> <<sender>>
|
From: <sendername> <<sender>>
|
||||||
To: <dest>\n
|
To: <dest>\n
|
||||||
Hi,\n
|
Hi,\n
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
# Fail2Ban configuration file
|
||||||
|
#
|
||||||
|
# Author: Viktor Szépe
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
[INCLUDES]
|
||||||
|
|
||||||
|
before = sendmail-common.conf
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
|
||||||
|
# Option: actionban
|
||||||
|
# Notes.: Command executed when banning an IP. Take care that the
|
||||||
|
# command is executed with Fail2Ban user rights.
|
||||||
|
# You need to install geoiplookup and the GeoLite or GeoIP databases.
|
||||||
|
# (geoip-bin and geoip-database in Debian)
|
||||||
|
# The host command comes from bind9-host package.
|
||||||
|
# Tags: See jail.conf(5) man page
|
||||||
|
# Values: CMD
|
||||||
|
#
|
||||||
|
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
||||||
|
Date: `LC_ALL=C date +"%%a, %%d %%h %%Y %%T %%z"`
|
||||||
|
From: <sendername> <<sender>>
|
||||||
|
To: <dest>\n
|
||||||
|
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
|
||||||
|
http://bgp.he.net/ip/<ip>
|
||||||
|
http://www.projecthoneypot.org/ip_<ip>
|
||||||
|
http://whois.domaintools.com/<ip>\n\n
|
||||||
|
Country:`geoiplookup -f /usr/share/GeoIP/GeoIP.dat "<ip>" | cut -d':' -f2-`
|
||||||
|
AS:`geoiplookup -f /usr/share/GeoIP/GeoIPASNum.dat "<ip>" | cut -d':' -f2-`
|
||||||
|
hostname: `host -t A <ip> 2>&1`\n\n
|
||||||
|
Lines containing IP:<ip> in <logpath>\n
|
||||||
|
`grep -E '(^|[^0-9])<ip>([^0-9]|$)' <logpath>`\n\n
|
||||||
|
Regards,\n
|
||||||
|
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
|
||||||
|
|
||||||
|
[Init]
|
||||||
|
|
||||||
|
# Default name of the chain
|
||||||
|
#
|
||||||
|
name = default
|
||||||
|
|
||||||
|
# Path to the log files which contain relevant lines for the abuser IP
|
||||||
|
#
|
||||||
|
logpath = /dev/null
|
|
@ -17,13 +17,13 @@ before = sendmail-common.conf
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
||||||
Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"`
|
Date: `LC_ALL=C date +"%%a, %%d %%h %%Y %%T %%z"`
|
||||||
From: <sendername> <<sender>>
|
From: <sendername> <<sender>>
|
||||||
To: <dest>\n
|
To: <dest>\n
|
||||||
Hi,\n
|
Hi,\n
|
||||||
The IP <ip> has just been banned by Fail2Ban after
|
The IP <ip> has just been banned by Fail2Ban after
|
||||||
<failures> attempts against <name>.\n\n
|
<failures> attempts against <name>.\n\n
|
||||||
Here are more information about <ip>:\n
|
Here is more information about <ip>:\n
|
||||||
`/usr/bin/whois <ip>`\n\n
|
`/usr/bin/whois <ip>`\n\n
|
||||||
Matches for <name> with <ipjailfailures> failures IP:<ip>\n
|
Matches for <name> with <ipjailfailures> failures IP:<ip>\n
|
||||||
<ipjailmatches>\n\n
|
<ipjailmatches>\n\n
|
||||||
|
|
|
@ -17,13 +17,13 @@ before = sendmail-common.conf
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
||||||
Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"`
|
Date: `LC_ALL=C date +"%%a, %%d %%h %%Y %%T %%z"`
|
||||||
From: <sendername> <<sender>>
|
From: <sendername> <<sender>>
|
||||||
To: <dest>\n
|
To: <dest>\n
|
||||||
Hi,\n
|
Hi,\n
|
||||||
The IP <ip> has just been banned by Fail2Ban after
|
The IP <ip> has just been banned by Fail2Ban after
|
||||||
<failures> attempts against <name>.\n\n
|
<failures> attempts against <name>.\n\n
|
||||||
Here are more information about <ip>:\n
|
Here is more information about <ip>:\n
|
||||||
`/usr/bin/whois <ip>`\n\n
|
`/usr/bin/whois <ip>`\n\n
|
||||||
Matches with <ipfailures> failures IP:<ip>\n
|
Matches with <ipfailures> failures IP:<ip>\n
|
||||||
<ipmatches>\n\n
|
<ipmatches>\n\n
|
||||||
|
|
|
@ -17,7 +17,7 @@ before = sendmail-common.conf
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
||||||
Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"`
|
Date: `LC_ALL=C date +"%%a, %%d %%h %%Y %%T %%z"`
|
||||||
From: <sendername> <<sender>>
|
From: <sendername> <<sender>>
|
||||||
To: <dest>\n
|
To: <dest>\n
|
||||||
Hi,\n
|
Hi,\n
|
||||||
|
|
|
@ -17,13 +17,13 @@ before = sendmail-common.conf
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
||||||
Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"`
|
Date: `LC_ALL=C date +"%%a, %%d %%h %%Y %%T %%z"`
|
||||||
From: <sendername> <<sender>>
|
From: <sendername> <<sender>>
|
||||||
To: <dest>\n
|
To: <dest>\n
|
||||||
Hi,\n
|
Hi,\n
|
||||||
The IP <ip> has just been banned by Fail2Ban after
|
The IP <ip> has just been banned by Fail2Ban after
|
||||||
<failures> attempts against <name>.\n\n
|
<failures> attempts against <name>.\n\n
|
||||||
Here are more information about <ip>:\n
|
Here is more information about <ip>:\n
|
||||||
`/usr/bin/whois <ip>`\n\n
|
`/usr/bin/whois <ip>`\n\n
|
||||||
Matches:\n
|
Matches:\n
|
||||||
<matches>\n\n
|
<matches>\n\n
|
||||||
|
|
|
@ -17,7 +17,7 @@ before = sendmail-common.conf
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
||||||
Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"`
|
Date: `LC_ALL=C date +"%%a, %%d %%h %%Y %%T %%z"`
|
||||||
From: <sendername> <<sender>>
|
From: <sendername> <<sender>>
|
||||||
To: <dest>\n
|
To: <dest>\n
|
||||||
Hi,\n
|
Hi,\n
|
||||||
|
|
|
@ -17,7 +17,7 @@ before = sendmail-common.conf
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
||||||
Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"`
|
Date: `LC_ALL=C date +"%%a, %%d %%h %%Y %%T %%z"`
|
||||||
From: <sendername> <<sender>>
|
From: <sendername> <<sender>>
|
||||||
To: <dest>\n
|
To: <dest>\n
|
||||||
Hi,\n
|
Hi,\n
|
||||||
|
|
|
@ -68,6 +68,7 @@ Matches for %(ip)s for jail %(jailname)s:
|
||||||
%(ipjailmatches)s
|
%(ipjailmatches)s
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class SMTPAction(ActionBase):
|
class SMTPAction(ActionBase):
|
||||||
"""Fail2Ban action which sends emails to inform on jail starting,
|
"""Fail2Ban action which sends emails to inform on jail starting,
|
||||||
stopping and bans.
|
stopping and bans.
|
||||||
|
|
|
@ -13,9 +13,11 @@ actionstop =
|
||||||
|
|
||||||
actioncheck =
|
actioncheck =
|
||||||
|
|
||||||
actionban = [ -n "<application>" ] && app="app <application>" ; ufw insert <insertpos> <blocktype> from <ip> to <destination> $app
|
actionban = [ -n "<application>" ] && app="app <application>"
|
||||||
|
ufw insert <insertpos> <blocktype> from <ip> to <destination> $app
|
||||||
|
|
||||||
actionunban = [ -n "<application>" ] && app="app <application>" ; ufw delete <blocktype> from <ip> to <destination> $app
|
actionunban = [ -n "<application>" ] && app="app <application>"
|
||||||
|
ufw delete <blocktype> from <ip> to <destination> $app
|
||||||
|
|
||||||
[Init]
|
[Init]
|
||||||
# Option: insertpos
|
# Option: insertpos
|
||||||
|
|
|
@ -46,7 +46,7 @@ actionban = oifs=${IFS}; IFS=.;SEP_IP=( <ip> ); set -- ${SEP_IP}; ADDRESSES=$(di
|
||||||
REPORTID=<time>@`uname -n`
|
REPORTID=<time>@`uname -n`
|
||||||
TLP=<tlp>
|
TLP=<tlp>
|
||||||
PORT=<port>
|
PORT=<port>
|
||||||
DATE=`LC_TIME=C date -u --date=@<time> +"%%a, %%d %%h %%Y %%T +0000"`
|
DATE=`LC_ALL=C date --date=@<time> +"%%a, %%d %%h %%Y %%T %%z"`
|
||||||
if [ ! -z "$ADDRESSES" ]; then
|
if [ ! -z "$ADDRESSES" ]; then
|
||||||
(printf -- %%b "<header>\n<message>\n<report>\n";
|
(printf -- %%b "<header>\n<message>\n<report>\n";
|
||||||
date '+Note: Local timezone is %%z (%%Z)';
|
date '+Note: Local timezone is %%z (%%Z)';
|
||||||
|
|
|
@ -34,6 +34,12 @@ loglevel = INFO
|
||||||
#
|
#
|
||||||
logtarget = /var/log/fail2ban.log
|
logtarget = /var/log/fail2ban.log
|
||||||
|
|
||||||
|
# Option: syslogsocket
|
||||||
|
# Notes: Set the syslog socket file. Only used when logtarget is SYSLOG
|
||||||
|
# auto uses platform.system() to determine predefined paths
|
||||||
|
# Values: [ auto | FILE ] Default: auto
|
||||||
|
syslogsocket = auto
|
||||||
|
|
||||||
# Option: socket
|
# Option: socket
|
||||||
# Notes.: Set the socket file. This is used to communicate with the daemon. Do
|
# Notes.: Set the socket file. This is used to communicate with the daemon. Do
|
||||||
# not remove this file when Fail2ban runs. It will not be possible to
|
# not remove this file when Fail2ban runs. It will not be possible to
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
badbotscustom = EmailCollector|WebEMailExtrac|TrackBack/1\.02|sogou music spider
|
badbotscustom = EmailCollector|WebEMailExtrac|TrackBack/1\.02|sogou music spider
|
||||||
badbots = Atomic_Email_Hunter/4\.0|atSpider/1\.0|autoemailspider|bwh3_user_agent|China Local Browse 2\.6|ContactBot/0\.2|ContentSmartz|DataCha0s/2\.0|DBrowse 1\.4b|DBrowse 1\.4d|Demo Bot DOT 16b|Demo Bot Z 16b|DSurf15a 01|DSurf15a 71|DSurf15a 81|DSurf15a VA|EBrowse 1\.4b|Educate Search VxB|EmailSiphon|EmailSpider|EmailWolf 1\.00|ESurf15a 15|ExtractorPro|Franklin Locator 1\.8|FSurf15a 01|Full Web Bot 0416B|Full Web Bot 0516B|Full Web Bot 2816B|Guestbook Auto Submitter|Industry Program 1\.0\.x|ISC Systems iRc Search 2\.1|IUPUI Research Bot v 1\.9a|LARBIN-EXPERIMENTAL \(efp@gmx\.net\)|LetsCrawl\.com/1\.0 +http\://letscrawl\.com/|Lincoln State Web Browser|LMQueueBot/0\.2|LWP\:\:Simple/5\.803|Mac Finder 1\.0\.xx|MFC Foundation Class Library 4\.0|Microsoft URL Control - 6\.00\.8xxx|Missauga Locate 1\.0\.0|Missigua Locator 1\.9|Missouri College Browse|Mizzu Labs 2\.2|Mo College 1\.9|MVAClient|Mozilla/2\.0 \(compatible; NEWT ActiveX; Win32\)|Mozilla/3\.0 \(compatible; Indy Library\)|Mozilla/3\.0 \(compatible; scan4mail \(advanced version\) http\://www\.peterspages\.net/?scan4mail\)|Mozilla/4\.0 \(compatible; Advanced Email Extractor v2\.xx\)|Mozilla/4\.0 \(compatible; Iplexx Spider/1\.0 http\://www\.iplexx\.at\)|Mozilla/4\.0 \(compatible; MSIE 5\.0; Windows NT; DigExt; DTS Agent|Mozilla/4\.0 efp@gmx\.net|Mozilla/5\.0 \(Version\: xxxx Type\:xx\)|NameOfAgent \(CMS Spider\)|NASA Search 1\.0|Nsauditor/1\.x|PBrowse 1\.4b|PEval 1\.4b|Poirot|Port Huron Labs|Production Bot 0116B|Production Bot 2016B|Production Bot DOT 3016B|Program Shareware 1\.0\.2|PSurf15a 11|PSurf15a 51|PSurf15a VA|psycheclone|RSurf15a 41|RSurf15a 51|RSurf15a 81|searchbot admin@google\.com|ShablastBot 1\.0|snap\.com beta crawler v0|Snapbot/1\.0|Snapbot/1\.0 \(Snap Shots, +http\://www\.snap\.com\)|sogou develop spider|Sogou Orion spider/3\.0\(+http\://www\.sogou\.com/docs/help/webmasters\.htm#07\)|sogou spider|Sogou web spider/3\.0\(+http\://www\.sogou\.com/docs/help/webmasters\.htm#07\)|sohu agent|SSurf15a 11 |TSurf15a 11|Under the Rainbow 2\.2|User-Agent\: Mozilla/4\.0 \(compatible; MSIE 6\.0; Windows NT 5\.1\)|VadixBot|WebVulnCrawl\.unknown/1\.0 libwww-perl/5\.803|Wells Search II|WEP Search 00
|
badbots = Atomic_Email_Hunter/4\.0|atSpider/1\.0|autoemailspider|bwh3_user_agent|China Local Browse 2\.6|ContactBot/0\.2|ContentSmartz|DataCha0s/2\.0|DBrowse 1\.4b|DBrowse 1\.4d|Demo Bot DOT 16b|Demo Bot Z 16b|DSurf15a 01|DSurf15a 71|DSurf15a 81|DSurf15a VA|EBrowse 1\.4b|Educate Search VxB|EmailSiphon|EmailSpider|EmailWolf 1\.00|ESurf15a 15|ExtractorPro|Franklin Locator 1\.8|FSurf15a 01|Full Web Bot 0416B|Full Web Bot 0516B|Full Web Bot 2816B|Guestbook Auto Submitter|Industry Program 1\.0\.x|ISC Systems iRc Search 2\.1|IUPUI Research Bot v 1\.9a|LARBIN-EXPERIMENTAL \(efp@gmx\.net\)|LetsCrawl\.com/1\.0 +http\://letscrawl\.com/|Lincoln State Web Browser|LMQueueBot/0\.2|LWP\:\:Simple/5\.803|Mac Finder 1\.0\.xx|MFC Foundation Class Library 4\.0|Microsoft URL Control - 6\.00\.8xxx|Missauga Locate 1\.0\.0|Missigua Locator 1\.9|Missouri College Browse|Mizzu Labs 2\.2|Mo College 1\.9|MVAClient|Mozilla/2\.0 \(compatible; NEWT ActiveX; Win32\)|Mozilla/3\.0 \(compatible; Indy Library\)|Mozilla/3\.0 \(compatible; scan4mail \(advanced version\) http\://www\.peterspages\.net/?scan4mail\)|Mozilla/4\.0 \(compatible; Advanced Email Extractor v2\.xx\)|Mozilla/4\.0 \(compatible; Iplexx Spider/1\.0 http\://www\.iplexx\.at\)|Mozilla/4\.0 \(compatible; MSIE 5\.0; Windows NT; DigExt; DTS Agent|Mozilla/4\.0 efp@gmx\.net|Mozilla/5\.0 \(Version\: xxxx Type\:xx\)|NameOfAgent \(CMS Spider\)|NASA Search 1\.0|Nsauditor/1\.x|PBrowse 1\.4b|PEval 1\.4b|Poirot|Port Huron Labs|Production Bot 0116B|Production Bot 2016B|Production Bot DOT 3016B|Program Shareware 1\.0\.2|PSurf15a 11|PSurf15a 51|PSurf15a VA|psycheclone|RSurf15a 41|RSurf15a 51|RSurf15a 81|searchbot admin@google\.com|ShablastBot 1\.0|snap\.com beta crawler v0|Snapbot/1\.0|Snapbot/1\.0 \(Snap Shots, +http\://www\.snap\.com\)|sogou develop spider|Sogou Orion spider/3\.0\(+http\://www\.sogou\.com/docs/help/webmasters\.htm#07\)|sogou spider|Sogou web spider/3\.0\(+http\://www\.sogou\.com/docs/help/webmasters\.htm#07\)|sohu agent|SSurf15a 11 |TSurf15a 11|Under the Rainbow 2\.2|User-Agent\: Mozilla/4\.0 \(compatible; MSIE 6\.0; Windows NT 5\.1\)|VadixBot|WebVulnCrawl\.unknown/1\.0 libwww-perl/5\.803|Wells Search II|WEP Search 00
|
||||||
|
|
||||||
failregex = ^<HOST> -.*"(GET|POST).*HTTP.*"(?:%(badbots)s|%(badbotscustom)s)"$
|
failregex = ^<HOST> -.*"(GET|POST|HEAD).*HTTP.*"(?:%(badbots)s|%(badbotscustom)s)"$
|
||||||
|
|
||||||
ignoreregex =
|
ignoreregex =
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,9 @@
|
||||||
[INCLUDES]
|
[INCLUDES]
|
||||||
|
|
||||||
# overwrite with apache-common.local if _apache_error_client is incorrect.
|
# overwrite with apache-common.local if _apache_error_client is incorrect.
|
||||||
|
# Load regexes for filtering from botsearch-common.conf
|
||||||
before = apache-common.conf
|
before = apache-common.conf
|
||||||
|
botsearch-common.conf
|
||||||
|
|
||||||
[Definition]
|
[Definition]
|
||||||
|
|
||||||
|
@ -31,16 +33,6 @@ ignoreregex =
|
||||||
|
|
||||||
# Webroot represents the webroot on which all other files are based
|
# Webroot represents the webroot on which all other files are based
|
||||||
webroot = /var/www/
|
webroot = /var/www/
|
||||||
# Block is the actual non-found directories to block
|
|
||||||
block = (<webmail>|<phpmyadmin>|<wordpress>)[^,]*
|
|
||||||
|
|
||||||
# These are just convient definitions that assist the blocking of stuff that
|
|
||||||
# isn't installed
|
|
||||||
webmail = roundcube|(ext)?mail|horde|(v-?)?webmail
|
|
||||||
|
|
||||||
phpmyadmin = (typo3/|xampp/|admin/|)(pma|(php)?[Mm]y[Aa]dmin)
|
|
||||||
|
|
||||||
wordpress = wp-(login|signup)\.php
|
|
||||||
|
|
||||||
|
|
||||||
# DEV Notes:
|
# DEV Notes:
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
# Fail2Ban filter for fake Googlebot User Agents
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
|
||||||
|
failregex = ^<HOST> .*Googlebot.*$
|
||||||
|
|
||||||
|
ignoreregex =
|
||||||
|
|
||||||
|
|
||||||
|
# DEV Notes:
|
||||||
|
#
|
||||||
|
# Author: Lee Clemens
|
||||||
|
# Thanks: Johannes B. Ullrich, Ph.D.
|
||||||
|
# Reference: https://isc.sans.edu/forums/diary/When+Google+isnt+Google/15968/
|
|
@ -13,6 +13,8 @@ _daemon = asterisk
|
||||||
|
|
||||||
__pid_re = (?:\[\d+\])
|
__pid_re = (?:\[\d+\])
|
||||||
|
|
||||||
|
iso8601 = \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+[+-]\d{4}
|
||||||
|
|
||||||
# All Asterisk log messages begin like this:
|
# All Asterisk log messages begin like this:
|
||||||
log_prefix= (?:NOTICE|SECURITY)%(__pid_re)s:?(?:\[C-[\da-f]*\])? \S+:\d*( in \w+:)?
|
log_prefix= (?:NOTICE|SECURITY)%(__pid_re)s:?(?:\[C-[\da-f]*\])? \S+:\d*( in \w+:)?
|
||||||
|
|
||||||
|
@ -22,8 +24,8 @@ failregex = ^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s Registration from '[^']*'
|
||||||
^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s No registration for peer '[^']*' \(from <HOST>\)$
|
^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s No registration for peer '[^']*' \(from <HOST>\)$
|
||||||
^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s Host <HOST> failed MD5 authentication for '[^']*' \([^)]+\)$
|
^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s Host <HOST> failed MD5 authentication for '[^']*' \([^)]+\)$
|
||||||
^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s Failed to authenticate (user|device) [^@]+@<HOST>\S*$
|
^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s Failed to authenticate (user|device) [^@]+@<HOST>\S*$
|
||||||
^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s (?:handle_request_subscribe: )?Sending fake auth rejection for (device|user) \d*<sip:[^@]+@<HOST>>;tag=\w+\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>"$
|
^(%(__prefix_line)s|\[\]\s*WARNING%(__pid_re)s:?(?:\[C-[\da-f]*\])? )Ext\. s: "Rejecting unknown SIP connection from <HOST>"$
|
||||||
|
|
||||||
ignoreregex =
|
ignoreregex =
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Generic configuration file for -botsearch filters
|
||||||
|
|
||||||
|
[Init]
|
||||||
|
|
||||||
|
# Block is the actual non-found directories to block
|
||||||
|
block = \/?(<webmail>|<phpmyadmin>|<wordpress>|cgi-bin|mysqladmin)[^,]*
|
||||||
|
|
||||||
|
# These are just convient definitions that assist the blocking of stuff that
|
||||||
|
# isn't installed
|
||||||
|
webmail = roundcube|(ext)?mail|horde|(v-?)?webmail
|
||||||
|
|
||||||
|
phpmyadmin = (typo3/|xampp/|admin/|)(pma|(php)?[Mm]y[Aa]dmin)
|
||||||
|
|
||||||
|
wordpress = wp-(login|signup)\.php
|
||||||
|
|
||||||
|
# DEV Notes:
|
||||||
|
# Taken from apache-botsearch filter
|
||||||
|
#
|
||||||
|
# Author: Frantisek Sumsal
|
|
@ -53,4 +53,8 @@ __bsd_syslog_verbose = (<[^.]+\.[^.]+>)
|
||||||
# This can be optional (for instance if we match named native log files)
|
# This can be optional (for instance if we match named native log files)
|
||||||
__prefix_line = \s*%(__bsd_syslog_verbose)s?\s*(?:%(__hostname)s )?(?:%(__kernel_prefix)s )?(?:@vserver_\S+ )?%(__daemon_combs_re)s?\s%(__daemon_extra_re)s?\s*
|
__prefix_line = \s*%(__bsd_syslog_verbose)s?\s*(?:%(__hostname)s )?(?:%(__kernel_prefix)s )?(?:@vserver_\S+ )?%(__daemon_combs_re)s?\s%(__daemon_extra_re)s?\s*
|
||||||
|
|
||||||
|
# PAM authentication mechanism check for failures, e.g.: pam_unix, pam_sss,
|
||||||
|
# pam_ldap
|
||||||
|
__pam_auth = pam_unix
|
||||||
|
|
||||||
# Author: Yaroslav Halchenko
|
# Author: Yaroslav Halchenko
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
failregex = ^: Bad Rcon: "rcon \d+ "\S+" sv_contact ".*?"" from "<HOST>:\d+"$
|
failregex = ^: Bad Rcon: "rcon \d+ "\S+" sv_contact ".*?"" from "<HOST>:\d+"$
|
||||||
|
|
||||||
|
ignoreregex =
|
||||||
|
|
||||||
[Init]
|
[Init]
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,10 @@ before = common.conf
|
||||||
|
|
||||||
_daemon = (auth|dovecot(-auth)?|auth-worker)
|
_daemon = (auth|dovecot(-auth)?|auth-worker)
|
||||||
|
|
||||||
failregex = ^%(__prefix_line)s(pam_unix(\(dovecot:auth\))?:)?\s+authentication failure; logname=\S* uid=\S* euid=\S* tty=dovecot ruser=\S* rhost=<HOST>(\s+user=\S*)?\s*$
|
failregex = ^%(__prefix_line)s(%(__pam_auth)s(\(dovecot:auth\))?:)?\s+authentication failure; logname=\S* uid=\S* euid=\S* tty=dovecot ruser=\S* rhost=<HOST>(\s+user=\S*)?\s*$
|
||||||
^%(__prefix_line)s(pop3|imap)-login: (Info: )?(Aborted login|Disconnected)(: Inactivity)? \(((auth failed, \d+ attempts)( in \d+ secs)?|tried to use (disabled|disallowed) \S+ auth)\):( user=<\S*>,)?( method=\S+,)? rip=<HOST>(, lip=(\d{1,3}\.){3}\d{1,3})?(, TLS( handshaking(: SSL_accept\(\) failed: error:[\dA-F]+:SSL routines:[TLS\d]+_GET_CLIENT_HELLO:unknown protocol)?)?(: Disconnected)?)?(, session=<\S+>)?\s*$
|
^%(__prefix_line)s(pop3|imap)-login: (Info: )?(Aborted login|Disconnected)(: Inactivity)? \(((auth failed, \d+ attempts)( in \d+ secs)?|tried to use (disabled|disallowed) \S+ auth)\):( user=<\S*>,)?( method=\S+,)? rip=<HOST>(, lip=(\d{1,3}\.){3}\d{1,3})?(, TLS( handshaking(: SSL_accept\(\) failed: error:[\dA-F]+:SSL routines:[TLS\d]+_GET_CLIENT_HELLO:unknown protocol)?)?(: Disconnected)?)?(, session=<\S+>)?\s*$
|
||||||
^%(__prefix_line)s(Info|dovecot: auth\(default\)): pam\(\S+,<HOST>\): pam_authenticate\(\) failed: (User not known to the underlying authentication module: \d+ Time\(s\)|Authentication failure \(password mismatch\?\))\s*$
|
^%(__prefix_line)s(Info|dovecot: auth\(default\)|auth-worker\(\d+\)): pam\(\S+,<HOST>\): pam_authenticate\(\) failed: (User not known to the underlying authentication module: \d+ Time\(s\)|Authentication failure \(password mismatch\?\))\s*$
|
||||||
|
^%(__prefix_line)s(auth|auth-worker\(\d+\)): (pam|passwd-file)\(\S+,<HOST>\): unknown user\s*$
|
||||||
|
|
||||||
ignoreregex =
|
ignoreregex =
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
# Fail2Ban filter to block repeated failed login attempts to Drupal site(s)
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Drupal must be setup to use Syslog, which defaults to the following format:
|
||||||
|
#
|
||||||
|
# !base_url|!timestamp|!type|!ip|!request_uri|!referer|!uid|!link|!message
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
[INCLUDES]
|
||||||
|
|
||||||
|
before = common.conf
|
||||||
|
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
|
||||||
|
failregex = ^%(__prefix_line)s(https?:\/\/)([\da-z\.-]+)\.([a-z\.]{2,6})(\/[\w\.-]+)*\|\d{10}\|user\|<HOST>\|.+\|.+\|\d\|.*\|Login attempt failed for .+\.$
|
||||||
|
|
||||||
|
ignoreregex =
|
||||||
|
|
||||||
|
|
||||||
|
# DEV Notes:
|
||||||
|
#
|
||||||
|
# https://www.drupal.org/documentation/modules/syslog
|
||||||
|
#
|
||||||
|
# Author: Lee Clemens
|
|
@ -0,0 +1,37 @@
|
||||||
|
# Fail2Ban configuration file to block repeated failed login attempts to Frolor installation(s)
|
||||||
|
#
|
||||||
|
# Froxlor needs to log to Syslog User (e.g. /var/log/user.log) with one of the following messages
|
||||||
|
# <syslog prefix> Froxlor: [Login Action <HOST>] Unknown user '<USER>' tried to login.
|
||||||
|
# <syslog prefix> Froxlor: [Login Action <HOST>] User '<USER>' tried to login with wrong password.
|
||||||
|
#
|
||||||
|
# Author: Joern Muehlencord
|
||||||
|
#
|
||||||
|
|
||||||
|
[INCLUDES]
|
||||||
|
|
||||||
|
# Read common prefixes. If any customizations available -- read them from
|
||||||
|
# common.local
|
||||||
|
before = common.conf
|
||||||
|
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
|
||||||
|
_daemon = Froxlor
|
||||||
|
|
||||||
|
# Option: failregex
|
||||||
|
# Notes.: regex to match the password failures messages in the logfile. The
|
||||||
|
# host must be matched by a group named "host". The tag "<HOST>" can
|
||||||
|
# be used for standard IP/hostname matching and is only an alias for
|
||||||
|
# (?:::f{4,6}:)?(?P<host>[\w\-.^_]+)
|
||||||
|
# Values: TEXT
|
||||||
|
#
|
||||||
|
failregex = ^%(__prefix_line)s\[Login Action <HOST>\] Unknown user \S* tried to login.$
|
||||||
|
^%(__prefix_line)s\[Login Action <HOST>\] User \S* tried to login with wrong password.$
|
||||||
|
|
||||||
|
|
||||||
|
# Option: ignoreregex
|
||||||
|
# Notes.: regex to ignore. If this regex matches, the line is ignored.
|
||||||
|
# Values: TEXT
|
||||||
|
#
|
||||||
|
ignoreregex =
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
failregex = ^\[\]LOGIN FAILED for user: "\S+" from IP: <HOST>$
|
failregex = ^\[\]LOGIN FAILED for user: "\S+" from IP: <HOST>$
|
||||||
|
|
||||||
|
ignoreregex =
|
||||||
|
|
||||||
# Author: Daniel Black
|
# Author: Daniel Black
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
# Inspired by https://isc.sans.edu/forums/diary/When+Google+isnt+Google/15968/
|
||||||
|
#
|
||||||
|
# Written in Python to reuse built-in Python batteries and not depend on
|
||||||
|
# presence of host and cut commands
|
||||||
|
#
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def process_args(argv):
|
||||||
|
if len(argv) != 2:
|
||||||
|
sys.stderr.write("Please provide a single IP as an argument. Got: %s\n"
|
||||||
|
% (argv[1:]))
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
ip = argv[1]
|
||||||
|
|
||||||
|
from fail2ban.server.filter import DNSUtils
|
||||||
|
if not DNSUtils.isValidIP(ip):
|
||||||
|
sys.stderr.write("Argument must be a single valid IP. Got: %s\n"
|
||||||
|
% ip)
|
||||||
|
sys.exit(3)
|
||||||
|
return ip
|
||||||
|
|
||||||
|
def is_googlebot(ip):
|
||||||
|
import re
|
||||||
|
from fail2ban.server.filter import DNSUtils
|
||||||
|
|
||||||
|
host = DNSUtils.ipToName(ip)
|
||||||
|
sys.exit(0 if (host and re.match('crawl-.*\.googlebot\.com', host)) else 1)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
is_googlebot(process_args(sys.argv))
|
|
@ -6,6 +6,9 @@ failregex = ^ SMTP Spam attack detected from <HOST>,
|
||||||
^ IP address <HOST> found in DNS blacklist \S+, mail from \S+ to \S+$
|
^ IP address <HOST> found in DNS blacklist \S+, mail from \S+ to \S+$
|
||||||
^ Relay attempt from IP address <HOST>
|
^ Relay attempt from IP address <HOST>
|
||||||
^ Attempt to deliver to unknown recipient \S+, from \S+, IP address <HOST>$
|
^ Attempt to deliver to unknown recipient \S+, from \S+, IP address <HOST>$
|
||||||
|
|
||||||
|
ignoreregex =
|
||||||
|
|
||||||
[Init]
|
[Init]
|
||||||
|
|
||||||
datepattern = ^\[%%d/%%b/%%Y %%H:%%M:%%S\]
|
datepattern = ^\[%%d/%%b/%%Y %%H:%%M:%%S\]
|
||||||
|
|
|
@ -7,3 +7,4 @@
|
||||||
failregex = ^\[[A-Z]+\s+\]\s*error\s*:\s*Warning:\s+Client '<HOST>' supplied unknown user '\w+' accessing monit httpd$
|
failregex = ^\[[A-Z]+\s+\]\s*error\s*:\s*Warning:\s+Client '<HOST>' supplied unknown user '\w+' accessing monit httpd$
|
||||||
^\[[A-Z]+\s+\]\s*error\s*:\s*Warning:\s+Client '<HOST>' supplied wrong password for user '\w+' accessing monit httpd$
|
^\[[A-Z]+\s+\]\s*error\s*:\s*Warning:\s+Client '<HOST>' supplied wrong password for user '\w+' accessing monit httpd$
|
||||||
|
|
||||||
|
ignoreregex =
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
# Fail2Ban filter to match web requests for selected URLs that don't exist
|
||||||
|
#
|
||||||
|
|
||||||
|
[INCLUDES]
|
||||||
|
|
||||||
|
# Load regexes for filtering
|
||||||
|
before = botsearch-common.conf
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
|
||||||
|
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 =
|
||||||
|
|
||||||
|
|
||||||
|
# DEV Notes:
|
||||||
|
# Based on apache-botsearch filter
|
||||||
|
#
|
||||||
|
# Author: Frantisek Sumsal
|
|
@ -24,3 +24,5 @@ _daemon = nsd
|
||||||
|
|
||||||
failregex = ^\[\]%(__prefix_line)sinfo: ratelimit block .* query <HOST> TYPE255$
|
failregex = ^\[\]%(__prefix_line)sinfo: ratelimit block .* query <HOST> TYPE255$
|
||||||
^\[\]%(__prefix_line)sinfo: .* <HOST> refused, no acl matches\.$
|
^\[\]%(__prefix_line)sinfo: .* <HOST> refused, no acl matches\.$
|
||||||
|
|
||||||
|
ignoreregex =
|
||||||
|
|
|
@ -13,7 +13,7 @@ before = common.conf
|
||||||
# Default: catch all failed logins
|
# Default: catch all failed logins
|
||||||
_ttys_re=\S*
|
_ttys_re=\S*
|
||||||
|
|
||||||
__pam_re=\(?pam_unix(?:\(\S+\))?\)?:?
|
__pam_re=\(?%(__pam_auth)s(?:\(\S+\))?\)?:?
|
||||||
_daemon = \S+
|
_daemon = \S+
|
||||||
|
|
||||||
failregex = ^%(__prefix_line)s%(__pam_re)s\s+authentication failure; logname=\S* uid=\S* euid=\S* tty=%(_ttys_re)s ruser=\S* rhost=<HOST>(?:\s+user=.*)?\s*$
|
failregex = ^%(__prefix_line)s%(__pam_re)s\s+authentication failure; logname=\S* uid=\S* euid=\S* tty=%(_ttys_re)s ruser=\S* rhost=<HOST>(?:\s+user=.*)?\s*$
|
||||||
|
|
|
@ -6,5 +6,7 @@
|
||||||
|
|
||||||
failregex = \/<HOST> Port\: [0-9]+ (TCP|UDP) Blocked$
|
failregex = \/<HOST> Port\: [0-9]+ (TCP|UDP) Blocked$
|
||||||
|
|
||||||
|
ignoreregex =
|
||||||
|
|
||||||
# Author: Pacop <pacoparu@gmail.com>
|
# Author: Pacop <pacoparu@gmail.com>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Fail2Ban filter for Postfix's RBL based Blocked hosts
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
[INCLUDES]
|
||||||
|
|
||||||
|
# Read common prefixes. If any customizations available -- read them from
|
||||||
|
# common.local
|
||||||
|
before = common.conf
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
|
||||||
|
_daemon = postfix/smtpd
|
||||||
|
|
||||||
|
failregex = ^%(__prefix_line)sNOQUEUE: reject: RCPT from \S+\[<HOST>\]: 454 4\.7\.1 Service unavailable; Client host \[\S+\] blocked using .* from=<\S*> to=<\S+> proto=ESMTP helo=<\S*>$
|
||||||
|
|
||||||
|
ignoreregex =
|
||||||
|
|
||||||
|
# Author: Lee Clemens
|
|
@ -9,9 +9,9 @@ before = common.conf
|
||||||
|
|
||||||
_daemon = postfix/(submission/)?smtp(d|s)
|
_daemon = postfix/(submission/)?smtp(d|s)
|
||||||
|
|
||||||
failregex = ^%(__prefix_line)swarning: [-._\w]+\[<HOST>\]: SASL ((?i)LOGIN|PLAIN|(?:CRAM|DIGEST)-MD5) authentication failed(: [ A-Za-z0-9+/]*={0,2})?\s*$
|
failregex = ^%(__prefix_line)swarning: [-._\w]+\[<HOST>\]: SASL ((?i)LOGIN|PLAIN|(?:CRAM|DIGEST)-MD5) authentication failed(: [ A-Za-z0-9+/:]*={0,2})?\s*$
|
||||||
|
|
||||||
ignoreregex =
|
ignoreregex = authentication failed: Connection lost to authentication server$
|
||||||
|
|
||||||
[Init]
|
[Init]
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
#
|
#
|
||||||
# Set "UseReverseDNS off" in proftpd.conf to avoid the need for DNS.
|
# Set "UseReverseDNS off" in proftpd.conf to avoid the need for DNS.
|
||||||
# See: http://www.proftpd.org/docs/howto/DNS.html
|
# See: http://www.proftpd.org/docs/howto/DNS.html
|
||||||
|
# When the default locale for your system is not en_US.UTF-8
|
||||||
|
# on Debian-based systems be sure to add this to /etc/default/proftpd
|
||||||
|
# export LC_TIME="en_US.UTF-8"
|
||||||
|
|
||||||
[INCLUDES]
|
[INCLUDES]
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
# Fail2Ban configuration file for roundcube web server
|
# Fail2Ban configuration file for roundcube web server
|
||||||
#
|
#
|
||||||
|
# By default failed logins are printed to 'errors'. The first regex matches those
|
||||||
|
# The second regex matches those printed to 'userlogins'
|
||||||
|
# The userlogins log file can be enabled by setting $config['log_logins'] = true; in config.inc.php
|
||||||
#
|
#
|
||||||
|
# The logpath in your jail can be updated to userlogins if you wish
|
||||||
#
|
#
|
||||||
|
|
||||||
[INCLUDES]
|
[INCLUDES]
|
||||||
|
@ -9,7 +13,8 @@ before = common.conf
|
||||||
|
|
||||||
[Definition]
|
[Definition]
|
||||||
|
|
||||||
failregex = ^\s*(\[\])?(%(__hostname)s roundcube: IMAP Error)?: (FAILED login|Login failed) for .*? from <HOST>(\. .* in .*?/rcube_imap\.php on line \d+ \(\S+ \S+\))?$
|
failregex = ^\s*(\[\])?(%(__hostname)s\s*(roundcube:)?\s*(<[\w]+>)? IMAP Error)?: (FAILED login|Login failed) for .*? from <HOST>(\. .* in .*?/rcube_imap\.php on line \d+ \(\S+ \S+\))?$
|
||||||
|
^\[\]:\s*(<[\w]+>)? Failed login for [\w\-\.\+]+(@[\w\-\.\+]+\.[a-zA-Z]{2,6})? from <HOST> in session \w+( \(error: \d\))?$
|
||||||
|
|
||||||
ignoreregex =
|
ignoreregex =
|
||||||
# DEV Notes:
|
# DEV Notes:
|
||||||
|
@ -26,4 +31,4 @@ ignoreregex =
|
||||||
# arbitrary user input and IMAP response doesn't inject the wrong IP for
|
# arbitrary user input and IMAP response doesn't inject the wrong IP for
|
||||||
# fail2ban
|
# fail2ban
|
||||||
#
|
#
|
||||||
# Author: Teodor Micu & Yaroslav Halchenko & terence namusonge & Daniel Black
|
# Author: Teodor Micu & Yaroslav Halchenko & terence namusonge & Daniel Black & Lee Clemens
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
failregex = ^\s+\d\s<HOST>\s+[A-Z_]+_DENIED/403 .*$
|
failregex = ^\s+\d\s<HOST>\s+[A-Z_]+_DENIED/403 .*$
|
||||||
^\s+\d\s<HOST>\s+NONE/405 .*$
|
^\s+\d\s<HOST>\s+NONE/405 .*$
|
||||||
|
|
||||||
|
ignoreregex =
|
||||||
|
|
||||||
# Author: Daniel Black
|
# Author: Daniel Black
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
failregex = ^ \[LOGIN_ERROR\].*from <HOST>: Unknown user or password incorrect\.$
|
failregex = ^ \[LOGIN_ERROR\].*from <HOST>: Unknown user or password incorrect\.$
|
||||||
|
|
||||||
|
ignoreregex =
|
||||||
|
|
||||||
[Init]
|
[Init]
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ failregex = ^%(__prefix_line)s(?:error: PAM: )?[aA]uthentication (?:failure|erro
|
||||||
^(?P<__prefix>%(__prefix_line)s)User .+ not allowed because account is locked<SKIPLINES>(?P=__prefix)(?:error: )?Received disconnect from <HOST>: 11: .+ \[preauth\]$
|
^(?P<__prefix>%(__prefix_line)s)User .+ not allowed because account is locked<SKIPLINES>(?P=__prefix)(?:error: )?Received disconnect from <HOST>: 11: .+ \[preauth\]$
|
||||||
^(?P<__prefix>%(__prefix_line)s)Disconnecting: Too many authentication failures for .+? \[preauth\]<SKIPLINES>(?P=__prefix)(?:error: )?Connection closed by <HOST> \[preauth\]$
|
^(?P<__prefix>%(__prefix_line)s)Disconnecting: Too many authentication failures for .+? \[preauth\]<SKIPLINES>(?P=__prefix)(?:error: )?Connection closed by <HOST> \[preauth\]$
|
||||||
^(?P<__prefix>%(__prefix_line)s)Connection from <HOST> port \d+(?: on \S+ port \d+)?<SKIPLINES>(?P=__prefix)Disconnecting: Too many authentication failures for .+? \[preauth\]$
|
^(?P<__prefix>%(__prefix_line)s)Connection from <HOST> port \d+(?: on \S+ port \d+)?<SKIPLINES>(?P=__prefix)Disconnecting: Too many authentication failures for .+? \[preauth\]$
|
||||||
|
^%(__prefix_line)spam_unix\(sshd:auth\):\s+authentication failure;\s*logname=\S*\s*uid=\d*\s*euid=\d*\s*tty=\S*\s*ruser=\S*\s*rhost=<HOST>\s.*$
|
||||||
|
|
||||||
ignoreregex =
|
ignoreregex =
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
failregex = ^ LOG\d\[\d+:\d+\]:\ SSL_accept from <HOST>:\d+ : (?P<CODE>[\dA-F]+): error:(?P=CODE):SSL routines:SSL3_GET_CLIENT_CERTIFICATE:peer did not return a certificate$
|
failregex = ^ LOG\d\[\d+:\d+\]:\ SSL_accept from <HOST>:\d+ : (?P<CODE>[\dA-F]+): error:(?P=CODE):SSL routines:SSL3_GET_CLIENT_CERTIFICATE:peer did not return a certificate$
|
||||||
|
|
||||||
|
ignoreregex =
|
||||||
|
|
||||||
# DEV NOTES:
|
# DEV NOTES:
|
||||||
#
|
#
|
||||||
# Author: Daniel Black
|
# Author: Daniel Black
|
||||||
|
|
|
@ -10,7 +10,7 @@ before = common.conf
|
||||||
|
|
||||||
[Definition]
|
[Definition]
|
||||||
|
|
||||||
__pam_re=\(?pam_unix(?:\(\S+\))?\)?:?
|
__pam_re=\(?%(__pam_auth)s(?:\(\S+\))?\)?:?
|
||||||
_daemon = vsftpd
|
_daemon = vsftpd
|
||||||
|
|
||||||
failregex = ^%(__prefix_line)s%(__pam_re)s\s+authentication failure; logname=\S* uid=\S* euid=\S* tty=(ftp)? ruser=\S* rhost=<HOST>(?:\s+user=.*)?\s*$
|
failregex = ^%(__prefix_line)s%(__pam_re)s\s+authentication failure; logname=\S* uid=\S* euid=\S* tty=(ftp)? ruser=\S* rhost=<HOST>(?:\s+user=.*)?\s*$
|
||||||
|
|
|
@ -11,7 +11,7 @@ before = common.conf
|
||||||
[Definition]
|
[Definition]
|
||||||
|
|
||||||
_daemon = wu-ftpd
|
_daemon = wu-ftpd
|
||||||
__pam_re=\(?pam_unix(?:\(wu-ftpd:auth\))?\)?:?
|
__pam_re=\(?%(__pam_auth)s(?:\(wu-ftpd:auth\))?\)?:?
|
||||||
|
|
||||||
failregex = ^%(__prefix_line)sfailed login from \S+ \[<HOST>\]\s*$
|
failregex = ^%(__prefix_line)sfailed login from \S+ \[<HOST>\]\s*$
|
||||||
^%(__prefix_line)s%(__pam_re)s\s+authentication failure; logname=\S* uid=\S* euid=\S* tty=(ftp)? ruser=\S* rhost=<HOST>(?:\s+user=.*)?\s*$
|
^%(__prefix_line)s%(__pam_re)s\s+authentication failure; logname=\S* uid=\S* euid=\S* tty=(ftp)? ruser=\S* rhost=<HOST>(?:\s+user=.*)?\s*$
|
||||||
|
|
|
@ -117,6 +117,11 @@ maxretry = 5
|
||||||
# See "journalmatch" in the jails associated filter config
|
# See "journalmatch" in the jails associated filter config
|
||||||
# auto: will try to use the following backends, in order:
|
# auto: will try to use the following backends, in order:
|
||||||
# pyinotify, gamin, polling.
|
# pyinotify, gamin, polling.
|
||||||
|
#
|
||||||
|
# Note: if systemd backend is choses as the default but you enable a jail
|
||||||
|
# for which logs are present only in its own log files, specify some other
|
||||||
|
# backend for that jail (e.g. polling) and provide empty value for
|
||||||
|
# journalmatch. See https://github.com/fail2ban/fail2ban/issues/959#issuecomment-74901200
|
||||||
backend = auto
|
backend = auto
|
||||||
|
|
||||||
# "usedns" specifies if jails should trust hostnames in logs,
|
# "usedns" specifies if jails should trust hostnames in logs,
|
||||||
|
@ -207,6 +212,10 @@ action_mwl = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(por
|
||||||
action_xarf = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
|
action_xarf = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
|
||||||
xarf-login-attack[service=%(__name__)s, sender="%(sender)s", logpath=%(logpath)s, port="%(port)s"]
|
xarf-login-attack[service=%(__name__)s, sender="%(sender)s", logpath=%(logpath)s, port="%(port)s"]
|
||||||
|
|
||||||
|
# ban IP on CloudFlare & send an e-mail with whois report and relevant log lines
|
||||||
|
# to the destemail.
|
||||||
|
action_cf_mwl = cloudflare[cfuser="%(cfemail)s", cftoken="%(cfapikey)s"]
|
||||||
|
%(mta)s-whois-lines[name=%(__name__)s, dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s"]
|
||||||
|
|
||||||
# Report block via blocklist.de fail2ban reporting service API
|
# Report block via blocklist.de fail2ban reporting service API
|
||||||
#
|
#
|
||||||
|
@ -315,6 +324,14 @@ logpath = %(apache_error_log)s
|
||||||
maxretry = 2
|
maxretry = 2
|
||||||
|
|
||||||
|
|
||||||
|
[apache-fakegooglebot]
|
||||||
|
|
||||||
|
port = http,https
|
||||||
|
logpath = %(apache_access_log)s
|
||||||
|
maxretry = 1
|
||||||
|
ignorecommand = %(ignorecommands_dir)s/apache-fakegooglebot <ip>
|
||||||
|
|
||||||
|
|
||||||
[apache-modsecurity]
|
[apache-modsecurity]
|
||||||
|
|
||||||
port = http,https
|
port = http,https
|
||||||
|
@ -329,9 +346,14 @@ maxretry = 1
|
||||||
|
|
||||||
[nginx-http-auth]
|
[nginx-http-auth]
|
||||||
|
|
||||||
ports = http,https
|
port = http,https
|
||||||
logpath = %(nginx_error_log)s
|
logpath = %(nginx_error_log)s
|
||||||
|
|
||||||
|
[nginx-botsearch]
|
||||||
|
|
||||||
|
port = http,https
|
||||||
|
logpath = %(nginx_error_log)s
|
||||||
|
maxretry = 2
|
||||||
|
|
||||||
# Ban attackers that try to use PHP's URL-fopen() functionality
|
# Ban attackers that try to use PHP's URL-fopen() functionality
|
||||||
# through GET/POST variables. - Experimental, with more than a year
|
# through GET/POST variables. - Experimental, with more than a year
|
||||||
|
@ -364,7 +386,7 @@ logpath = %(lighttpd_error_log)s
|
||||||
[roundcube-auth]
|
[roundcube-auth]
|
||||||
|
|
||||||
port = http,https
|
port = http,https
|
||||||
logpath = /var/log/roundcube/userlogins
|
logpath = logpath = %(roundcube_errors_log)s
|
||||||
|
|
||||||
|
|
||||||
[openwebmail]
|
[openwebmail]
|
||||||
|
@ -405,6 +427,11 @@ maxretry = 5
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
|
[drupal-auth]
|
||||||
|
|
||||||
|
port = http,https
|
||||||
|
logpath = %(syslog_daemon)s
|
||||||
|
|
||||||
[guacamole]
|
[guacamole]
|
||||||
|
|
||||||
port = http,https
|
port = http,https
|
||||||
|
@ -423,6 +450,12 @@ port = 10000
|
||||||
logpath = %(syslog_authpriv)s
|
logpath = %(syslog_authpriv)s
|
||||||
|
|
||||||
|
|
||||||
|
[froxlor-auth]
|
||||||
|
|
||||||
|
port = http,https
|
||||||
|
logpath = %(syslog_authpriv)s
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# HTTP Proxy servers
|
# HTTP Proxy servers
|
||||||
#
|
#
|
||||||
|
@ -439,6 +472,7 @@ logpath = /var/log/squid/access.log
|
||||||
port = 3128
|
port = 3128
|
||||||
logpath = /var/log/3proxy.log
|
logpath = /var/log/3proxy.log
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# FTP servers
|
# FTP servers
|
||||||
#
|
#
|
||||||
|
@ -503,6 +537,13 @@ port = smtp,465,submission
|
||||||
logpath = %(postfix_log)s
|
logpath = %(postfix_log)s
|
||||||
|
|
||||||
|
|
||||||
|
[postfix-rbl]
|
||||||
|
|
||||||
|
port = smtp,465,submission
|
||||||
|
logpath = %(syslog_mail)s
|
||||||
|
maxretry = 1
|
||||||
|
|
||||||
|
|
||||||
[sendmail-auth]
|
[sendmail-auth]
|
||||||
|
|
||||||
port = submission,465,smtp
|
port = submission,465,smtp
|
||||||
|
@ -687,15 +728,16 @@ maxretry = 5
|
||||||
|
|
||||||
|
|
||||||
# Jail for more extended banning of persistent abusers
|
# Jail for more extended banning of persistent abusers
|
||||||
# !!! WARNING !!!
|
# !!! WARNINGS !!!
|
||||||
# Make sure that your loglevel specified in fail2ban.conf/.local
|
# 1. Make sure that your loglevel specified in fail2ban.conf/.local
|
||||||
# is not at DEBUG level -- which might then cause fail2ban to fall into
|
# is not at DEBUG level -- which might then cause fail2ban to fall into
|
||||||
# an infinite loop constantly feeding itself with non-informative lines
|
# an infinite loop constantly feeding itself with non-informative lines
|
||||||
|
# 2. Increase dbpurgeage defined in fail2ban.conf to e.g. 648000 (7.5 days)
|
||||||
|
# to maintain entries for failed logins for sufficient amount of time
|
||||||
[recidive]
|
[recidive]
|
||||||
|
|
||||||
logpath = /var/log/fail2ban.log
|
logpath = /var/log/fail2ban.log
|
||||||
port = all
|
banaction = iptables-allports
|
||||||
protocol = all
|
|
||||||
bantime = 1w
|
bantime = 1w
|
||||||
findtime = 1d
|
findtime = 1d
|
||||||
maxretry = 5
|
maxretry = 5
|
||||||
|
|
|
@ -61,3 +61,8 @@ dovecot_log = %(syslog_mail_warn)s
|
||||||
solidpop3d_log = %(syslog_local0)s
|
solidpop3d_log = %(syslog_local0)s
|
||||||
|
|
||||||
mysql_log = %(syslog_daemon)s
|
mysql_log = %(syslog_daemon)s
|
||||||
|
|
||||||
|
roundcube_errors_log = /var/log/roundcube/errors
|
||||||
|
|
||||||
|
# Directory with ignorecommand scripts
|
||||||
|
ignorecommands_dir = /etc/fail2ban/filter.d/ignorecommands
|
||||||
|
|
|
@ -35,6 +35,3 @@ exim_main_log = /var/log/exim4/mainlog
|
||||||
# was in debian squeezy but not in wheezy
|
# was in debian squeezy but not in wheezy
|
||||||
# /etc/proftpd/proftpd.conf (SystemLog)
|
# /etc/proftpd/proftpd.conf (SystemLog)
|
||||||
proftpd_log = /var/log/proftpd/proftpd.log
|
proftpd_log = /var/log/proftpd/proftpd.log
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -35,3 +35,5 @@ apache_access_log = /var/log/httpd/*access_log
|
||||||
exim_main_log = /var/log/exim/main.log
|
exim_main_log = /var/log/exim/main.log
|
||||||
|
|
||||||
mysql_log = /var/lib/mysql/mysqld.log
|
mysql_log = /var/lib/mysql/mysqld.log
|
||||||
|
|
||||||
|
roundcube_errors_log = /var/log/roundcubemail/errors
|
||||||
|
|
|
@ -37,6 +37,7 @@ Below derived from:
|
||||||
logging.NOTICE = logging.INFO + 5
|
logging.NOTICE = logging.INFO + 5
|
||||||
logging.addLevelName(logging.NOTICE, 'NOTICE')
|
logging.addLevelName(logging.NOTICE, 'NOTICE')
|
||||||
|
|
||||||
|
|
||||||
# define a new logger function for notice
|
# define a new logger function for notice
|
||||||
# this is exactly like existing info, critical, debug...etc
|
# this is exactly like existing info, critical, debug...etc
|
||||||
def _Logger_notice(self, msg, *args, **kwargs):
|
def _Logger_notice(self, msg, *args, **kwargs):
|
||||||
|
@ -53,6 +54,7 @@ def _Logger_notice(self, msg, *args, **kwargs):
|
||||||
|
|
||||||
logging.Logger.notice = _Logger_notice
|
logging.Logger.notice = _Logger_notice
|
||||||
|
|
||||||
|
|
||||||
# define a new root level notice function
|
# define a new root level notice function
|
||||||
# this is exactly like existing info, critical, debug...etc
|
# this is exactly like existing info, critical, debug...etc
|
||||||
def _root_notice(msg, *args, **kwargs):
|
def _root_notice(msg, *args, **kwargs):
|
||||||
|
@ -68,3 +70,7 @@ logging.notice = _root_notice
|
||||||
|
|
||||||
# add NOTICE to the priority map of all the levels
|
# add NOTICE to the priority map of all the levels
|
||||||
logging.handlers.SysLogHandler.priority_map['NOTICE'] = 'notice'
|
logging.handlers.SysLogHandler.priority_map['NOTICE'] = 'notice'
|
||||||
|
|
||||||
|
from time import strptime
|
||||||
|
# strptime thread safety hack-around - http://bugs.python.org/issue7980
|
||||||
|
strptime("2012", "%Y")
|
||||||
|
|
|
@ -32,6 +32,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ActionReader(DefinitionInitConfigReader):
|
class ActionReader(DefinitionInitConfigReader):
|
||||||
|
|
||||||
_configOpts = [
|
_configOpts = [
|
||||||
|
|
|
@ -27,6 +27,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Beautify the output of the client.
|
# Beautify the output of the client.
|
||||||
#
|
#
|
||||||
|
@ -45,7 +46,8 @@ class Beautifier:
|
||||||
return self.__inputCmd
|
return self.__inputCmd
|
||||||
|
|
||||||
def beautify(self, response):
|
def beautify(self, response):
|
||||||
logSys.debug("Beautify " + `response` + " with " + `self.__inputCmd`)
|
logSys.debug(
|
||||||
|
"Beautify " + repr(response) + " with " + repr(self.__inputCmd))
|
||||||
inC = self.__inputCmd
|
inC = self.__inputCmd
|
||||||
msg = response
|
msg = response
|
||||||
try:
|
try:
|
||||||
|
@ -85,6 +87,9 @@ class Beautifier:
|
||||||
val = " ".join(res1[1]) if isinstance(res1[1], list) else res1[1]
|
val = " ".join(res1[1]) if isinstance(res1[1], list) else res1[1]
|
||||||
msg.append("%s %s:\t%s" % (prefix1, res1[0], val))
|
msg.append("%s %s:\t%s" % (prefix1, res1[0], val))
|
||||||
msg = "\n".join(msg)
|
msg = "\n".join(msg)
|
||||||
|
elif inC[1] == "syslogsocket":
|
||||||
|
msg = "Current syslog socket is:\n"
|
||||||
|
msg = msg + "`- " + response
|
||||||
elif inC[1] == "logtarget":
|
elif inC[1] == "logtarget":
|
||||||
msg = "Current logging target is:\n"
|
msg = "Current logging target is:\n"
|
||||||
msg = msg + "`- " + response
|
msg = msg + "`- " + response
|
||||||
|
@ -99,7 +104,7 @@ class Beautifier:
|
||||||
elif response == 4:
|
elif response == 4:
|
||||||
msg = msg + "DEBUG"
|
msg = msg + "DEBUG"
|
||||||
else:
|
else:
|
||||||
msg = msg + `response`
|
msg = msg + repr(response)
|
||||||
elif inC[1] == "dbfile":
|
elif inC[1] == "dbfile":
|
||||||
if response is None:
|
if response is None:
|
||||||
msg = "Database currently disabled"
|
msg = "Database currently disabled"
|
||||||
|
@ -180,13 +185,14 @@ class Beautifier:
|
||||||
msg += ", ".join(response)
|
msg += ", ".join(response)
|
||||||
except Exception:
|
except Exception:
|
||||||
logSys.warning("Beautifier error. Please report the error")
|
logSys.warning("Beautifier error. Please report the error")
|
||||||
logSys.error("Beautify " + `response` + " with " + `self.__inputCmd` +
|
logSys.error("Beautify " + repr(response) + " with "
|
||||||
" failed")
|
+ repr(self.__inputCmd) + " failed")
|
||||||
msg = msg + `response`
|
msg = msg + repr(response)
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
def beautifyError(self, response):
|
def beautifyError(self, response):
|
||||||
logSys.debug("Beautify (error) " + `response` + " with " + `self.__inputCmd`)
|
logSys.debug("Beautify (error) " + repr(response) + " with "
|
||||||
|
+ repr(self.__inputCmd))
|
||||||
msg = response
|
msg = response
|
||||||
if isinstance(response, UnknownJailException):
|
if isinstance(response, UnknownJailException):
|
||||||
msg = "Sorry but the jail '" + response.args[0] + "' does not exist"
|
msg = "Sorry but the jail '" + response.args[0] + "' does not exist"
|
||||||
|
|
|
@ -24,7 +24,8 @@ __author__ = 'Yaroslav Halhenko'
|
||||||
__copyright__ = 'Copyright (c) 2007 Yaroslav Halchenko'
|
__copyright__ = 'Copyright (c) 2007 Yaroslav Halchenko'
|
||||||
__license__ = 'GPL'
|
__license__ = 'GPL'
|
||||||
|
|
||||||
import os, sys
|
import os
|
||||||
|
import sys
|
||||||
from ..helpers import getLogger
|
from ..helpers import getLogger
|
||||||
|
|
||||||
if sys.version_info >= (3,2): # pragma: no cover
|
if sys.version_info >= (3,2): # pragma: no cover
|
||||||
|
@ -66,6 +67,7 @@ logLevel = 7
|
||||||
|
|
||||||
__all__ = ['SafeConfigParserWithIncludes']
|
__all__ = ['SafeConfigParserWithIncludes']
|
||||||
|
|
||||||
|
|
||||||
class SafeConfigParserWithIncludes(SafeConfigParser):
|
class SafeConfigParserWithIncludes(SafeConfigParser):
|
||||||
"""
|
"""
|
||||||
Class adds functionality to SafeConfigParser to handle included
|
Class adds functionality to SafeConfigParser to handle included
|
||||||
|
|
|
@ -24,7 +24,8 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import glob, os
|
import glob
|
||||||
|
import os
|
||||||
from ConfigParser import NoOptionError, NoSectionError
|
from ConfigParser import NoOptionError, NoSectionError
|
||||||
|
|
||||||
from .configparserinc import SafeConfigParserWithIncludes, logLevel
|
from .configparserinc import SafeConfigParserWithIncludes, logLevel
|
||||||
|
@ -33,6 +34,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ConfigReader():
|
class ConfigReader():
|
||||||
"""Generic config reader class.
|
"""Generic config reader class.
|
||||||
|
|
||||||
|
@ -135,6 +137,7 @@ class ConfigReader():
|
||||||
return self._cfg.getOptions(*args, **kwargs)
|
return self._cfg.getOptions(*args, **kwargs)
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
class ConfigReaderUnshared(SafeConfigParserWithIncludes):
|
class ConfigReaderUnshared(SafeConfigParserWithIncludes):
|
||||||
"""Unshared config reader (previously ConfigReader).
|
"""Unshared config reader (previously ConfigReader).
|
||||||
|
|
||||||
|
@ -186,7 +189,7 @@ class ConfigReaderUnshared(SafeConfigParserWithIncludes):
|
||||||
if config_files_read:
|
if config_files_read:
|
||||||
return True
|
return True
|
||||||
logSys.error("Found no accessible config files for %r under %s",
|
logSys.error("Found no accessible config files for %r under %s",
|
||||||
( filename, self.getBaseDir() ))
|
filename, self.getBaseDir())
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
logSys.error("Found no accessible config files for %r " % filename
|
logSys.error("Found no accessible config files for %r " % filename
|
||||||
|
@ -232,10 +235,11 @@ class ConfigReaderUnshared(SafeConfigParserWithIncludes):
|
||||||
logSys.log(logLevel, "Non essential option '%s' not defined in '%s'.", option[1], sec)
|
logSys.log(logLevel, "Non essential option '%s' not defined in '%s'.", option[1], sec)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
logSys.warning("Wrong value for '" + option[1] + "' in '" + sec +
|
logSys.warning("Wrong value for '" + option[1] + "' in '" + sec +
|
||||||
"'. Using default one: '" + `option[2]` + "'")
|
"'. Using default one: '" + repr(option[2]) + "'")
|
||||||
values[option[1]] = option[2]
|
values[option[1]] = option[2]
|
||||||
return values
|
return values
|
||||||
|
|
||||||
|
|
||||||
class DefinitionInitConfigReader(ConfigReader):
|
class DefinitionInitConfigReader(ConfigReader):
|
||||||
"""Config reader for files with options grouped in [Definition] and
|
"""Config reader for files with options grouped in [Definition] and
|
||||||
[Init] sections.
|
[Init] sections.
|
||||||
|
@ -281,7 +285,7 @@ class DefinitionInitConfigReader(ConfigReader):
|
||||||
|
|
||||||
if self.has_section("Init"):
|
if self.has_section("Init"):
|
||||||
for opt in self.options("Init"):
|
for opt in self.options("Init"):
|
||||||
if not self._initOpts.has_key(opt):
|
if not opt in self._initOpts:
|
||||||
self._initOpts[opt] = self.get("Init", opt)
|
self._initOpts[opt] = self.get("Init", opt)
|
||||||
|
|
||||||
def convert(self):
|
def convert(self):
|
||||||
|
|
|
@ -31,6 +31,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Configurator:
|
class Configurator:
|
||||||
|
|
||||||
def __init__(self, force_enable=False, share_config=None):
|
def __init__(self, force_enable=False, share_config=None):
|
||||||
|
|
|
@ -26,43 +26,42 @@ __license__ = "GPL"
|
||||||
|
|
||||||
#from cPickle import dumps, loads, HIGHEST_PROTOCOL
|
#from cPickle import dumps, loads, HIGHEST_PROTOCOL
|
||||||
from pickle import dumps, loads, HIGHEST_PROTOCOL
|
from pickle import dumps, loads, HIGHEST_PROTOCOL
|
||||||
import socket, sys
|
from ..protocol import CSPROTO
|
||||||
|
import socket
|
||||||
if sys.version_info >= (3,):
|
import sys
|
||||||
# b"" causes SyntaxError in python <= 2.5, so below implements equivalent
|
|
||||||
EMPTY_BYTES = bytes("", encoding="ascii")
|
|
||||||
else:
|
|
||||||
# python 2.x, string type is equivalent to bytes.
|
|
||||||
EMPTY_BYTES = ""
|
|
||||||
|
|
||||||
class CSocket:
|
class CSocket:
|
||||||
|
|
||||||
if sys.version_info >= (3,):
|
def __init__(self, sock="/var/run/fail2ban/fail2ban.sock"):
|
||||||
END_STRING = bytes("<F2B_END_COMMAND>", encoding='ascii')
|
|
||||||
else:
|
|
||||||
END_STRING = "<F2B_END_COMMAND>"
|
|
||||||
|
|
||||||
def __init__(self, sock = "/var/run/fail2ban/fail2ban.sock"):
|
|
||||||
# Create an INET, STREAMing socket
|
# Create an INET, STREAMing socket
|
||||||
#self.csock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
#self.csock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
self.__csock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
self.__csock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||||
#self.csock.connect(("localhost", 2222))
|
#self.csock.connect(("localhost", 2222))
|
||||||
self.__csock.connect(sock)
|
self.__csock.connect(sock)
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.close(False)
|
||||||
|
|
||||||
def send(self, msg):
|
def send(self, msg):
|
||||||
# Convert every list member to string
|
# Convert every list member to string
|
||||||
obj = dumps([str(m) for m in msg], HIGHEST_PROTOCOL)
|
obj = dumps([str(m) for m in msg], HIGHEST_PROTOCOL)
|
||||||
self.__csock.send(obj + CSocket.END_STRING)
|
self.__csock.send(obj + CSPROTO.END)
|
||||||
ret = self.receive(self.__csock)
|
return self.receive(self.__csock)
|
||||||
|
|
||||||
|
def close(self, sendEnd=True):
|
||||||
|
if not self.__csock:
|
||||||
|
return
|
||||||
|
if sendEnd:
|
||||||
|
self.__csock.sendall(CSPROTO.CLOSE + CSPROTO.END)
|
||||||
self.__csock.close()
|
self.__csock.close()
|
||||||
return ret
|
self.__csock = None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def receive(sock):
|
def receive(sock):
|
||||||
msg = EMPTY_BYTES
|
msg = CSPROTO.EMPTY
|
||||||
while msg.rfind(CSocket.END_STRING) == -1:
|
while msg.rfind(CSPROTO.END) == -1:
|
||||||
chunk = sock.recv(6)
|
chunk = sock.recv(6)
|
||||||
if chunk == '':
|
if chunk == '':
|
||||||
raise RuntimeError, "socket connection broken"
|
raise RuntimeError("socket connection broken")
|
||||||
msg = msg + chunk
|
msg = msg + chunk
|
||||||
return loads(msg)
|
return loads(msg)
|
||||||
|
|
|
@ -30,6 +30,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Fail2banReader(ConfigReader):
|
class Fail2banReader(ConfigReader):
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
@ -46,12 +47,13 @@ class Fail2banReader(ConfigReader):
|
||||||
def getOptions(self):
|
def getOptions(self):
|
||||||
opts = [["string", "loglevel", "INFO" ],
|
opts = [["string", "loglevel", "INFO" ],
|
||||||
["string", "logtarget", "STDERR"],
|
["string", "logtarget", "STDERR"],
|
||||||
|
["string", "syslogsocket", "auto"],
|
||||||
["string", "dbfile", "/var/lib/fail2ban/fail2ban.sqlite3"],
|
["string", "dbfile", "/var/lib/fail2ban/fail2ban.sqlite3"],
|
||||||
["string", "dbpurgeage", "1d"]]
|
["string", "dbpurgeage", "1d"]]
|
||||||
self.__opts = ConfigReader.getOptions(self, "Definition", opts)
|
self.__opts = ConfigReader.getOptions(self, "Definition", opts)
|
||||||
|
|
||||||
def convert(self):
|
def convert(self):
|
||||||
order = {"loglevel":0, "logtarget":1, "dbfile":2, "dbpurgeage":3}
|
order = {"loglevel":0, "logtarget":1, "syslogsocket":2, "dbfile":50, "dbpurgeage":51}
|
||||||
stream = list()
|
stream = list()
|
||||||
for opt in self.__opts:
|
for opt in self.__opts:
|
||||||
if opt in order:
|
if opt in order:
|
||||||
|
|
|
@ -24,7 +24,8 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import os, shlex
|
import os
|
||||||
|
import shlex
|
||||||
|
|
||||||
from .configreader import DefinitionInitConfigReader
|
from .configreader import DefinitionInitConfigReader
|
||||||
from ..server.action import CommandAction
|
from ..server.action import CommandAction
|
||||||
|
@ -33,6 +34,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class FilterReader(DefinitionInitConfigReader):
|
class FilterReader(DefinitionInitConfigReader):
|
||||||
|
|
||||||
_configOpts = [
|
_configOpts = [
|
||||||
|
@ -50,17 +52,17 @@ class FilterReader(DefinitionInitConfigReader):
|
||||||
def getCombined(self):
|
def getCombined(self):
|
||||||
combinedopts = dict(list(self._opts.items()) + list(self._initOpts.items()))
|
combinedopts = dict(list(self._opts.items()) + list(self._initOpts.items()))
|
||||||
if not len(combinedopts):
|
if not len(combinedopts):
|
||||||
return {};
|
return {}
|
||||||
opts = CommandAction.substituteRecursiveTags(combinedopts)
|
opts = CommandAction.substituteRecursiveTags(combinedopts)
|
||||||
if not opts:
|
if not opts:
|
||||||
raise ValueError('recursive tag definitions unable to be resolved')
|
raise ValueError('recursive tag definitions unable to be resolved')
|
||||||
return opts;
|
return opts
|
||||||
|
|
||||||
def convert(self):
|
def convert(self):
|
||||||
stream = list()
|
stream = list()
|
||||||
opts = self.getCombined()
|
opts = self.getCombined()
|
||||||
if not len(opts):
|
if not len(opts):
|
||||||
return stream;
|
return stream
|
||||||
for opt, value in opts.iteritems():
|
for opt, value in opts.iteritems():
|
||||||
if opt == "failregex":
|
if opt == "failregex":
|
||||||
for regex in value.split('\n'):
|
for regex in value.split('\n'):
|
||||||
|
|
|
@ -24,8 +24,10 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import re, glob, os.path
|
import glob
|
||||||
import json
|
import json
|
||||||
|
import os.path
|
||||||
|
import re
|
||||||
|
|
||||||
from .configreader import ConfigReaderUnshared, ConfigReader
|
from .configreader import ConfigReaderUnshared, ConfigReader
|
||||||
from .filterreader import FilterReader
|
from .filterreader import FilterReader
|
||||||
|
@ -35,6 +37,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class JailReader(ConfigReader):
|
class JailReader(ConfigReader):
|
||||||
|
|
||||||
optionCRE = re.compile("^((?:\w|-|_|\.)+)(?:\[(.*)\])?$")
|
optionCRE = re.compile("^((?:\w|-|_|\.)+)(?:\[(.*)\])?$")
|
||||||
|
|
|
@ -31,6 +31,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class JailsReader(ConfigReader):
|
class JailsReader(ConfigReader):
|
||||||
|
|
||||||
def __init__(self, force_enable=False, **kwargs):
|
def __init__(self, force_enable=False, **kwargs):
|
||||||
|
@ -50,6 +51,7 @@ class JailsReader(ConfigReader):
|
||||||
return self.__jails
|
return self.__jails
|
||||||
|
|
||||||
def read(self):
|
def read(self):
|
||||||
|
self.__jails = list()
|
||||||
return ConfigReader.read(self, "jail")
|
return ConfigReader.read(self, "jail")
|
||||||
|
|
||||||
def getOptions(self, section=None):
|
def getOptions(self, section=None):
|
||||||
|
|
|
@ -23,12 +23,14 @@ __author__ = "Cyril Jaquier, Yaroslav Halchenko"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2012 Yaroslav Halchenko"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2012 Yaroslav Halchenko"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Jails
|
# Jails
|
||||||
#
|
#
|
||||||
class DuplicateJailException(Exception):
|
class DuplicateJailException(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class UnknownJailException(KeyError):
|
class UnknownJailException(KeyError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -26,11 +26,13 @@ import traceback
|
||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|
||||||
def formatExceptionInfo():
|
def formatExceptionInfo():
|
||||||
""" Consistently format exception information """
|
""" Consistently format exception information """
|
||||||
cla, exc = sys.exc_info()[:2]
|
cla, exc = sys.exc_info()[:2]
|
||||||
return (cla.__name__, str(exc))
|
return (cla.__name__, str(exc))
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Following "traceback" functions are adopted from PyMVPA distributed
|
# Following "traceback" functions are adopted from PyMVPA distributed
|
||||||
# under MIT/Expat and copyright by PyMVPA developers (i.e. me and
|
# under MIT/Expat and copyright by PyMVPA developers (i.e. me and
|
||||||
|
@ -49,6 +51,7 @@ def mbasename(s):
|
||||||
base = os.path.basename(os.path.dirname(s)) + '.' + base
|
base = os.path.basename(os.path.dirname(s)) + '.' + base
|
||||||
return base
|
return base
|
||||||
|
|
||||||
|
|
||||||
class TraceBack(object):
|
class TraceBack(object):
|
||||||
"""Customized traceback to be included in debug messages
|
"""Customized traceback to be included in debug messages
|
||||||
"""
|
"""
|
||||||
|
@ -94,6 +97,7 @@ class TraceBack(object):
|
||||||
|
|
||||||
return sftb
|
return sftb
|
||||||
|
|
||||||
|
|
||||||
class FormatterWithTraceBack(logging.Formatter):
|
class FormatterWithTraceBack(logging.Formatter):
|
||||||
"""Custom formatter which expands %(tb) and %(tbc) with tracebacks
|
"""Custom formatter which expands %(tb) and %(tbc) with tracebacks
|
||||||
|
|
||||||
|
@ -108,6 +112,7 @@ class FormatterWithTraceBack(logging.Formatter):
|
||||||
record.tbc = record.tb = self._tb()
|
record.tbc = record.tb = self._tb()
|
||||||
return logging.Formatter.format(self, record)
|
return logging.Formatter.format(self, record)
|
||||||
|
|
||||||
|
|
||||||
def getLogger(name):
|
def getLogger(name):
|
||||||
"""Get logging.Logger instance with Fail2Ban logger name convention
|
"""Get logging.Logger instance with Fail2Ban logger name convention
|
||||||
"""
|
"""
|
||||||
|
@ -115,6 +120,7 @@ def getLogger(name):
|
||||||
name = "fail2ban.%s" % name.rpartition(".")[-1]
|
name = "fail2ban.%s" % name.rpartition(".")[-1]
|
||||||
return logging.getLogger(name)
|
return logging.getLogger(name)
|
||||||
|
|
||||||
|
|
||||||
def excepthook(exctype, value, traceback):
|
def excepthook(exctype, value, traceback):
|
||||||
"""Except hook used to log unhandled exceptions to Fail2Ban log
|
"""Except hook used to log unhandled exceptions to Fail2Ban log
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -29,6 +29,16 @@ import textwrap
|
||||||
##
|
##
|
||||||
# Describes the protocol used to communicate with the server.
|
# Describes the protocol used to communicate with the server.
|
||||||
|
|
||||||
|
class dotdict(dict):
|
||||||
|
def __getattr__(self, name):
|
||||||
|
return self[name]
|
||||||
|
|
||||||
|
CSPROTO = dotdict({
|
||||||
|
"EMPTY": b"",
|
||||||
|
"END": b"<F2B_END_COMMAND>",
|
||||||
|
"CLOSE": b"<F2B_CLOSE_COMMAND>"
|
||||||
|
})
|
||||||
|
|
||||||
protocol = [
|
protocol = [
|
||||||
['', "BASIC", ""],
|
['', "BASIC", ""],
|
||||||
["start", "starts the server and the jails"],
|
["start", "starts the server and the jails"],
|
||||||
|
@ -44,6 +54,8 @@ protocol = [
|
||||||
["get loglevel", "gets the logging level"],
|
["get loglevel", "gets the logging level"],
|
||||||
["set logtarget <TARGET>", "sets logging target to <TARGET>. Can be STDOUT, STDERR, SYSLOG or a file"],
|
["set logtarget <TARGET>", "sets logging target to <TARGET>. Can be STDOUT, STDERR, SYSLOG or a file"],
|
||||||
["get logtarget", "gets logging target"],
|
["get logtarget", "gets logging target"],
|
||||||
|
["set syslogsocket auto|<SOCKET>", "sets the syslog socket path to auto or <SOCKET>. Only used if logtarget is SYSLOG"],
|
||||||
|
["get syslogsocket", "gets syslog socket path"],
|
||||||
["flushlogs", "flushes the logtarget if a file and reopens it. For log rotation."],
|
["flushlogs", "flushes the logtarget if a file and reopens it. For log rotation."],
|
||||||
['', "DATABASE", ""],
|
['', "DATABASE", ""],
|
||||||
["set dbfile <FILE>", "set the location of fail2ban persistent datastore. Set to \"None\" to disable"],
|
["set dbfile <FILE>", "set the location of fail2ban persistent datastore. Set to \"None\" to disable"],
|
||||||
|
@ -54,7 +66,7 @@ protocol = [
|
||||||
["add <JAIL> <BACKEND>", "creates <JAIL> using <BACKEND>"],
|
["add <JAIL> <BACKEND>", "creates <JAIL> using <BACKEND>"],
|
||||||
["start <JAIL>", "starts the jail <JAIL>"],
|
["start <JAIL>", "starts the jail <JAIL>"],
|
||||||
["stop <JAIL>", "stops the jail <JAIL>. The jail is removed"],
|
["stop <JAIL>", "stops the jail <JAIL>. The jail is removed"],
|
||||||
["status <JAIL>", "gets the current status of <JAIL>"],
|
["status <JAIL> [FLAVOR]", "gets the current status of <JAIL>, with optional flavor or extended info"],
|
||||||
['', "JAIL CONFIGURATION", ""],
|
['', "JAIL CONFIGURATION", ""],
|
||||||
["set <JAIL> idle on|off", "sets the idle state of <JAIL>"],
|
["set <JAIL> idle on|off", "sets the idle state of <JAIL>"],
|
||||||
["set <JAIL> addignoreip <IP>", "adds <IP> to the ignore list of <JAIL>"],
|
["set <JAIL> addignoreip <IP>", "adds <IP> to the ignore list of <JAIL>"],
|
||||||
|
@ -117,6 +129,7 @@ protocol = [
|
||||||
["get <JAIL> action <ACT> <PROPERTY>", "gets the value of <PROPERTY> for the action <ACT> for <JAIL>"],
|
["get <JAIL> action <ACT> <PROPERTY>", "gets the value of <PROPERTY> for the action <ACT> for <JAIL>"],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Prints the protocol in a "man" format. This is used for the
|
# Prints the protocol in a "man" format. This is used for the
|
||||||
# "-h" output of fail2ban-client.
|
# "-h" output of fail2ban-client.
|
||||||
|
@ -141,6 +154,7 @@ def printFormatted():
|
||||||
line = ' ' * (INDENT + MARGIN) + n.strip()
|
line = ' ' * (INDENT + MARGIN) + n.strip()
|
||||||
print line
|
print line
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Prints the protocol in a "mediawiki" format.
|
# Prints the protocol in a "mediawiki" format.
|
||||||
|
|
||||||
|
@ -157,6 +171,7 @@ def printWiki():
|
||||||
print "| <span style=\"white-space:nowrap;\"><tt>" + m[0] + "</tt></span> || || " + m[1]
|
print "| <span style=\"white-space:nowrap;\"><tt>" + m[0] + "</tt></span> || || " + m[1]
|
||||||
print "|}"
|
print "|}"
|
||||||
|
|
||||||
|
|
||||||
def __printWikiHeader(section, desc):
|
def __printWikiHeader(section, desc):
|
||||||
print
|
print
|
||||||
print "=== " + section + " ==="
|
print "=== " + section + " ==="
|
||||||
|
|
|
@ -21,8 +21,14 @@ __author__ = "Cyril Jaquier and Fail2Ban Contributors"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2012 Yaroslav Halchenko"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2012 Yaroslav Halchenko"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import logging, os, subprocess, time, signal, tempfile
|
import logging
|
||||||
import threading, re
|
import os
|
||||||
|
import re
|
||||||
|
import signal
|
||||||
|
import subprocess
|
||||||
|
import tempfile
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
from abc import ABCMeta
|
from abc import ABCMeta
|
||||||
from collections import MutableMapping
|
from collections import MutableMapping
|
||||||
|
|
||||||
|
@ -49,6 +55,7 @@ _RETCODE_HINTS = {
|
||||||
signame = dict((num, name)
|
signame = dict((num, name)
|
||||||
for name, num in signal.__dict__.iteritems() if name.startswith("SIG"))
|
for name, num in signal.__dict__.iteritems() if name.startswith("SIG"))
|
||||||
|
|
||||||
|
|
||||||
class CallingMap(MutableMapping):
|
class CallingMap(MutableMapping):
|
||||||
"""A Mapping type which returns the result of callable values.
|
"""A Mapping type which returns the result of callable values.
|
||||||
|
|
||||||
|
@ -94,6 +101,7 @@ class CallingMap(MutableMapping):
|
||||||
def copy(self):
|
def copy(self):
|
||||||
return self.__class__(self.data.copy())
|
return self.__class__(self.data.copy())
|
||||||
|
|
||||||
|
|
||||||
class ActionBase(object):
|
class ActionBase(object):
|
||||||
"""An abstract base class for actions in Fail2Ban.
|
"""An abstract base class for actions in Fail2Ban.
|
||||||
|
|
||||||
|
@ -176,6 +184,7 @@ class ActionBase(object):
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CommandAction(ActionBase):
|
class CommandAction(ActionBase):
|
||||||
"""A action which executes OS shell commands.
|
"""A action which executes OS shell commands.
|
||||||
|
|
||||||
|
@ -362,6 +371,7 @@ class CommandAction(ActionBase):
|
||||||
@classmethod
|
@classmethod
|
||||||
def substituteRecursiveTags(cls, tags):
|
def substituteRecursiveTags(cls, tags):
|
||||||
"""Sort out tag definitions within other tags.
|
"""Sort out tag definitions within other tags.
|
||||||
|
Since v.0.9.2 supports embedded interpolation (see test cases for examples).
|
||||||
|
|
||||||
so: becomes:
|
so: becomes:
|
||||||
a = 3 a = 3
|
a = 3 a = 3
|
||||||
|
@ -378,12 +388,17 @@ class CommandAction(ActionBase):
|
||||||
Dictionary of tags(keys) and their values, with tags
|
Dictionary of tags(keys) and their values, with tags
|
||||||
within the values recursively replaced.
|
within the values recursively replaced.
|
||||||
"""
|
"""
|
||||||
t = re.compile(r'<([^ >]+)>')
|
t = re.compile(r'<([^ <>]+)>')
|
||||||
|
# repeat substitution while embedded-recursive (repFlag is True)
|
||||||
|
while True:
|
||||||
|
repFlag = False
|
||||||
|
# substitute each value:
|
||||||
for tag in tags.iterkeys():
|
for tag in tags.iterkeys():
|
||||||
if tag in cls._escapedTags:
|
if tag in cls._escapedTags:
|
||||||
# Escaped so won't match
|
# Escaped so won't match
|
||||||
continue
|
continue
|
||||||
value = str(tags[tag])
|
value = str(tags[tag])
|
||||||
|
# search and replace all tags within value, that can be interpolated using other tags:
|
||||||
m = t.search(value)
|
m = t.search(value)
|
||||||
done = []
|
done = []
|
||||||
#logSys.log(5, 'TAG: %s, value: %s' % (tag, value))
|
#logSys.log(5, 'TAG: %s, value: %s' % (tag, value))
|
||||||
|
@ -394,22 +409,25 @@ class CommandAction(ActionBase):
|
||||||
# recursive definitions are bad
|
# recursive definitions are bad
|
||||||
#logSys.log(5, 'recursion fail tag: %s value: %s' % (tag, value) )
|
#logSys.log(5, 'recursion fail tag: %s value: %s' % (tag, value) )
|
||||||
return False
|
return False
|
||||||
elif found_tag in cls._escapedTags:
|
if found_tag in cls._escapedTags or not found_tag in tags:
|
||||||
# Escaped so won't match
|
# Escaped or missing tags - just continue on searching after end of match
|
||||||
|
# Missing tags are ok - cInfo can contain aInfo elements like <HOST> and valid shell
|
||||||
|
# constructs like <STDIN>.
|
||||||
|
m = t.search(value, m.end())
|
||||||
continue
|
continue
|
||||||
else:
|
|
||||||
if tags.has_key(found_tag):
|
|
||||||
value = value.replace('<%s>' % found_tag , tags[found_tag])
|
value = value.replace('<%s>' % found_tag , tags[found_tag])
|
||||||
#logSys.log(5, 'value now: %s' % value)
|
#logSys.log(5, 'value now: %s' % value)
|
||||||
done.append(found_tag)
|
done.append(found_tag)
|
||||||
m = t.search(value, m.start())
|
m = t.search(value, m.start())
|
||||||
else:
|
|
||||||
# Missing tags are ok so we just continue on searching.
|
|
||||||
# cInfo can contain aInfo elements like <HOST> and valid shell
|
|
||||||
# constructs like <STDIN>.
|
|
||||||
m = t.search(value, m.start() + 1)
|
|
||||||
#logSys.log(5, 'TAG: %s, newvalue: %s' % (tag, value))
|
#logSys.log(5, 'TAG: %s, newvalue: %s' % (tag, value))
|
||||||
|
# was substituted?
|
||||||
|
if tags[tag] != value:
|
||||||
|
# check still contains any tag - should be repeated (possible embedded-recursive substitution):
|
||||||
|
if t.search(value):
|
||||||
|
repFlag = True
|
||||||
tags[tag] = value
|
tags[tag] = value
|
||||||
|
if not repFlag:
|
||||||
|
break
|
||||||
return tags
|
return tags
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
@ -24,9 +24,10 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import time, logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
if sys.version_info >= (3, 3):
|
if sys.version_info >= (3, 3):
|
||||||
import importlib.machinery
|
import importlib.machinery
|
||||||
else:
|
else:
|
||||||
|
@ -47,6 +48,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Actions(JailThread, Mapping):
|
class Actions(JailThread, Mapping):
|
||||||
"""Handles jail actions.
|
"""Handles jail actions.
|
||||||
|
|
||||||
|
@ -297,7 +299,7 @@ class Actions(JailThread, Mapping):
|
||||||
True if an IP address get banned.
|
True if an IP address get banned.
|
||||||
"""
|
"""
|
||||||
ticket = self._jail.getFailTicket()
|
ticket = self._jail.getFailTicket()
|
||||||
if ticket != False:
|
if ticket:
|
||||||
aInfo = CallingMap()
|
aInfo = CallingMap()
|
||||||
bTicket = BanManager.createBanTicket(ticket)
|
bTicket = BanManager.createBanTicket(ticket)
|
||||||
if ticket.getBanTime() is not None:
|
if ticket.getBanTime() is not None:
|
||||||
|
@ -391,11 +393,21 @@ class Actions(JailThread, Mapping):
|
||||||
self._jail.name, name, aInfo, e,
|
self._jail.name, name, aInfo, e,
|
||||||
exc_info=logSys.getEffectiveLevel()<=logging.DEBUG)
|
exc_info=logSys.getEffectiveLevel()<=logging.DEBUG)
|
||||||
|
|
||||||
@property
|
def status(self, flavor="basic"):
|
||||||
def status(self):
|
"""Status of current and total ban counts and current banned IP list.
|
||||||
"""Status of active bans, and total ban counts.
|
|
||||||
"""
|
"""
|
||||||
|
# TODO: Allow this list to be printed as 'status' output
|
||||||
|
supported_flavors = ["basic", "cymru"]
|
||||||
|
if flavor is None or flavor not in supported_flavors:
|
||||||
|
logSys.warning("Unsupported extended jail status flavor %r. Supported: %s" % (flavor, supported_flavors))
|
||||||
|
# Always print this information (basic)
|
||||||
ret = [("Currently banned", self.__banManager.size()),
|
ret = [("Currently banned", self.__banManager.size()),
|
||||||
("Total banned", self.__banManager.getBanTotal()),
|
("Total banned", self.__banManager.getBanTotal()),
|
||||||
("Banned IP list", self.__banManager.getBanList())]
|
("Banned IP list", self.__banManager.getBanList())]
|
||||||
|
if flavor == "cymru":
|
||||||
|
cymru_info = self.__banManager.getBanListExtendedCymruInfo()
|
||||||
|
ret += \
|
||||||
|
[("Banned ASN list", self.__banManager.geBanListExtendedASN(cymru_info)),
|
||||||
|
("Banned Country list", self.__banManager.geBanListExtendedCountry(cymru_info)),
|
||||||
|
("Banned RIR list", self.__banManager.geBanListExtendedRIR(cymru_info))]
|
||||||
return ret
|
return ret
|
||||||
|
|
|
@ -25,20 +25,20 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
from pickle import dumps, loads, HIGHEST_PROTOCOL
|
from pickle import dumps, loads, HIGHEST_PROTOCOL
|
||||||
import asyncore, asynchat, socket, os, sys, traceback, fcntl
|
import asynchat
|
||||||
|
import asyncore
|
||||||
|
import fcntl
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
from ..protocol import CSPROTO
|
||||||
from ..helpers import getLogger,formatExceptionInfo
|
from ..helpers import getLogger,formatExceptionInfo
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
if sys.version_info >= (3,):
|
|
||||||
# b"" causes SyntaxError in python <= 2.5, so below implements equivalent
|
|
||||||
EMPTY_BYTES = bytes("", encoding="ascii")
|
|
||||||
else:
|
|
||||||
# python 2.x, string type is equivalent to bytes.
|
|
||||||
EMPTY_BYTES = ""
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Request handler class.
|
# Request handler class.
|
||||||
#
|
#
|
||||||
|
@ -47,17 +47,12 @@ else:
|
||||||
|
|
||||||
class RequestHandler(asynchat.async_chat):
|
class RequestHandler(asynchat.async_chat):
|
||||||
|
|
||||||
if sys.version_info >= (3,):
|
|
||||||
END_STRING = bytes("<F2B_END_COMMAND>", encoding="ascii")
|
|
||||||
else:
|
|
||||||
END_STRING = "<F2B_END_COMMAND>"
|
|
||||||
|
|
||||||
def __init__(self, conn, transmitter):
|
def __init__(self, conn, transmitter):
|
||||||
asynchat.async_chat.__init__(self, conn)
|
asynchat.async_chat.__init__(self, conn)
|
||||||
self.__transmitter = transmitter
|
self.__transmitter = transmitter
|
||||||
self.__buffer = []
|
self.__buffer = []
|
||||||
# Sets the terminator.
|
# Sets the terminator.
|
||||||
self.set_terminator(RequestHandler.END_STRING)
|
self.set_terminator(CSPROTO.END)
|
||||||
|
|
||||||
def collect_incoming_data(self, data):
|
def collect_incoming_data(self, data):
|
||||||
#logSys.debug("Received raw data: " + str(data))
|
#logSys.debug("Received raw data: " + str(data))
|
||||||
|
@ -69,16 +64,23 @@ class RequestHandler(asynchat.async_chat):
|
||||||
# This method is called once we have a complete request.
|
# This method is called once we have a complete request.
|
||||||
|
|
||||||
def found_terminator(self):
|
def found_terminator(self):
|
||||||
|
# Pop whole buffer
|
||||||
|
message = self.__buffer
|
||||||
|
self.__buffer = []
|
||||||
# Joins the buffer items.
|
# Joins the buffer items.
|
||||||
message = loads(EMPTY_BYTES.join(self.__buffer))
|
message = CSPROTO.EMPTY.join(message)
|
||||||
|
# Closes the channel if close was received
|
||||||
|
if message == CSPROTO.CLOSE:
|
||||||
|
self.close_when_done()
|
||||||
|
return
|
||||||
|
# Deserialize
|
||||||
|
message = loads(message)
|
||||||
# Gives the message to the transmitter.
|
# Gives the message to the transmitter.
|
||||||
message = self.__transmitter.proceed(message)
|
message = self.__transmitter.proceed(message)
|
||||||
# Serializes the response.
|
# Serializes the response.
|
||||||
message = dumps(message, HIGHEST_PROTOCOL)
|
message = dumps(message, HIGHEST_PROTOCOL)
|
||||||
# Sends the response to the client.
|
# Sends the response to the client.
|
||||||
self.push(message + RequestHandler.END_STRING)
|
self.push(message + CSPROTO.END)
|
||||||
# Closes the channel.
|
|
||||||
self.close_when_done()
|
|
||||||
|
|
||||||
def handle_error(self):
|
def handle_error(self):
|
||||||
e1, e2 = formatExceptionInfo()
|
e1, e2 = formatExceptionInfo()
|
||||||
|
@ -86,6 +88,7 @@ class RequestHandler(asynchat.async_chat):
|
||||||
logSys.error(traceback.format_exc().splitlines())
|
logSys.error(traceback.format_exc().splitlines())
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Asynchronous server class.
|
# Asynchronous server class.
|
||||||
#
|
#
|
||||||
|
@ -149,7 +152,11 @@ class AsyncServer(asyncore.dispatcher):
|
||||||
self.__init = True
|
self.__init = True
|
||||||
# TODO Add try..catch
|
# TODO Add try..catch
|
||||||
# There's a bug report for Python 2.6/3.0 that use_poll=True yields some 2.5 incompatibilities:
|
# There's a bug report for Python 2.6/3.0 that use_poll=True yields some 2.5 incompatibilities:
|
||||||
logSys.debug("Detected Python 2.6 or greater. asyncore.loop() not using poll")
|
if (sys.version_info >= (2, 7) and sys.version_info < (2, 8)) \
|
||||||
|
or (sys.version_info >= (3, 4)): # if python 2.7 ...
|
||||||
|
logSys.debug("Detected Python 2.7. asyncore.loop() using poll")
|
||||||
|
asyncore.loop(use_poll=True) # workaround for the "Bad file descriptor" issue on Python 2.7, gh-161
|
||||||
|
else:
|
||||||
asyncore.loop(use_poll=False) # fixes the "Unexpected communication problem" issue on Python 2.6 and 3.0
|
asyncore.loop(use_poll=False) # fixes the "Unexpected communication problem" issue on Python 2.6 and 3.0
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -177,6 +184,7 @@ class AsyncServer(asyncore.dispatcher):
|
||||||
flags = fcntl.fcntl(fd, fcntl.F_GETFD)
|
flags = fcntl.fcntl(fd, fcntl.F_GETFD)
|
||||||
fcntl.fcntl(fd, fcntl.F_SETFD, flags|fcntl.FD_CLOEXEC)
|
fcntl.fcntl(fd, fcntl.F_SETFD, flags|fcntl.FD_CLOEXEC)
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# AsyncServerException is used to wrap communication exceptions.
|
# AsyncServerException is used to wrap communication exceptions.
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Banning Manager.
|
# Banning Manager.
|
||||||
#
|
#
|
||||||
|
@ -118,6 +119,124 @@ class BanManager:
|
||||||
finally:
|
finally:
|
||||||
self.__lock.release()
|
self.__lock.release()
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns normalized value
|
||||||
|
#
|
||||||
|
# @return value or "unknown" if value is None or empty string
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def handleBlankResult(value):
|
||||||
|
if value is None or len(value) == 0:
|
||||||
|
return "unknown"
|
||||||
|
else:
|
||||||
|
return value
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns Cymru DNS query information
|
||||||
|
#
|
||||||
|
# @return {"asn": [], "country": [], "rir": []} dict for self.__banList IPs
|
||||||
|
|
||||||
|
def getBanListExtendedCymruInfo(self):
|
||||||
|
return_dict = {"asn": [], "country": [], "rir": []}
|
||||||
|
try:
|
||||||
|
import dns.exception
|
||||||
|
import dns.resolver
|
||||||
|
except ImportError:
|
||||||
|
logSys.error("dnspython package is required but could not be imported")
|
||||||
|
return_dict["asn"].append("error")
|
||||||
|
return_dict["country"].append("error")
|
||||||
|
return_dict["rir"].append("error")
|
||||||
|
return return_dict
|
||||||
|
self.__lock.acquire()
|
||||||
|
try:
|
||||||
|
for banData in self.__banList:
|
||||||
|
ip = banData.getIP()
|
||||||
|
# Reference: http://www.team-cymru.org/Services/ip-to-asn.html#dns
|
||||||
|
# TODO: IPv6 compatibility
|
||||||
|
reversed_ip = ".".join(reversed(ip.split(".")))
|
||||||
|
question = "%s.origin.asn.cymru.com" % reversed_ip
|
||||||
|
try:
|
||||||
|
answers = dns.resolver.query(question, "TXT")
|
||||||
|
for rdata in answers:
|
||||||
|
asn, net, country, rir, changed =\
|
||||||
|
[answer.strip("'\" ") for answer in rdata.to_text().split("|")]
|
||||||
|
asn = self.handleBlankResult(asn)
|
||||||
|
country = self.handleBlankResult(country)
|
||||||
|
rir = self.handleBlankResult(rir)
|
||||||
|
return_dict["asn"].append(self.handleBlankResult(asn))
|
||||||
|
return_dict["country"].append(self.handleBlankResult(country))
|
||||||
|
return_dict["rir"].append(self.handleBlankResult(rir))
|
||||||
|
except dns.resolver.NXDOMAIN:
|
||||||
|
return_dict["asn"].append("nxdomain")
|
||||||
|
return_dict["country"].append("nxdomain")
|
||||||
|
return_dict["rir"].append("nxdomain")
|
||||||
|
except dns.exception.DNSException as dnse:
|
||||||
|
logSys.error("Unhandled DNSException querying Cymru for %s TXT" % question)
|
||||||
|
logSys.exception(dnse)
|
||||||
|
except Exception as e:
|
||||||
|
logSys.error("Unhandled Exception querying Cymru for %s TXT" % question)
|
||||||
|
logSys.exception(e)
|
||||||
|
except Exception as e:
|
||||||
|
logSys.error("Failure looking up extended Cymru info")
|
||||||
|
logSys.exception(e)
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
||||||
|
return return_dict
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns list of Banned ASNs from Cymru info
|
||||||
|
#
|
||||||
|
# Use getBanListExtendedCymruInfo() to provide cymru_info
|
||||||
|
#
|
||||||
|
# @return list of Banned ASNs
|
||||||
|
|
||||||
|
def geBanListExtendedASN(self, cymru_info):
|
||||||
|
self.__lock.acquire()
|
||||||
|
try:
|
||||||
|
return [asn for asn in cymru_info["asn"]]
|
||||||
|
except Exception as e:
|
||||||
|
logSys.error("Failed to lookup ASN")
|
||||||
|
logSys.exception(e)
|
||||||
|
return []
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns list of Banned Countries from Cymru info
|
||||||
|
#
|
||||||
|
# Use getBanListExtendedCymruInfo() to provide cymru_info
|
||||||
|
#
|
||||||
|
# @return list of Banned Countries
|
||||||
|
|
||||||
|
def geBanListExtendedCountry(self, cymru_info):
|
||||||
|
self.__lock.acquire()
|
||||||
|
try:
|
||||||
|
return [country for country in cymru_info["country"]]
|
||||||
|
except Exception as e:
|
||||||
|
logSys.error("Failed to lookup Country")
|
||||||
|
logSys.exception(e)
|
||||||
|
return []
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns list of Banned RIRs from Cymru info
|
||||||
|
#
|
||||||
|
# Use getBanListExtendedCymruInfo() to provide cymru_info
|
||||||
|
#
|
||||||
|
# @return list of Banned RIRs
|
||||||
|
|
||||||
|
def geBanListExtendedRIR(self, cymru_info):
|
||||||
|
self.__lock.acquire()
|
||||||
|
try:
|
||||||
|
return [rir for rir in cymru_info["rir"]]
|
||||||
|
except Exception as e:
|
||||||
|
logSys.error("Failed to lookup RIR")
|
||||||
|
logSys.exception(e)
|
||||||
|
return []
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
##
|
##
|
||||||
# Create a ban ticket.
|
# Create a ban ticket.
|
||||||
#
|
#
|
||||||
|
@ -171,7 +290,6 @@ class BanManager:
|
||||||
finally:
|
finally:
|
||||||
self.__lock.release()
|
self.__lock.release()
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Get the size of the ban list.
|
# Get the size of the ban list.
|
||||||
#
|
#
|
||||||
|
|
|
@ -21,11 +21,12 @@ __author__ = "Steven Hiscocks"
|
||||||
__copyright__ = "Copyright (c) 2013 Steven Hiscocks"
|
__copyright__ = "Copyright (c) 2013 Steven Hiscocks"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import sys
|
|
||||||
import shutil, time
|
|
||||||
import sqlite3
|
|
||||||
import json
|
import json
|
||||||
import locale
|
import locale
|
||||||
|
import shutil
|
||||||
|
import sqlite3
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from threading import RLock
|
from threading import RLock
|
||||||
|
|
||||||
|
@ -37,17 +38,55 @@ from ..helpers import getLogger
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
if sys.version_info >= (3,):
|
if sys.version_info >= (3,):
|
||||||
sqlite3.register_adapter(
|
def _json_dumps_safe(x):
|
||||||
dict,
|
try:
|
||||||
lambda x: json.dumps(x, ensure_ascii=False).encode(
|
x = json.dumps(x, ensure_ascii=False).encode(
|
||||||
|
locale.getpreferredencoding(), 'replace')
|
||||||
|
except Exception, e: # pragma: no cover
|
||||||
|
logSys.error('json dumps failed: %s', e)
|
||||||
|
x = '{}'
|
||||||
|
return x
|
||||||
|
|
||||||
|
def _json_loads_safe(x):
|
||||||
|
try:
|
||||||
|
x = json.loads(x.decode(
|
||||||
locale.getpreferredencoding(), 'replace'))
|
locale.getpreferredencoding(), 'replace'))
|
||||||
sqlite3.register_converter(
|
except Exception, e: # pragma: no cover
|
||||||
"JSON",
|
logSys.error('json loads failed: %s', e)
|
||||||
lambda x: json.loads(x.decode(
|
x = {}
|
||||||
locale.getpreferredencoding(), 'replace')))
|
return x
|
||||||
else:
|
else:
|
||||||
sqlite3.register_adapter(dict, json.dumps)
|
def _normalize(x):
|
||||||
sqlite3.register_converter("JSON", json.loads)
|
if isinstance(x, dict):
|
||||||
|
return dict((_normalize(k), _normalize(v)) for k, v in x.iteritems())
|
||||||
|
elif isinstance(x, list):
|
||||||
|
return [_normalize(element) for element in x]
|
||||||
|
elif isinstance(x, unicode):
|
||||||
|
return x.encode(locale.getpreferredencoding())
|
||||||
|
else:
|
||||||
|
return x
|
||||||
|
|
||||||
|
def _json_dumps_safe(x):
|
||||||
|
try:
|
||||||
|
x = json.dumps(_normalize(x), ensure_ascii=False).decode(
|
||||||
|
locale.getpreferredencoding(), 'replace')
|
||||||
|
except Exception, e: # pragma: no cover
|
||||||
|
logSys.error('json dumps failed: %s', e)
|
||||||
|
x = '{}'
|
||||||
|
return x
|
||||||
|
|
||||||
|
def _json_loads_safe(x):
|
||||||
|
try:
|
||||||
|
x = _normalize(json.loads(x.decode(
|
||||||
|
locale.getpreferredencoding(), 'replace')))
|
||||||
|
except Exception, e: # pragma: no cover
|
||||||
|
logSys.error('json loads failed: %s', e)
|
||||||
|
x = {}
|
||||||
|
return x
|
||||||
|
|
||||||
|
sqlite3.register_adapter(dict, _json_dumps_safe)
|
||||||
|
sqlite3.register_converter("JSON", _json_loads_safe)
|
||||||
|
|
||||||
|
|
||||||
def commitandrollback(f):
|
def commitandrollback(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
|
@ -57,6 +96,7 @@ def commitandrollback(f):
|
||||||
return f(self, self._db.cursor(), *args, **kwargs)
|
return f(self, self._db.cursor(), *args, **kwargs)
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
class Fail2BanDb(object):
|
class Fail2BanDb(object):
|
||||||
"""Fail2Ban database for storing persistent data.
|
"""Fail2Ban database for storing persistent data.
|
||||||
|
|
||||||
|
@ -136,6 +176,7 @@ class Fail2BanDb(object):
|
||||||
"CREATE INDEX bips_timeofban ON bips(timeofban);" \
|
"CREATE INDEX bips_timeofban ON bips(timeofban);" \
|
||||||
"CREATE INDEX bips_ip ON bips(ip);" \
|
"CREATE INDEX bips_ip ON bips(ip);" \
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, filename, purgeAge=24*60*60, outDatedFactor=3):
|
def __init__(self, filename, purgeAge=24*60*60, outDatedFactor=3):
|
||||||
try:
|
try:
|
||||||
self._lock = RLock()
|
self._lock = RLock()
|
||||||
|
@ -425,8 +466,8 @@ class Fail2BanDb(object):
|
||||||
----------
|
----------
|
||||||
jail : Jail
|
jail : Jail
|
||||||
Jail in which the ban has occurred.
|
Jail in which the ban has occurred.
|
||||||
ticket : BanTicket
|
ip : str
|
||||||
Ticket of the ban to be removed.
|
IP to be removed.
|
||||||
"""
|
"""
|
||||||
queryArgs = (jail.name, ip);
|
queryArgs = (jail.name, ip);
|
||||||
cur.execute(
|
cur.execute(
|
||||||
|
@ -476,8 +517,8 @@ class Fail2BanDb(object):
|
||||||
tickets = []
|
tickets = []
|
||||||
for ip, timeofban, data in self._getBans(**kwargs):
|
for ip, timeofban, data in self._getBans(**kwargs):
|
||||||
#TODO: Implement data parts once arbitrary match keys completed
|
#TODO: Implement data parts once arbitrary match keys completed
|
||||||
tickets.append(FailTicket(ip, timeofban, data['matches']))
|
tickets.append(FailTicket(ip, timeofban, data.get('matches')))
|
||||||
tickets[-1].setAttempt(data['failures'])
|
tickets[-1].setAttempt(data.get('failures', 1))
|
||||||
return tickets
|
return tickets
|
||||||
|
|
||||||
def getBansMerged(self, ip=None, jail=None, bantime=None):
|
def getBansMerged(self, ip=None, jail=None, bantime=None):
|
||||||
|
@ -529,8 +570,8 @@ class Fail2BanDb(object):
|
||||||
prev_banip = banip
|
prev_banip = banip
|
||||||
matches = []
|
matches = []
|
||||||
failures = 0
|
failures = 0
|
||||||
matches.extend(data['matches'])
|
matches.extend(data.get('matches', []))
|
||||||
failures += data['failures']
|
failures += data.get('failures', 1)
|
||||||
prev_timeofban = timeofban
|
prev_timeofban = timeofban
|
||||||
ticket = FailTicket(banip, prev_timeofban, matches)
|
ticket = FailTicket(banip, prev_timeofban, matches)
|
||||||
ticket.setAttempt(failures)
|
ticket.setAttempt(failures)
|
||||||
|
|
|
@ -31,6 +31,7 @@ logSys = getLogger(__name__)
|
||||||
|
|
||||||
logLevel = 6
|
logLevel = 6
|
||||||
|
|
||||||
|
|
||||||
class DateDetector(object):
|
class DateDetector(object):
|
||||||
"""Manages one or more date templates to find a date within a log line.
|
"""Manages one or more date templates to find a date within a log line.
|
||||||
|
|
||||||
|
|
|
@ -155,6 +155,7 @@ class DateEpoch(DateTemplate):
|
||||||
return (float(dateMatch.group()), dateMatch)
|
return (float(dateMatch.group()), dateMatch)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
class DatePatternRegex(DateTemplate):
|
class DatePatternRegex(DateTemplate):
|
||||||
"""Date template, with regex/pattern
|
"""Date template, with regex/pattern
|
||||||
|
|
||||||
|
@ -238,6 +239,7 @@ class DatePatternRegex(DateTemplate):
|
||||||
if value is not None)
|
if value is not None)
|
||||||
return reGroupDictStrptime(groupdict), dateMatch
|
return reGroupDictStrptime(groupdict), dateMatch
|
||||||
|
|
||||||
|
|
||||||
class DateTai64n(DateTemplate):
|
class DateTai64n(DateTemplate):
|
||||||
"""A date template which matches TAI64N formate timestamps.
|
"""A date template which matches TAI64N formate timestamps.
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class FailData:
|
class FailData:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
|
@ -34,6 +34,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class FailManager:
|
class FailManager:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -91,7 +92,7 @@ class FailManager:
|
||||||
ip = ticket.getIP()
|
ip = ticket.getIP()
|
||||||
unixTime = ticket.getTime()
|
unixTime = ticket.getTime()
|
||||||
matches = ticket.getMatches()
|
matches = ticket.getMatches()
|
||||||
if self.__failList.has_key(ip):
|
if ip in self.__failList:
|
||||||
fData = self.__failList[ip]
|
fData = self.__failList[ip]
|
||||||
if fData.getLastReset() < unixTime - self.__maxTime:
|
if fData.getLastReset() < unixTime - self.__maxTime:
|
||||||
fData.setLastReset(unixTime)
|
fData.setLastReset(unixTime)
|
||||||
|
@ -139,7 +140,7 @@ class FailManager:
|
||||||
self.__lock.release()
|
self.__lock.release()
|
||||||
|
|
||||||
def __delFailure(self, ip):
|
def __delFailure(self, ip):
|
||||||
if self.__failList.has_key(ip):
|
if ip in self.__failList:
|
||||||
del self.__failList[ip]
|
del self.__failList[ip]
|
||||||
|
|
||||||
def toBan(self, ip=None):
|
def toBan(self, ip=None):
|
||||||
|
@ -157,5 +158,6 @@ class FailManager:
|
||||||
finally:
|
finally:
|
||||||
self.__lock.release()
|
self.__lock.release()
|
||||||
|
|
||||||
|
|
||||||
class FailManagerEmpty(Exception):
|
class FailManagerEmpty(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -21,7 +21,10 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import re, sre_constants, sys
|
import re
|
||||||
|
import sre_constants
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Regular expression class.
|
# Regular expression class.
|
||||||
|
@ -55,6 +58,7 @@ class Regex:
|
||||||
except sre_constants.error:
|
except sre_constants.error:
|
||||||
raise RegexException("Unable to compile regular expression '%s'" %
|
raise RegexException("Unable to compile regular expression '%s'" %
|
||||||
regex)
|
regex)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s(%r)" % (self.__class__.__name__, self._regex)
|
return "%s(%r)" % (self.__class__.__name__, self._regex)
|
||||||
##
|
##
|
||||||
|
@ -91,7 +95,6 @@ class Regex:
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self._matchLineEnd = len(self._matchCache.string)
|
self._matchLineEnd = len(self._matchCache.string)
|
||||||
|
|
||||||
|
|
||||||
lineCount1 = self._matchCache.string.count(
|
lineCount1 = self._matchCache.string.count(
|
||||||
"\n", 0, self._matchLineStart)
|
"\n", 0, self._matchLineStart)
|
||||||
lineCount2 = self._matchCache.string.count(
|
lineCount2 = self._matchCache.string.count(
|
||||||
|
@ -182,6 +185,7 @@ class Regex:
|
||||||
else:
|
else:
|
||||||
return ["".join(line) for line in self._matchedTupleLines]
|
return ["".join(line) for line in self._matchedTupleLines]
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Exception dedicated to the class Regex.
|
# Exception dedicated to the class Regex.
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,14 @@ __author__ = "Cyril Jaquier and Fail2Ban Contributors"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2013 Yaroslav Halchenko"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2013 Yaroslav Halchenko"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import re, os, fcntl, sys, locale, codecs, datetime, logging
|
import codecs
|
||||||
|
import datetime
|
||||||
|
import fcntl
|
||||||
|
import locale
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
from .failmanager import FailManagerEmpty, FailManager
|
from .failmanager import FailManagerEmpty, FailManager
|
||||||
from .observer import Observers
|
from .observer import Observers
|
||||||
|
@ -44,6 +51,7 @@ logSys = getLogger(__name__)
|
||||||
# that matches a given regular expression. This class is instantiated by
|
# that matches a given regular expression. This class is instantiated by
|
||||||
# a Jail object.
|
# a Jail object.
|
||||||
|
|
||||||
|
|
||||||
class Filter(JailThread):
|
class Filter(JailThread):
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -82,7 +90,6 @@ class Filter(JailThread):
|
||||||
self.dateDetector.addDefaultTemplate()
|
self.dateDetector.addDefaultTemplate()
|
||||||
logSys.debug("Created %s" % self)
|
logSys.debug("Created %s" % self)
|
||||||
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%s(%r)" % (self.__class__.__name__, self.jail)
|
return "%s(%r)" % (self.__class__.__name__, self.jail)
|
||||||
|
|
||||||
|
@ -105,7 +112,6 @@ class Filter(JailThread):
|
||||||
logSys.error(e)
|
logSys.error(e)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
|
||||||
def delFailRegex(self, index):
|
def delFailRegex(self, index):
|
||||||
try:
|
try:
|
||||||
del self.__failRegex[index]
|
del self.__failRegex[index]
|
||||||
|
@ -339,6 +345,10 @@ class Filter(JailThread):
|
||||||
logSys.debug("Remove " + ip + " from ignore list")
|
logSys.debug("Remove " + ip + " from ignore list")
|
||||||
self.__ignoreIpList.remove(ip)
|
self.__ignoreIpList.remove(ip)
|
||||||
|
|
||||||
|
def logIgnoreIp(self, ip, log_ignore, ignore_source="unknown source"):
|
||||||
|
if log_ignore:
|
||||||
|
logSys.info("[%s] Ignore %s by %s" % (self.jail.name, ip, ignore_source))
|
||||||
|
|
||||||
def getIgnoreIP(self):
|
def getIgnoreIP(self):
|
||||||
return self.__ignoreIpList
|
return self.__ignoreIpList
|
||||||
|
|
||||||
|
@ -350,7 +360,7 @@ class Filter(JailThread):
|
||||||
# @param ip IP address
|
# @param ip IP address
|
||||||
# @return True if IP address is in ignore list
|
# @return True if IP address is in ignore list
|
||||||
|
|
||||||
def inIgnoreIPList(self, ip):
|
def inIgnoreIPList(self, ip, log_ignore=False):
|
||||||
for i in self.__ignoreIpList:
|
for i in self.__ignoreIpList:
|
||||||
# An empty string is always false
|
# An empty string is always false
|
||||||
if i == "":
|
if i == "":
|
||||||
|
@ -364,26 +374,29 @@ class Filter(JailThread):
|
||||||
"(?<=b)1+", bin(DNSUtils.addr2bin(s[1]))).group())
|
"(?<=b)1+", bin(DNSUtils.addr2bin(s[1]))).group())
|
||||||
s[1] = long(s[1])
|
s[1] = long(s[1])
|
||||||
try:
|
try:
|
||||||
a = DNSUtils.cidr(s[0], s[1])
|
a = DNSUtils.addr2bin(s[0], cidr=s[1])
|
||||||
b = DNSUtils.cidr(ip, s[1])
|
b = DNSUtils.addr2bin(ip, cidr=s[1])
|
||||||
except Exception:
|
except Exception:
|
||||||
# Check if IP in DNS
|
# Check if IP in DNS
|
||||||
ips = DNSUtils.dnsToIp(i)
|
ips = DNSUtils.dnsToIp(i)
|
||||||
if ip in ips:
|
if ip in ips:
|
||||||
|
self.logIgnoreIp(ip, log_ignore, ignore_source="dns")
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
if a == b:
|
if a == b:
|
||||||
|
self.logIgnoreIp(ip, log_ignore, ignore_source="ip")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if self.__ignoreCommand:
|
if self.__ignoreCommand:
|
||||||
command = CommandAction.replaceTag(self.__ignoreCommand, { 'ip': ip } )
|
command = CommandAction.replaceTag(self.__ignoreCommand, { 'ip': ip } )
|
||||||
logSys.debug('ignore command: ' + command)
|
logSys.debug('ignore command: ' + command)
|
||||||
return CommandAction.executeCmd(command)
|
ret_ignore = CommandAction.executeCmd(command)
|
||||||
|
self.logIgnoreIp(ip, log_ignore and ret_ignore, ignore_source="command")
|
||||||
|
return ret_ignore
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def processLine(self, line, date=None, returnRawHost=False,
|
def processLine(self, line, date=None, returnRawHost=False,
|
||||||
checkAllRegex=False):
|
checkAllRegex=False):
|
||||||
"""Split the time portion from log msg and return findFailures on them
|
"""Split the time portion from log msg and return findFailures on them
|
||||||
|
@ -421,8 +434,7 @@ class Filter(JailThread):
|
||||||
logSys.debug("Ignore line since time %s < %s - %s"
|
logSys.debug("Ignore line since time %s < %s - %s"
|
||||||
% (unixTime, MyTime.time(), self.getFindTime()))
|
% (unixTime, MyTime.time(), self.getFindTime()))
|
||||||
break
|
break
|
||||||
if self.inIgnoreIPList(ip):
|
if self.inIgnoreIPList(ip, log_ignore=True):
|
||||||
logSys.info("[%s] Ignore %s" % (self.jail.name, ip))
|
|
||||||
continue
|
continue
|
||||||
logSys.info(
|
logSys.info(
|
||||||
"[%s] Found %s - %s", self.jail.name, ip, datetime.datetime.fromtimestamp(unixTime).strftime("%Y-%m-%d %H:%M:%S")
|
"[%s] Found %s - %s", self.jail.name, ip, datetime.datetime.fromtimestamp(unixTime).strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
@ -537,8 +549,7 @@ class Filter(JailThread):
|
||||||
logSys.error(e)
|
logSys.error(e)
|
||||||
return failList
|
return failList
|
||||||
|
|
||||||
@property
|
def status(self, flavor="basic"):
|
||||||
def status(self):
|
|
||||||
"""Status of failures detected by filter.
|
"""Status of failures detected by filter.
|
||||||
"""
|
"""
|
||||||
ret = [("Currently failed", self.failManager.size()),
|
ret = [("Currently failed", self.failManager.size()),
|
||||||
|
@ -578,7 +589,6 @@ class FileFilter(Filter):
|
||||||
# to be overridden by backends
|
# to be overridden by backends
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Delete a log path
|
# Delete a log path
|
||||||
#
|
#
|
||||||
|
@ -774,11 +784,10 @@ class FileFilter(Filter):
|
||||||
logSys.debug("Position %s from %s, found time %s (%s) within %s seeks", lastpos, fs, unixTime,
|
logSys.debug("Position %s from %s, found time %s (%s) within %s seeks", lastpos, fs, unixTime,
|
||||||
(datetime.datetime.fromtimestamp(unixTime).strftime("%Y-%m-%d %H:%M:%S") if unixTime is not None else ''), cntr)
|
(datetime.datetime.fromtimestamp(unixTime).strftime("%Y-%m-%d %H:%M:%S") if unixTime is not None else ''), cntr)
|
||||||
|
|
||||||
@property
|
def status(self, flavor="basic"):
|
||||||
def status(self):
|
|
||||||
"""Status of Filter plus files being monitored.
|
"""Status of Filter plus files being monitored.
|
||||||
"""
|
"""
|
||||||
ret = super(FileFilter, self).status
|
ret = super(FileFilter, self).status(flavor=flavor)
|
||||||
path = [m.getFileName() for m in self.getLogPath()]
|
path = [m.getFileName() for m in self.getLogPath()]
|
||||||
ret.append(("File list", path))
|
ret.append(("File list", path))
|
||||||
return ret
|
return ret
|
||||||
|
@ -799,6 +808,7 @@ except ImportError: # pragma: no cover
|
||||||
import md5
|
import md5
|
||||||
md5sum = md5.new
|
md5sum = md5.new
|
||||||
|
|
||||||
|
|
||||||
class FileContainer:
|
class FileContainer:
|
||||||
|
|
||||||
def __init__(self, filename, encoding, tail = False):
|
def __init__(self, filename, encoding, tail = False):
|
||||||
|
@ -893,11 +903,13 @@ class FileContainer:
|
||||||
line = line.decode(self.getEncoding(), 'strict')
|
line = line.decode(self.getEncoding(), 'strict')
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
logSys.warning(
|
logSys.warning(
|
||||||
"Error decoding line from '%s' with '%s'. Continuing "
|
"Error decoding line from '%s' with '%s'."
|
||||||
|
" Consider setting logencoding=utf-8 (or another appropriate"
|
||||||
|
" encoding) for this jail. Continuing"
|
||||||
" to process line ignoring invalid characters: %r" %
|
" to process line ignoring invalid characters: %r" %
|
||||||
(self.getFileName(), self.getEncoding(), line))
|
(self.getFileName(), self.getEncoding(), line))
|
||||||
if sys.version_info >= (3,): # In python3, must be decoded
|
# decode with replacing error chars:
|
||||||
line = line.decode(self.getEncoding(), 'ignore')
|
line = line.decode(self.getEncoding(), 'replace')
|
||||||
return line
|
return line
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
@ -933,7 +945,9 @@ class JournalFilter(Filter): # pragma: systemd no cover
|
||||||
# This class contains only static methods used to handle DNS and IP
|
# This class contains only static methods used to handle DNS and IP
|
||||||
# addresses.
|
# addresses.
|
||||||
|
|
||||||
import socket, struct
|
import socket
|
||||||
|
import struct
|
||||||
|
|
||||||
|
|
||||||
class DNSUtils:
|
class DNSUtils:
|
||||||
|
|
||||||
|
@ -951,6 +965,14 @@ class DNSUtils:
|
||||||
% (dns, e))
|
% (dns, e))
|
||||||
return list()
|
return list()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def ipToName(ip):
|
||||||
|
try:
|
||||||
|
return socket.gethostbyaddr(ip)[0]
|
||||||
|
except socket.error, e:
|
||||||
|
logSys.debug("Unable to find a name for the IP %s: %s" % (ip, e))
|
||||||
|
return None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def searchIP(text):
|
def searchIP(text):
|
||||||
""" Search if an IP address if directly available and return
|
""" Search if an IP address if directly available and return
|
||||||
|
@ -997,22 +1019,18 @@ class DNSUtils:
|
||||||
return ipList
|
return ipList
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def cidr(i, n):
|
def addr2bin(ipstring, cidr=None):
|
||||||
""" Convert an IP address string with a CIDR mask into a 32-bit
|
""" Convert a string IPv4 address into binary form.
|
||||||
integer.
|
If cidr is supplied, return the network address for the given block
|
||||||
"""
|
"""
|
||||||
# 32-bit IPv4 address mask
|
if cidr is None:
|
||||||
|
return struct.unpack("!L", socket.inet_aton(ipstring))[0]
|
||||||
|
else:
|
||||||
MASK = 0xFFFFFFFFL
|
MASK = 0xFFFFFFFFL
|
||||||
return ~(MASK >> n) & MASK & DNSUtils.addr2bin(i)
|
return ~(MASK >> cidr) & MASK & DNSUtils.addr2bin(ipstring)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def addr2bin(string):
|
def bin2addr(ipbin):
|
||||||
""" Convert a string IPv4 address into an unsigned integer.
|
""" Convert a binary IPv4 address into string n.n.n.n form.
|
||||||
"""
|
"""
|
||||||
return struct.unpack("!L", socket.inet_aton(string))[0]
|
return socket.inet_ntoa(struct.pack("!L", ipbin))
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def bin2addr(addr):
|
|
||||||
""" Convert a numeric IPv4 address into string n.n.n.n form.
|
|
||||||
"""
|
|
||||||
return socket.inet_ntoa(struct.pack("!L", addr))
|
|
||||||
|
|
|
@ -23,7 +23,8 @@ __author__ = "Cyril Jaquier, Yaroslav Halchenko"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2012 Yaroslav Halchenko"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2012 Yaroslav Halchenko"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import time, fcntl
|
import fcntl
|
||||||
|
import time
|
||||||
|
|
||||||
import gamin
|
import gamin
|
||||||
|
|
||||||
|
@ -35,6 +36,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Log reader class.
|
# Log reader class.
|
||||||
#
|
#
|
||||||
|
@ -60,16 +62,14 @@ class FilterGamin(FileFilter):
|
||||||
fcntl.fcntl(fd, fcntl.F_SETFD, flags|fcntl.FD_CLOEXEC)
|
fcntl.fcntl(fd, fcntl.F_SETFD, flags|fcntl.FD_CLOEXEC)
|
||||||
logSys.debug("Created FilterGamin")
|
logSys.debug("Created FilterGamin")
|
||||||
|
|
||||||
|
|
||||||
def callback(self, path, event):
|
def callback(self, path, event):
|
||||||
logSys.debug("Got event: " + `event` + " for " + path)
|
logSys.debug("Got event: " + repr(event) + " for " + path)
|
||||||
if event in (gamin.GAMCreated, gamin.GAMChanged, gamin.GAMExists):
|
if event in (gamin.GAMCreated, gamin.GAMChanged, gamin.GAMExists):
|
||||||
logSys.debug("File changed: " + path)
|
logSys.debug("File changed: " + path)
|
||||||
self.__modified = True
|
self.__modified = True
|
||||||
|
|
||||||
self._process_file(path)
|
self._process_file(path)
|
||||||
|
|
||||||
|
|
||||||
def _process_file(self, path):
|
def _process_file(self, path):
|
||||||
"""Process a given file
|
"""Process a given file
|
||||||
|
|
||||||
|
@ -121,7 +121,6 @@ class FilterGamin(FileFilter):
|
||||||
logSys.debug(self.jail.name + ": filter terminated")
|
logSys.debug(self.jail.name + ": filter terminated")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
super(FilterGamin, self).stop()
|
super(FilterGamin, self).stop()
|
||||||
self.__cleanup()
|
self.__cleanup()
|
||||||
|
|
|
@ -24,7 +24,8 @@ __author__ = "Cyril Jaquier, Yaroslav Halchenko"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier; 2012 Yaroslav Halchenko"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier; 2012 Yaroslav Halchenko"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import time, os
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
from .failmanager import FailManagerEmpty
|
from .failmanager import FailManagerEmpty
|
||||||
from .filter import FileFilter
|
from .filter import FileFilter
|
||||||
|
@ -34,6 +35,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Log reader class.
|
# Log reader class.
|
||||||
#
|
#
|
||||||
|
|
|
@ -51,6 +51,7 @@ except Exception, e:
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Log reader class.
|
# Log reader class.
|
||||||
#
|
#
|
||||||
|
@ -73,7 +74,6 @@ class FilterPyinotify(FileFilter):
|
||||||
self.__watches = dict()
|
self.__watches = dict()
|
||||||
logSys.debug("Created FilterPyinotify")
|
logSys.debug("Created FilterPyinotify")
|
||||||
|
|
||||||
|
|
||||||
def callback(self, event, origin=''):
|
def callback(self, event, origin=''):
|
||||||
logSys.debug("%sCallback for Event: %s", origin, event)
|
logSys.debug("%sCallback for Event: %s", origin, event)
|
||||||
path = event.pathname
|
path = event.pathname
|
||||||
|
@ -95,7 +95,6 @@ class FilterPyinotify(FileFilter):
|
||||||
|
|
||||||
self._process_file(path)
|
self._process_file(path)
|
||||||
|
|
||||||
|
|
||||||
def _process_file(self, path):
|
def _process_file(self, path):
|
||||||
"""Process a given file
|
"""Process a given file
|
||||||
|
|
||||||
|
@ -112,7 +111,6 @@ class FilterPyinotify(FileFilter):
|
||||||
self.dateDetector.sortTemplate()
|
self.dateDetector.sortTemplate()
|
||||||
self.__modified = False
|
self.__modified = False
|
||||||
|
|
||||||
|
|
||||||
def _addFileWatcher(self, path):
|
def _addFileWatcher(self, path):
|
||||||
wd = self.__monitor.add_watch(path, pyinotify.IN_MODIFY)
|
wd = self.__monitor.add_watch(path, pyinotify.IN_MODIFY)
|
||||||
self.__watches.update(wd)
|
self.__watches.update(wd)
|
||||||
|
@ -144,7 +142,6 @@ class FilterPyinotify(FileFilter):
|
||||||
self._addFileWatcher(path)
|
self._addFileWatcher(path)
|
||||||
self._process_file(path)
|
self._process_file(path)
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Delete a log path
|
# Delete a log path
|
||||||
#
|
#
|
||||||
|
@ -163,7 +160,6 @@ class FilterPyinotify(FileFilter):
|
||||||
self.__monitor.rm_watch(wdInt)
|
self.__monitor.rm_watch(wdInt)
|
||||||
logSys.debug("Removed monitor for the parent directory %s", path_dir)
|
logSys.debug("Removed monitor for the parent directory %s", path_dir)
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Main loop.
|
# Main loop.
|
||||||
#
|
#
|
||||||
|
|
|
@ -22,7 +22,8 @@ __author__ = "Steven Hiscocks"
|
||||||
__copyright__ = "Copyright (c) 2013 Steven Hiscocks"
|
__copyright__ = "Copyright (c) 2013 Steven Hiscocks"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import datetime, time
|
import datetime
|
||||||
|
import time
|
||||||
from distutils.version import LooseVersion
|
from distutils.version import LooseVersion
|
||||||
|
|
||||||
from systemd import journal
|
from systemd import journal
|
||||||
|
@ -37,6 +38,7 @@ from ..helpers import getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Journal reader class.
|
# Journal reader class.
|
||||||
#
|
#
|
||||||
|
@ -60,7 +62,6 @@ class FilterSystemd(JournalFilter): # pragma: systemd no cover
|
||||||
self.setDatePattern(None)
|
self.setDatePattern(None)
|
||||||
logSys.debug("Created FilterSystemd")
|
logSys.debug("Created FilterSystemd")
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Add a journal match filters from list structure
|
# Add a journal match filters from list structure
|
||||||
#
|
#
|
||||||
|
@ -259,9 +260,8 @@ class FilterSystemd(JournalFilter): # pragma: systemd no cover
|
||||||
or "jailless") +" filter terminated")
|
or "jailless") +" filter terminated")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@property
|
def status(self, flavor="basic"):
|
||||||
def status(self):
|
ret = super(FilterSystemd, self).status(flavor=flavor)
|
||||||
ret = super(FilterSystemd, self).status
|
|
||||||
ret.append(("Journal matches",
|
ret.append(("Journal matches",
|
||||||
[" + ".join(" ".join(match) for match in self.__matches)]))
|
[" + ".join(" ".join(match) for match in self.__matches)]))
|
||||||
return ret
|
return ret
|
||||||
|
|
|
@ -23,7 +23,10 @@ __author__ = "Cyril Jaquier, Lee Clemens, Yaroslav Halchenko"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2012 Lee Clemens, 2012 Yaroslav Halchenko"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2012 Lee Clemens, 2012 Yaroslav Halchenko"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import Queue, logging, math, random
|
import logging
|
||||||
|
import math
|
||||||
|
import random
|
||||||
|
import Queue
|
||||||
|
|
||||||
from .actions import Actions
|
from .actions import Actions
|
||||||
from ..helpers import getLogger
|
from ..helpers import getLogger
|
||||||
|
@ -32,6 +35,7 @@ from .mytime import MyTime
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Jail:
|
class Jail:
|
||||||
"""Fail2Ban jail, which manages a filter and associated actions.
|
"""Fail2Ban jail, which manages a filter and associated actions.
|
||||||
|
|
||||||
|
@ -120,7 +124,6 @@ class Jail:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"Failed to initialize any backend for Jail %r" % self.name)
|
"Failed to initialize any backend for Jail %r" % self.name)
|
||||||
|
|
||||||
|
|
||||||
def _initPolling(self):
|
def _initPolling(self):
|
||||||
from filterpoll import FilterPoll
|
from filterpoll import FilterPoll
|
||||||
logSys.info("Jail '%s' uses poller" % self.name)
|
logSys.info("Jail '%s' uses poller" % self.name)
|
||||||
|
@ -179,13 +182,12 @@ class Jail:
|
||||||
self.filter.idle = value
|
self.filter.idle = value
|
||||||
self.actions.idle = value
|
self.actions.idle = value
|
||||||
|
|
||||||
@property
|
def status(self, flavor="basic"):
|
||||||
def status(self):
|
|
||||||
"""The status of the jail.
|
"""The status of the jail.
|
||||||
"""
|
"""
|
||||||
return [
|
return [
|
||||||
("Filter", self.filter.status),
|
("Filter", self.filter.status(flavor=flavor)),
|
||||||
("Actions", self.actions.status),
|
("Actions", self.actions.status(flavor=flavor)),
|
||||||
]
|
]
|
||||||
|
|
||||||
def putFailTicket(self, ticket):
|
def putFailTicket(self, ticket):
|
||||||
|
@ -269,7 +271,7 @@ class Jail:
|
||||||
forbantime = self.actions.getBanTime()
|
forbantime = self.actions.getBanTime()
|
||||||
for ticket in self.database.getCurrentBans(jail=self, forbantime=forbantime):
|
for ticket in self.database.getCurrentBans(jail=self, forbantime=forbantime):
|
||||||
#logSys.debug('restored ticket: %s', ticket)
|
#logSys.debug('restored ticket: %s', ticket)
|
||||||
if not self.filter.inIgnoreIPList(ticket.getIP()):
|
if not self.filter.inIgnoreIPList(ticket.getIP(), log_ignore=True):
|
||||||
# mark ticked was restored from database - does not put it again into db:
|
# mark ticked was restored from database - does not put it again into db:
|
||||||
ticket.setRestored(True)
|
ticket.setRestored(True)
|
||||||
self.putFailTicket(ticket)
|
self.putFailTicket(ticket)
|
||||||
|
|
|
@ -26,10 +26,11 @@ __license__ = "GPL"
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from abc import abstractproperty, abstractmethod
|
from abc import abstractmethod
|
||||||
|
|
||||||
from ..helpers import excepthook
|
from ..helpers import excepthook
|
||||||
|
|
||||||
|
|
||||||
class JailThread(Thread):
|
class JailThread(Thread):
|
||||||
"""Abstract class for threading elements in Fail2Ban.
|
"""Abstract class for threading elements in Fail2Ban.
|
||||||
|
|
||||||
|
@ -59,6 +60,7 @@ class JailThread(Thread):
|
||||||
# excepthook workaround for threads, derived from:
|
# excepthook workaround for threads, derived from:
|
||||||
# http://bugs.python.org/issue1230540#msg91244
|
# http://bugs.python.org/issue1230540#msg91244
|
||||||
run = self.run
|
run = self.run
|
||||||
|
|
||||||
def run_with_except_hook(*args, **kwargs):
|
def run_with_except_hook(*args, **kwargs):
|
||||||
try:
|
try:
|
||||||
run(*args, **kwargs)
|
run(*args, **kwargs)
|
||||||
|
@ -66,8 +68,8 @@ class JailThread(Thread):
|
||||||
excepthook(*sys.exc_info())
|
excepthook(*sys.exc_info())
|
||||||
self.run = run_with_except_hook
|
self.run = run_with_except_hook
|
||||||
|
|
||||||
@abstractproperty
|
@abstractmethod
|
||||||
def status(self): # pragma: no cover - abstract
|
def status(self, flavor="basic"): # pragma: no cover - abstract
|
||||||
"""Abstract - Should provide status information.
|
"""Abstract - Should provide status information.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -21,7 +21,10 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import time, datetime, re
|
import datetime
|
||||||
|
import re
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# MyTime class.
|
# MyTime class.
|
||||||
|
|
|
@ -25,7 +25,12 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
from threading import Lock, RLock
|
from threading import Lock, RLock
|
||||||
import logging, logging.handlers, sys, os, signal
|
import logging
|
||||||
|
import logging.handlers
|
||||||
|
import os
|
||||||
|
import signal
|
||||||
|
import stat
|
||||||
|
import sys
|
||||||
|
|
||||||
from .observer import Observers, ObserverThread
|
from .observer import Observers, ObserverThread
|
||||||
from .jails import Jails
|
from .jails import Jails
|
||||||
|
@ -44,6 +49,7 @@ except ImportError:
|
||||||
# Dont print error here, as database may not even be used
|
# Dont print error here, as database may not even be used
|
||||||
Fail2BanDb = None
|
Fail2BanDb = None
|
||||||
|
|
||||||
|
|
||||||
class Server:
|
class Server:
|
||||||
|
|
||||||
def __init__(self, daemon = False):
|
def __init__(self, daemon = False):
|
||||||
|
@ -56,20 +62,32 @@ class Server:
|
||||||
self.__asyncServer = AsyncServer(self.__transm)
|
self.__asyncServer = AsyncServer(self.__transm)
|
||||||
self.__logLevel = None
|
self.__logLevel = None
|
||||||
self.__logTarget = None
|
self.__logTarget = None
|
||||||
|
self.__syslogSocket = None
|
||||||
|
self.__autoSyslogSocketPaths = {
|
||||||
|
'Darwin': '/var/run/syslog',
|
||||||
|
'FreeBSD': '/var/run/log',
|
||||||
|
'Linux': '/dev/log',
|
||||||
|
}
|
||||||
# Set logging level
|
# Set logging level
|
||||||
self.setLogLevel("INFO")
|
self.setLogLevel("INFO")
|
||||||
self.setLogTarget("STDOUT")
|
self.setLogTarget("STDOUT")
|
||||||
|
self.setSyslogSocket("auto")
|
||||||
|
|
||||||
def __sigTERMhandler(self, signum, frame):
|
def __sigTERMhandler(self, signum, frame):
|
||||||
logSys.debug("Caught signal %d. Exiting" % signum)
|
logSys.debug("Caught signal %d. Exiting" % signum)
|
||||||
self.quit()
|
self.quit()
|
||||||
|
|
||||||
|
def __sigUSR1handler(self, signum, fname):
|
||||||
|
logSys.debug("Caught signal %d. Flushing logs" % signum)
|
||||||
|
self.flushLogs()
|
||||||
|
|
||||||
def start(self, sock, pidfile, force = False):
|
def start(self, sock, pidfile, force = False):
|
||||||
logSys.info("Starting Fail2ban v%s", version.version)
|
logSys.info("Starting Fail2ban v%s", version.version)
|
||||||
|
|
||||||
# Install signal handlers
|
# Install signal handlers
|
||||||
signal.signal(signal.SIGTERM, self.__sigTERMhandler)
|
signal.signal(signal.SIGTERM, self.__sigTERMhandler)
|
||||||
signal.signal(signal.SIGINT, self.__sigTERMhandler)
|
signal.signal(signal.SIGINT, self.__sigTERMhandler)
|
||||||
|
signal.signal(signal.SIGUSR1, self.__sigUSR1handler)
|
||||||
|
|
||||||
# Ensure unhandled exceptions are logged
|
# Ensure unhandled exceptions are logged
|
||||||
sys.excepthook = excepthook
|
sys.excepthook = excepthook
|
||||||
|
@ -131,7 +149,6 @@ class Server:
|
||||||
finally:
|
finally:
|
||||||
self.__loggingLock.release()
|
self.__loggingLock.release()
|
||||||
|
|
||||||
|
|
||||||
def addJail(self, name, backend):
|
def addJail(self, name, backend):
|
||||||
# Create an observer if not yet created and start it:
|
# Create an observer if not yet created and start it:
|
||||||
if Observers.Main is None:
|
if Observers.Main is None:
|
||||||
|
@ -337,8 +354,8 @@ class Server:
|
||||||
finally:
|
finally:
|
||||||
self.__lock.release()
|
self.__lock.release()
|
||||||
|
|
||||||
def statusJail(self, name):
|
def statusJail(self, name, flavor="basic"):
|
||||||
return self.__jails[name].status
|
return self.__jails[name].status(flavor=flavor)
|
||||||
|
|
||||||
# Logging
|
# Logging
|
||||||
|
|
||||||
|
@ -393,7 +410,21 @@ class Server:
|
||||||
# Syslog daemons already add date to the message.
|
# Syslog daemons already add date to the message.
|
||||||
formatter = logging.Formatter("%(name)s[%(process)d]: %(levelname)s %(message)s")
|
formatter = logging.Formatter("%(name)s[%(process)d]: %(levelname)s %(message)s")
|
||||||
facility = logging.handlers.SysLogHandler.LOG_DAEMON
|
facility = logging.handlers.SysLogHandler.LOG_DAEMON
|
||||||
hdlr = logging.handlers.SysLogHandler("/dev/log", facility=facility)
|
if self.__syslogSocket == "auto":
|
||||||
|
import platform
|
||||||
|
self.__syslogSocket = self.__autoSyslogSocketPaths.get(
|
||||||
|
platform.system())
|
||||||
|
if self.__syslogSocket is not None\
|
||||||
|
and os.path.exists(self.__syslogSocket)\
|
||||||
|
and stat.S_ISSOCK(os.stat(
|
||||||
|
self.__syslogSocket).st_mode):
|
||||||
|
hdlr = logging.handlers.SysLogHandler(
|
||||||
|
self.__syslogSocket, facility=facility)
|
||||||
|
else:
|
||||||
|
logSys.error(
|
||||||
|
"Syslog socket file: %s does not exists"
|
||||||
|
" or is not a socket" % self.__syslogSocket)
|
||||||
|
return False
|
||||||
elif target == "STDOUT":
|
elif target == "STDOUT":
|
||||||
hdlr = logging.StreamHandler(sys.stdout)
|
hdlr = logging.StreamHandler(sys.stdout)
|
||||||
elif target == "STDERR":
|
elif target == "STDERR":
|
||||||
|
@ -430,13 +461,30 @@ class Server:
|
||||||
# Does not display this message at startup.
|
# Does not display this message at startup.
|
||||||
if not self.__logTarget is None:
|
if not self.__logTarget is None:
|
||||||
logSys.info("Start Fail2ban v%s", version.version)
|
logSys.info("Start Fail2ban v%s", version.version)
|
||||||
logSys.info("Changed logging target to %s", target)
|
logSys.info(
|
||||||
|
"Changed logging target to %s for Fail2ban v%s"
|
||||||
|
% ((target
|
||||||
|
if target != "SYSLOG"
|
||||||
|
else "%s (%s)"
|
||||||
|
% (target, self.__syslogSocket)),
|
||||||
|
version.version))
|
||||||
# Sets the logging target.
|
# Sets the logging target.
|
||||||
self.__logTarget = target
|
self.__logTarget = target
|
||||||
return True
|
return True
|
||||||
finally:
|
finally:
|
||||||
self.__loggingLock.release()
|
self.__loggingLock.release()
|
||||||
|
|
||||||
|
##
|
||||||
|
# Sets the syslog socket.
|
||||||
|
#
|
||||||
|
# syslogsocket is the full path to the syslog socket
|
||||||
|
# @param syslogsocket the syslog socket path
|
||||||
|
def setSyslogSocket(self, syslogsocket):
|
||||||
|
self.__syslogSocket = syslogsocket
|
||||||
|
# Conditionally reload, logtarget depends on socket path when SYSLOG
|
||||||
|
return self.__logTarget != "SYSLOG"\
|
||||||
|
or self.setLogTarget(self.__logTarget)
|
||||||
|
|
||||||
def getLogTarget(self):
|
def getLogTarget(self):
|
||||||
try:
|
try:
|
||||||
self.__loggingLock.acquire()
|
self.__loggingLock.acquire()
|
||||||
|
@ -444,6 +492,13 @@ class Server:
|
||||||
finally:
|
finally:
|
||||||
self.__loggingLock.release()
|
self.__loggingLock.release()
|
||||||
|
|
||||||
|
def getSyslogSocket(self):
|
||||||
|
try:
|
||||||
|
self.__loggingLock.acquire()
|
||||||
|
return self.__syslogSocket
|
||||||
|
finally:
|
||||||
|
self.__loggingLock.release()
|
||||||
|
|
||||||
def flushLogs(self):
|
def flushLogs(self):
|
||||||
if self.__logTarget not in ['STDERR', 'STDOUT', 'SYSLOG']:
|
if self.__logTarget not in ['STDERR', 'STDOUT', 'SYSLOG']:
|
||||||
for handler in getLogger("fail2ban").handlers:
|
for handler in getLogger("fail2ban").handlers:
|
||||||
|
@ -461,7 +516,14 @@ class Server:
|
||||||
return "flushed"
|
return "flushed"
|
||||||
|
|
||||||
def setDatabase(self, filename):
|
def setDatabase(self, filename):
|
||||||
if len(self.__jails) == 0:
|
# if not changed - nothing to do
|
||||||
|
if self.__db and self.__db.filename == filename:
|
||||||
|
return
|
||||||
|
if not self.__db and filename.lower() == 'none':
|
||||||
|
return
|
||||||
|
if len(self.__jails) != 0:
|
||||||
|
raise RuntimeError(
|
||||||
|
"Cannot change database when there are jails present")
|
||||||
if filename.lower() == "none":
|
if filename.lower() == "none":
|
||||||
self.__db = None
|
self.__db = None
|
||||||
else:
|
else:
|
||||||
|
@ -472,14 +534,10 @@ class Server:
|
||||||
logSys.error(
|
logSys.error(
|
||||||
"Unable to import fail2ban database module as sqlite "
|
"Unable to import fail2ban database module as sqlite "
|
||||||
"is not available.")
|
"is not available.")
|
||||||
else:
|
|
||||||
raise RuntimeError(
|
|
||||||
"Cannot change database when there are jails present")
|
|
||||||
|
|
||||||
def getDatabase(self):
|
def getDatabase(self):
|
||||||
return self.__db
|
return self.__db
|
||||||
|
|
||||||
|
|
||||||
def __createDaemon(self): # pragma: no cover
|
def __createDaemon(self): # pragma: no cover
|
||||||
""" Detach a process from the controlling terminal and run it in the
|
""" Detach a process from the controlling terminal and run it in the
|
||||||
background as a daemon.
|
background as a daemon.
|
||||||
|
|
|
@ -28,6 +28,7 @@ locale_time = LocaleTime()
|
||||||
timeRE = TimeRE()
|
timeRE = TimeRE()
|
||||||
timeRE['z'] = r"(?P<z>Z|[+-]\d{2}(?::?[0-5]\d)?)"
|
timeRE['z'] = r"(?P<z>Z|[+-]\d{2}(?::?[0-5]\d)?)"
|
||||||
|
|
||||||
|
|
||||||
def reGroupDictStrptime(found_dict):
|
def reGroupDictStrptime(found_dict):
|
||||||
"""Return time from dictionary of strptime fields
|
"""Return time from dictionary of strptime fields
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ from .mytime import MyTime
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Ticket:
|
class Ticket:
|
||||||
|
|
||||||
def __init__(self, ip, time=None, matches=None):
|
def __init__(self, ip, time=None, matches=None):
|
||||||
|
@ -59,7 +60,7 @@ class Ticket:
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
try:
|
try:
|
||||||
return self.__ip == other.__ip and \
|
return self.__ip == other.__ip and \
|
||||||
round(self.__time,2) == round(other.__time,2) and \
|
round(self.__time, 2) == round(other.__time, 2) and \
|
||||||
self.__attempt == other.__attempt and \
|
self.__attempt == other.__attempt and \
|
||||||
self.__matches == other.__matches
|
self.__matches == other.__matches
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue