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)
|
||||||
|
|
112
ChangeLog
112
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,32 +153,58 @@ class Fail2banClient:
|
||||||
return self.__processCmd([["ping"]], False)
|
return self.__processCmd([["ping"]], False)
|
||||||
|
|
||||||
def __processCmd(self, cmd, showRet = True):
|
def __processCmd(self, cmd, showRet = True):
|
||||||
beautifier = Beautifier()
|
client = None
|
||||||
streamRet = True
|
try:
|
||||||
for c in cmd:
|
beautifier = Beautifier()
|
||||||
beautifier.setInputCmd(c)
|
streamRet = True
|
||||||
try:
|
for c in cmd:
|
||||||
client = CSocket(self.__conf["socket"])
|
beautifier.setInputCmd(c)
|
||||||
ret = client.send(c)
|
try:
|
||||||
if ret[0] == 0:
|
if not client:
|
||||||
logSys.debug("OK : " + `ret[1]`)
|
client = CSocket(self.__conf["socket"])
|
||||||
|
ret = client.send(c)
|
||||||
|
if ret[0] == 0:
|
||||||
|
logSys.debug("OK : " + `ret[1]`)
|
||||||
|
if showRet:
|
||||||
|
print beautifier.beautify(ret[1])
|
||||||
|
else:
|
||||||
|
logSys.error("NOK: " + `ret[1].args`)
|
||||||
|
if showRet:
|
||||||
|
print beautifier.beautifyError(ret[1])
|
||||||
|
streamRet = False
|
||||||
|
except socket.error:
|
||||||
if showRet:
|
if showRet:
|
||||||
print beautifier.beautify(ret[1])
|
self.__logSocketError()
|
||||||
else:
|
return False
|
||||||
logSys.error("NOK: " + `ret[1].args`)
|
except Exception, e:
|
||||||
if showRet:
|
if showRet:
|
||||||
print beautifier.beautifyError(ret[1])
|
logSys.error(e)
|
||||||
streamRet = False
|
return False
|
||||||
except socket.error:
|
finally:
|
||||||
if showRet:
|
if client:
|
||||||
logSys.error("Unable to contact server. Is it running?")
|
client.close()
|
||||||
return False
|
|
||||||
except Exception, e:
|
|
||||||
if showRet:
|
|
||||||
logSys.error(e)
|
|
||||||
return False
|
|
||||||
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,38 +269,47 @@ 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'):
|
||||||
print "Use %11s file : %s" % (regex, value)
|
if os.path.basename(os.path.dirname(value)) == 'filter.d':
|
||||||
reader = FilterReader(value, 'fail2ban-regex-jail', {})
|
## within filter.d folder - use standard loading algorithm to load filter completely (with .local etc.):
|
||||||
reader.setBaseDir(None)
|
basedir = os.path.dirname(os.path.dirname(value))
|
||||||
|
value = os.path.splitext(os.path.basename(value))[0]
|
||||||
if reader.readexplicit():
|
print "Use %11s filter file : %s, basedir: %s" % (regex, value, basedir)
|
||||||
reader.getOptions(None)
|
reader = FilterReader(value, 'fail2ban-regex-jail', {}, share_config=self.share_config, basedir=basedir)
|
||||||
readercommands = reader.convert()
|
if not reader.read():
|
||||||
regex_values = [
|
print "ERROR: failed to load filter %s" % value
|
||||||
RegexStat(m[3])
|
return False
|
||||||
for m in filter(
|
|
||||||
lambda x: x[0] == 'set' and x[2] == "add%sregex" % regextype,
|
|
||||||
readercommands)]
|
|
||||||
# Read out and set possible value of maxlines
|
|
||||||
for command in readercommands:
|
|
||||||
if command[2] == "maxlines":
|
|
||||||
maxlines = int(command[3])
|
|
||||||
try:
|
|
||||||
self.setMaxLines(maxlines)
|
|
||||||
except ValueError:
|
|
||||||
print "ERROR: Invalid value for maxlines (%(maxlines)r) " \
|
|
||||||
"read from %(value)s" % locals()
|
|
||||||
return False
|
|
||||||
elif command[2] == 'addjournalmatch':
|
|
||||||
journalmatch = command[3]
|
|
||||||
self.setJournalMatch(shlex.split(journalmatch))
|
|
||||||
elif command[2] == 'datepattern':
|
|
||||||
datepattern = command[3]
|
|
||||||
self.setDatePattern(datepattern)
|
|
||||||
else:
|
else:
|
||||||
print "ERROR: failed to read %s" % value
|
## foreign file - readexplicit this file and includes if possible:
|
||||||
return False
|
print "Use %11s file : %s" % (regex, value)
|
||||||
|
reader = FilterReader(value, 'fail2ban-regex-jail', {}, share_config=self.share_config)
|
||||||
|
reader.setBaseDir(None)
|
||||||
|
if not reader.readexplicit():
|
||||||
|
print "ERROR: failed to read %s" % value
|
||||||
|
return False
|
||||||
|
reader.getOptions(None)
|
||||||
|
readercommands = reader.convert()
|
||||||
|
regex_values = [
|
||||||
|
RegexStat(m[3])
|
||||||
|
for m in filter(
|
||||||
|
lambda x: x[0] == 'set' and x[2] == "add%sregex" % regextype,
|
||||||
|
readercommands)]
|
||||||
|
# Read out and set possible value of maxlines
|
||||||
|
for command in readercommands:
|
||||||
|
if command[2] == "maxlines":
|
||||||
|
maxlines = int(command[3])
|
||||||
|
try:
|
||||||
|
self.setMaxLines(maxlines)
|
||||||
|
except ValueError:
|
||||||
|
print "ERROR: Invalid value for maxlines (%(maxlines)r) " \
|
||||||
|
"read from %(value)s" % locals()
|
||||||
|
return False
|
||||||
|
elif command[2] == 'addjournalmatch':
|
||||||
|
journalmatch = command[3:]
|
||||||
|
self.setJournalMatch(journalmatch)
|
||||||
|
elif command[2] == 'datepattern':
|
||||||
|
datepattern = command[3]
|
||||||
|
self.setDatePattern(datepattern)
|
||||||
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
|
||||||
cftoken =
|
# 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 =
|
||||||
|
|
||||||
# Default Cloudflare username
|
cftoken =
|
||||||
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
|
||||||
|
@ -40,7 +40,7 @@ actionstop = printf %%b "Subject: [Fail2Ban] <name>: stopped on `uname -n`
|
||||||
# Notes.: command executed once before each actionban command
|
# Notes.: command executed once before each actionban command
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actioncheck =
|
actioncheck =
|
||||||
|
|
||||||
# Option: actionban
|
# Option: actionban
|
||||||
# Notes.: command executed when banning an IP. Take care that the
|
# Notes.: command executed when banning an IP. Take care that the
|
||||||
|
@ -48,7 +48,7 @@ actioncheck =
|
||||||
# Tags: See jail.conf(5) man page
|
# Tags: See jail.conf(5) man page
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionban =
|
actionban =
|
||||||
|
|
||||||
# Option: actionunban
|
# Option: actionunban
|
||||||
# Notes.: command executed when unbanning an IP. Take care that the
|
# Notes.: command executed when unbanning an IP. Take care that the
|
||||||
|
@ -56,7 +56,7 @@ actionban =
|
||||||
# Tags: See jail.conf(5) man page
|
# Tags: See jail.conf(5) man page
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionunban =
|
actionunban =
|
||||||
|
|
||||||
[Init]
|
[Init]
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# Fail2Ban action for sending xarf Login-Attack messages to IP owner
|
# Fail2Ban action for sending xarf Login-Attack messages to IP owner
|
||||||
#
|
#
|
||||||
# IMPORTANT:
|
# IMPORTANT:
|
||||||
#
|
#
|
||||||
# Emailing a IP owner of abuse is a serious complain. Make sure that it is
|
# Emailing a IP owner of abuse is a serious complain. Make sure that it is
|
||||||
# serious. Fail2ban developers and network owners recommend you only use this
|
# serious. Fail2ban developers and network owners recommend you only use this
|
||||||
# action for:
|
# action for:
|
||||||
|
@ -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)';
|
||||||
|
@ -70,7 +70,7 @@ footer = \n\n--Abuse-bfbb0f920793ac03cb8634bde14d8a1e--
|
||||||
report = --Abuse-bfbb0f920793ac03cb8634bde14d8a1e\nMIME-Version: 1.0\nContent-Transfer-Encoding: 7bit\nContent-Type: text/plain; charset=utf-8; name=\"report.txt\";\n\n---\nReported-From: $FROM\nCategory: abuse\nReport-ID: $REPORTID\nReport-Type: login-attack\nService: $SERVICE\nVersion: 0.2\nUser-Agent: Fail2ban v0.9\nDate: $DATE\nSource-Type: ip-address\nSource: $IP\nPort: $PORT\nSchema-URL: http://www.x-arf.org/schema/abuse_login-attack_0.1.2.json\nAttachment: text/plain\nOccurances: $FAILURES\nTLP: $TLP\n\n\n--Abuse-bfbb0f920793ac03cb8634bde14d8a1e\nMIME-Version: 1.0\nContent-Transfer-Encoding: 7bit\nContent-Type: text/plain; charset=utf8; name=\"logfile.log\";
|
report = --Abuse-bfbb0f920793ac03cb8634bde14d8a1e\nMIME-Version: 1.0\nContent-Transfer-Encoding: 7bit\nContent-Type: text/plain; charset=utf-8; name=\"report.txt\";\n\n---\nReported-From: $FROM\nCategory: abuse\nReport-ID: $REPORTID\nReport-Type: login-attack\nService: $SERVICE\nVersion: 0.2\nUser-Agent: Fail2ban v0.9\nDate: $DATE\nSource-Type: ip-address\nSource: $IP\nPort: $PORT\nSchema-URL: http://www.x-arf.org/schema/abuse_login-attack_0.1.2.json\nAttachment: text/plain\nOccurances: $FAILURES\nTLP: $TLP\n\n\n--Abuse-bfbb0f920793ac03cb8634bde14d8a1e\nMIME-Version: 1.0\nContent-Transfer-Encoding: 7bit\nContent-Type: text/plain; charset=utf8; name=\"logfile.log\";
|
||||||
|
|
||||||
# Option: Message
|
# Option: Message
|
||||||
# Notes: This can be modified by the users
|
# Notes: This can be modified by the users
|
||||||
message = Dear Sir/Madam,\n\nWe have detected abuse from the IP address $IP, which according to abusix.com is on your network. We would appreciate if you would investigate and take action as appropriate.\n\nLog lines are given below, but please ask if you require any further information.\n\n(If you are not the correct person to contact about this please accept our apologies - your e-mail address was extracted from the whois record by an automated process.)\n\n This mail was generated by Fail2Ban in a X-ARF format! You can find more information about x-arf at http://www.x-arf.org/specification.html.\n\nThe recipient address of this report was provided by the Abuse Contact DB by abusix.com. abusix.com does not maintain the content of the database. All information which we pass out, derives from the RIR databases and is processed for ease of use. If you want to change or report non working abuse contacts please contact the appropriate RIR. If you have any further question, contact abusix.com directly via email (info@abusix.com). Information about the Abuse Contact Database can be found here: https://abusix.com/global-reporting/abuse-contact-db\nabusix.com is neither responsible nor liable for the content or accuracy of this message.\n
|
message = Dear Sir/Madam,\n\nWe have detected abuse from the IP address $IP, which according to abusix.com is on your network. We would appreciate if you would investigate and take action as appropriate.\n\nLog lines are given below, but please ask if you require any further information.\n\n(If you are not the correct person to contact about this please accept our apologies - your e-mail address was extracted from the whois record by an automated process.)\n\n This mail was generated by Fail2Ban in a X-ARF format! You can find more information about x-arf at http://www.x-arf.org/specification.html.\n\nThe recipient address of this report was provided by the Abuse Contact DB by abusix.com. abusix.com does not maintain the content of the database. All information which we pass out, derives from the RIR databases and is processed for ease of use. If you want to change or report non working abuse contacts please contact the appropriate RIR. If you have any further question, contact abusix.com directly via email (info@abusix.com). Information about the Abuse Contact Database can be found here: https://abusix.com/global-reporting/abuse-contact-db\nabusix.com is neither responsible nor liable for the content or accuracy of this message.\n
|
||||||
|
|
||||||
# Option: loglines
|
# Option: loglines
|
||||||
|
@ -97,7 +97,7 @@ mailargs = -f <sender>
|
||||||
# Option: tlp
|
# Option: tlp
|
||||||
# Notes.: Traffic light protocol defining the sharing of this information.
|
# Notes.: Traffic light protocol defining the sharing of this information.
|
||||||
# http://www.trusted-introducer.org/ISTLPv11.pdf
|
# http://www.trusted-introducer.org/ISTLPv11.pdf
|
||||||
# green is share to those involved in network security but it is not
|
# green is share to those involved in network security but it is not
|
||||||
# to be released to the public.
|
# to be released to the public.
|
||||||
tlp = green
|
tlp = green
|
||||||
|
|
||||||
|
|
|
@ -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,18 +33,8 @@ 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:
|
||||||
#
|
#
|
||||||
# Author: Daniel Black
|
# Author: Daniel Black
|
|
@ -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.
|
||||||
#
|
#
|
||||||
|
@ -34,18 +35,19 @@ logSys = getLogger(__name__)
|
||||||
# converted into user readable messages.
|
# converted into user readable messages.
|
||||||
|
|
||||||
class Beautifier:
|
class Beautifier:
|
||||||
|
|
||||||
def __init__(self, cmd = None):
|
def __init__(self, cmd = None):
|
||||||
self.__inputCmd = cmd
|
self.__inputCmd = cmd
|
||||||
|
|
||||||
def setInputCmd(self, cmd):
|
def setInputCmd(self, cmd):
|
||||||
self.__inputCmd = cmd
|
self.__inputCmd = cmd
|
||||||
|
|
||||||
def getInputCmd(self):
|
def getInputCmd(self):
|
||||||
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'):
|
||||||
|
@ -71,7 +73,7 @@ class FilterReader(DefinitionInitConfigReader):
|
||||||
for regex in value.split('\n'):
|
for regex in value.split('\n'):
|
||||||
# Do not send a command if the rule is empty.
|
# Do not send a command if the rule is empty.
|
||||||
if regex != '':
|
if regex != '':
|
||||||
stream.append(["set", self._jailName, "addignoreregex", regex])
|
stream.append(["set", self._jailName, "addignoreregex", regex])
|
||||||
if self._initOpts:
|
if self._initOpts:
|
||||||
if 'maxlines' in self._initOpts:
|
if 'maxlines' in self._initOpts:
|
||||||
# We warn when multiline regex is used without maxlines > 1
|
# We warn when multiline regex is used without maxlines > 1
|
||||||
|
|
|
@ -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,38 +388,46 @@ 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'<([^ <>]+)>')
|
||||||
for tag in tags.iterkeys():
|
# repeat substitution while embedded-recursive (repFlag is True)
|
||||||
if tag in cls._escapedTags:
|
while True:
|
||||||
# Escaped so won't match
|
repFlag = False
|
||||||
continue
|
# substitute each value:
|
||||||
value = str(tags[tag])
|
for tag in tags.iterkeys():
|
||||||
m = t.search(value)
|
if tag in cls._escapedTags:
|
||||||
done = []
|
|
||||||
#logSys.log(5, 'TAG: %s, value: %s' % (tag, value))
|
|
||||||
while m:
|
|
||||||
found_tag = m.group(1)
|
|
||||||
#logSys.log(5, 'found: %s' % found_tag)
|
|
||||||
if found_tag == tag or found_tag in done:
|
|
||||||
# recursive definitions are bad
|
|
||||||
#logSys.log(5, 'recursion fail tag: %s value: %s' % (tag, value) )
|
|
||||||
return False
|
|
||||||
elif found_tag in cls._escapedTags:
|
|
||||||
# Escaped so won't match
|
# Escaped so won't match
|
||||||
continue
|
continue
|
||||||
else:
|
value = str(tags[tag])
|
||||||
if tags.has_key(found_tag):
|
# search and replace all tags within value, that can be interpolated using other tags:
|
||||||
value = value.replace('<%s>' % found_tag , tags[found_tag])
|
m = t.search(value)
|
||||||
#logSys.log(5, 'value now: %s' % value)
|
done = []
|
||||||
done.append(found_tag)
|
#logSys.log(5, 'TAG: %s, value: %s' % (tag, value))
|
||||||
m = t.search(value, m.start())
|
while m:
|
||||||
else:
|
found_tag = m.group(1)
|
||||||
# Missing tags are ok so we just continue on searching.
|
#logSys.log(5, 'found: %s' % found_tag)
|
||||||
# cInfo can contain aInfo elements like <HOST> and valid shell
|
if found_tag == tag or found_tag in done:
|
||||||
|
# recursive definitions are bad
|
||||||
|
#logSys.log(5, 'recursion fail tag: %s value: %s' % (tag, value) )
|
||||||
|
return False
|
||||||
|
if found_tag in cls._escapedTags or not found_tag in tags:
|
||||||
|
# Escaped or missing tags - just continue on searching after end of match
|
||||||
|
# Missing tags are ok - cInfo can contain aInfo elements like <HOST> and valid shell
|
||||||
# constructs like <STDIN>.
|
# constructs like <STDIN>.
|
||||||
m = t.search(value, m.start() + 1)
|
m = t.search(value, m.end())
|
||||||
#logSys.log(5, 'TAG: %s, newvalue: %s' % (tag, value))
|
continue
|
||||||
tags[tag] = value
|
value = value.replace('<%s>' % found_tag , tags[found_tag])
|
||||||
|
#logSys.log(5, 'value now: %s' % value)
|
||||||
|
done.append(found_tag)
|
||||||
|
m = t.search(value, m.start())
|
||||||
|
#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
|
||||||
|
if not repFlag:
|
||||||
|
break
|
||||||
return tags
|
return tags
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -507,10 +525,10 @@ class CommandAction(ActionBase):
|
||||||
realCmd = self.replaceTag(cmd, aInfo)
|
realCmd = self.replaceTag(cmd, aInfo)
|
||||||
else:
|
else:
|
||||||
realCmd = cmd
|
realCmd = cmd
|
||||||
|
|
||||||
# Replace static fields
|
# Replace static fields
|
||||||
realCmd = self.replaceTag(realCmd, self._properties)
|
realCmd = self.replaceTag(realCmd, self._properties)
|
||||||
|
|
||||||
return self.executeCmd(realCmd, self.timeout)
|
return self.executeCmd(realCmd, self.timeout)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -540,7 +558,7 @@ class CommandAction(ActionBase):
|
||||||
if not realCmd:
|
if not realCmd:
|
||||||
logSys.debug("Nothing to do")
|
logSys.debug("Nothing to do")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
_cmd_lock.acquire()
|
_cmd_lock.acquire()
|
||||||
try: # Try wrapped within another try needed for python version < 2.5
|
try: # Try wrapped within another try needed for python version < 2.5
|
||||||
stdout = tempfile.TemporaryFile(suffix=".stdout", prefix="fai2ban_")
|
stdout = tempfile.TemporaryFile(suffix=".stdout", prefix="fai2ban_")
|
||||||
|
@ -595,4 +613,4 @@ class CommandAction(ActionBase):
|
||||||
% (retcode, msg % locals()))
|
% (retcode, msg % locals()))
|
||||||
return False
|
return False
|
||||||
raise RuntimeError("Command execution failed: %s" % realCmd)
|
raise RuntimeError("Command execution failed: %s" % realCmd)
|
||||||
|
|
||||||
|
|
|
@ -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.
|
|
||||||
"""
|
"""
|
||||||
ret = [("Currently banned", self.__banManager.size()),
|
# 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()),
|
||||||
("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,8 +152,12 @@ 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)) \
|
||||||
asyncore.loop(use_poll=False) # fixes the "Unexpected communication problem" issue on Python 2.6 and 3.0
|
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
|
||||||
|
|
||||||
##
|
##
|
||||||
# Stops the communication server.
|
# Stops the communication server.
|
||||||
|
@ -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.
|
||||||
#
|
#
|
||||||
|
@ -170,20 +289,19 @@ class BanManager:
|
||||||
return True
|
return True
|
||||||
finally:
|
finally:
|
||||||
self.__lock.release()
|
self.__lock.release()
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Get the size of the ban list.
|
# Get the size of the ban list.
|
||||||
#
|
#
|
||||||
# @return the size
|
# @return the size
|
||||||
|
|
||||||
def size(self):
|
def size(self):
|
||||||
try:
|
try:
|
||||||
self.__lock.acquire()
|
self.__lock.acquire()
|
||||||
return len(self.__banList)
|
return len(self.__banList)
|
||||||
finally:
|
finally:
|
||||||
self.__lock.release()
|
self.__lock.release()
|
||||||
|
|
||||||
##
|
##
|
||||||
# Check if a ticket is in the list.
|
# Check if a ticket is in the 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'))
|
locale.getpreferredencoding(), 'replace')
|
||||||
sqlite3.register_converter(
|
except Exception, e: # pragma: no cover
|
||||||
"JSON",
|
logSys.error('json dumps failed: %s', e)
|
||||||
lambda x: json.loads(x.decode(
|
x = '{}'
|
||||||
locale.getpreferredencoding(), 'replace')))
|
return x
|
||||||
|
|
||||||
|
def _json_loads_safe(x):
|
||||||
|
try:
|
||||||
|
x = json.loads(x.decode(
|
||||||
|
locale.getpreferredencoding(), 'replace'))
|
||||||
|
except Exception, e: # pragma: no cover
|
||||||
|
logSys.error('json loads failed: %s', e)
|
||||||
|
x = {}
|
||||||
|
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()
|
||||||
|
@ -410,7 +451,7 @@ class Fail2BanDb(object):
|
||||||
"INSERT INTO bans(jail, ip, timeofban, bantime, bancount, data) VALUES(?, ?, ?, ?, ?, ?)",
|
"INSERT INTO bans(jail, ip, timeofban, bantime, bancount, data) VALUES(?, ?, ?, ?, ?, ?)",
|
||||||
(jail.name, ticket.getIP(), int(round(ticket.getTime())), ticket.getBanTime(jail.actions.getBanTime()), ticket.getBanCount(),
|
(jail.name, ticket.getIP(), int(round(ticket.getTime())), ticket.getBanTime(jail.actions.getBanTime()), ticket.getBanCount(),
|
||||||
{"matches": ticket.getMatches(),
|
{"matches": ticket.getMatches(),
|
||||||
"failures": ticket.getAttempt()}))
|
"failures": ticket.getAttempt()}))
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"INSERT OR REPLACE INTO bips(ip, jail, timeofban, bantime, bancount, data) VALUES(?, ?, ?, ?, ?, ?)",
|
"INSERT OR REPLACE INTO bips(ip, jail, timeofban, bantime, bancount, data) VALUES(?, ?, ?, ?, ?, ?)",
|
||||||
(ticket.getIP(), jail.name, int(round(ticket.getTime())), ticket.getBanTime(jail.actions.getBanTime()), ticket.getBanCount(),
|
(ticket.getIP(), jail.name, int(round(ticket.getTime())), ticket.getBanTime(jail.actions.getBanTime()), ticket.getBanCount(),
|
||||||
|
@ -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:
|
||||||
MASK = 0xFFFFFFFFL
|
return struct.unpack("!L", socket.inet_aton(ipstring))[0]
|
||||||
return ~(MASK >> n) & MASK & DNSUtils.addr2bin(i)
|
else:
|
||||||
|
MASK = 0xFFFFFFFFL
|
||||||
|
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,9 +354,9 @@ 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
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -377,7 +394,7 @@ class Server:
|
||||||
return self.__logLevel
|
return self.__logLevel
|
||||||
finally:
|
finally:
|
||||||
self.__loggingLock.release()
|
self.__loggingLock.release()
|
||||||
|
|
||||||
##
|
##
|
||||||
# Sets the logging target.
|
# Sets the logging target.
|
||||||
#
|
#
|
||||||
|
@ -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,20 +461,44 @@ 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()
|
||||||
return self.__logTarget
|
return self.__logTarget
|
||||||
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,24 +516,27 @@ 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 filename.lower() == "none":
|
if self.__db and self.__db.filename == filename:
|
||||||
self.__db = None
|
return
|
||||||
else:
|
if not self.__db and filename.lower() == 'none':
|
||||||
if Fail2BanDb is not None:
|
return
|
||||||
self.__db = Fail2BanDb(filename)
|
if len(self.__jails) != 0:
|
||||||
self.__db.delAllJails()
|
|
||||||
else:
|
|
||||||
logSys.error(
|
|
||||||
"Unable to import fail2ban database module as sqlite "
|
|
||||||
"is not available.")
|
|
||||||
else:
|
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"Cannot change database when there are jails present")
|
"Cannot change database when there are jails present")
|
||||||
|
if filename.lower() == "none":
|
||||||
|
self.__db = None
|
||||||
|
else:
|
||||||
|
if Fail2BanDb is not None:
|
||||||
|
self.__db = Fail2BanDb(filename)
|
||||||
|
self.__db.delAllJails()
|
||||||
|
else:
|
||||||
|
logSys.error(
|
||||||
|
"Unable to import fail2ban database module as sqlite "
|
||||||
|
"is not available.")
|
||||||
|
|
||||||
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
|
||||||
|
|
|
@ -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