mirror of
https://github.com/fail2ban/fail2ban.git
synced 2026-03-11 08:55:31 +00:00
Merge tag '0.9.0' into debian
This commit is contained in:
commit
e2caa2b220
281 changed files with 11902 additions and 5225 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -6,3 +6,5 @@ htmlcov
|
|||
.coverage
|
||||
*.orig
|
||||
*.rej
|
||||
*.bak
|
||||
__pycache__
|
||||
|
|
|
|||
15
.travis.yml
15
.travis.yml
|
|
@ -2,17 +2,20 @@
|
|||
# travis-ci.org definition for Fail2Ban build
|
||||
language: python
|
||||
python:
|
||||
- "2.5"
|
||||
- "2.6"
|
||||
- "2.7"
|
||||
- "3.2"
|
||||
- "3.3"
|
||||
- "pypy"
|
||||
before_install:
|
||||
- sudo apt-get update -qq
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then sudo apt-get update -qq; fi
|
||||
install:
|
||||
- pip install pyinotify
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then sudo apt-get install -qq python-gamin; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then pip install -q coveralls; fi
|
||||
- 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
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then cd ..; pip install -q coveralls; cd -; fi
|
||||
script:
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then export PYTHONPATH="$PYTHONPATH:/usr/share/pyshared:/usr/lib/pyshared/python2.7"; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then coverage run --rcfile=.travis_coveragerc fail2ban-testcases; else python ./fail2ban-testcases; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then coverage run --rcfile=.travis_coveragerc setup.py test; else python setup.py test; fi
|
||||
after_success:
|
||||
# Coverage config file must be .coveragerc for coveralls
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then cp -v .travis_coveragerc .coveragerc; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then coveralls; fi
|
||||
|
|
|
|||
|
|
@ -4,3 +4,4 @@ branch = True
|
|||
omit =
|
||||
/usr/*
|
||||
/home/travis/virtualenv/*
|
||||
fail2ban/server/filtersystemd.py
|
||||
|
|
|
|||
191
ChangeLog
191
ChangeLog
|
|
@ -4,12 +4,197 @@
|
|||
|_| \__,_|_|_/___|_.__/\__,_|_||_|
|
||||
|
||||
================================================================================
|
||||
Fail2Ban (version 0.8.11) 2013/11/13
|
||||
Fail2Ban (version 0.9.0) 2014/03/14
|
||||
================================================================================
|
||||
|
||||
ver. 0.8.11 (2013/11/13) - loves-unittests-and-tight-DoS-free-filter-regexes
|
||||
ver. 0.9.0 (2014/03/14 - beta
|
||||
----------
|
||||
|
||||
Carries all fixes, features and enhancements from 0.8.13 (unreleased) with
|
||||
major changes.
|
||||
|
||||
The minimum supported python version is now 2.6. If you have python-2.4 or 2.5
|
||||
you can use the 0.8.12 version of fail2ban.
|
||||
|
||||
Please take note of release notes:
|
||||
https://github.com/fail2ban/fail2ban/releases/tag/0.9.0
|
||||
|
||||
Please test your configuration before relying on it.
|
||||
|
||||
Nearly all development is thanks to Steven Hiscocks (THANKS!), merging,
|
||||
testcases and timezone support from Daniel Black, and code-review and minor
|
||||
additions from Yaroslav Halchenko.
|
||||
|
||||
- Refactoring (IMPORTANT -- Please review your setup and configuration):
|
||||
* [..bddbf1e] jail.conf was heavily refactored and now is similar
|
||||
to how it looked on Debian systems:
|
||||
- default action could be configured once for all jails
|
||||
- jails definitions only provide customizations (port, logpath)
|
||||
- no need to specify 'filter' if name matches jail name
|
||||
* [..5aef036] Core functionality moved into fail2ban/ module.
|
||||
Closes gh-26
|
||||
- tests included in module to aid testing and debugging
|
||||
* Added fail2ban persistent database
|
||||
- default location at /var/lib/fail2ban/fail2ban.sqlite3
|
||||
- allows active bans to be reinstated on restart
|
||||
- log files read from last position after restart
|
||||
* Added systemd journal backend
|
||||
- Dependency on python-systemd
|
||||
- New "journalmatch" option added to filter configs files
|
||||
- New "systemd-journal" option added to fail2ban-regex
|
||||
* Added python3 support
|
||||
* Support %z (Timezone offset) and %f (sub-seconds) support for
|
||||
datedetector. Enhanced existing date/time have been updated patterns to
|
||||
support these. ISO8601 now defaults to localtime unless specified otherwise.
|
||||
Some filters have been change as required to capture these elements in the
|
||||
right timezone correctly.
|
||||
* Log levels are now set by Syslog style strings e.g. DEBUG, ERROR.
|
||||
- Log level INFO is now more verbose
|
||||
* Optionally can read log files starting from "head" or "tail".
|
||||
- See "logpath" option in jail.conf(5) man page.
|
||||
* Can now set log encoding for files per jail.
|
||||
- Default uses systemd locale.
|
||||
|
||||
- New features:
|
||||
* [..c7ae460] Multiline failregex. Close gh-54
|
||||
* [8af32ed] Guacamole filter and support for Apache Tomcat date
|
||||
format
|
||||
* [..b6059f4] 'timeout' option for actions Close gh-60 and Debian bug
|
||||
#410077. Also it would now capture and include stdout and stderr
|
||||
into logging messages in case of error or at DEBUG loglevel.
|
||||
* Added action xarf-login-attack to report formatted attack messages
|
||||
according to the XARF standard (v0.2). Close gh-105
|
||||
* Support PyPy
|
||||
* Add filter for apache-botsearch
|
||||
* Add filter for kerio. Thanks Tony Lawrence for blog of regexs and
|
||||
providing samples. Close gh-120
|
||||
* Filter for stunnel
|
||||
* Filter for Counter Strike 1.6. Thanks to onorua for logs.
|
||||
Close gh-347
|
||||
* Filter for squirrelmail. Close gh-261
|
||||
* Filter for tine20. Close gh-583
|
||||
* Custom date formats (strptime) can now be set in filters and jail.conf
|
||||
* Python based actions can now be created.
|
||||
- SMTP action for sending emails on jail start, stop and ban.
|
||||
* Added action to use badips.com reporting and blacklist
|
||||
- Requires Python 2.7+
|
||||
|
||||
- Enhancements
|
||||
* Fail2ban-regex - don't accumulate lines if not printing them.
|
||||
add options to suppress output of missed/ignored lines. Close gh-644
|
||||
* Asterisk now supports syslog format
|
||||
* Jail names increased to 26 characters and iptables prefix reduced
|
||||
from fail2ban- to f2b- as suggested by buanzo in gh-462.
|
||||
* Multiline filter for sendmail-spam. Close gh-418
|
||||
* Multiline regex for Disconnecting: Too many authentication failures for
|
||||
root [preauth]\nConnection closed by 6X.XXX.XXX.XXX [preauth]
|
||||
* Multiline regex for Disconnecting: Connection from 61.XX.XX.XX port
|
||||
51353\nToo many authentication failures for root [preauth]. Thanks
|
||||
Helmut Grohne. Close gh-457
|
||||
* Replacing use of deprecated API (.warning, .assertEqual, etc)
|
||||
* [..a648cc2] Filters can have options now too which are substituted into
|
||||
failregex / ignoreregex
|
||||
* [..e019ab7] Multiple instances of the same action are allowed in the
|
||||
same jail -- use actname option to disambiguate.
|
||||
* Add honeypot email address to exim-spam filter as argument
|
||||
* Properties and methods of actions accessible from fail2ban-client
|
||||
- Use of properties replaces command actions "cinfo" interface
|
||||
|
||||
|
||||
ver. 0.8.13 (2014/XX/XXX) - maintenance-only-from-now-on
|
||||
-----------
|
||||
|
||||
- Fixes:
|
||||
- action firewallcmd-ipset had non-working actioncheck. Removed.
|
||||
redhat bug #1046816.
|
||||
- filter pureftpd - added _daemon which got removed. Added
|
||||
|
||||
- New Features:
|
||||
- filter nagios - detects unauthorized access to the nrpe daemon (Ivo Truxa)
|
||||
- filter sendmail-{auth,reject} (jserrachinha and cepheid666 and fab23).
|
||||
|
||||
- Enhancements:
|
||||
- filter pureftpd - added all translations of "Authentication failed for
|
||||
user"
|
||||
- filter dovecot - lip= was optional and extended TLS errors can occur.
|
||||
Thanks Noel Butler.
|
||||
|
||||
ver. 0.8.12 (2014/01/22) - things-can-only-get-better
|
||||
----------
|
||||
|
||||
- IMPORTANT incompatible changes:
|
||||
- Rename firewall-cmd-direct-new to firewallcmd-new to fit within jail name
|
||||
name length. As per gh-395
|
||||
- mysqld-syslog-iptables jailname was too long. Renamed to mysqld-syslog.
|
||||
Part of gh-447.
|
||||
|
||||
- Fixes:
|
||||
- allow for ",milliseconds" in the custom date format of proftpd.log
|
||||
- allow for ", referer ..." in apache-* filter for apache error logs.
|
||||
- allow for spaces at the beginning of kernel messages. Closes gh-448
|
||||
- recidive jail to block all protocols. Closes gh-440. Thanks Ioan Indreias
|
||||
- smtps not a IANA standard and has been removed from Arch. Replaced with
|
||||
465. Thanks Stefan. Closes gh-447
|
||||
- add 'flushlogs' command to allow logrotation without clobbering logtarget
|
||||
settings. Closes gh-458, Debian bug #697333, Redhat bug #891798.
|
||||
- complain action - ensure where not matching other IPs in log sample.
|
||||
Closes gh-467
|
||||
- Fix firewall-cmd actioncheck - patch from Adam Tkac. Redhat Bug #979622
|
||||
- Fix apache-common for apache-2.4 log file format. Thanks Mark White.
|
||||
Closes gh-516
|
||||
- Asynchat changed to use push method which verifys whether all data was
|
||||
send. This ensures that all data is sent before closing the connection.
|
||||
- Removed unnecessary reference to as yet undeclared $jail_name when checking
|
||||
a specific jail in nagios script.
|
||||
- Filter dovecot reordered session and TLS items in regex with wider scope
|
||||
for session characters. Thanks Ivo Truxa. Closes gh-586
|
||||
- A single bad failregex or command syntax in configuration files won't stop
|
||||
fail2ban from starting. Thanks Tomasz Ciolek. Closes gh-585.
|
||||
|
||||
- Enhancements:
|
||||
- long names on jails documented based on iptables limit of 30 less
|
||||
len("fail2ban-").
|
||||
- remove indentation of name and loglevel while logging to SYSLOG to
|
||||
resolve syslog(-ng) parsing problems. Closes Debian bug #730202.
|
||||
- updated check_fail2ban to return performance data for all jails.
|
||||
- filter apache-noscript now includes php cgi scripts.
|
||||
Thanks dani. Closes gh-503
|
||||
- exim-spam filter to match spamassassin log entry for option SAdevnull.
|
||||
Thanks Ivo Truxa. Closes gh-533
|
||||
- filter.d/nsd.conf -- also amended Unix date template to match nsd format
|
||||
- Added to sshd filter expression for "Received disconnect from <HOST>: 3:
|
||||
...: Auth fail". Thanks Marcel Dopita. Closes gh-289
|
||||
- loglines now also report "[PID]" after the name portion
|
||||
- Added filter.d/ejabberd-auth
|
||||
- Improved ACL-handling for Asterisk
|
||||
- loglines now also report "[PID]" after the name portion
|
||||
- Added improper command pipelining to postfix filter.
|
||||
|
||||
- New Features:
|
||||
|
||||
- filter.d/solid-pop3d -- added thanks to Jacques Lav!gnotte on mailinglist.
|
||||
- Add filter for apache-modsecurity.
|
||||
- filter.d/nsd.conf -- also amended Unix date template to match nsd format
|
||||
- Added openwebmail filter thanks Ivo Truxa. Closes gh-543
|
||||
- Added filter for freeswitch. Thanks Jim and editors and authors of
|
||||
http://wiki.freeswitch.org/wiki/Fail2ban
|
||||
- Added groupoffice filter thanks to logs from Merijn Schering.
|
||||
Closes gh-566
|
||||
- Added filter for horde
|
||||
- Added filter for squid. Thanks Roman Gelfand.
|
||||
- Added filter for ejabberd-auth.
|
||||
- Added filter.d/openwebmail filter thanks Ivo Truxa. Closes gh-543
|
||||
- Added filter.d/groupoffice filter thanks to logs from Merijn Schering.
|
||||
Closes gh-566
|
||||
- Added action.d/badips. Thanks to Amy for making a nice API.
|
||||
- Added firewallcmd-ipset action.
|
||||
- Added ufw action. Thanks Guilhem Lettron. lp-#701522
|
||||
- Added blocklist_de action.
|
||||
|
||||
|
||||
ver. 0.8.11 (2013/11/13) - loves-unittests-and-tight-DoS-free-filter-regexes
|
||||
----------
|
||||
|
||||
In light of CVE-2013-2178 that triggered our last release we have put
|
||||
a significant effort into tightening all of the regexs of our filters
|
||||
to avoid another similar vulnerability. All filters have been updated
|
||||
|
|
@ -805,7 +990,7 @@ ver. 0.5.4 (2005/09/13) - beta
|
|||
* Fixed errata in config/gentoo-confd
|
||||
* Introduced findtime configuration variable to control the lifetime of caught
|
||||
"failed" log entries
|
||||
|
||||
|
||||
ver. 0.5.3 (2005/09/08) - beta
|
||||
----------
|
||||
- Fixed a bug when overriding "maxfailures" or "bantime". Thanks to Yaroslav
|
||||
|
|
|
|||
613
DEVELOP
613
DEVELOP
|
|
@ -34,472 +34,14 @@ When submitting pull requests on GitHub we ask you to:
|
|||
* Include a change to the relevant section of the ChangeLog; and
|
||||
* Include yourself in THANKS if not already there.
|
||||
|
||||
Filters
|
||||
=======
|
||||
|
||||
Filters are tricky. They need to:
|
||||
* work with a variety of the versions of the software that generates the logs;
|
||||
* work with the range of logging configuration options available in the
|
||||
software;
|
||||
* work with multiple operating systems;
|
||||
* not make assumptions about the log format in excess of the software
|
||||
(e.g. do not assume a username doesn't contain spaces and use \S+ unless
|
||||
you've checked the source code);
|
||||
* account for how future versions of the software will log messages
|
||||
(e.g. guess what would happen to the log message if different authentication
|
||||
types are added);
|
||||
* not be susceptible to DoS vulnerabilities (see Filter Security below); and
|
||||
* match intended log lines only.
|
||||
|
||||
Please follow the steps from Filter Test Cases to Developing Filter Regular
|
||||
Expressions and submit a GitHub pull request (PR) afterwards. If you get stuck,
|
||||
you can push your unfinished changes and still submit a PR -- describe
|
||||
what you have done, what is the hurdle, and we'll attempt to help (PR
|
||||
will be automagically updated with future commits you would push to
|
||||
complete it).
|
||||
|
||||
Filter test cases
|
||||
-----------------
|
||||
|
||||
Purpose:
|
||||
|
||||
Start by finding the log messages that the application generates related to
|
||||
some form of authentication failure. If you are adding to an existing filter
|
||||
think about whether the log messages are of a similar importance and purpose
|
||||
to the existing filter. If you were a user of Fail2Ban, and did a package
|
||||
update of Fail2Ban that started matching new log messages, would anything
|
||||
unexpected happen? Would the bantime/findtime for the jail be appropriate for
|
||||
the new log messages? If it doesn't, perhaps it needs to be in a separate
|
||||
filter definition, for example like exim filter aims at authentication failures
|
||||
and exim-spam at log messages related to spam.
|
||||
|
||||
Even if it is a new filter you may consider separating the log messages into
|
||||
different filters based on purpose.
|
||||
|
||||
Cause:
|
||||
|
||||
Are some of the log lines a result of the same action? For example, is a PAM
|
||||
failure log message, followed by an application specific failure message the
|
||||
result of the same user/script action? If you add regular expressions for
|
||||
both you would end up with two failures for a single action.
|
||||
Therefore, select the most appropriate log message and document the other log
|
||||
message) with a test case not to match it and a description as to why you chose
|
||||
one over another.
|
||||
|
||||
With the selected log lines consider what action has caused those log
|
||||
messages and whether they could have been generated by accident? Could
|
||||
the log message be occurring due to the first step towards the application
|
||||
asking for authentication? Could the log messages occur often? If some of
|
||||
these are true make a note of this in the jail.conf example that you provide.
|
||||
|
||||
Samples:
|
||||
|
||||
It is important to include log file samples so any future change in the regular
|
||||
expression will still work with the log lines you have identified.
|
||||
|
||||
The sample log messages are provided in a file under testcases/files/logs/
|
||||
named identically as the corresponding filter (but without .conf extension).
|
||||
Each log line should be preceded by a line with failJSON metadata (so the logs
|
||||
lines are tested in the test suite) directly above the log line. If there is
|
||||
any specific information about the log message, such as version or an
|
||||
application configuration option that is needed for the message to occur,
|
||||
include this in a comment (line beginning with #) above the failJSON metadata.
|
||||
|
||||
Log samples should include only one, definitely not more than 3, examples of
|
||||
log messages of the same form. If log messages are different in different
|
||||
versions of the application log messages that show this are encouraged.
|
||||
|
||||
Also attempt to inject an IP into the application (e.g. by specifying
|
||||
it as a username) so that Fail2Ban possibly detects the IP
|
||||
from user input rather than the true origin. See the Filter Security section
|
||||
and the top example in testcases/files/logs/apache-auth as to how to do this.
|
||||
One you have discovered that this is possible, correct the regex so it doesn't
|
||||
match and provide this as a test case with "match": false (see failJSON below).
|
||||
|
||||
If the mechanism to create the log message isn't obvious provide a
|
||||
configuration and/or sample scripts testcases/files/config/{filtername} and
|
||||
reference these in the comments above the log line.
|
||||
|
||||
FailJSON metadata:
|
||||
|
||||
A failJSON metadata is a comment immediately above the log message. It will
|
||||
look like:
|
||||
|
||||
# failJSON: { "time": "2013-06-10T10:10:59", "match": true , "host": "93.184.216.119" }
|
||||
|
||||
Time should match the time of the log message. It is in a specific format of
|
||||
Year-Month-Day'T'Hour:minute:Second. If your log message does not include a
|
||||
year, like the example below, the year should be listed as 2005, if before Sun
|
||||
Aug 14 10am UTC, and 2004 if afterwards. Here is an example failJSON
|
||||
line preceding a sample log line:
|
||||
|
||||
# failJSON: { "time": "2005-03-24T15:25:51", "match": true , "host": "198.51.100.87" }
|
||||
Mar 24 15:25:51 buffalo1 dropbear[4092]: bad password attempt for 'root' from 198.51.100.87:5543
|
||||
|
||||
The "host" in failJSON should contain the IP or domain that should be blocked.
|
||||
|
||||
For long lines that you do not want to be matched (e.g. from log injection
|
||||
attacks) and any log lines to be excluded (see "Cause" section above), set
|
||||
"match": false in the failJSON and describe the reason in the comment above.
|
||||
|
||||
After developing regexes, the following command will test all failJSON metadata
|
||||
against the log lines in all sample log files
|
||||
|
||||
./fail2ban-testcases testSampleRegex
|
||||
|
||||
Developing Filter Regular Expressions
|
||||
-------------------------------------
|
||||
|
||||
Date/Time:
|
||||
|
||||
At the moment, Fail2Ban depends on log lines to have time stamps. That is why
|
||||
before starting to develop failregex, check if your log line format known to
|
||||
Fail2Ban. Copy the time component from the log line and append an IP address to
|
||||
test with following command:
|
||||
|
||||
./fail2ban-regex "2013-09-19 02:46:12 1.2.3.4" "<HOST>"
|
||||
|
||||
Output of such command should contain something like:
|
||||
|
||||
Date template hits:
|
||||
|- [# of hits] date format
|
||||
| [1] Year-Month-Day Hour:Minute:Second
|
||||
|
||||
Ensure that the template description matches time/date elements in your log line
|
||||
time stamp. If there is no matched format then date template needs to be added
|
||||
to server/datedetector.py. Ensure that a new template is added in the order
|
||||
that more specific matches occur first and that there is no confusion between a
|
||||
Day and a Month.
|
||||
|
||||
Filter file:
|
||||
|
||||
The filter is specified in a config/filter.d/{filtername}.conf file. Filter file
|
||||
can have sections INCLUDES (optional) and Definition as follows:
|
||||
|
||||
[INCLUDES]
|
||||
|
||||
before = common.conf
|
||||
|
||||
after = filtername.local
|
||||
|
||||
[Definition]
|
||||
|
||||
failregex = ....
|
||||
|
||||
ignoreregex = ....
|
||||
|
||||
This is also documented in the man page jail.conf (section 5). Other definitions
|
||||
can be added to make failregex's more readable and maintainable to be used
|
||||
through string Interpolations (see http://docs.python.org/2.7/library/configparser.html)
|
||||
|
||||
|
||||
General rules:
|
||||
|
||||
Use "before" if you need to include a common set of rules, like syslog or if
|
||||
there is a common set of regexes for multiple filters.
|
||||
|
||||
Use "after" if you wish to allow the user to overwrite a set of customisations
|
||||
of the current filter. This file doesn't need to exist.
|
||||
|
||||
Try to avoid using ignoreregex mainly for performance reasons. The case when you
|
||||
would use it is if in trying to avoid using it, you end up with an unreadable
|
||||
failregex.
|
||||
|
||||
Syslog:
|
||||
|
||||
If your application logs to syslog you can take advantage of log line prefix
|
||||
definitions present in common.conf. So as a base use:
|
||||
|
||||
[INCLUDES]
|
||||
|
||||
before = common.conf
|
||||
|
||||
[Definition]
|
||||
|
||||
_daemon = app
|
||||
|
||||
failregex = ^%(__prefix_line)s
|
||||
|
||||
In this example common.conf defines __prefix_line which also contains the
|
||||
_daemon name (in syslog terms the service) you have just specified. _daemon
|
||||
can also be a regex.
|
||||
|
||||
For example, to capture following line _daemon should be set to "dovecot"
|
||||
|
||||
Dec 12 11:19:11 dunnart dovecot: pop3-login: Aborted login (tried to use disabled plaintext auth): rip=190.210.136.21, lip=113.212.99.193
|
||||
|
||||
and then ^%(__prefix_line)s would match "Dec 12 11:19:11 dunnart dovecot:
|
||||
". Note it matches the trailing space(s) as well.
|
||||
|
||||
Substitutions (AKA string interpolations):
|
||||
|
||||
We have used string interpolations in above examples. They are useful for
|
||||
making the regexes more readable, reuse generic patterns in multiple failregex
|
||||
lines, and also to refer definition of regex parts to specific filters or even
|
||||
to the user. General principle is that value of a _name variable replaces
|
||||
occurrences of %(_name)s within the same section or anywhere in the config file
|
||||
if defined in [DEFAULT] section.
|
||||
|
||||
Regular Expressions:
|
||||
|
||||
Regular expressions (failregex, ignoreregex) assume that the date/time has been
|
||||
removed from the log line (this is just how fail2ban works internally ATM).
|
||||
|
||||
If the format is like '<date...> error 1.2.3.4 is evil' then you need to match
|
||||
the < at the start so regex should be similar to '^<> <HOST> is evil$' using
|
||||
<HOST> where the IP/domain name appears in the log line.
|
||||
|
||||
The following general rules apply to regular expressions:
|
||||
|
||||
* ensure regexes start with a ^ and are as restrictive as possible. E.g. do not
|
||||
use .* if \d+ is sufficient;
|
||||
* use functionality of Python regexes defined in the standard Python re library
|
||||
http://docs.python.org/2/library/re.html;
|
||||
* make regular expressions readable (as much as possible). E.g.
|
||||
(?:...) represents a non-capturing regex but (...) is more readable, thus
|
||||
preferred.
|
||||
|
||||
If you have only a basic knowledge of regular repressions we advise to read
|
||||
http://docs.python.org/2/library/re.html first. It doesn't take long and would
|
||||
remind you e.g. which characters you need to escape and which you don't.
|
||||
|
||||
Developing/testing a regex:
|
||||
|
||||
You can develop a regex in a file or using command line depending on your
|
||||
preference. You can also use samples you have already created in the test cases
|
||||
or test them one at a time.
|
||||
|
||||
The general tool for testing Fail2Ban regexes is fail2ban-regex. To see how to
|
||||
use it run:
|
||||
|
||||
./fail2ban-regex --help
|
||||
|
||||
Take note of -l heavydebug / -l debug and -v as they might be very useful.
|
||||
|
||||
TIP: Take a look at the source code of the application you are developing
|
||||
failregex for. You may see optional or extra log messages, or parts there
|
||||
of, that need to form part of your regex. It may also reveal how some
|
||||
parts are constrained and different formats depending on configuration or
|
||||
less common usages.
|
||||
|
||||
TIP: For looking through source code - http://sourcecodebrowser.com/ . It has
|
||||
call graphs and can browse different versions.
|
||||
|
||||
TIP: Some applications log spaces at the end. If you are not sure add \s*$ as
|
||||
the end part of the regex.
|
||||
|
||||
If your regex is not matching, http://www.debuggex.com/?flavor=python can help
|
||||
to tune it. fail2ban-regex -D ... will present Debuggex URLs for the regexs
|
||||
and sample log files that you pass into it.
|
||||
|
||||
In general use when using regex debuggers for generating fail2ban filters:
|
||||
* use regex from the ./fail2ban-regex output (to ensure all substitutions are
|
||||
done)
|
||||
* replace <HOST> with (?&.ipv4)
|
||||
* make sure that regex type set to Python
|
||||
* for the test data put your log output with the date/time removed
|
||||
|
||||
When you have fixed the regex put it back into your filter file.
|
||||
|
||||
Please spread the good word about Debuggex - Serge Toarca is kindly continuing
|
||||
its free availability to Open Source developers.
|
||||
|
||||
Finishing up:
|
||||
|
||||
If you've added a new filter, add a new entry in config/jail.conf. The theory
|
||||
here is that a user will create a jail.local with [filtername]\nenable=true to
|
||||
enable your jail.
|
||||
|
||||
So more specifically in the [filter] section in jail.conf:
|
||||
* ensure that you have "enabled = false" (users will enable as needed);
|
||||
* use "filter =" set to your filter name;
|
||||
* use a typical action to disable ports associated with the application;
|
||||
* set "logpath" to the usual location of application log file;
|
||||
* if the default findtime or bantime isn't appropriate to the filter, specify
|
||||
more appropriate choices (possibly with a brief comment line).
|
||||
|
||||
Submit github pull request (See "Pull Requests" above) for
|
||||
github.com/fail2ban/fail2ban containing your great work.
|
||||
|
||||
Filter Security
|
||||
---------------
|
||||
|
||||
Poor filter regular expressions are susceptible to DoS attacks.
|
||||
|
||||
When a remote user has the ability to introduce text that would match filter's
|
||||
failregex, while matching inserted text to the <HOST> part, they have the
|
||||
ability to deny any host they choose.
|
||||
|
||||
So the <HOST> part must be anchored on text generated by the application, and
|
||||
not the user, to an extent sufficient to prevent user inserting the entire text
|
||||
matching this or any other failregex.
|
||||
|
||||
Ideally filter regex should anchor at the beginning and at the end of log line.
|
||||
However as more applications log at the beginning than the end, anchoring the
|
||||
beginning is more important. If the log file used by the application is shared
|
||||
with other applications, like system logs, ensure the other application that use
|
||||
that log file do not log user generated text at the beginning of the line, or,
|
||||
if they do, ensure the regexes of the filter are sufficient to mitigate the risk
|
||||
of insertion.
|
||||
|
||||
|
||||
Examples of poor filters
|
||||
------------------------
|
||||
|
||||
1. Too restrictive
|
||||
|
||||
We find a log message:
|
||||
|
||||
Apr-07-13 07:08:36 Invalid command fial2ban from 1.2.3.4
|
||||
|
||||
We make a failregex
|
||||
|
||||
^Invalid command \S+ from <HOST>
|
||||
|
||||
Now think evil. The user does the command 'blah from 1.2.3.44'
|
||||
|
||||
The program diligently logs:
|
||||
|
||||
Apr-07-13 07:08:36 Invalid command blah from 1.2.3.44 from 1.2.3.4
|
||||
|
||||
And fail2ban matches 1.2.3.44 as the IP that it ban. A DoS attack was successful.
|
||||
|
||||
The fix here is that the command can be anything so .* is appropriate.
|
||||
|
||||
^Invalid command .* from <HOST>
|
||||
|
||||
Here the .* will match until the end of the string. Then realise it has more to
|
||||
match, i.e. "from <HOST>" and go back until it find this. Then it will ban
|
||||
1.2.3.4 correctly. Since the <HOST> is always at the end, end the regex with a $.
|
||||
|
||||
^Invalid command .* from <HOST>$
|
||||
|
||||
Note if we'd just had the expression:
|
||||
|
||||
^Invalid command \S+ from <HOST>$
|
||||
|
||||
Then provided the user put a space in their command they would have never been
|
||||
banned.
|
||||
|
||||
2. Unanchored regex can match other user injected data
|
||||
|
||||
From the Apache vulnerability CVE-2013-2178
|
||||
( original ref: https://vndh.net/note:fail2ban-089-denial-service ).
|
||||
|
||||
An example bad regex for Apache:
|
||||
|
||||
failregex = [[]client <HOST>[]] user .* not found
|
||||
|
||||
Since the user can do a get request on:
|
||||
|
||||
GET /[client%20192.168.0.1]%20user%20root%20not%20found HTTP/1.0
|
||||
Host: remote.site
|
||||
|
||||
Now the log line will be:
|
||||
|
||||
[Sat Jun 01 02:17:42 2013] [error] [client 192.168.33.1] File does not exist: /srv/http/site/[client 192.168.0.1] user root not found
|
||||
|
||||
As this log line doesn't match other expressions hence it matches the above
|
||||
regex and blocks 192.168.33.1 as a denial of service from the HTTP requester.
|
||||
|
||||
3. Over greedy pattern matching
|
||||
|
||||
From: https://github.com/fail2ban/fail2ban/pull/426
|
||||
|
||||
An example ssh log (simplified)
|
||||
|
||||
Sep 29 17:15:02 spaceman sshd[12946]: Failed password for user from 127.0.0.1 port 20000 ssh1: ruser remoteuser
|
||||
|
||||
As we assume username can include anything including spaces its prudent to put
|
||||
.* here. The remote user can also exist as anything so lets not make assumptions again.
|
||||
|
||||
failregex = ^%(__prefix_line)sFailed \S+ for .* from <HOST>( port \d*)?( ssh\d+)?(: ruser .*)?$
|
||||
|
||||
So this works. The problem is if the .* after remote user is injected by the
|
||||
user to be 'from 1.2.3.4'. The resultant log line is.
|
||||
|
||||
Sep 29 17:15:02 spaceman sshd[12946]: Failed password for user from 127.0.0.1 port 20000 ssh1: ruser from 1.2.3.4
|
||||
|
||||
Testing with:
|
||||
|
||||
fail2ban-regex -v 'Sep 29 17:15:02 Failed password for user from 127.0.0.1 port 20000 ssh1: ruser from 1.2.3.4' '^ Failed \S+ for .* from <HOST>( port \d*)?( ssh\d+)?(: ruser .*)?$'
|
||||
|
||||
TIP: I've removed the bit that matches __prefix_line from the regex and log.
|
||||
|
||||
Shows:
|
||||
|
||||
1) [1] ^ Failed \S+ for .* from <HOST>( port \d*)?( ssh\d+)?(: ruser .*)?$
|
||||
1.2.3.4 Sun Sep 29 17:15:02 2013
|
||||
|
||||
It should of matched 127.0.0.1. So the first greedy part of the greedy regex
|
||||
matched until the end of the string. The was no "from <HOST>" so the regex
|
||||
engine worked backwards from the end of the string until this was matched.
|
||||
|
||||
The result was that 1.2.3.4 was matched, injected by the user, and the wrong IP
|
||||
was banned.
|
||||
|
||||
The solution here is to make the first .* non-greedy with .*?. Here it matches
|
||||
as little as required and the fail2ban-regex tool shows the output:
|
||||
|
||||
fail2ban-regex -v 'Sep 29 17:15:02 Failed password for user from 127.0.0.1 port 20000 ssh1: ruser from 1.2.3.4' '^ Failed \S+ for .*? from <HOST>( port \d*)?( ssh\d+)?(: ruser .*)?$'
|
||||
|
||||
1) [1] ^ Failed \S+ for .*? from <HOST>( port \d*)?( ssh\d+)?(: ruser .*)?$
|
||||
127.0.0.1 Sun Sep 29 17:15:02 2013
|
||||
|
||||
So the general case here is a log line that contains:
|
||||
|
||||
(fixed_data_1)<HOST>(fixed_data_2)(user_injectable_data)
|
||||
|
||||
Where the regex that matches fixed_data_1 is gready and matches the entire
|
||||
string, before moving backwards and user_injectable_data can match the entire
|
||||
string.
|
||||
|
||||
Another case:
|
||||
|
||||
ref: https://www.debuggex.com/r/CtAbeKMa2sDBEfA2/0
|
||||
|
||||
A webserver logs the following without URL escaping:
|
||||
|
||||
[error] 2865#0: *66647 user "xyz" was not found in "/file", client: 1.2.3.1, server: www.host.com, request: "GET ", client: 3.2.1.1, server: fake.com, request: "GET exploited HTTP/3.3", host: "injected.host", host: "www.myhost.com"
|
||||
|
||||
regex:
|
||||
|
||||
failregex = ^ \[error\] \d+#\d+: \*\d+ user "\S+":? (?:password mismatch|was not found in ".*"), client: <HOST>, server: \S+, request: "\S+ .+ HTTP/\d+\.\d+", host: "\S+"
|
||||
|
||||
The .* matches to the end of the string. Finds that it can't continue to match
|
||||
", client ... so it moves from the back and find that the user injected web URL:
|
||||
|
||||
", client: 3.2.1.1, server: fake.com, request: "GET exploited HTTP/3.3", host: "injected.host
|
||||
|
||||
In this case there is a fixed host: "www.myhost.com" at the end so the solution
|
||||
is to anchor the regex at the end with a $.
|
||||
|
||||
If this wasn't the case then first .* needed to be made so it didn't capture
|
||||
beyond <HOST>.
|
||||
|
||||
4. Application generates two identical log messages with different meanings
|
||||
|
||||
If the application generates the following two messages under different
|
||||
circumstances:
|
||||
|
||||
client <IP>: authentication failed
|
||||
client <USER>: authentication failed
|
||||
|
||||
|
||||
Then it's obvious that a regex of "^client <HOST>: authentication
|
||||
failed$" will still cause problems if the user can trigger the second
|
||||
log message with a <USER> of 123.1.1.1.
|
||||
|
||||
Here there's nothing to do except request/change the application so it logs
|
||||
messages differently.
|
||||
|
||||
If you are developing filters see the FILTERS file for documentation.
|
||||
|
||||
Code Testing
|
||||
============
|
||||
|
||||
Existing tests can be run by executing `fail2ban-testcases`. This has options
|
||||
like --log-level that will probably be useful. `fail2ban-testcases --help` for
|
||||
full options.
|
||||
Existing tests can be run by executing `bin/fail2ban-testcases`. It has
|
||||
options like --log-level that will probably be useful. Run
|
||||
`bin/fail2ban-testcases --help` for the full list of options.
|
||||
|
||||
Test cases should cover all usual cases, all exception cases and all inside
|
||||
/ outside boundary conditions.
|
||||
|
|
@ -512,7 +54,7 @@ Install the package python-coverage to visualise your test coverage. Run the
|
|||
following (note: on Debian-based systems, the script is called
|
||||
`python-coverage`):
|
||||
|
||||
coverage run fail2ban-testcases
|
||||
coverage run bin/fail2ban-testcases
|
||||
coverage html
|
||||
|
||||
Then look at htmlcov/index.html and see how much coverage your test cases
|
||||
|
|
@ -726,148 +268,3 @@ action.py
|
|||
|
||||
Takes care about executing start/check/ban/unban/stop commands
|
||||
|
||||
|
||||
Releasing
|
||||
=========
|
||||
|
||||
# Check distribution patches and see if they can be included
|
||||
|
||||
* https://apps.fedoraproject.org/packages/fail2ban/sources
|
||||
* http://sources.gentoo.org/cgi-bin/viewvc.cgi/gentoo-x86/net-analyzer/fail2ban/
|
||||
* http://svnweb.freebsd.org/ports/head/security/py-fail2ban/
|
||||
* https://build.opensuse.org/package/show?package=fail2ban&project=openSUSE%3AFactory
|
||||
* http://sophie.zarb.org/sources/fail2ban (Mageia)
|
||||
* https://trac.macports.org/browser/trunk/dports/security/fail2ban
|
||||
|
||||
# Check distribution outstanding bugs
|
||||
|
||||
* https://github.com/fail2ban/fail2ban/issues?sort=updated&state=open
|
||||
* http://bugs.debian.org/cgi-bin/pkgreport.cgi?dist=unstable;package=fail2ban
|
||||
* http://bugs.sabayon.org/buglist.cgi?quicksearch=net-analyzer%2Ffail2ban
|
||||
* https://bugs.gentoo.org/buglist.cgi?query_format=advanced&short_desc=fail2ban&bug_status=UNCONFIRMED&bug_status=CONFIRMED&bug_status=IN_PROGRESS&short_desc_type=allwords
|
||||
* https://bugzilla.redhat.com/buglist.cgi?query_format=advanced&bug_status=NEW&bug_status=ASSIGNED&component=fail2ban&classification=Red%20Hat&classification=Fedora
|
||||
* http://www.freebsd.org/cgi/query-pr-summary.cgi?text=fail2ban
|
||||
|
||||
# Make sure the tests pass
|
||||
|
||||
./fail2ban-testcases-all
|
||||
|
||||
# Ensure the version is correct
|
||||
|
||||
in:
|
||||
* ./common/version.py
|
||||
* top of ChangeLog
|
||||
* README.md
|
||||
|
||||
# Ensure the MANIFEST is complete
|
||||
|
||||
Run:
|
||||
|
||||
python setup.py sdist
|
||||
|
||||
Look for errors like:
|
||||
'testcases/files/logs/mysqld.log' not a regular file -- skipping
|
||||
|
||||
Which indicates that testcases/files/logs/mysqld.log has been moved or is a directory
|
||||
|
||||
tar -C /tmp -jxf dist/fail2ban-0.8.11.tar.bz2
|
||||
|
||||
# clean up current direcory
|
||||
|
||||
diff -rul --exclude \*.pyc . /tmp/fail2ban-0.8.11/
|
||||
|
||||
# Only differences should be files that you don't want distributed.
|
||||
|
||||
# Ensure the tests work from the tarball
|
||||
|
||||
cd /tmp/fail2ban-0.8.11/ && ./fail2ban-testcases-all
|
||||
|
||||
# Add/finalize the corresponding entry in the ChangeLog
|
||||
|
||||
To generate a list of committers use e.g.
|
||||
|
||||
git shortlog -sn 0.8.10.. | sed -e 's,^[ 0-9\t]*,,g' | tr '\n' '\|' | sed -e 's:|:, :g'
|
||||
|
||||
Ensure the top of the ChangeLog has the right version and current date.
|
||||
|
||||
Ensure the top entry of the ChangeLog has the right version and current date.
|
||||
|
||||
# Update man pages
|
||||
|
||||
(cd man ; ./generate-man )
|
||||
git commit -m 'DOC/ENH: update man pages for release' man/*
|
||||
|
||||
# Prepare source and rpm binary distributions
|
||||
|
||||
python setup.py sdist
|
||||
python setup.py bdist_rpm
|
||||
python setup.py upload
|
||||
|
||||
# Provide a release sample to distributors
|
||||
|
||||
* Debian: Yaroslav Halchenko <debian@onerussian.com>
|
||||
http://packages.qa.debian.org/f/fail2ban.html
|
||||
* FreeBSD: Christoph Theis theis@gmx.at>, Nick Hilliard <nick@foobar.org>
|
||||
http://svnweb.freebsd.org/ports/head/security/py-fail2ban/Makefile?view=markup
|
||||
http://www.freebsd.org/cgi/query-pr-summary.cgi?text=fail2ban
|
||||
* Fedora: Axel Thimm <Axel.Thimm@atrpms.net>
|
||||
https://apps.fedoraproject.org/packages/fail2ban
|
||||
http://pkgs.fedoraproject.org/cgit/fail2ban.git
|
||||
https://admin.fedoraproject.org/pkgdb/acls/bugs/fail2ban
|
||||
* Gentoo: netmon@gentoo.org
|
||||
http://sources.gentoo.org/cgi-bin/viewvc.cgi/gentoo-x86/net-analyzer/fail2ban/metadata.xml?view=markup
|
||||
https://bugs.gentoo.org/buglist.cgi?quicksearch=fail2ban
|
||||
* openSUSE: Stephan Kulow <coolo@suse.com>
|
||||
https://build.opensuse.org/package/show/openSUSE:Factory/fail2ban
|
||||
* Mac Ports: @Malbrouck on github (gh-49)
|
||||
https://trac.macports.org/browser/trunk/dports/security/fail2ban/Portfile
|
||||
* Mageia:
|
||||
https://bugs.mageia.org/buglist.cgi?quicksearch=fail2ban
|
||||
An potentially to the fail2ban-users directory.
|
||||
|
||||
# Wait for feedback from distributors
|
||||
|
||||
# Prepare a release notice https://github.com/fail2ban/fail2ban/releases/new
|
||||
|
||||
Upload the source/binaries from the dist directory and tag the release using the URL
|
||||
|
||||
# Upload source/binaries to sourceforge http://sourceforge.net/projects/fail2ban/
|
||||
|
||||
# Run the following and update the wiki with output:
|
||||
python -c 'import common.protocol; common.protocol.printWiki()'
|
||||
|
||||
page: http://www.fail2ban.org/wiki/index.php/Commands
|
||||
|
||||
* Update:
|
||||
http://www.fail2ban.org/wiki/index.php/Downloads
|
||||
http://www.fail2ban.org/wiki/index.php/ChangeLog
|
||||
http://www.fail2ban.org/wiki/index.php/Requirements (Check requirement)
|
||||
http://www.fail2ban.org/wiki/index.php/Main_Page (Add to News)
|
||||
http://www.fail2ban.org/wiki/index.php/Features
|
||||
|
||||
* See if any filters are upgraded:
|
||||
http://www.fail2ban.org/wiki/index.php/Special:AllPages
|
||||
|
||||
# Email users and development list of release
|
||||
|
||||
# notify distributors
|
||||
|
||||
Post Release
|
||||
============
|
||||
|
||||
Add the following to the top of the ChangeLog
|
||||
|
||||
ver. 0.8.12 (2013/XX/XXX) - wanna-be-released
|
||||
-----------
|
||||
|
||||
- Fixes:
|
||||
|
||||
- New Features:
|
||||
|
||||
- Enhancements:
|
||||
|
||||
Alter the git shortlog command in the previous section to refer to the just
|
||||
released version.
|
||||
|
||||
and adjust common/version.py to carry .dev suffix to signal
|
||||
a version under development.
|
||||
|
|
|
|||
469
FILTERS
Normal file
469
FILTERS
Normal file
|
|
@ -0,0 +1,469 @@
|
|||
__ _ _ ___ _
|
||||
/ _|__ _(_) |_ ) |__ __ _ _ _
|
||||
| _/ _` | | |/ /| '_ \/ _` | ' \
|
||||
|_| \__,_|_|_/___|_.__/\__,_|_||_|
|
||||
|
||||
================================================================================
|
||||
Developing Filters
|
||||
================================================================================
|
||||
|
||||
Filters
|
||||
=======
|
||||
|
||||
Filters are tricky. They need to:
|
||||
* work with a variety of the versions of the software that generates the logs;
|
||||
* work with the range of logging configuration options available in the
|
||||
software;
|
||||
* work with multiple operating systems;
|
||||
* not make assumptions about the log format in excess of the software
|
||||
(e.g. do not assume a username doesn't contain spaces and use \S+ unless
|
||||
you've checked the source code);
|
||||
* account for how future versions of the software will log messages
|
||||
(e.g. guess what would happen to the log message if different authentication
|
||||
types are added);
|
||||
* not be susceptible to DoS vulnerabilities (see Filter Security below); and
|
||||
* match intended log lines only.
|
||||
|
||||
Please follow the steps from Filter Test Cases to Developing Filter Regular
|
||||
Expressions and submit a GitHub pull request (PR) afterwards. If you get stuck,
|
||||
you can push your unfinished changes and still submit a PR -- describe
|
||||
what you have done, what is the hurdle, and we'll attempt to help (PR
|
||||
will be automagically updated with future commits you would push to
|
||||
complete it).
|
||||
|
||||
Filter test cases
|
||||
-----------------
|
||||
|
||||
Purpose:
|
||||
|
||||
Start by finding the log messages that the application generates related to
|
||||
some form of authentication failure. If you are adding to an existing filter
|
||||
think about whether the log messages are of a similar importance and purpose
|
||||
to the existing filter. If you were a user of Fail2Ban, and did a package
|
||||
update of Fail2Ban that started matching new log messages, would anything
|
||||
unexpected happen? Would the bantime/findtime for the jail be appropriate for
|
||||
the new log messages? If it doesn't, perhaps it needs to be in a separate
|
||||
filter definition, for example like exim filter aims at authentication failures
|
||||
and exim-spam at log messages related to spam.
|
||||
|
||||
Even if it is a new filter you may consider separating the log messages into
|
||||
different filters based on purpose.
|
||||
|
||||
Cause:
|
||||
|
||||
Are some of the log lines a result of the same action? For example, is a PAM
|
||||
failure log message, followed by an application specific failure message the
|
||||
result of the same user/script action? If you add regular expressions for
|
||||
both you would end up with two failures for a single action.
|
||||
Therefore, select the most appropriate log message and document the other log
|
||||
message) with a test case not to match it and a description as to why you chose
|
||||
one over another.
|
||||
|
||||
With the selected log lines consider what action has caused those log
|
||||
messages and whether they could have been generated by accident? Could
|
||||
the log message be occurring due to the first step towards the application
|
||||
asking for authentication? Could the log messages occur often? If some of
|
||||
these are true make a note of this in the jail.conf example that you provide.
|
||||
|
||||
Samples:
|
||||
|
||||
It is important to include log file samples so any future change in the regular
|
||||
expression will still work with the log lines you have identified.
|
||||
|
||||
The sample log messages are provided in a file under testcases/files/logs/
|
||||
named identically as the corresponding filter (but without .conf extension).
|
||||
Each log line should be preceded by a line with failJSON metadata (so the logs
|
||||
lines are tested in the test suite) directly above the log line. If there is
|
||||
any specific information about the log message, such as version or an
|
||||
application configuration option that is needed for the message to occur,
|
||||
include this in a comment (line beginning with #) above the failJSON metadata.
|
||||
|
||||
Log samples should include only one, definitely not more than 3, examples of
|
||||
log messages of the same form. If log messages are different in different
|
||||
versions of the application log messages that show this are encouraged.
|
||||
|
||||
Also attempt to inject an IP into the application (e.g. by specifying
|
||||
it as a username) so that Fail2Ban possibly detects the IP
|
||||
from user input rather than the true origin. See the Filter Security section
|
||||
and the top example in testcases/files/logs/apache-auth as to how to do this.
|
||||
One you have discovered that this is possible, correct the regex so it doesn't
|
||||
match and provide this as a test case with "match": false (see failJSON below).
|
||||
|
||||
If the mechanism to create the log message isn't obvious provide a
|
||||
configuration and/or sample scripts testcases/files/config/{filtername} and
|
||||
reference these in the comments above the log line.
|
||||
|
||||
FailJSON metadata:
|
||||
|
||||
A failJSON metadata is a comment immediately above the log message. It will
|
||||
look like:
|
||||
|
||||
# failJSON: { "time": "2013-06-10T10:10:59", "match": true , "host": "93.184.216.119" }
|
||||
|
||||
Time should match the time of the log message. It is in a specific format of
|
||||
Year-Month-Day'T'Hour:minute:Second. If your log message does not include a
|
||||
year, like the example below, the year should be listed as 2005, if before Sun
|
||||
Aug 14 10am UTC, and 2004 if afterwards. Here is an example failJSON
|
||||
line preceding a sample log line:
|
||||
|
||||
# failJSON: { "time": "2005-03-24T15:25:51", "match": true , "host": "198.51.100.87" }
|
||||
Mar 24 15:25:51 buffalo1 dropbear[4092]: bad password attempt for 'root' from 198.51.100.87:5543
|
||||
|
||||
The "host" in failJSON should contain the IP or domain that should be blocked.
|
||||
|
||||
For long lines that you do not want to be matched (e.g. from log injection
|
||||
attacks) and any log lines to be excluded (see "Cause" section above), set
|
||||
"match": false in the failJSON and describe the reason in the comment above.
|
||||
|
||||
After developing regexes, the following command will test all failJSON metadata
|
||||
against the log lines in all sample log files
|
||||
|
||||
./fail2ban-testcases testSampleRegex
|
||||
|
||||
Developing Filter Regular Expressions
|
||||
-------------------------------------
|
||||
|
||||
Date/Time:
|
||||
|
||||
At the moment, Fail2Ban depends on log lines to have time stamps. That is why
|
||||
before starting to develop failregex, check if your log line format known to
|
||||
Fail2Ban. Copy the time component from the log line and append an IP address to
|
||||
test with following command:
|
||||
|
||||
./fail2ban-regex "2013-09-19 02:46:12 1.2.3.4" "<HOST>"
|
||||
|
||||
Output of such command should contain something like:
|
||||
|
||||
Date template hits:
|
||||
|- [# of hits] date format
|
||||
| [1] Year-Month-Day Hour:Minute:Second
|
||||
|
||||
Ensure that the template description matches time/date elements in your log line
|
||||
time stamp. If there is no matched format then date template needs to be added
|
||||
to server/datedetector.py. Ensure that a new template is added in the order
|
||||
that more specific matches occur first and that there is no confusion between a
|
||||
Day and a Month.
|
||||
|
||||
Filter file:
|
||||
|
||||
The filter is specified in a config/filter.d/{filtername}.conf file. Filter file
|
||||
can have sections INCLUDES (optional) and Definition as follows:
|
||||
|
||||
[INCLUDES]
|
||||
|
||||
before = common.conf
|
||||
|
||||
after = filtername.local
|
||||
|
||||
[Definition]
|
||||
|
||||
failregex = ....
|
||||
|
||||
ignoreregex = ....
|
||||
|
||||
This is also documented in the man page jail.conf (section 5). Other definitions
|
||||
can be added to make failregex's more readable and maintainable to be used
|
||||
through string Interpolations (see http://docs.python.org/2.7/library/configparser.html)
|
||||
|
||||
|
||||
General rules:
|
||||
|
||||
Use "before" if you need to include a common set of rules, like syslog or if
|
||||
there is a common set of regexes for multiple filters.
|
||||
|
||||
Use "after" if you wish to allow the user to overwrite a set of customisations
|
||||
of the current filter. This file doesn't need to exist.
|
||||
|
||||
Try to avoid using ignoreregex mainly for performance reasons. The case when you
|
||||
would use it is if in trying to avoid using it, you end up with an unreadable
|
||||
failregex.
|
||||
|
||||
Syslog:
|
||||
|
||||
If your application logs to syslog you can take advantage of log line prefix
|
||||
definitions present in common.conf. So as a base use:
|
||||
|
||||
[INCLUDES]
|
||||
|
||||
before = common.conf
|
||||
|
||||
[Definition]
|
||||
|
||||
_daemon = app
|
||||
|
||||
failregex = ^%(__prefix_line)s
|
||||
|
||||
In this example common.conf defines __prefix_line which also contains the
|
||||
_daemon name (in syslog terms the service) you have just specified. _daemon
|
||||
can also be a regex.
|
||||
|
||||
For example, to capture following line _daemon should be set to "dovecot"
|
||||
|
||||
Dec 12 11:19:11 dunnart dovecot: pop3-login: Aborted login (tried to use disabled plaintext auth): rip=190.210.136.21, lip=113.212.99.193
|
||||
|
||||
and then ^%(__prefix_line)s would match "Dec 12 11:19:11 dunnart dovecot:
|
||||
". Note it matches the trailing space(s) as well.
|
||||
|
||||
Substitutions (AKA string interpolations):
|
||||
|
||||
We have used string interpolations in above examples. They are useful for
|
||||
making the regexes more readable, reuse generic patterns in multiple failregex
|
||||
lines, and also to refer definition of regex parts to specific filters or even
|
||||
to the user. General principle is that value of a _name variable replaces
|
||||
occurrences of %(_name)s within the same section or anywhere in the config file
|
||||
if defined in [DEFAULT] section.
|
||||
|
||||
Regular Expressions:
|
||||
|
||||
Regular expressions (failregex, ignoreregex) assume that the date/time has been
|
||||
removed from the log line (this is just how fail2ban works internally ATM).
|
||||
|
||||
If the format is like '<date...> error 1.2.3.4 is evil' then you need to match
|
||||
the < at the start so regex should be similar to '^<> <HOST> is evil$' using
|
||||
<HOST> where the IP/domain name appears in the log line.
|
||||
|
||||
The following general rules apply to regular expressions:
|
||||
|
||||
* ensure regexes start with a ^ and are as restrictive as possible. E.g. do not
|
||||
use .* if \d+ is sufficient;
|
||||
* use functionality of Python regexes defined in the standard Python re library
|
||||
http://docs.python.org/2/library/re.html;
|
||||
* make regular expressions readable (as much as possible). E.g.
|
||||
(?:...) represents a non-capturing regex but (...) is more readable, thus
|
||||
preferred.
|
||||
|
||||
If you have only a basic knowledge of regular repressions we advise to read
|
||||
http://docs.python.org/2/library/re.html first. It doesn't take long and would
|
||||
remind you e.g. which characters you need to escape and which you don't.
|
||||
|
||||
Developing/testing a regex:
|
||||
|
||||
You can develop a regex in a file or using command line depending on your
|
||||
preference. You can also use samples you have already created in the test cases
|
||||
or test them one at a time.
|
||||
|
||||
The general tool for testing Fail2Ban regexes is fail2ban-regex. To see how to
|
||||
use it run:
|
||||
|
||||
./fail2ban-regex --help
|
||||
|
||||
Take note of -l heavydebug / -l debug and -v as they might be very useful.
|
||||
|
||||
TIP: Take a look at the source code of the application you are developing
|
||||
failregex for. You may see optional or extra log messages, or parts there
|
||||
of, that need to form part of your regex. It may also reveal how some
|
||||
parts are constrained and different formats depending on configuration or
|
||||
less common usages.
|
||||
|
||||
TIP: For looking through source code - http://sourcecodebrowser.com/ . It has
|
||||
call graphs and can browse different versions.
|
||||
|
||||
TIP: Some applications log spaces at the end. If you are not sure add \s*$ as
|
||||
the end part of the regex.
|
||||
|
||||
If your regex is not matching, http://www.debuggex.com/?flavor=python can help
|
||||
to tune it. fail2ban-regex -D ... will present Debuggex URLs for the regexs
|
||||
and sample log files that you pass into it.
|
||||
|
||||
In general use when using regex debuggers for generating fail2ban filters:
|
||||
* use regex from the ./fail2ban-regex output (to ensure all substitutions are
|
||||
done)
|
||||
* replace <HOST> with (?&.ipv4)
|
||||
* make sure that regex type set to Python
|
||||
* for the test data put your log output with the date/time removed
|
||||
|
||||
When you have fixed the regex put it back into your filter file.
|
||||
|
||||
Please spread the good word about Debuggex - Serge Toarca is kindly continuing
|
||||
its free availability to Open Source developers.
|
||||
|
||||
Finishing up:
|
||||
|
||||
If you've added a new filter, add a new entry in config/jail.conf. The theory
|
||||
here is that a user will create a jail.local with [filtername]\nenable=true to
|
||||
enable your jail.
|
||||
|
||||
So more specifically in the [filter] section in jail.conf:
|
||||
* ensure that you have "enabled = false" (users will enable as needed);
|
||||
* use "filter =" set to your filter name;
|
||||
* use a typical action to disable ports associated with the application;
|
||||
* set "logpath" to the usual location of application log file;
|
||||
* if the default findtime or bantime isn't appropriate to the filter, specify
|
||||
more appropriate choices (possibly with a brief comment line).
|
||||
|
||||
Submit github pull request (See "Pull Requests" above) for
|
||||
github.com/fail2ban/fail2ban containing your great work.
|
||||
|
||||
Filter Security
|
||||
---------------
|
||||
|
||||
Poor filter regular expressions are susceptible to DoS attacks.
|
||||
|
||||
When a remote user has the ability to introduce text that would match filter's
|
||||
failregex, while matching inserted text to the <HOST> part, they have the
|
||||
ability to deny any host they choose.
|
||||
|
||||
So the <HOST> part must be anchored on text generated by the application, and
|
||||
not the user, to an extent sufficient to prevent user inserting the entire text
|
||||
matching this or any other failregex.
|
||||
|
||||
Ideally filter regex should anchor at the beginning and at the end of log line.
|
||||
However as more applications log at the beginning than the end, anchoring the
|
||||
beginning is more important. If the log file used by the application is shared
|
||||
with other applications, like system logs, ensure the other application that use
|
||||
that log file do not log user generated text at the beginning of the line, or,
|
||||
if they do, ensure the regexes of the filter are sufficient to mitigate the risk
|
||||
of insertion.
|
||||
|
||||
|
||||
Examples of poor filters
|
||||
------------------------
|
||||
|
||||
1. Too restrictive
|
||||
|
||||
We find a log message:
|
||||
|
||||
Apr-07-13 07:08:36 Invalid command fial2ban from 1.2.3.4
|
||||
|
||||
We make a failregex
|
||||
|
||||
^Invalid command \S+ from <HOST>
|
||||
|
||||
Now think evil. The user does the command 'blah from 1.2.3.44'
|
||||
|
||||
The program diligently logs:
|
||||
|
||||
Apr-07-13 07:08:36 Invalid command blah from 1.2.3.44 from 1.2.3.4
|
||||
|
||||
And fail2ban matches 1.2.3.44 as the IP that it ban. A DoS attack was successful.
|
||||
|
||||
The fix here is that the command can be anything so .* is appropriate.
|
||||
|
||||
^Invalid command .* from <HOST>
|
||||
|
||||
Here the .* will match until the end of the string. Then realise it has more to
|
||||
match, i.e. "from <HOST>" and go back until it find this. Then it will ban
|
||||
1.2.3.4 correctly. Since the <HOST> is always at the end, end the regex with a $.
|
||||
|
||||
^Invalid command .* from <HOST>$
|
||||
|
||||
Note if we'd just had the expression:
|
||||
|
||||
^Invalid command \S+ from <HOST>$
|
||||
|
||||
Then provided the user put a space in their command they would have never been
|
||||
banned.
|
||||
|
||||
2. Unanchored regex can match other user injected data
|
||||
|
||||
From the Apache vulnerability CVE-2013-2178
|
||||
( original ref: https://vndh.net/note:fail2ban-089-denial-service ).
|
||||
|
||||
An example bad regex for Apache:
|
||||
|
||||
failregex = [[]client <HOST>[]] user .* not found
|
||||
|
||||
Since the user can do a get request on:
|
||||
|
||||
GET /[client%20192.168.0.1]%20user%20root%20not%20found HTTP/1.0
|
||||
Host: remote.site
|
||||
|
||||
Now the log line will be:
|
||||
|
||||
[Sat Jun 01 02:17:42 2013] [error] [client 192.168.33.1] File does not exist: /srv/http/site/[client 192.168.0.1] user root not found
|
||||
|
||||
As this log line doesn't match other expressions hence it matches the above
|
||||
regex and blocks 192.168.33.1 as a denial of service from the HTTP requester.
|
||||
|
||||
3. Over greedy pattern matching
|
||||
|
||||
From: https://github.com/fail2ban/fail2ban/pull/426
|
||||
|
||||
An example ssh log (simplified)
|
||||
|
||||
Sep 29 17:15:02 spaceman sshd[12946]: Failed password for user from 127.0.0.1 port 20000 ssh1: ruser remoteuser
|
||||
|
||||
As we assume username can include anything including spaces its prudent to put
|
||||
.* here. The remote user can also exist as anything so lets not make assumptions again.
|
||||
|
||||
failregex = ^%(__prefix_line)sFailed \S+ for .* from <HOST>( port \d*)?( ssh\d+)?(: ruser .*)?$
|
||||
|
||||
So this works. The problem is if the .* after remote user is injected by the
|
||||
user to be 'from 1.2.3.4'. The resultant log line is.
|
||||
|
||||
Sep 29 17:15:02 spaceman sshd[12946]: Failed password for user from 127.0.0.1 port 20000 ssh1: ruser from 1.2.3.4
|
||||
|
||||
Testing with:
|
||||
|
||||
fail2ban-regex -v 'Sep 29 17:15:02 Failed password for user from 127.0.0.1 port 20000 ssh1: ruser from 1.2.3.4' '^ Failed \S+ for .* from <HOST>( port \d*)?( ssh\d+)?(: ruser .*)?$'
|
||||
|
||||
TIP: I've removed the bit that matches __prefix_line from the regex and log.
|
||||
|
||||
Shows:
|
||||
|
||||
1) [1] ^ Failed \S+ for .* from <HOST>( port \d*)?( ssh\d+)?(: ruser .*)?$
|
||||
1.2.3.4 Sun Sep 29 17:15:02 2013
|
||||
|
||||
It should of matched 127.0.0.1. So the first greedy part of the greedy regex
|
||||
matched until the end of the string. The was no "from <HOST>" so the regex
|
||||
engine worked backwards from the end of the string until this was matched.
|
||||
|
||||
The result was that 1.2.3.4 was matched, injected by the user, and the wrong IP
|
||||
was banned.
|
||||
|
||||
The solution here is to make the first .* non-greedy with .*?. Here it matches
|
||||
as little as required and the fail2ban-regex tool shows the output:
|
||||
|
||||
fail2ban-regex -v 'Sep 29 17:15:02 Failed password for user from 127.0.0.1 port 20000 ssh1: ruser from 1.2.3.4' '^ Failed \S+ for .*? from <HOST>( port \d*)?( ssh\d+)?(: ruser .*)?$'
|
||||
|
||||
1) [1] ^ Failed \S+ for .*? from <HOST>( port \d*)?( ssh\d+)?(: ruser .*)?$
|
||||
127.0.0.1 Sun Sep 29 17:15:02 2013
|
||||
|
||||
So the general case here is a log line that contains:
|
||||
|
||||
(fixed_data_1)<HOST>(fixed_data_2)(user_injectable_data)
|
||||
|
||||
Where the regex that matches fixed_data_1 is gready and matches the entire
|
||||
string, before moving backwards and user_injectable_data can match the entire
|
||||
string.
|
||||
|
||||
Another case:
|
||||
|
||||
ref: https://www.debuggex.com/r/CtAbeKMa2sDBEfA2/0
|
||||
|
||||
A webserver logs the following without URL escaping:
|
||||
|
||||
[error] 2865#0: *66647 user "xyz" was not found in "/file", client: 1.2.3.1, server: www.host.com, request: "GET ", client: 3.2.1.1, server: fake.com, request: "GET exploited HTTP/3.3", host: "injected.host", host: "www.myhost.com"
|
||||
|
||||
regex:
|
||||
|
||||
failregex = ^ \[error\] \d+#\d+: \*\d+ user "\S+":? (?:password mismatch|was not found in ".*"), client: <HOST>, server: \S+, request: "\S+ .+ HTTP/\d+\.\d+", host: "\S+"
|
||||
|
||||
The .* matches to the end of the string. Finds that it can't continue to match
|
||||
", client ... so it moves from the back and find that the user injected web URL:
|
||||
|
||||
", client: 3.2.1.1, server: fake.com, request: "GET exploited HTTP/3.3", host: "injected.host
|
||||
|
||||
In this case there is a fixed host: "www.myhost.com" at the end so the solution
|
||||
is to anchor the regex at the end with a $.
|
||||
|
||||
If this wasn't the case then first .* needed to be made so it didn't capture
|
||||
beyond <HOST>.
|
||||
|
||||
4. Application generates two identical log messages with different meanings
|
||||
|
||||
If the application generates the following two messages under different
|
||||
circumstances:
|
||||
|
||||
client <IP>: authentication failed
|
||||
client <USER>: authentication failed
|
||||
|
||||
|
||||
Then it's obvious that a regex of "^client <HOST>: authentication
|
||||
failed$" will still cause problems if the user can trigger the second
|
||||
log message with a <USER> of 123.1.1.1.
|
||||
|
||||
Here there's nothing to do except request/change the application so it logs
|
||||
messages differently.
|
||||
|
||||
|
||||
326
MANIFEST
326
MANIFEST
|
|
@ -5,149 +5,203 @@ TODO
|
|||
THANKS
|
||||
COPYING
|
||||
DEVELOP
|
||||
fail2ban-client
|
||||
fail2ban-server
|
||||
fail2ban-testcases
|
||||
fail2ban-regex
|
||||
FILTERS
|
||||
fail2ban-2to3
|
||||
fail2ban-testcases-all
|
||||
fail2ban-testcases-all-python3
|
||||
bin/fail2ban-client
|
||||
bin/fail2ban-server
|
||||
bin/fail2ban-testcases
|
||||
bin/fail2ban-regex
|
||||
doc/run-rootless.txt
|
||||
fail2ban/client/configreader.py
|
||||
fail2ban/client/configparserinc.py
|
||||
fail2ban/client/jailreader.py
|
||||
fail2ban/client/fail2banreader.py
|
||||
fail2ban/client/jailsreader.py
|
||||
fail2ban/client/beautifier.py
|
||||
fail2ban/client/filterreader.py
|
||||
fail2ban/client/actionreader.py
|
||||
fail2ban/client/__init__.py
|
||||
fail2ban/client/configurator.py
|
||||
fail2ban/client/csocket.py
|
||||
fail2ban/server/asyncserver.py
|
||||
fail2ban/server/database.py
|
||||
fail2ban/server/filter.py
|
||||
fail2ban/server/filterpyinotify.py
|
||||
fail2ban/server/filtergamin.py
|
||||
fail2ban/server/filterpoll.py
|
||||
fail2ban/server/filtersystemd.py
|
||||
fail2ban/server/iso8601.py
|
||||
fail2ban/server/server.py
|
||||
fail2ban/server/actions.py
|
||||
fail2ban/server/faildata.py
|
||||
fail2ban/server/failmanager.py
|
||||
fail2ban/server/datedetector.py
|
||||
fail2ban/server/jailthread.py
|
||||
fail2ban/server/transmitter.py
|
||||
fail2ban/server/action.py
|
||||
fail2ban/server/ticket.py
|
||||
fail2ban/server/jail.py
|
||||
fail2ban/server/jails.py
|
||||
fail2ban/server/__init__.py
|
||||
fail2ban/server/banmanager.py
|
||||
fail2ban/server/datetemplate.py
|
||||
fail2ban/server/mytime.py
|
||||
fail2ban/server/failregex.py
|
||||
fail2ban/server/database.py
|
||||
fail2ban/tests/banmanagertestcase.py
|
||||
fail2ban/tests/failmanagertestcase.py
|
||||
fail2ban/tests/clientreadertestcase.py
|
||||
fail2ban/tests/filtertestcase.py
|
||||
fail2ban/tests/__init__.py
|
||||
fail2ban/tests/dummyjail.py
|
||||
fail2ban/tests/samplestestcase.py
|
||||
fail2ban/tests/datedetectortestcase.py
|
||||
fail2ban/tests/actiontestcase.py
|
||||
fail2ban/tests/servertestcase.py
|
||||
fail2ban/tests/sockettestcase.py
|
||||
fail2ban/tests/utils.py
|
||||
fail2ban/tests/misctestcase.py
|
||||
fail2ban/tests/databasetestcase.py
|
||||
fail2ban/tests/config/jail.conf
|
||||
fail2ban/tests/config/fail2ban.conf
|
||||
fail2ban/tests/config/paths-common.conf
|
||||
fail2ban/tests/config/paths-freebsd.conf
|
||||
fail2ban/tests/config/paths-osx.conf
|
||||
fail2ban/tests/config/paths-debian.conf
|
||||
fail2ban/tests/config/filter.d/simple.conf
|
||||
fail2ban/tests/config/action.d/brokenaction.conf
|
||||
fail2ban/tests/files/config/apache-auth/digest/.htaccess
|
||||
fail2ban/tests/files/config/apache-auth/digest/.htpasswd
|
||||
fail2ban/tests/files/config/apache-auth/digest_time/.htaccess
|
||||
fail2ban/tests/files/config/apache-auth/digest_time/.htpasswd
|
||||
fail2ban/tests/files/config/apache-auth/basic/authz_owner/.htaccess
|
||||
fail2ban/tests/files/config/apache-auth/basic/authz_owner/cant_get_me.html
|
||||
fail2ban/tests/files/config/apache-auth/basic/authz_owner/.htpasswd
|
||||
fail2ban/tests/files/config/apache-auth/basic/file/.htaccess
|
||||
fail2ban/tests/files/config/apache-auth/basic/file/.htpasswd
|
||||
fail2ban/tests/files/config/apache-auth/digest.py
|
||||
fail2ban/tests/files/config/apache-auth/digest_wrongrelm/.htaccess
|
||||
fail2ban/tests/files/config/apache-auth/digest_wrongrelm/.htpasswd
|
||||
fail2ban/tests/files/config/apache-auth/digest_anon/.htaccess
|
||||
fail2ban/tests/files/config/apache-auth/digest_anon/.htpasswd
|
||||
fail2ban/tests/files/config/apache-auth/README
|
||||
fail2ban/tests/files/config/apache-auth/noentry/.htaccess
|
||||
fail2ban/tests/files/database_v1.db
|
||||
fail2ban/tests/files/ignorecommand.py
|
||||
fail2ban/tests/files/filter.d/substition.conf
|
||||
fail2ban/tests/files/filter.d/testcase-common.conf
|
||||
fail2ban/tests/files/filter.d/testcase01.conf
|
||||
fail2ban/tests/files/testcase01.log
|
||||
fail2ban/tests/files/testcase02.log
|
||||
fail2ban/tests/files/testcase03.log
|
||||
fail2ban/tests/files/testcase04.log
|
||||
fail2ban/tests/files/testcase-usedns.log
|
||||
fail2ban/tests/files/testcase-journal.log
|
||||
fail2ban/tests/files/testcase-multiline.log
|
||||
fail2ban/tests/files/logs/bsd/syslog-plain.txt
|
||||
fail2ban/tests/files/logs/bsd/syslog-v.txt
|
||||
fail2ban/tests/files/logs/bsd/syslog-vv.txt
|
||||
fail2ban/tests/files/logs/3proxy
|
||||
fail2ban/tests/files/logs/apache-auth
|
||||
fail2ban/tests/files/logs/apache-badbots
|
||||
fail2ban/tests/files/logs/apache-botscripts
|
||||
fail2ban/tests/files/logs/apache-modsecurity
|
||||
fail2ban/tests/files/logs/apache-nohome
|
||||
fail2ban/tests/files/logs/apache-noscript
|
||||
fail2ban/tests/files/logs/apache-overflows
|
||||
fail2ban/tests/files/logs/assp
|
||||
fail2ban/tests/files/logs/asterisk
|
||||
fail2ban/tests/files/logs/counter-strike
|
||||
fail2ban/tests/files/logs/courier-auth
|
||||
fail2ban/tests/files/logs/courier-smtp
|
||||
fail2ban/tests/files/logs/cyrus-imap
|
||||
fail2ban/tests/files/logs/dovecot
|
||||
fail2ban/tests/files/logs/dropbear
|
||||
fail2ban/tests/files/logs/ejabberd-auth
|
||||
fail2ban/tests/files/logs/exim
|
||||
fail2ban/tests/files/logs/exim-spam
|
||||
fail2ban/tests/files/logs/freeswitch
|
||||
fail2ban/tests/files/logs/groupoffice
|
||||
fail2ban/tests/files/logs/gssftpd
|
||||
fail2ban/tests/files/logs/guacamole
|
||||
fail2ban/tests/files/logs/kerio
|
||||
fail2ban/tests/files/logs/lighttpd-auth
|
||||
fail2ban/tests/files/logs/mysqld-auth
|
||||
fail2ban/tests/files/logs/nsd
|
||||
fail2ban/tests/files/logs/perdition
|
||||
fail2ban/tests/files/logs/php-url-fopen
|
||||
fail2ban/tests/files/logs/postfix-sasl
|
||||
fail2ban/tests/files/logs/named-refused
|
||||
fail2ban/tests/files/logs/nginx-http-auth
|
||||
fail2ban/tests/files/logs/pam-generic
|
||||
fail2ban/tests/files/logs/postfix
|
||||
fail2ban/tests/files/logs/proftpd
|
||||
fail2ban/tests/files/logs/pure-ftpd
|
||||
fail2ban/tests/files/logs/qmail
|
||||
fail2ban/tests/files/logs/recidive
|
||||
fail2ban/tests/files/logs/roundcube-auth
|
||||
fail2ban/tests/files/logs/selinux-ssh
|
||||
fail2ban/tests/files/logs/sendmail-spam
|
||||
fail2ban/tests/files/logs/sieve
|
||||
fail2ban/tests/files/logs/squid
|
||||
fail2ban/tests/files/logs/stunnel
|
||||
fail2ban/tests/files/logs/suhosin
|
||||
fail2ban/tests/files/logs/sogo-auth
|
||||
fail2ban/tests/files/logs/solid-pop3d
|
||||
fail2ban/tests/files/logs/sshd
|
||||
fail2ban/tests/files/logs/sshd-ddos
|
||||
fail2ban/tests/files/logs/vsftpd
|
||||
fail2ban/tests/files/logs/webmin-auth
|
||||
fail2ban/tests/files/logs/wuftpd
|
||||
fail2ban/tests/files/logs/uwimap-auth
|
||||
fail2ban/tests/files/logs/xinetd-fail
|
||||
fail2ban/tests/config/jail.conf
|
||||
fail2ban/tests/config/fail2ban.conf
|
||||
fail2ban/tests/config/filter.d/simple.conf
|
||||
fail2ban/tests/config/action.d/brokenaction.conf
|
||||
setup.py
|
||||
setup.cfg
|
||||
fail2ban/__init__.py
|
||||
fail2ban/exceptions.py
|
||||
fail2ban/helpers.py
|
||||
fail2ban/version.py
|
||||
fail2ban/protocol.py
|
||||
setup.py
|
||||
setup.cfg
|
||||
kill-server
|
||||
client/configreader.py
|
||||
client/configparserinc.py
|
||||
client/jailreader.py
|
||||
client/fail2banreader.py
|
||||
client/jailsreader.py
|
||||
client/beautifier.py
|
||||
client/filterreader.py
|
||||
client/actionreader.py
|
||||
client/__init__.py
|
||||
client/configurator.py
|
||||
client/csocket.py
|
||||
server/asyncserver.py
|
||||
server/filter.py
|
||||
server/filterpyinotify.py
|
||||
server/filtergamin.py
|
||||
server/filterpoll.py
|
||||
server/iso8601.py
|
||||
server/server.py
|
||||
server/actions.py
|
||||
server/faildata.py
|
||||
server/failmanager.py
|
||||
server/datedetector.py
|
||||
server/jailthread.py
|
||||
server/transmitter.py
|
||||
server/action.py
|
||||
server/ticket.py
|
||||
server/jail.py
|
||||
server/jails.py
|
||||
server/__init__.py
|
||||
server/banmanager.py
|
||||
server/datetemplate.py
|
||||
server/mytime.py
|
||||
server/failregex.py
|
||||
testcases/actionstestcase.py
|
||||
testcases/dummyjail.py
|
||||
testcases/files/testcase-usedns.log
|
||||
testcases/files/logs/bsd/syslog-plain.txt
|
||||
testcases/files/logs/bsd/syslog-v.txt
|
||||
testcases/files/logs/bsd/syslog-vv.txt
|
||||
testcases/files/logs/apache-overflows
|
||||
testcases/files/logs/assp
|
||||
testcases/files/logs/asterisk
|
||||
testcases/files/logs/dovecot
|
||||
testcases/files/logs/exim
|
||||
testcases/files/logs/suhosin
|
||||
testcases/files/logs/mysqld-auth
|
||||
testcases/files/logs/named-refused
|
||||
testcases/files/logs/nginx-http-auth
|
||||
testcases/files/logs/pam-generic
|
||||
testcases/files/logs/postfix
|
||||
testcases/files/logs/proftpd
|
||||
testcases/files/logs/pure-ftpd
|
||||
testcases/files/logs/roundcube-auth
|
||||
testcases/files/logs/postfix-sasl
|
||||
testcases/files/logs/sogo-auth
|
||||
testcases/files/logs/sshd
|
||||
testcases/files/logs/sshd-ddos
|
||||
testcases/files/logs/vsftpd
|
||||
testcases/files/logs/webmin-auth
|
||||
testcases/files/logs/wuftpd
|
||||
testcases/files/logs/3proxy
|
||||
testcases/files/logs/apache-auth
|
||||
testcases/files/logs/apache-badbots
|
||||
testcases/files/logs/apache-nohome
|
||||
testcases/files/logs/apache-noscript
|
||||
testcases/files/logs/courierlogin
|
||||
testcases/files/logs/couriersmtp
|
||||
testcases/files/logs/cyrus-imap
|
||||
testcases/files/logs/dropbear
|
||||
testcases/files/logs/exim-spam
|
||||
testcases/files/logs/gssftpd
|
||||
testcases/files/logs/lighttpd-auth
|
||||
testcases/files/logs/mysqld-auth
|
||||
testcases/files/logs/perdition
|
||||
testcases/files/logs/php-url-fopen
|
||||
testcases/files/logs/qmail
|
||||
testcases/files/logs/recidive
|
||||
testcases/files/logs/sieve
|
||||
testcases/files/logs/selinux-ssh
|
||||
testcases/files/logs/suhosin
|
||||
testcases/files/logs/uwimap-auth
|
||||
testcases/files/logs/wuftpd
|
||||
testcases/files/logs/xinetd-fail
|
||||
testcases/files/config/apache-auth/digest/.htaccess
|
||||
testcases/files/config/apache-auth/digest/.htpasswd
|
||||
testcases/files/config/apache-auth/digest_time/.htaccess
|
||||
testcases/files/config/apache-auth/digest_time/.htpasswd
|
||||
testcases/files/config/apache-auth/basic/authz_owner/.htaccess
|
||||
testcases/files/config/apache-auth/basic/authz_owner/cant_get_me.html
|
||||
testcases/files/config/apache-auth/basic/authz_owner/.htpasswd
|
||||
testcases/files/config/apache-auth/basic/file/.htaccess
|
||||
testcases/files/config/apache-auth/basic/file/.htpasswd
|
||||
testcases/files/config/apache-auth/digest.py
|
||||
testcases/files/config/apache-auth/digest_wrongrelm/.htaccess
|
||||
testcases/files/config/apache-auth/digest_wrongrelm/.htpasswd
|
||||
testcases/files/config/apache-auth/digest_anon/.htaccess
|
||||
testcases/files/config/apache-auth/digest_anon/.htpasswd
|
||||
testcases/files/config/apache-auth/README
|
||||
testcases/files/config/apache-auth/noentry/.htaccess
|
||||
testcases/samplestestcase.py
|
||||
testcases/banmanagertestcase.py
|
||||
testcases/failmanagertestcase.py
|
||||
testcases/clientreadertestcase.py
|
||||
testcases/filtertestcase.py
|
||||
testcases/__init__.py
|
||||
testcases/datedetectortestcase.py
|
||||
testcases/actiontestcase.py
|
||||
testcases/servertestcase.py
|
||||
testcases/sockettestcase.py
|
||||
testcases/files/testcase01.log
|
||||
testcases/files/testcase02.log
|
||||
testcases/files/testcase03.log
|
||||
testcases/files/testcase04.log
|
||||
testcases/misctestcase.py
|
||||
testcases/utils.py
|
||||
common/__init__.py
|
||||
common/exceptions.py
|
||||
common/helpers.py
|
||||
common/version.py
|
||||
common/protocol.py
|
||||
config/jail.conf
|
||||
config/fail2ban.conf
|
||||
config/filter.d/common.conf
|
||||
config/filter.d/apache-auth.conf
|
||||
config/filter.d/apache-badbots.conf
|
||||
config/filter.d/apache-botsearch.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/nginx-http-auth.conf
|
||||
config/filter.d/courierlogin.conf
|
||||
config/filter.d/couriersmtp.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/ejabberd-auth.conf
|
||||
config/filter.d/exim.conf
|
||||
config/filter.d/freeswitch.conf
|
||||
config/filter.d/gssftpd.conf
|
||||
config/filter.d/kerio.conf
|
||||
config/filter.d/horde.conf
|
||||
config/filter.d/suhosin.conf
|
||||
config/filter.d/named-refused.conf
|
||||
config/filter.d/nsd.conf
|
||||
config/filter.d/openwebmail.conf
|
||||
config/filter.d/pam-generic.conf
|
||||
config/filter.d/php-url-fopen.conf
|
||||
config/filter.d/postfix-sasl.conf
|
||||
config/filter.d/pam-generic.conf
|
||||
config/filter.d/php-url-fopen.conf
|
||||
config/filter.d/postfix-sasl.conf
|
||||
config/filter.d/postfix.conf
|
||||
config/filter.d/proftpd.conf
|
||||
config/filter.d/pure-ftpd.conf
|
||||
|
|
@ -155,9 +209,14 @@ config/filter.d/qmail.conf
|
|||
config/filter.d/pam-generic.conf
|
||||
config/filter.d/php-url-fopen.conf
|
||||
config/filter.d/postfix-sasl.conf
|
||||
config/filter.d/sendmail-auth.conf
|
||||
config/filter.d/sendmail-reject.conf
|
||||
config/filter.d/sieve.conf
|
||||
config/filter.d/solid-pop3d.conf
|
||||
config/filter.d/squid.conf
|
||||
config/filter.d/sshd.conf
|
||||
config/filter.d/sshd-ddos.conf
|
||||
config/filter.d/stunnel.conf
|
||||
config/filter.d/vsftpd.conf
|
||||
config/filter.d/webmin-auth.conf
|
||||
config/filter.d/wuftpd.conf
|
||||
|
|
@ -177,15 +236,25 @@ config/filter.d/3proxy.conf
|
|||
config/filter.d/apache-common.conf
|
||||
config/filter.d/exim-common.conf
|
||||
config/filter.d/exim-spam.conf
|
||||
config/filter.d/freeswitch.conf
|
||||
config/filter.d/groupoffice.conf
|
||||
config/filter.d/perdition.conf
|
||||
config/filter.d/uwimap-auth.conf
|
||||
config/filter.d/courier-auth.conf
|
||||
config/filter.d/courier-smtp.conf
|
||||
config/filter.d/ejabberd-auth.conf
|
||||
config/filter.d/guacamole.conf
|
||||
config/filter.d/sendmail-spam.conf
|
||||
config/action.d/apf.conf
|
||||
config/action.d/blocklist_de.conf
|
||||
config/action.d/osx-afctl.conf
|
||||
config/action.d/osx-ipfw.conf
|
||||
config/action.d/sendmail-common.conf
|
||||
config/action.d/badips.conf
|
||||
config/action.d/bsd-ipfw.conf
|
||||
config/action.d/dummy.conf
|
||||
config/action.d/firewall-cmd-direct-new.conf
|
||||
config/action.d/firewallcmd-new.conf
|
||||
config/action.d/firewallcmd-ipset.conf
|
||||
config/action.d/iptables-ipset-proto6-allports.conf
|
||||
config/action.d/iptables-blocktype.conf
|
||||
config/action.d/iptables-ipset-proto4.conf
|
||||
|
|
@ -210,9 +279,12 @@ config/action.d/mynetwatchman.conf
|
|||
config/action.d/pf.conf
|
||||
config/action.d/sendmail.conf
|
||||
config/action.d/sendmail-buffered.conf
|
||||
config/action.d/sendmail-whois-ipmatches.conf
|
||||
config/action.d/sendmail-whois.conf
|
||||
config/action.d/sendmail-whois-lines.conf
|
||||
config/action.d/shorewall.conf
|
||||
config/action.d/xarf-login-attack.conf
|
||||
config/action.d/ufw.conf
|
||||
config/fail2ban.conf
|
||||
doc/run-rootless.txt
|
||||
man/fail2ban-client.1
|
||||
|
|
|
|||
21
README.md
21
README.md
|
|
@ -2,7 +2,7 @@
|
|||
/ _|__ _(_) |_ ) |__ __ _ _ _
|
||||
| _/ _` | | |/ /| '_ \/ _` | ' \
|
||||
|_| \__,_|_|_/___|_.__/\__,_|_||_|
|
||||
v0.8.11 2013/11/13
|
||||
v0.9.0 2014/03/14
|
||||
|
||||
## Fail2Ban: ban hosts that cause multiple authentication errors
|
||||
|
||||
|
|
@ -11,6 +11,11 @@ password failures. It updates firewall rules to reject the IP address. These
|
|||
rules can be defined by the user. Fail2Ban can read multiple log files such as
|
||||
sshd or Apache web server ones.
|
||||
|
||||
Fail2Ban is able to reduce the rate of incorrect authentications attempts
|
||||
however it cannot eliminate the risk that weak authentication presents.
|
||||
Configure services to use only two factor or public/private authentication
|
||||
mechanisms if you really want to protect services.
|
||||
|
||||
This README is a quick introduction to Fail2ban. More documentation, FAQ, HOWTOs
|
||||
are available in fail2ban(1) manpage and on the website http://www.fail2ban.org
|
||||
|
||||
|
|
@ -21,21 +26,22 @@ Installation:
|
|||
this case, you should use it instead.**
|
||||
|
||||
Required:
|
||||
- [Python >= 2.4](http://www.python.org)
|
||||
- [Python2 >= 2.6 or Python >= 3.2](http://www.python.org) or [PyPy](http://pypy.org)
|
||||
|
||||
Optional:
|
||||
- [pyinotify >= 0.8.3](https://github.com/seb-m/pyinotify)
|
||||
- Linux >= 2.6.13
|
||||
- [gamin >= 0.0.21](http://www.gnome.org/~veillard/gamin)
|
||||
- [systemd >= 204](http://www.freedesktop.org/wiki/Software/systemd)
|
||||
|
||||
To install, just do:
|
||||
|
||||
tar xvfj fail2ban-0.8.11.tar.bz2
|
||||
cd fail2ban-0.8.11
|
||||
tar xvfj fail2ban-0.9.0.tar.bz2
|
||||
cd fail2ban-0.9.0
|
||||
python setup.py install
|
||||
|
||||
This will install Fail2Ban into /usr/share/fail2ban. The executable scripts are
|
||||
placed into /usr/bin, and configuration under /etc/fail2ban.
|
||||
This will install Fail2Ban into the python library directory. The executable
|
||||
scripts are placed into /usr/bin, and configuration under /etc/fail2ban.
|
||||
|
||||
Fail2Ban should be correctly installed now. Just type:
|
||||
|
||||
|
|
@ -50,8 +56,7 @@ Configuration:
|
|||
You can configure Fail2Ban using the files in /etc/fail2ban. It is possible to
|
||||
configure the server using commands sent to it by fail2ban-client. The
|
||||
available commands are described in the fail2ban-client(1) manpage. Also see
|
||||
fail2ban(1) manpage for further references and find even more documentation on
|
||||
the website: http://www.fail2ban.org
|
||||
fail2ban(1) and jail.conf(5) manpages for further references.
|
||||
|
||||
Code status:
|
||||
------------
|
||||
|
|
|
|||
31
THANKS
31
THANKS
|
|
@ -6,71 +6,102 @@ 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
|
||||
be added
|
||||
|
||||
Adam Tkac
|
||||
Adrien Clerc
|
||||
ache
|
||||
ag4ve (Shawn)
|
||||
Alasdair D. Campbell
|
||||
Amir Caspi
|
||||
Amy
|
||||
Andrey G. Grozin
|
||||
Andy Fragen
|
||||
Arturo 'Buanzo' Busleiman
|
||||
Axel Thimm
|
||||
Bas van den Dikkenberg
|
||||
Beau Raines
|
||||
Bill Heaton
|
||||
Carlos Alberto Lopez Perez
|
||||
cepheid666
|
||||
Christian Rauch
|
||||
Christophe Carles
|
||||
Christoph Haas
|
||||
Christos Psonis
|
||||
Cyril Jaquier
|
||||
Daniel B. Cid
|
||||
Daniel B.
|
||||
Daniel Black
|
||||
David Nutter
|
||||
Derek Atkins
|
||||
Eric Gerbier
|
||||
Enrico Labedzki
|
||||
ftoppi
|
||||
François Boulogne
|
||||
Frédéric
|
||||
Georgiy Mernov
|
||||
Guilhem Lettron
|
||||
Guillaume Delvit
|
||||
Hank Leininger
|
||||
Hanno 'Rince' Wagner
|
||||
Helmut Grohne
|
||||
Iain Lea
|
||||
Ivo Truxa
|
||||
John Thoe
|
||||
Jacques Lav!gnotte
|
||||
Ioan Indreias
|
||||
Jonathan Kamens
|
||||
Jonathan Lanning
|
||||
Jonathan Underwood
|
||||
Joël Bertrand
|
||||
JP Espinosa
|
||||
jserrachinha
|
||||
Justin Shore
|
||||
Kévin Drapel
|
||||
kjohnsonecl
|
||||
kojiro
|
||||
Lars Kneschke
|
||||
Lee Clemens
|
||||
Manuel Arostegui Ramirez
|
||||
Marcel Dopita
|
||||
Mark Edgington
|
||||
Mark McKinstry
|
||||
Mark White
|
||||
Markus Hoffmann
|
||||
Marvin Rouge
|
||||
mEDI
|
||||
Мернов Георгий
|
||||
Merijn Schering
|
||||
Michael C. Haller
|
||||
Michael Hanselmann
|
||||
Mika (mkl)
|
||||
Nick Munger
|
||||
onorua
|
||||
Noel Butler
|
||||
Patrick Börjesson
|
||||
Raphaël Marichez
|
||||
RealRancor
|
||||
René Berber
|
||||
Robert Edeker
|
||||
Rolf Fokkens
|
||||
Roman Gelfand
|
||||
Russell Odom
|
||||
Sebastian Arcus
|
||||
Sireyessire
|
||||
silviogarbes
|
||||
Stefan Tatschner
|
||||
Stephen Gildea
|
||||
Steven Hiscocks
|
||||
TESTOVIK
|
||||
Tom Pike
|
||||
Tomas Pihl
|
||||
Tony Lawrence
|
||||
Tomasz Ciolek
|
||||
Tyler
|
||||
Vaclav Misek
|
||||
Vincent Deffontaines
|
||||
Yaroslav Halchenko
|
||||
Winston Smith
|
||||
ykimon
|
||||
Yehuda Katz
|
||||
Zbigniew Jędrzejewski-Szmek
|
||||
zugeschmiert
|
||||
Zurd
|
||||
|
|
|
|||
27
TODO
27
TODO
|
|
@ -13,20 +13,6 @@ Legend:
|
|||
# partially done
|
||||
* done
|
||||
|
||||
- more detailed explaination in DEVELOP for new developers (eg. howto build this HEX numbers in ChangeLog)
|
||||
|
||||
- Run tests though all filters/examples files - (see sshd example file) as unit
|
||||
test
|
||||
|
||||
* Removed relative imports
|
||||
|
||||
* Cleanup fail2ban-client and fail2ban-server. Move code to server/ and client/
|
||||
|
||||
- Add timeout to external commands (signal alarm, watchdog thread, etc)
|
||||
|
||||
- Uniformize filters and actions name. Use the software name (openssh, postfix,
|
||||
proftp) and possible qualifier (e.g. auth) after a '-'
|
||||
|
||||
- Added <USER> tag for failregex. Add features using this information. Maybe add
|
||||
more tags
|
||||
|
||||
|
|
@ -37,23 +23,10 @@ Legend:
|
|||
- Auto-enable function (search for log files), check modification date to see if
|
||||
service is still in use
|
||||
|
||||
- Improve parsing of the action parameters in jailreader.py
|
||||
|
||||
- Better handling of the protocol in transmitter.py
|
||||
|
||||
- Add gettext support (I18N)
|
||||
|
||||
- Multiline log reading
|
||||
|
||||
- Improve execution of action. Why does subprocess.call deadlock with
|
||||
multi-jails?
|
||||
|
||||
# see Feature Request Tracking System at SourceForge.net
|
||||
|
||||
# improve documentation and website for user
|
||||
|
||||
# better return values in function
|
||||
|
||||
# refactoring in server.py, actions.py, filter.py
|
||||
|
||||
* New backend: pyinotify
|
||||
|
|
|
|||
|
|
@ -25,19 +25,11 @@ __license__ = "GPL"
|
|||
import sys, string, os, pickle, re, logging, signal
|
||||
import getopt, time, shlex, socket
|
||||
|
||||
# Inserts our own modules path first in the list
|
||||
# fix for bug #343821
|
||||
try:
|
||||
from common.version import version
|
||||
except ImportError, e:
|
||||
sys.path.insert(1, "/usr/share/fail2ban")
|
||||
from common.version import version
|
||||
|
||||
# Now we can import the rest of modules
|
||||
from common.protocol import printFormatted
|
||||
from client.csocket import CSocket
|
||||
from client.configurator import Configurator
|
||||
from client.beautifier import Beautifier
|
||||
from fail2ban.version import version
|
||||
from fail2ban.protocol import printFormatted
|
||||
from fail2ban.client.csocket import CSocket
|
||||
from fail2ban.client.configurator import Configurator
|
||||
from fail2ban.client.beautifier import Beautifier
|
||||
|
||||
# Gets the instance of the logger.
|
||||
logSys = logging.getLogger("fail2ban.client")
|
||||
|
|
@ -110,7 +102,7 @@ class Fail2banClient:
|
|||
def __sigTERMhandler(self, signum, frame):
|
||||
# Print a new line because we probably come from wait
|
||||
print
|
||||
logSys.warn("Caught signal %d. Exiting" % signum)
|
||||
logSys.warning("Caught signal %d. Exiting" % signum)
|
||||
sys.exit(-1)
|
||||
|
||||
def __getCmdLineOptions(self, optList):
|
||||
|
|
@ -145,6 +137,7 @@ class Fail2banClient:
|
|||
|
||||
def __processCmd(self, cmd, showRet = True):
|
||||
beautifier = Beautifier()
|
||||
streamRet = True
|
||||
for c in cmd:
|
||||
beautifier.setInputCmd(c)
|
||||
try:
|
||||
|
|
@ -158,7 +151,7 @@ class Fail2banClient:
|
|||
logSys.error("NOK: " + `ret[1].args`)
|
||||
if showRet:
|
||||
print beautifier.beautifyError(ret[1])
|
||||
return False
|
||||
streamRet = False
|
||||
except socket.error:
|
||||
if showRet:
|
||||
logSys.error("Unable to contact server. Is it running?")
|
||||
|
|
@ -167,7 +160,7 @@ class Fail2banClient:
|
|||
if showRet:
|
||||
logSys.error(e)
|
||||
return False
|
||||
return True
|
||||
return streamRet
|
||||
|
||||
##
|
||||
# Process a command line.
|
||||
|
|
@ -332,7 +325,7 @@ class Fail2banClient:
|
|||
if verbose <= 0:
|
||||
logSys.setLevel(logging.ERROR)
|
||||
elif verbose == 1:
|
||||
logSys.setLevel(logging.WARN)
|
||||
logSys.setLevel(logging.WARNING)
|
||||
elif verbose == 2:
|
||||
logSys.setLevel(logging.INFO)
|
||||
else:
|
||||
|
|
@ -29,24 +29,23 @@ __author__ = "Cyril Jaquier, Yaroslav Halchenko"
|
|||
__copyright__ = "Copyright (c) 2004-2008 Cyril Jaquier, 2012-2013 Yaroslav Halchenko"
|
||||
__license__ = "GPL"
|
||||
|
||||
import getopt, sys, time, logging, os, urllib
|
||||
|
||||
# Inserts our own modules path first in the list
|
||||
# fix for bug #343821
|
||||
try:
|
||||
from common.version import version
|
||||
except ImportError, e:
|
||||
sys.path.insert(1, "/usr/share/fail2ban")
|
||||
from common.version import version
|
||||
|
||||
import getopt, sys, time, logging, os, locale, shlex, urllib
|
||||
from optparse import OptionParser, Option
|
||||
|
||||
from client.configparserinc import SafeConfigParserWithIncludes
|
||||
from ConfigParser import NoOptionError, NoSectionError, MissingSectionHeaderError
|
||||
from server.filter import Filter
|
||||
from server.failregex import RegexException
|
||||
|
||||
from testcases.utils import FormatterWithTraceBack
|
||||
try:
|
||||
from systemd import journal
|
||||
from fail2ban.server.filtersystemd import FilterSystemd
|
||||
except ImportError:
|
||||
journal = None
|
||||
|
||||
from fail2ban.version import version
|
||||
from fail2ban.client.filterreader import FilterReader
|
||||
from fail2ban.server.filter import Filter
|
||||
from fail2ban.server.failregex import RegexException
|
||||
|
||||
from fail2ban.tests.utils import FormatterWithTraceBack
|
||||
# Gets the instance of the logger.
|
||||
logSys = logging.getLogger("fail2ban")
|
||||
|
||||
|
|
@ -72,6 +71,24 @@ def pprint_list(l, header=None):
|
|||
s = ''
|
||||
print s + "| " + "\n| ".join(l) + '\n`-'
|
||||
|
||||
def file_lines_gen(hdlr):
|
||||
for line in hdlr:
|
||||
try:
|
||||
line = line.decode(fail2banRegex.encoding, 'strict')
|
||||
except UnicodeDecodeError:
|
||||
if sys.version_info >= (3,): # Python 3 must be decoded
|
||||
line = line.decode(fail2banRegex.encoding, 'ignore')
|
||||
yield line
|
||||
|
||||
def journal_lines_gen(myjournal):
|
||||
while True:
|
||||
try:
|
||||
entry = myjournal.get_next()
|
||||
except OSError:
|
||||
continue
|
||||
if not entry:
|
||||
break
|
||||
yield FilterSystemd.formatJournalEntry(entry)
|
||||
|
||||
def get_opt_parser():
|
||||
# use module docstring for help output
|
||||
|
|
@ -81,6 +98,7 @@ def get_opt_parser():
|
|||
LOG:
|
||||
string a string representing a log line
|
||||
filename path to a log file (/var/log/auth.log)
|
||||
"systemd-journal" search systemd journal (systemd-python required)
|
||||
|
||||
REGEX:
|
||||
string a string representing a 'failregex'
|
||||
|
|
@ -102,19 +120,32 @@ Report bugs to https://github.com/fail2ban/fail2ban/issues
|
|||
version="%prog " + version)
|
||||
|
||||
p.add_options([
|
||||
Option("-d", "--datepattern",
|
||||
help="set custom pattern used to match date/times"),
|
||||
Option("-e", "--encoding",
|
||||
help="File encoding. Default: system locale"),
|
||||
Option("-L", "--maxlines", type=int, default=0,
|
||||
help="maxlines for multi-line regex"),
|
||||
Option("-m", "--journalmatch",
|
||||
help="journalctl style matches overriding filter file. "
|
||||
"\"systemd-journal\" only"),
|
||||
Option('-l', "--log-level", type="choice",
|
||||
dest="log_level",
|
||||
choices=('heavydebug', 'debug', 'info', 'warning', 'error', 'fatal'),
|
||||
choices=('heavydebug', 'debug', 'info', 'notice', 'warning', 'error', 'critical'),
|
||||
default=None,
|
||||
help="Log level for the Fail2Ban logger to use"),
|
||||
Option("-v", "--verbose", action='store_true',
|
||||
help="Be verbose in output"),
|
||||
Option("-D", "--debuggex", action='store_true',
|
||||
help="Produce debuggex.com urls for debugging there"),
|
||||
Option("--print-no-missed", action='store_true',
|
||||
help="Do not print any missed lines"),
|
||||
Option("--print-no-ignored", action='store_true',
|
||||
help="Do not print any ignored lines"),
|
||||
Option("--print-all-missed", action='store_true',
|
||||
help="Either to print all missed lines"),
|
||||
help="Print all missed lines, no matter how many"),
|
||||
Option("--print-all-ignored", action='store_true',
|
||||
help="Either to print all ignored lines"),
|
||||
help="Print all ignored lines, no matter how many"),
|
||||
Option("-t", "--log-traceback", action='store_true',
|
||||
help="Enrich log-messages with compressed tracebacks"),
|
||||
Option("--full-traceback", action='store_true',
|
||||
|
|
@ -156,22 +187,16 @@ class LineStats(object):
|
|||
"""
|
||||
def __init__(self):
|
||||
self.tested = self.matched = 0
|
||||
self.missed = 0
|
||||
self.missed_lines = []
|
||||
self.missed_lines_timeextracted = []
|
||||
self.ignored = 0
|
||||
self.ignored_lines = []
|
||||
self.ignored_lines_timeextracted = []
|
||||
|
||||
def __str__(self):
|
||||
return "%(tested)d lines, %(ignored)d ignored, %(matched)d matched, %(missed)d missed" % self
|
||||
|
||||
@property
|
||||
def ignored(self):
|
||||
return len(self.ignored_lines)
|
||||
|
||||
@property
|
||||
def missed(self):
|
||||
return self.tested - (self.ignored + self.matched)
|
||||
|
||||
# just for convenient str
|
||||
def __getitem__(self, key):
|
||||
return getattr(self, key)
|
||||
|
|
@ -179,41 +204,88 @@ class LineStats(object):
|
|||
|
||||
class Fail2banRegex(object):
|
||||
|
||||
CONFIG_DEFAULTS = {'configpath' : "/etc/fail2ban/"}
|
||||
|
||||
def __init__(self, opts):
|
||||
self._verbose = opts.verbose
|
||||
self._debuggex = opts.debuggex
|
||||
self._maxlines = 20
|
||||
self._print_no_missed = opts.print_no_missed
|
||||
self._print_no_ignored = opts.print_no_ignored
|
||||
self._print_all_missed = opts.print_all_missed
|
||||
self._print_all_ignored = opts.print_all_ignored
|
||||
self._maxlines_set = False # so we allow to override maxlines in cmdline
|
||||
self._datepattern_set = False
|
||||
self._journalmatch = None
|
||||
|
||||
self._filter = Filter(None)
|
||||
self._ignoreregex = list()
|
||||
self._failregex = list()
|
||||
self._line_stats = LineStats()
|
||||
|
||||
if opts.maxlines:
|
||||
self.setMaxLines(opts.maxlines)
|
||||
if opts.journalmatch is not None:
|
||||
self.setJournalMatch(opts.journalmatch.split())
|
||||
if opts.datepattern:
|
||||
self.setDatePattern(opts.datepattern)
|
||||
if opts.encoding:
|
||||
self.encoding = opts.encoding
|
||||
else:
|
||||
self.encoding = locale.getpreferredencoding()
|
||||
|
||||
|
||||
|
||||
def setDatePattern(self, pattern):
|
||||
if not self._datepattern_set:
|
||||
self._filter.setDatePattern(pattern)
|
||||
self._datepattern_set = True
|
||||
if pattern is not None:
|
||||
print "Use datepattern : %s" % (
|
||||
self._filter.getDatePattern()[1], )
|
||||
|
||||
def setMaxLines(self, v):
|
||||
if not self._maxlines_set:
|
||||
self._filter.setMaxLines(int(v))
|
||||
self._maxlines_set = True
|
||||
print "Use maxlines : %d" % self._filter.getMaxLines()
|
||||
|
||||
def setJournalMatch(self, v):
|
||||
if self._journalmatch is None:
|
||||
self._journalmatch = v
|
||||
|
||||
def readRegex(self, value, regextype):
|
||||
assert(regextype in ('fail', 'ignore'))
|
||||
regex = regextype + 'regex'
|
||||
if os.path.isfile(value):
|
||||
reader = SafeConfigParserWithIncludes(defaults=self.CONFIG_DEFAULTS)
|
||||
try:
|
||||
reader.read(value)
|
||||
print "Use %11s file : %s" % (regex, value)
|
||||
# TODO: reuse functionality in client
|
||||
print "Use %11s file : %s" % (regex, value)
|
||||
reader = FilterReader(value, 'fail2ban-regex-jail', {})
|
||||
reader.setBaseDir(None)
|
||||
|
||||
if reader.readexplicit():
|
||||
reader.getOptions(None)
|
||||
readercommands = reader.convert()
|
||||
regex_values = [
|
||||
RegexStat(m)
|
||||
for m in reader.get("Definition", regex).split('\n')
|
||||
if m != ""]
|
||||
except NoSectionError:
|
||||
print "No [Definition] section in %s" % value
|
||||
return False
|
||||
except NoOptionError:
|
||||
print "No %s option in %s" % (regex, value)
|
||||
return False
|
||||
except MissingSectionHeaderError:
|
||||
print "No section headers in %s" % value
|
||||
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(shlex.split(journalmatch))
|
||||
elif command[2] == 'datepattern':
|
||||
datepattern = command[3]
|
||||
self.setDatePattern(datepattern)
|
||||
else:
|
||||
print "ERROR: failed to read %s" % value
|
||||
return False
|
||||
else:
|
||||
print "Use %11s line : %s" % (regex, shortstr(value))
|
||||
|
|
@ -229,7 +301,7 @@ class Fail2banRegex(object):
|
|||
def testIgnoreRegex(self, line):
|
||||
found = False
|
||||
try:
|
||||
ret = self._filter.ignoreLine(line)
|
||||
ret = self._filter.ignoreLine([(line, "", "")])
|
||||
if ret is not None:
|
||||
found = True
|
||||
regex = self._ignoreregex[ret].inc()
|
||||
|
|
@ -238,9 +310,11 @@ class Fail2banRegex(object):
|
|||
return False
|
||||
return found
|
||||
|
||||
def testRegex(self, line):
|
||||
def testRegex(self, line, date=None):
|
||||
orgLineBuffer = self._filter._Filter__lineBuffer
|
||||
fullBuffer = len(orgLineBuffer) >= self._filter.getMaxLines()
|
||||
try:
|
||||
line, ret = self._filter.processLine(line, checkAllRegex=True)
|
||||
line, ret = self._filter.processLine(line, date, checkAllRegex=True)
|
||||
for match in ret:
|
||||
# Append True/False flag depending if line was matched by
|
||||
# more than one regex
|
||||
|
|
@ -254,41 +328,63 @@ class Fail2banRegex(object):
|
|||
except IndexError:
|
||||
print "Sorry, but no <HOST> found in regex"
|
||||
return False
|
||||
for bufLine in orgLineBuffer[int(fullBuffer):]:
|
||||
if bufLine not in self._filter._Filter__lineBuffer:
|
||||
try:
|
||||
self._line_stats.missed_lines.pop(
|
||||
self._line_stats.missed_lines.index("".join(bufLine)))
|
||||
self._line_stats.missed_lines_timeextracted.pop(
|
||||
self._line_stats.missed_lines_timeextracted.index(
|
||||
"".join(bufLine[::2])))
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
self._line_stats.matched += 1
|
||||
return line, ret
|
||||
|
||||
|
||||
def process(self, test_lines):
|
||||
|
||||
for line_no, line in enumerate(test_lines):
|
||||
if line.startswith('#') or not line.strip():
|
||||
# skip comment and empty lines
|
||||
continue
|
||||
is_ignored = fail2banRegex.testIgnoreRegex(line)
|
||||
line_datetimestripped, ret = fail2banRegex.testRegex(line)
|
||||
if isinstance(line, tuple):
|
||||
line_datetimestripped, ret = fail2banRegex.testRegex(
|
||||
line[0], line[1])
|
||||
line = "".join(line[0])
|
||||
else:
|
||||
line = line.rstrip('\r\n')
|
||||
if line.startswith('#') or not line:
|
||||
# skip comment and empty lines
|
||||
continue
|
||||
line_datetimestripped, ret = fail2banRegex.testRegex(line)
|
||||
is_ignored = fail2banRegex.testIgnoreRegex(line_datetimestripped)
|
||||
|
||||
if is_ignored:
|
||||
self._line_stats.ignored_lines.append(line)
|
||||
self._line_stats.ignored_lines_timeextracted.append(line_datetimestripped)
|
||||
self._line_stats.ignored += 1
|
||||
if not self._print_no_ignored and (self._print_all_ignored or self._line_stats.ignored <= self._maxlines + 1):
|
||||
self._line_stats.ignored_lines.append(line)
|
||||
self._line_stats.ignored_lines_timeextracted.append(line_datetimestripped)
|
||||
|
||||
if len(ret) > 0:
|
||||
assert(not is_ignored)
|
||||
self._line_stats.matched += 1
|
||||
else:
|
||||
if not is_ignored:
|
||||
self._line_stats.missed_lines.append(line)
|
||||
self._line_stats.missed_lines_timeextracted.append(line_datetimestripped)
|
||||
self._line_stats.missed += 1
|
||||
if not self._print_no_missed and (self._print_all_missed or self._line_stats.missed <= self._maxlines + 1):
|
||||
self._line_stats.missed_lines.append(line)
|
||||
self._line_stats.missed_lines_timeextracted.append(line_datetimestripped)
|
||||
self._line_stats.tested += 1
|
||||
|
||||
if line_no % 10 == 0:
|
||||
if line_no % 10 == 0 and self._filter.dateDetector is not None:
|
||||
self._filter.dateDetector.sortTemplate()
|
||||
|
||||
|
||||
|
||||
def printLines(self, ltype):
|
||||
lstats = self._line_stats
|
||||
assert(len(lstats.missed_lines) == lstats.tested - (lstats.matched + lstats.ignored))
|
||||
assert(self._line_stats.missed == lstats.tested - (lstats.matched + lstats.ignored))
|
||||
lines = lstats[ltype]
|
||||
l = lstats[ltype + '_lines']
|
||||
if len(l):
|
||||
if lines:
|
||||
header = "%s line(s):" % (ltype.capitalize(),)
|
||||
if self._debuggex:
|
||||
if ltype == 'missed':
|
||||
|
|
@ -296,21 +392,20 @@ class Fail2banRegex(object):
|
|||
else:
|
||||
regexlist = self._ignoreregex
|
||||
l = lstats[ltype + '_lines_timeextracted']
|
||||
lines = len(l)*len(regexlist)
|
||||
if lines < 20 or getattr(self, '_print_all_' + ltype):
|
||||
if lines < self._maxlines or getattr(self, '_print_all_' + ltype):
|
||||
ans = [[]]
|
||||
for arg in [l, regexlist]:
|
||||
ans = [ x + [y] for x in ans for y in arg ]
|
||||
b = map(lambda a: a[0] + ' | ' + a[1].getFailRegex() + ' | ' + debuggexURL(a[0], a[1].getFailRegex()), ans)
|
||||
pprint_list([x.rstrip() for x in b], header)
|
||||
else:
|
||||
print "%s: too many to print. Use --print-all-%s " \
|
||||
print "%s too many to print. Use --print-all-%s " \
|
||||
"to print all %d lines" % (header, ltype, lines)
|
||||
elif len(l) < 20 or getattr(self, '_print_all_' + ltype):
|
||||
elif lines < self._maxlines or getattr(self, '_print_all_' + ltype):
|
||||
pprint_list([x.rstrip() for x in l], header)
|
||||
else:
|
||||
print "%s: too many to print. Use --print-all-%s " \
|
||||
"to print all %d lines" % (header, ltype, len(l))
|
||||
print "%s too many to print. Use --print-all-%s " \
|
||||
"to print all %d lines" % (header, ltype, lines)
|
||||
|
||||
def printStats(self):
|
||||
print
|
||||
|
|
@ -334,7 +429,7 @@ class Fail2banRegex(object):
|
|||
" %s %s%s" % (
|
||||
ip[1],
|
||||
timeString,
|
||||
ip[3] and " (multiple regex matched)" or ""))
|
||||
ip[-1] and " (multiple regex matched)" or ""))
|
||||
|
||||
print "\n%s: %d total" % (title, total)
|
||||
pprint_list(out, " #) [# of hits] regular expression")
|
||||
|
|
@ -345,17 +440,21 @@ class Fail2banRegex(object):
|
|||
_ = print_failregexes("Ignoreregex", self._ignoreregex)
|
||||
|
||||
|
||||
print "\nDate template hits:"
|
||||
out = []
|
||||
for template in self._filter.dateDetector.getTemplates():
|
||||
if self._verbose or template.getHits():
|
||||
out.append("[%d] %s" % (template.getHits(), template.getName()))
|
||||
pprint_list(out, "[# of hits] date format")
|
||||
if self._filter.dateDetector is not None:
|
||||
print "\nDate template hits:"
|
||||
out = []
|
||||
for template in self._filter.dateDetector.templates:
|
||||
if self._verbose or template.hits:
|
||||
out.append("[%d] %s" % (
|
||||
template.hits, template.name))
|
||||
pprint_list(out, "[# of hits] date format")
|
||||
|
||||
print "\nLines: %s" % self._line_stats
|
||||
|
||||
self.printLines('ignored')
|
||||
self.printLines('missed')
|
||||
if not self._print_no_ignored:
|
||||
self.printLines('ignored')
|
||||
if not self._print_no_missed:
|
||||
self.printLines('missed')
|
||||
|
||||
return True
|
||||
|
||||
|
|
@ -364,6 +463,19 @@ if __name__ == "__main__":
|
|||
|
||||
parser = get_opt_parser()
|
||||
(opts, args) = parser.parse_args()
|
||||
if opts.print_no_missed and opts.print_all_missed:
|
||||
sys.stderr.write("ERROR: --print-no-missed and --print-all-missed are mutually exclusive.\n\n")
|
||||
parser.print_help()
|
||||
sys.exit(-1)
|
||||
if opts.print_no_ignored and opts.print_all_ignored:
|
||||
sys.stderr.write("ERROR: --print-no-ignored and --print-all-ignored are mutually exclusive.\n\n")
|
||||
parser.print_help()
|
||||
sys.exit(-1)
|
||||
|
||||
print
|
||||
print "Running tests"
|
||||
print "============="
|
||||
print
|
||||
|
||||
fail2banRegex = Fail2banRegex(opts)
|
||||
|
||||
|
|
@ -379,9 +491,9 @@ if __name__ == "__main__":
|
|||
logSys.setLevel(getattr(logging, opts.log_level.upper()))
|
||||
else: # pragma: no cover
|
||||
# suppress the logging but it would leave unittests' progress dots
|
||||
# ticking, unless like with '-l fatal' which would be silent
|
||||
# ticking, unless like with '-l critical' which would be silent
|
||||
# unless error occurs
|
||||
logSys.setLevel(getattr(logging, 'FATAL'))
|
||||
logSys.setLevel(getattr(logging, 'CRITICAL'))
|
||||
|
||||
# Add the default logging handler
|
||||
stdout = logging.StreamHandler(sys.stdout)
|
||||
|
|
@ -395,33 +507,48 @@ if __name__ == "__main__":
|
|||
Formatter = logging.Formatter
|
||||
|
||||
# Custom log format for the verbose tests runs
|
||||
if opts.verbose > 1: # pragma: no cover
|
||||
if opts.verbose: # pragma: no cover
|
||||
stdout.setFormatter(Formatter(' %(asctime)-15s %(thread)s' + fmt))
|
||||
else: # pragma: no cover
|
||||
# just prefix with the space
|
||||
stdout.setFormatter(Formatter(fmt))
|
||||
logSys.addHandler(stdout)
|
||||
|
||||
print
|
||||
print "Running tests"
|
||||
print "============="
|
||||
print
|
||||
|
||||
cmd_log, cmd_regex = args[:2]
|
||||
|
||||
fail2banRegex.readRegex(cmd_regex, 'fail') or sys.exit(-1)
|
||||
|
||||
if len(args) == 3:
|
||||
fail2banRegex.readRegex(args[2], 'ignore') or sys.exit(-1)
|
||||
|
||||
fail2banRegex.readRegex(cmd_regex, 'fail') or sys.exit(-1)
|
||||
|
||||
if os.path.isfile(cmd_log):
|
||||
try:
|
||||
hdlr = open(cmd_log)
|
||||
hdlr = open(cmd_log, 'rb')
|
||||
print "Use log file : %s" % cmd_log
|
||||
test_lines = hdlr # Iterable
|
||||
print "Use encoding : %s" % fail2banRegex.encoding
|
||||
test_lines = file_lines_gen(hdlr)
|
||||
except IOError, e:
|
||||
print e
|
||||
sys.exit(-1)
|
||||
elif cmd_log == "systemd-journal":
|
||||
if not journal:
|
||||
print "Error: systemd library not found. Exiting..."
|
||||
sys.exit(-1)
|
||||
myjournal = journal.Reader(converters={'__CURSOR': lambda x: x})
|
||||
journalmatch = fail2banRegex._journalmatch
|
||||
fail2banRegex.setDatePattern(None)
|
||||
if journalmatch:
|
||||
try:
|
||||
for element in journalmatch:
|
||||
if element == "+":
|
||||
myjournal.add_disjunction()
|
||||
else:
|
||||
myjournal.add_match(element)
|
||||
except ValueError:
|
||||
print "Error: Invalid journalmatch: %s" % shortstr(" ".join(journalmatch))
|
||||
sys.exit(-1)
|
||||
print "Use journal match : %s" % " ".join(journalmatch)
|
||||
test_lines = journal_lines_gen(myjournal)
|
||||
else:
|
||||
print "Use single line : %s" % shortstr(cmd_log)
|
||||
test_lines = [ cmd_log ]
|
||||
|
|
@ -24,15 +24,8 @@ __license__ = "GPL"
|
|||
|
||||
import getopt, sys, logging, os
|
||||
|
||||
# Inserts our own modules path first in the list
|
||||
# fix for bug #343821
|
||||
try:
|
||||
from common.version import version
|
||||
except ImportError, e:
|
||||
sys.path.insert(1, "/usr/share/fail2ban")
|
||||
from common.version import version
|
||||
|
||||
from server.server import Server
|
||||
from fail2ban.version import version
|
||||
from fail2ban.server.server import Server
|
||||
|
||||
# Gets the instance of the logger.
|
||||
logSys = logging.getLogger("fail2ban")
|
||||
|
|
@ -104,10 +97,10 @@ class Fail2banServer:
|
|||
if opt[0] == "-x":
|
||||
self.__conf["force"] = True
|
||||
if opt[0] in ["-h", "--help"]:
|
||||
self.dispUsage()
|
||||
self.dispUsage()
|
||||
sys.exit(0)
|
||||
if opt[0] in ["-V", "--version"]:
|
||||
self.dispVersion()
|
||||
self.dispVersion()
|
||||
sys.exit(0)
|
||||
|
||||
def start(self, argv):
|
||||
128
bin/fail2ban-testcases
Executable file
128
bin/fail2ban-testcases
Executable file
|
|
@ -0,0 +1,128 @@
|
|||
#!/usr/bin/python
|
||||
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
|
||||
# vi: set ft=python sts=4 ts=4 sw=4 noet :
|
||||
"""Script to run Fail2Ban tests battery
|
||||
"""
|
||||
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Fail2Ban; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2012- Yaroslav Halchenko"
|
||||
__license__ = "GPL"
|
||||
|
||||
|
||||
import unittest, logging, sys, time, os
|
||||
|
||||
# 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
|
||||
# environment.
|
||||
if os.path.exists("fail2ban/__init__.py"):
|
||||
sys.path.insert(0, ".")
|
||||
from fail2ban.version import version
|
||||
|
||||
from fail2ban.tests.utils import FormatterWithTraceBack, gatherTests
|
||||
from fail2ban.server.mytime import MyTime
|
||||
|
||||
from optparse import OptionParser, Option
|
||||
|
||||
def get_opt_parser():
|
||||
# use module docstring for help output
|
||||
p = OptionParser(
|
||||
usage="%s [OPTIONS] [regexps]\n" % sys.argv[0] + __doc__,
|
||||
version="%prog " + version)
|
||||
|
||||
p.add_options([
|
||||
Option('-l', "--log-level", type="choice",
|
||||
dest="log_level",
|
||||
choices=('heavydebug', 'debug', 'info', 'notice', 'warning', 'error', 'critical'),
|
||||
default=None,
|
||||
help="Log level for the logger to use during running tests"),
|
||||
Option('-n', "--no-network", action="store_true",
|
||||
dest="no_network",
|
||||
help="Do not run tests that require the network"),
|
||||
Option("-t", "--log-traceback", action='store_true',
|
||||
help="Enrich log-messages with compressed tracebacks"),
|
||||
Option("--full-traceback", action='store_true',
|
||||
help="Either to make the tracebacks full, not compressed (as by default)"),
|
||||
|
||||
])
|
||||
|
||||
return p
|
||||
|
||||
parser = get_opt_parser()
|
||||
(opts, regexps) = parser.parse_args()
|
||||
|
||||
#
|
||||
# Logging
|
||||
#
|
||||
logSys = logging.getLogger("fail2ban")
|
||||
|
||||
# Numerical level of verbosity corresponding to a log "level"
|
||||
verbosity = {'heavydebug': 4,
|
||||
'debug': 3,
|
||||
'info': 2,
|
||||
'notice': 2,
|
||||
'warning': 1,
|
||||
'error': 1,
|
||||
'critical': 0,
|
||||
None: 1}[opts.log_level]
|
||||
|
||||
if opts.log_level is not None: # pragma: no cover
|
||||
# so we had explicit settings
|
||||
logSys.setLevel(getattr(logging, opts.log_level.upper()))
|
||||
else: # pragma: no cover
|
||||
# suppress the logging but it would leave unittests' progress dots
|
||||
# ticking, unless like with '-l critical' which would be silent
|
||||
# unless error occurs
|
||||
logSys.setLevel(getattr(logging, 'CRITICAL'))
|
||||
|
||||
# Add the default logging handler
|
||||
stdout = logging.StreamHandler(sys.stdout)
|
||||
|
||||
fmt = ' %(message)s'
|
||||
|
||||
if opts.log_traceback:
|
||||
Formatter = FormatterWithTraceBack
|
||||
fmt = (opts.full_traceback and ' %(tb)s' or ' %(tbc)s') + fmt
|
||||
else:
|
||||
Formatter = logging.Formatter
|
||||
|
||||
# Custom log format for the verbose tests runs
|
||||
if verbosity > 1: # pragma: no cover
|
||||
stdout.setFormatter(Formatter(' %(asctime)-15s %(thread)s' + fmt))
|
||||
else: # pragma: no cover
|
||||
# just prefix with the space
|
||||
stdout.setFormatter(Formatter(fmt))
|
||||
logSys.addHandler(stdout)
|
||||
|
||||
#
|
||||
# Let know the version
|
||||
#
|
||||
if not opts.log_level or opts.log_level != 'critical': # pragma: no cover
|
||||
print("Fail2ban %s test suite. Python %s. Please wait..." \
|
||||
% (version, str(sys.version).replace('\n', '')))
|
||||
|
||||
tests = gatherTests(regexps, opts.no_network)
|
||||
#
|
||||
# Run the tests
|
||||
#
|
||||
testRunner = unittest.TextTestRunner(verbosity=verbosity)
|
||||
|
||||
tests_results = testRunner.run(tests)
|
||||
|
||||
if not tests_results.wasSuccessful(): # pragma: no cover
|
||||
sys.exit(1)
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
|
||||
# vi: set ft=python sts=4 ts=4 sw=4 noet :
|
||||
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Fail2Ban; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
import logging
|
||||
from configreader import ConfigReader
|
||||
|
||||
# Gets the instance of the logger.
|
||||
logSys = logging.getLogger("fail2ban.client.config")
|
||||
|
||||
class ActionReader(ConfigReader):
|
||||
|
||||
def __init__(self, action, name, **kwargs):
|
||||
ConfigReader.__init__(self, **kwargs)
|
||||
self.__file = action[0]
|
||||
self.__cInfo = action[1]
|
||||
self.__name = name
|
||||
|
||||
def setFile(self, fileName):
|
||||
self.__file = fileName
|
||||
|
||||
def getFile(self):
|
||||
return self.__file
|
||||
|
||||
def setName(self, name):
|
||||
self.__name = name
|
||||
|
||||
def getName(self):
|
||||
return self.__name
|
||||
|
||||
def read(self):
|
||||
return ConfigReader.read(self, "action.d/" + self.__file)
|
||||
|
||||
def getOptions(self, pOpts):
|
||||
opts = [["string", "actionstart", ""],
|
||||
["string", "actionstop", ""],
|
||||
["string", "actioncheck", ""],
|
||||
["string", "actionban", ""],
|
||||
["string", "actionunban", ""]]
|
||||
self.__opts = ConfigReader.getOptions(self, "Definition", opts, pOpts)
|
||||
|
||||
if self.has_section("Init"):
|
||||
for opt in self.options("Init"):
|
||||
if not self.__cInfo.has_key(opt):
|
||||
self.__cInfo[opt] = self.get("Init", opt)
|
||||
|
||||
def convert(self):
|
||||
head = ["set", self.__name]
|
||||
stream = list()
|
||||
stream.append(head + ["addaction", self.__file])
|
||||
for opt in self.__opts:
|
||||
if opt == "actionstart":
|
||||
stream.append(head + ["actionstart", self.__file, self.__opts[opt]])
|
||||
elif opt == "actionstop":
|
||||
stream.append(head + ["actionstop", self.__file, self.__opts[opt]])
|
||||
elif opt == "actioncheck":
|
||||
stream.append(head + ["actioncheck", self.__file, self.__opts[opt]])
|
||||
elif opt == "actionban":
|
||||
stream.append(head + ["actionban", self.__file, self.__opts[opt]])
|
||||
elif opt == "actionunban":
|
||||
stream.append(head + ["actionunban", self.__file, self.__opts[opt]])
|
||||
# cInfo
|
||||
if self.__cInfo:
|
||||
for p in self.__cInfo:
|
||||
stream.append(head + ["setcinfo", self.__file, p, self.__cInfo[p]])
|
||||
|
||||
return stream
|
||||
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
|
||||
# vi: set ft=python sts=4 ts=4 sw=4 noet :
|
||||
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Fail2Ban; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
import logging
|
||||
from configreader import ConfigReader
|
||||
|
||||
# Gets the instance of the logger.
|
||||
logSys = logging.getLogger("fail2ban.client.config")
|
||||
|
||||
class FilterReader(ConfigReader):
|
||||
|
||||
def __init__(self, fileName, name, **kwargs):
|
||||
ConfigReader.__init__(self, **kwargs)
|
||||
self.__file = fileName
|
||||
self.__name = name
|
||||
|
||||
def setFile(self, fileName):
|
||||
self.__file = fileName
|
||||
|
||||
def getFile(self):
|
||||
return self.__file
|
||||
|
||||
def setName(self, name):
|
||||
self.__name = name
|
||||
|
||||
def getName(self):
|
||||
return self.__name
|
||||
|
||||
def read(self):
|
||||
return ConfigReader.read(self, "filter.d/" + self.__file)
|
||||
|
||||
def getOptions(self, pOpts):
|
||||
opts = [["string", "ignoreregex", ""],
|
||||
["string", "failregex", ""]]
|
||||
self.__opts = ConfigReader.getOptions(self, "Definition", opts, pOpts)
|
||||
|
||||
def convert(self):
|
||||
stream = list()
|
||||
for opt in self.__opts:
|
||||
if opt == "failregex":
|
||||
for regex in self.__opts[opt].split('\n'):
|
||||
# Do not send a command if the rule is empty.
|
||||
if regex != '':
|
||||
stream.append(["set", self.__name, "addfailregex", regex])
|
||||
elif opt == "ignoreregex":
|
||||
for regex in self.__opts[opt].split('\n'):
|
||||
# Do not send a command if the rule is empty.
|
||||
if regex != '':
|
||||
stream.append(["set", self.__name, "addignoreregex", regex])
|
||||
return stream
|
||||
|
||||
|
|
@ -1,43 +1,25 @@
|
|||
# Fail2Ban configuration file
|
||||
# https://www.rfxn.com/projects/advanced-policy-firewall/
|
||||
#
|
||||
# Author: Mark McKinstry
|
||||
# Note: APF doesn't play nicely with other actions. It has been observed to
|
||||
# remove bans created by other iptables based actions. If you are going to use
|
||||
# this action, use it for all of your jails.
|
||||
#
|
||||
# DON'T MIX APF and other IPTABLES based actions
|
||||
[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: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = apf --deny <ip> "banned by Fail2Ban <name>"
|
||||
|
||||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban = apf --remove <ip>
|
||||
|
||||
[Init]
|
||||
|
||||
# Name used in APF configuration
|
||||
#
|
||||
name = default
|
||||
|
||||
# DEV NOTES:
|
||||
#
|
||||
# Author: Mark McKinstry
|
||||
|
|
|
|||
19
config/action.d/badips.conf
Normal file
19
config/action.d/badips.conf
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# Fail2ban reporting to badips.com
|
||||
#
|
||||
# Note: This reports and IP only and does not actually ban traffic. Use
|
||||
# another action in the same jail if you want bans to occur.
|
||||
#
|
||||
# Set the category to the appropriate value before use.
|
||||
#
|
||||
# To get see register and optional key to get personalised graphs see:
|
||||
# http://www.badips.com/blog/personalized-statistics-track-the-attackers-of-all-your-servers-with-one-key
|
||||
|
||||
[Definition]
|
||||
|
||||
actionban = curl --fail --user-agent "fail2ban v0.8.12" http://www.badips.com/add/<category>/<ip>
|
||||
|
||||
[Init]
|
||||
|
||||
# Option: category
|
||||
# Notes.: Values are from the list here: http://www.badips.com/get/categories
|
||||
category =
|
||||
365
config/action.d/badips.py
Normal file
365
config/action.d/badips.py
Normal file
|
|
@ -0,0 +1,365 @@
|
|||
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
|
||||
# vi: set ft=python sts=4 ts=4 sw=4 noet :
|
||||
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Fail2Ban; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
import sys
|
||||
if sys.version_info < (2, 7):
|
||||
raise ImportError("badips.py action requires Python >= 2.7")
|
||||
import json
|
||||
from functools import partial
|
||||
import threading
|
||||
import logging
|
||||
if sys.version_info >= (3, ):
|
||||
from urllib.request import Request, urlopen
|
||||
from urllib.parse import urlencode
|
||||
from urllib.error import HTTPError
|
||||
else:
|
||||
from urllib2 import Request, urlopen, HTTPError
|
||||
from urllib import urlencode
|
||||
|
||||
from fail2ban.server.actions import ActionBase
|
||||
from fail2ban.version import version as f2bVersion
|
||||
|
||||
class BadIPsAction(ActionBase):
|
||||
"""Fail2Ban action which resports bans to badips.com, and also
|
||||
blacklist bad IPs listed on badips.com by using another action's
|
||||
ban method.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
jail : Jail
|
||||
The jail which the action belongs to.
|
||||
name : str
|
||||
Name assigned to the action.
|
||||
category : str
|
||||
Valid badips.com category for reporting failures.
|
||||
score : int, optional
|
||||
Minimum score for bad IPs. Default 3.
|
||||
age : str, optional
|
||||
Age of last report for bad IPs, per badips.com syntax.
|
||||
Default "24h" (24 hours)
|
||||
key : str, optional
|
||||
Key issued by badips.com to report bans, for later retrieval
|
||||
of personalised content.
|
||||
banaction : str, optional
|
||||
Name of banaction to use for blacklisting bad IPs. If `None`,
|
||||
no blacklist of IPs will take place.
|
||||
Default `None`.
|
||||
bancategory : str, optional
|
||||
Name of category to use for blacklisting, which can differ
|
||||
from category used for reporting. e.g. may want to report
|
||||
"postfix", but want to use whole "mail" category for blacklist.
|
||||
Default `category`.
|
||||
bankey : str, optional
|
||||
Key issued by badips.com to blacklist IPs reported with the
|
||||
associated key.
|
||||
updateperiod : int, optional
|
||||
Time in seconds between updating bad IPs blacklist.
|
||||
Default 900 (15 minutes)
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If invalid `category`, `score`, `banaction` or `updateperiod`.
|
||||
"""
|
||||
|
||||
_badips = "http://www.badips.com"
|
||||
_Request = partial(
|
||||
Request, headers={'User-Agent': "Fail2Ban %s" % f2bVersion})
|
||||
|
||||
def __init__(self, jail, name, category, score=3, age="24h", key=None,
|
||||
banaction=None, bancategory=None, bankey=None, updateperiod=900):
|
||||
super(BadIPsAction, self).__init__(jail, name)
|
||||
|
||||
self.category = category
|
||||
self.score = score
|
||||
self.age = age
|
||||
self.key = key
|
||||
self.banaction = banaction
|
||||
self.bancategory = bancategory or category
|
||||
self.bankey = bankey
|
||||
self.updateperiod = updateperiod
|
||||
|
||||
self._bannedips = set()
|
||||
# Used later for threading.Timer for updating badips
|
||||
self._timer = None
|
||||
|
||||
@classmethod
|
||||
def getCategories(cls, incParents=False):
|
||||
"""Get badips.com categories.
|
||||
|
||||
Returns
|
||||
-------
|
||||
set
|
||||
Set of categories.
|
||||
|
||||
Raises
|
||||
------
|
||||
HTTPError
|
||||
Any issues with badips.com request.
|
||||
"""
|
||||
try:
|
||||
response = urlopen(
|
||||
cls._Request("/".join([cls._badips, "get", "categories"])))
|
||||
except HTTPError as response:
|
||||
messages = json.loads(response.read().decode('utf-8'))
|
||||
self._logSys.error(
|
||||
"Failed to fetch categories. badips.com response: '%s'",
|
||||
messages['err'])
|
||||
raise
|
||||
else:
|
||||
categories = json.loads(response.read().decode('utf-8'))['categories']
|
||||
categories_names = set(
|
||||
value['Name'] for value in categories)
|
||||
if incParents:
|
||||
categories_names.update(set(
|
||||
value['Parent'] for value in categories
|
||||
if "Parent" in value))
|
||||
return categories_names
|
||||
|
||||
@classmethod
|
||||
def getList(cls, category, score, age, key=None):
|
||||
"""Get badips.com list of bad IPs.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
category : str
|
||||
Valid badips.com category.
|
||||
score : int
|
||||
Minimum score for bad IPs.
|
||||
age : str
|
||||
Age of last report for bad IPs, per badips.com syntax.
|
||||
key : str, optional
|
||||
Key issued by badips.com to fetch IPs reported with the
|
||||
associated key.
|
||||
|
||||
Returns
|
||||
-------
|
||||
set
|
||||
Set of bad IPs.
|
||||
|
||||
Raises
|
||||
------
|
||||
HTTPError
|
||||
Any issues with badips.com request.
|
||||
"""
|
||||
try:
|
||||
url = "?".join([
|
||||
"/".join([cls._badips, "get", "list", category, str(score)]),
|
||||
urlencode({'age': age})])
|
||||
if key:
|
||||
url = "&".join([url, urlencode({"key", key})])
|
||||
response = urlopen(cls._Request(url))
|
||||
except HTTPError as response:
|
||||
messages = json.loads(response.read().decode('utf-8'))
|
||||
self._logSys.error(
|
||||
"Failed to fetch bad IP list. badips.com response: '%s'",
|
||||
messages['err'])
|
||||
raise
|
||||
else:
|
||||
return set(response.read().decode('utf-8').split())
|
||||
|
||||
@property
|
||||
def category(self):
|
||||
"""badips.com category for reporting IPs.
|
||||
"""
|
||||
return self._category
|
||||
|
||||
@category.setter
|
||||
def category(self, category):
|
||||
if category not in self.getCategories():
|
||||
self._logSys.error("Category name '%s' not valid. "
|
||||
"see badips.com for list of valid categories",
|
||||
category)
|
||||
raise ValueError("Invalid category: %s" % category)
|
||||
self._category = category
|
||||
|
||||
@property
|
||||
def bancategory(self):
|
||||
"""badips.com bancategory for fetching IPs.
|
||||
"""
|
||||
return self._bancategory
|
||||
|
||||
@bancategory.setter
|
||||
def bancategory(self, bancategory):
|
||||
if bancategory not in self.getCategories(incParents=True):
|
||||
self._logSys.error("Category name '%s' not valid. "
|
||||
"see badips.com for list of valid categories",
|
||||
bancategory)
|
||||
raise ValueError("Invalid bancategory: %s" % bancategory)
|
||||
self._bancategory = bancategory
|
||||
|
||||
@property
|
||||
def score(self):
|
||||
"""badips.com minimum score for fetching IPs.
|
||||
"""
|
||||
return self._score
|
||||
|
||||
@score.setter
|
||||
def score(self, score):
|
||||
score = int(score)
|
||||
if 0 <= score <= 5:
|
||||
self._score = score
|
||||
else:
|
||||
raise ValueError("Score must be 0-5")
|
||||
|
||||
@property
|
||||
def banaction(self):
|
||||
"""Jail action to use for banning/unbanning.
|
||||
"""
|
||||
return self._banaction
|
||||
|
||||
@banaction.setter
|
||||
def banaction(self, banaction):
|
||||
if banaction is not None and banaction not in self._jail.actions:
|
||||
self._logSys.error("Action name '%s' not in jail '%s'",
|
||||
banaction, self._jail.name)
|
||||
raise ValueError("Invalid banaction")
|
||||
self._banaction = banaction
|
||||
|
||||
@property
|
||||
def updateperiod(self):
|
||||
"""Period in seconds between banned bad IPs will be updated.
|
||||
"""
|
||||
return self._updateperiod
|
||||
|
||||
@updateperiod.setter
|
||||
def updateperiod(self, updateperiod):
|
||||
updateperiod = int(updateperiod)
|
||||
if updateperiod > 0:
|
||||
self._updateperiod = updateperiod
|
||||
else:
|
||||
raise ValueError("Update period must be integer greater than 0")
|
||||
|
||||
def _banIPs(self, ips):
|
||||
for ip in ips:
|
||||
try:
|
||||
self._jail.actions[self.banaction].ban({
|
||||
'ip': ip,
|
||||
'failures': 0,
|
||||
'matches': "",
|
||||
'ipmatches': "",
|
||||
'ipjailmatches': "",
|
||||
})
|
||||
except Exception as e:
|
||||
self._logSys.error(
|
||||
"Error banning IP %s for jail '%s' with action '%s': %s",
|
||||
ip, self._jail.name, self.banaction, e,
|
||||
exc_info=self._logSys.getEffectiveLevel<=logging.DEBUG)
|
||||
else:
|
||||
self._bannedips.add(ip)
|
||||
self._logSys.info(
|
||||
"Banned IP %s for jail '%s' with action '%s'",
|
||||
ip, self._jail.name, self.banaction)
|
||||
|
||||
def _unbanIPs(self, ips):
|
||||
for ip in ips:
|
||||
try:
|
||||
self._jail.actions[self.banaction].unban({
|
||||
'ip': ip,
|
||||
'failures': 0,
|
||||
'matches': "",
|
||||
'ipmatches': "",
|
||||
'ipjailmatches': "",
|
||||
})
|
||||
except Exception as e:
|
||||
self._logSys.info(
|
||||
"Error unbanning IP %s for jail '%s' with action '%s': %s",
|
||||
ip, self._jail.name, self.banaction, e,
|
||||
exc_info=self._logSys.getEffectiveLevel<=logging.DEBUG)
|
||||
else:
|
||||
self._logSys.info(
|
||||
"Unbanned IP %s for jail '%s' with action '%s'",
|
||||
ip, self._jail.name, self.banaction)
|
||||
finally:
|
||||
self._bannedips.remove(ip)
|
||||
|
||||
def start(self):
|
||||
"""If `banaction` set, blacklists bad IPs.
|
||||
"""
|
||||
if self.banaction is not None:
|
||||
self.update()
|
||||
|
||||
def update(self):
|
||||
"""If `banaction` set, updates blacklisted IPs.
|
||||
|
||||
Queries badips.com for list of bad IPs, removing IPs from the
|
||||
blacklist if no longer present, and adds new bad IPs to the
|
||||
blacklist.
|
||||
"""
|
||||
if self.banaction is not None:
|
||||
if self._timer:
|
||||
self._timer.cancel()
|
||||
self._timer = None
|
||||
|
||||
try:
|
||||
ips = self.getList(
|
||||
self.bancategory, self.score, self.age, self.bankey)
|
||||
# Remove old IPs no longer listed
|
||||
self._unbanIPs(self._bannedips - ips)
|
||||
# Add new IPs which are now listed
|
||||
self._banIPs(ips - self._bannedips)
|
||||
|
||||
self._logSys.info(
|
||||
"Updated IPs for jail '%s'. Update again in %i seconds",
|
||||
self._jail.name, self.updateperiod)
|
||||
finally:
|
||||
self._timer = threading.Timer(self.updateperiod, self.update)
|
||||
self._timer.start()
|
||||
|
||||
def stop(self):
|
||||
"""If `banaction` set, clears blacklisted IPs.
|
||||
"""
|
||||
if self.banaction is not None:
|
||||
if self._timer:
|
||||
self._timer.cancel()
|
||||
self._timer = None
|
||||
self._unbanIPs(self._bannedips.copy())
|
||||
|
||||
def ban(self, aInfo):
|
||||
"""Reports banned IP to badips.com.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
aInfo : dict
|
||||
Dictionary which includes information in relation to
|
||||
the ban.
|
||||
|
||||
Raises
|
||||
------
|
||||
HTTPError
|
||||
Any issues with badips.com request.
|
||||
"""
|
||||
try:
|
||||
url = "/".join([self._badips, "add", self.category, aInfo['ip']])
|
||||
if self.key:
|
||||
url = "?".join([url, urlencode({"key", self.key})])
|
||||
response = urlopen(self._Request(url))
|
||||
except HTTPError as response:
|
||||
messages = json.loads(response.read().decode('utf-8'))
|
||||
self._logSys.error(
|
||||
"Response from badips.com report: '%s'",
|
||||
messages['err'])
|
||||
raise
|
||||
else:
|
||||
messages = json.loads(response.read().decode('utf-8'))
|
||||
self._logSys.info(
|
||||
"Response from badips.com report: '%s'",
|
||||
messages['suc'])
|
||||
|
||||
Action = BadIPsAction
|
||||
86
config/action.d/blocklist_de.conf
Normal file
86
config/action.d/blocklist_de.conf
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
# Fail2Ban configuration file
|
||||
#
|
||||
# Author: Steven Hiscocks
|
||||
#
|
||||
#
|
||||
|
||||
# Action to report IP address to blocklist.de
|
||||
# Blocklist.de must be signed up to at www.blocklist.de
|
||||
# Once registered, one or more servers can be added.
|
||||
# This action requires the server 'email address' and the assoicate apikey.
|
||||
#
|
||||
# From blocklist.de:
|
||||
# www.blocklist.de is a free and voluntary service provided by a
|
||||
# Fraud/Abuse-specialist, whose servers are often attacked on SSH-,
|
||||
# Mail-Login-, FTP-, Webserver- and other services.
|
||||
# The mission is to report all attacks to the abuse deparments of the
|
||||
# infected PCs/servers to ensure that the responsible provider can inform
|
||||
# the customer about the infection and disable them
|
||||
#
|
||||
# IMPORTANT:
|
||||
#
|
||||
# Reporting an IP of abuse is a serious complaint. Make sure that it is
|
||||
# serious. Fail2ban developers and network owners recommend you only use this
|
||||
# action for:
|
||||
# * The recidive where the IP has been banned multiple times
|
||||
# * Where maxretry has been set quite high, beyond the normal user typing
|
||||
# password incorrectly.
|
||||
# * For filters that have a low likelyhood of receiving human errors
|
||||
#
|
||||
|
||||
[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 = curl --fail --data-urlencode 'server=<email>' --data 'apikey=<apikey>' --data 'service=<service>' --data 'ip=<ip>' --data-urlencode 'logs=<matches>' --data 'format=text' --user-agent "fail2ban v0.8.12" "https://www.blocklist.de/en/httpreports.html"
|
||||
|
||||
# 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 =
|
||||
|
||||
[Init]
|
||||
|
||||
# Option: email
|
||||
# Notes server email address, as per blocklise.de account
|
||||
# Values: STRING Default: None
|
||||
#
|
||||
#email =
|
||||
|
||||
# Option: apikey
|
||||
# Notes your user blocklist.de user account apikey
|
||||
# Values: STRING Default: None
|
||||
#
|
||||
#apikey =
|
||||
|
||||
# Option: service
|
||||
# Notes service name you are reporting on, typically aligns with filter name
|
||||
# see http://www.blocklist.de/en/httpreports.html for full list
|
||||
# Values: STRING Default: None
|
||||
#
|
||||
#service =
|
||||
|
|
@ -1,17 +1,16 @@
|
|||
# Fail2Ban configuration file
|
||||
#
|
||||
# Author: Russell Odom <russ@gloomytrousers.co.uk>
|
||||
# Author: Russell Odom <russ@gloomytrousers.co.uk>, Daniel Black
|
||||
# Sends a complaint e-mail to addresses listed in the whois record for an
|
||||
# offending IP address.
|
||||
# This uses the https://abusix.com/contactdb.html to lookup abuse contacts.
|
||||
#
|
||||
# DEPENDANCIES:
|
||||
# This requires the dig command from bind-utils
|
||||
#
|
||||
# You should provide the <logpath> in the jail config - lines from the log
|
||||
# matching the given IP address will be provided in the complaint as evidence.
|
||||
#
|
||||
# Note that we will try to use e-mail addresses that are most likely to be abuse
|
||||
# addresses (based on various keywords). If they aren't found we fall back on
|
||||
# any other addresses found in the whois record, with a few exceptions.
|
||||
# If no addresses are found, no e-mail is sent.
|
||||
#
|
||||
# WARNING
|
||||
# -------
|
||||
#
|
||||
|
|
@ -55,10 +54,10 @@ actioncheck =
|
|||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = ADDRESSES=`whois <ip> | perl -e 'while (<STDIN>) { next if /^changed|@(ripe|apnic)\.net/io; $m += (/abuse|trouble:|report|spam|security/io?3:0); if (/([a-z0-9_\-\.+]+@[a-z0-9\-]+(\.[[a-z0-9\-]+)+)/io) { while (s/([a-z0-9_\-\.+]+@[a-z0-9\-]+(\.[[a-z0-9\-]+)+)//io) { if ($m) { $a{lc($1)}=$m } else { $b{lc($1)}=$m } } $m=0 } else { $m && --$m } } if (%%a) {print join(",",keys(%%a))} else {print join(",",keys(%%b))}'`
|
||||
actionban = oifs=${IFS}; IFS=.;SEP_IP=( <ip> ); set -- ${SEP_IP}; ADDRESSES=$(dig +short -t txt -q $4.$3.$2.$1.abuse-contacts.abusix.org); IFS=${oifs}
|
||||
IP=<ip>
|
||||
if [ ! -z "$ADDRESSES" ]; then
|
||||
(printf %%b "<message>\n"; date '+Note: Local timezone is %%z (%%Z)'; grep '<ip>' <logpath>) | <mailcmd> "Abuse from <ip>" <mailargs> $ADDRESSES
|
||||
(printf %%b "<message>\n"; date '+Note: Local timezone is %%z (%%Z)'; grep -E '(^|[^0-9])<ip>([^0-9]|$)' <logpath>) | <mailcmd> "Abuse from <ip>" <mailargs> ${ADDRESSES//,/\" \"}
|
||||
fi
|
||||
|
||||
# Option: actionunban
|
||||
|
|
@ -70,7 +69,7 @@ actionban = ADDRESSES=`whois <ip> | perl -e 'while (<STDIN>) { next if /^changed
|
|||
actionunban =
|
||||
|
||||
[Init]
|
||||
message = Dear Sir/Madam,\n\nWe have detected abuse from the IP address $IP, which according to a whois lookup 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. This mail was generated by Fail2Ban.)\n
|
||||
message = Dear Sir/Madam,\n\nWe have detected abuse from the IP address $IP, which according to a 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.\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
|
||||
|
||||
# Path to the log files which contain relevant lines for the abuser IP
|
||||
#
|
||||
|
|
|
|||
|
|
@ -1,52 +0,0 @@
|
|||
# Fail2Ban configuration file
|
||||
#
|
||||
# Author: Edgar Hoch
|
||||
# Copied from iptables-new.conf and modified for use with firewalld by Edgar Hoch.
|
||||
# It uses "firewall-cmd" instead of "iptables".
|
||||
#
|
||||
# Because of the --remove-rules in stop this action requires firewalld-0.3.8+
|
||||
|
||||
[INCLUDES]
|
||||
|
||||
before = iptables-blocktype.conf
|
||||
|
||||
[Definition]
|
||||
|
||||
actionstart = firewall-cmd --direct --add-chain ipv4 filter fail2ban-<name>
|
||||
firewall-cmd --direct --add-rule ipv4 filter fail2ban-<name> 1000 -j RETURN
|
||||
firewall-cmd --direct --add-rule ipv4 filter <chain> 0 -m state --state NEW -p <protocol> --dport <port> -j fail2ban-<name>
|
||||
|
||||
actionstop = firewall-cmd --direct --remove-rule ipv4 filter <chain> 0 -m state --state NEW -p <protocol> --dport <port> -j fail2ban-<name>
|
||||
firewall-cmd --direct --remove-rules ipv4 filter fail2ban-<name>
|
||||
firewall-cmd --direct --remove-chain ipv4 filter fail2ban-<name>
|
||||
|
||||
actioncheck = firewall-cmd --direct --get-chains ipv4 filter | grep -q 'fail2ban-<name>[ \t]'
|
||||
|
||||
actionban = firewall-cmd --direct --add-rule ipv4 filter fail2ban-<name> 0 -s <ip> -j <blocktype>
|
||||
|
||||
actionunban = firewall-cmd --direct --remove-rule ipv4 filter fail2ban-<name> 0 -s <ip> -j <blocktype>
|
||||
|
||||
[Init]
|
||||
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
# Option: port
|
||||
# Notes.: specifies port to monitor
|
||||
# Values: [ NUM | STRING ]
|
||||
#
|
||||
port = ssh
|
||||
|
||||
# Option: protocol
|
||||
# Notes.: internally used by config reader for interpolations.
|
||||
# Values: [ tcp | udp | icmp | all ]
|
||||
#
|
||||
protocol = tcp
|
||||
|
||||
# Option: chain
|
||||
# Notes specifies the iptables chain to which the fail2ban rules should be
|
||||
# added
|
||||
# Values: [ STRING ]
|
||||
#
|
||||
chain = INPUT_direct
|
||||
67
config/action.d/firewallcmd-ipset.conf
Normal file
67
config/action.d/firewallcmd-ipset.conf
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
# Fail2Ban action file for firewall-cmd/ipset
|
||||
#
|
||||
# This requires:
|
||||
# ipset (package: ipset)
|
||||
# firewall-cmd (package: firewalld)
|
||||
#
|
||||
# This is for ipset protocol 6 (and hopefully later) (ipset v6.14).
|
||||
# Use ipset -V to see the protocol and version.
|
||||
#
|
||||
# IPset was a feature introduced in the linux kernel 2.6.39 and 3.0.0 kernels.
|
||||
#
|
||||
# If you are running on an older kernel you make need to patch in external
|
||||
# modules.
|
||||
|
||||
[INCLUDES]
|
||||
|
||||
before = iptables-blocktype.conf
|
||||
|
||||
[Definition]
|
||||
|
||||
actionstart = ipset create fail2ban-<name> hash:ip timeout <bantime>
|
||||
firewall-cmd --direct --add-rule ipv4 filter <chain> 0 -p <protocol> -m multiport --dports <port> -m set --match-set fail2ban-<name> src -j <blocktype>
|
||||
|
||||
actionstop = firewall-cmd --direct --remove-rule ipv4 filter <chain> 0 -p <protocol> -m multiport --dports <port> -m set --match-set fail2ban-<name> src -j <blocktype>
|
||||
ipset flush fail2ban-<name>
|
||||
ipset destroy fail2ban-<name>
|
||||
|
||||
actionban = ipset add fail2ban-<name> <ip> timeout <bantime> -exist
|
||||
|
||||
actionunban = ipset del fail2ban-<name> <ip> -exist
|
||||
|
||||
[Init]
|
||||
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
# Option: port
|
||||
# Notes.: specifies port to monitor
|
||||
# Values: [ NUM | STRING ]
|
||||
#
|
||||
port = ssh
|
||||
|
||||
# Option: protocol
|
||||
# Notes.: internally used by config reader for interpolations.
|
||||
# Values: [ tcp | udp | icmp | all ]
|
||||
#
|
||||
protocol = tcp
|
||||
|
||||
# Option: chain
|
||||
# Notes specifies the iptables chain to which the fail2ban rules should be
|
||||
# added
|
||||
# Values: [ STRING ]
|
||||
#
|
||||
chain = INPUT_direct
|
||||
|
||||
# Option: bantime
|
||||
# Notes: specifies the bantime in seconds (handled internally rather than by fail2ban)
|
||||
# Values: [ NUM ] Default: 600
|
||||
|
||||
bantime = 600
|
||||
|
||||
|
||||
# DEV NOTES:
|
||||
#
|
||||
# Author: Edgar Hoch and Daniel Black
|
||||
# firewallcmd-new / iptables-ipset-proto6 combined for maximium goodness
|
||||
72
config/action.d/firewallcmd-new.conf
Normal file
72
config/action.d/firewallcmd-new.conf
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
# Fail2Ban configuration file
|
||||
#
|
||||
# Because of the --remove-rules in stop this action requires firewalld-0.3.8+
|
||||
|
||||
[INCLUDES]
|
||||
|
||||
before = iptables-blocktype.conf
|
||||
|
||||
[Definition]
|
||||
|
||||
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 <chain> 0 -m state --state NEW -p <protocol> --dport <port> -j f2b-<name>
|
||||
|
||||
actionstop = firewall-cmd --direct --remove-rule ipv4 filter <chain> 0 -m state --state NEW -p <protocol> --dport <port> -j f2b-<name>
|
||||
firewall-cmd --direct --remove-rules ipv4 filter f2b-<name>
|
||||
firewall-cmd --direct --remove-chain ipv4 filter f2b-<name>
|
||||
|
||||
actioncheck = firewall-cmd --direct --get-chains ipv4 filter | grep -q 'f2b-<name>$'
|
||||
|
||||
actionban = firewall-cmd --direct --add-rule ipv4 filter f2b-<name> 0 -s <ip> -j <blocktype>
|
||||
|
||||
actionunban = firewall-cmd --direct --remove-rule ipv4 filter f2b-<name> 0 -s <ip> -j <blocktype>
|
||||
|
||||
[Init]
|
||||
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
# Option: port
|
||||
# Notes.: specifies port to monitor
|
||||
# Values: [ NUM | STRING ]
|
||||
#
|
||||
port = ssh
|
||||
|
||||
# Option: protocol
|
||||
# Notes.: internally used by config reader for interpolations.
|
||||
# Values: [ tcp | udp | icmp | all ]
|
||||
#
|
||||
protocol = tcp
|
||||
|
||||
# Option: chain
|
||||
# Notes specifies the iptables chain to which the fail2ban rules should be
|
||||
# added
|
||||
# Values: [ STRING ]
|
||||
#
|
||||
chain = INPUT_direct
|
||||
|
||||
# DEV NOTES:
|
||||
#
|
||||
# Author: Edgar Hoch
|
||||
# Copied from iptables-new.conf and modified for use with firewalld by Edgar Hoch.
|
||||
# It uses "firewall-cmd" instead of "iptables".
|
||||
#
|
||||
# Output:
|
||||
#
|
||||
# $ firewall-cmd --direct --add-chain ipv4 filter fail2ban-name
|
||||
# success
|
||||
# $ firewall-cmd --direct --add-rule ipv4 filter fail2ban-name 1000 -j RETURN
|
||||
# success
|
||||
# $ sudo firewall-cmd --direct --add-rule ipv4 filter INPUT_direct 0 -m state --state NEW -p tcp --dport 22 -j fail2ban-name
|
||||
# success
|
||||
# $ firewall-cmd --direct --get-chains ipv4 filter
|
||||
# fail2ban-name
|
||||
# $ firewall-cmd --direct --get-chains ipv4 filter | od -h
|
||||
# 0000000 6166 6c69 6232 6e61 6e2d 6d61 0a65
|
||||
# $ firewall-cmd --direct --get-chains ipv4 filter | grep -Eq 'fail2ban-name( |$)' ; echo $?
|
||||
# 0
|
||||
# $ firewall-cmd -V
|
||||
# 0.3.8
|
||||
|
||||
|
|
@ -43,7 +43,7 @@ actionban = ipfw add <blocktype> tcp from <ip> to <localhost> <port>
|
|||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban = ipfw delete `ipfw list | grep -i <ip> | awk '{print $1;}'`
|
||||
actionunban = ipfw delete `ipfw list | grep -i "[^0-9]<ip>[^0-9]" | awk '{print $1;}'`
|
||||
|
||||
[Init]
|
||||
|
||||
|
|
|
|||
|
|
@ -17,23 +17,23 @@ before = iptables-blocktype.conf
|
|||
# Notes.: command executed once at the start of Fail2Ban.
|
||||
# Values: CMD
|
||||
#
|
||||
actionstart = iptables -N fail2ban-<name>
|
||||
iptables -A fail2ban-<name> -j RETURN
|
||||
iptables -I <chain> -p <protocol> -j fail2ban-<name>
|
||||
actionstart = iptables -N f2b-<name>
|
||||
iptables -A f2b-<name> -j RETURN
|
||||
iptables -I <chain> -p <protocol> -j f2b-<name>
|
||||
|
||||
# Option: actionstop
|
||||
# Notes.: command executed once at the end of Fail2Ban
|
||||
# Values: CMD
|
||||
#
|
||||
actionstop = iptables -D <chain> -p <protocol> -j fail2ban-<name>
|
||||
iptables -F fail2ban-<name>
|
||||
iptables -X fail2ban-<name>
|
||||
actionstop = iptables -D <chain> -p <protocol> -j f2b-<name>
|
||||
iptables -F f2b-<name>
|
||||
iptables -X f2b-<name>
|
||||
|
||||
# Option: actioncheck
|
||||
# Notes.: command executed once before each actionban command
|
||||
# Values: CMD
|
||||
#
|
||||
actioncheck = iptables -n -L <chain> | grep -q 'fail2ban-<name>[ \t]'
|
||||
actioncheck = iptables -n -L <chain> | grep -q 'f2b-<name>[ \t]'
|
||||
|
||||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
|
|
@ -41,7 +41,7 @@ actioncheck = iptables -n -L <chain> | grep -q 'fail2ban-<name>[ \t]'
|
|||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j <blocktype>
|
||||
actionban = iptables -I f2b-<name> 1 -s <ip> -j <blocktype>
|
||||
|
||||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
|
|
@ -49,7 +49,7 @@ actionban = iptables -I fail2ban-<name> 1 -s <ip> -j <blocktype>
|
|||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban = iptables -D fail2ban-<name> -s <ip> -j <blocktype>
|
||||
actionunban = iptables -D f2b-<name> -s <ip> -j <blocktype>
|
||||
|
||||
[Init]
|
||||
|
||||
|
|
|
|||
|
|
@ -27,16 +27,16 @@ before = iptables-blocktype.conf
|
|||
# Notes.: command executed once at the start of Fail2Ban.
|
||||
# Values: CMD
|
||||
#
|
||||
actionstart = ipset --create fail2ban-<name> iphash
|
||||
iptables -I INPUT -p <protocol> -m multiport --dports <port> -m set --match-set fail2ban-<name> src -j <blocktype>
|
||||
actionstart = ipset --create f2b-<name> iphash
|
||||
iptables -I INPUT -p <protocol> -m multiport --dports <port> -m set --match-set f2b-<name> src -j <blocktype>
|
||||
|
||||
# Option: actionstop
|
||||
# Notes.: command executed once at the end of Fail2Ban
|
||||
# Values: CMD
|
||||
#
|
||||
actionstop = iptables -D INPUT -p <protocol> -m multiport --dports <port> -m set --match-set fail2ban-<name> src -j <blocktype>
|
||||
ipset --flush fail2ban-<name>
|
||||
ipset --destroy fail2ban-<name>
|
||||
actionstop = iptables -D INPUT -p <protocol> -m multiport --dports <port> -m set --match-set f2b-<name> src -j <blocktype>
|
||||
ipset --flush f2b-<name>
|
||||
ipset --destroy f2b-<name>
|
||||
|
||||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
|
|
@ -44,7 +44,7 @@ actionstop = iptables -D INPUT -p <protocol> -m multiport --dports <port> -m set
|
|||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = ipset --test fail2ban-<name> <ip> || ipset --add fail2ban-<name> <ip>
|
||||
actionban = ipset --test f2b-<name> <ip> || ipset --add f2b-<name> <ip>
|
||||
|
||||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
|
|
@ -52,7 +52,7 @@ actionban = ipset --test fail2ban-<name> <ip> || ipset --add fail2ban-<name> <i
|
|||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban = ipset --test fail2ban-<name> <ip> && ipset --del fail2ban-<name> <ip>
|
||||
actionunban = ipset --test f2b-<name> <ip> && ipset --del f2b-<name> <ip>
|
||||
|
||||
[Init]
|
||||
|
||||
|
|
|
|||
|
|
@ -24,16 +24,16 @@ before = iptables-blocktype.conf
|
|||
# Notes.: command executed once at the start of Fail2Ban.
|
||||
# Values: CMD
|
||||
#
|
||||
actionstart = ipset create fail2ban-<name> hash:ip timeout <bantime>
|
||||
iptables -I INPUT -m set --match-set fail2ban-<name> src -j <blocktype>
|
||||
actionstart = ipset create f2b-<name> hash:ip timeout <bantime>
|
||||
iptables -I INPUT -m set --match-set f2b-<name> src -j <blocktype>
|
||||
|
||||
# Option: actionstop
|
||||
# Notes.: command executed once at the end of Fail2Ban
|
||||
# Values: CMD
|
||||
#
|
||||
actionstop = iptables -D INPUT -m set --match-set fail2ban-<name> src -j <blocktype>
|
||||
ipset flush fail2ban-<name>
|
||||
ipset destroy fail2ban-<name>
|
||||
actionstop = iptables -D INPUT -m set --match-set f2b-<name> src -j <blocktype>
|
||||
ipset flush f2b-<name>
|
||||
ipset destroy f2b-<name>
|
||||
|
||||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
|
|
@ -41,7 +41,7 @@ actionstop = iptables -D INPUT -m set --match-set fail2ban-<name> src -j <blockt
|
|||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = ipset add fail2ban-<name> <ip> timeout <bantime> -exist
|
||||
actionban = ipset add f2b-<name> <ip> timeout <bantime> -exist
|
||||
|
||||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
|
|
@ -49,7 +49,7 @@ actionban = ipset add fail2ban-<name> <ip> timeout <bantime> -exist
|
|||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban = ipset del fail2ban-<name> <ip> -exist
|
||||
actionunban = ipset del f2b-<name> <ip> -exist
|
||||
|
||||
[Init]
|
||||
|
||||
|
|
|
|||
|
|
@ -24,16 +24,16 @@ before = iptables-blocktype.conf
|
|||
# Notes.: command executed once at the start of Fail2Ban.
|
||||
# Values: CMD
|
||||
#
|
||||
actionstart = ipset create fail2ban-<name> hash:ip timeout <bantime>
|
||||
iptables -I INPUT -p <protocol> -m multiport --dports <port> -m set --match-set fail2ban-<name> src -j <blocktype>
|
||||
actionstart = ipset create f2b-<name> hash:ip timeout <bantime>
|
||||
iptables -I INPUT -p <protocol> -m multiport --dports <port> -m set --match-set f2b-<name> src -j <blocktype>
|
||||
|
||||
# Option: actionstop
|
||||
# Notes.: command executed once at the end of Fail2Ban
|
||||
# Values: CMD
|
||||
#
|
||||
actionstop = iptables -D INPUT -p <protocol> -m multiport --dports <port> -m set --match-set fail2ban-<name> src -j <blocktype>
|
||||
ipset flush fail2ban-<name>
|
||||
ipset destroy fail2ban-<name>
|
||||
actionstop = iptables -D INPUT -p <protocol> -m multiport --dports <port> -m set --match-set f2b-<name> src -j <blocktype>
|
||||
ipset flush f2b-<name>
|
||||
ipset destroy f2b-<name>
|
||||
|
||||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
|
|
@ -41,7 +41,7 @@ actionstop = iptables -D INPUT -p <protocol> -m multiport --dports <port> -m set
|
|||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = ipset add fail2ban-<name> <ip> timeout <bantime> -exist
|
||||
actionban = ipset add f2b-<name> <ip> timeout <bantime> -exist
|
||||
|
||||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
|
|
@ -49,7 +49,7 @@ actionban = ipset add fail2ban-<name> <ip> timeout <bantime> -exist
|
|||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban = ipset del fail2ban-<name> <ip> -exist
|
||||
actionunban = ipset del f2b-<name> <ip> -exist
|
||||
|
||||
[Init]
|
||||
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@
|
|||
# Author: Guido Bozzetto
|
||||
# Modified: Cyril Jaquier
|
||||
#
|
||||
# make "fail2ban-<name>" chain to match drop IP
|
||||
# make "fail2ban-<name>-log" chain to log and drop
|
||||
# insert a jump to fail2ban-<name> from -I <chain> if proto/port match
|
||||
# make "f2b-<name>" chain to match drop IP
|
||||
# make "f2b-<name>-log" chain to log and drop
|
||||
# insert a jump to f2b-<name> from -I <chain> if proto/port match
|
||||
#
|
||||
#
|
||||
|
||||
|
|
@ -19,28 +19,28 @@ before = iptables-blocktype.conf
|
|||
# Notes.: command executed once at the start of Fail2Ban.
|
||||
# Values: CMD
|
||||
#
|
||||
actionstart = iptables -N fail2ban-<name>
|
||||
iptables -A fail2ban-<name> -j RETURN
|
||||
iptables -I <chain> 1 -p <protocol> -m multiport --dports <port> -j fail2ban-<name>
|
||||
iptables -N fail2ban-<name>-log
|
||||
iptables -I fail2ban-<name>-log -j LOG --log-prefix "$(expr fail2ban-<name> : '\(.\{1,23\}\)'):DROP " --log-level warning -m limit --limit 6/m --limit-burst 2
|
||||
iptables -A fail2ban-<name>-log -j <blocktype>
|
||||
actionstart = iptables -N f2b-<name>
|
||||
iptables -A f2b-<name> -j RETURN
|
||||
iptables -I <chain> 1 -p <protocol> -m multiport --dports <port> -j f2b-<name>
|
||||
iptables -N f2b-<name>-log
|
||||
iptables -I f2b-<name>-log -j LOG --log-prefix "$(expr f2b-<name> : '\(.\{1,23\}\)'):DROP " --log-level warning -m limit --limit 6/m --limit-burst 2
|
||||
iptables -A f2b-<name>-log -j <blocktype>
|
||||
|
||||
# Option: actionstop
|
||||
# Notes.: command executed once at the end of Fail2Ban
|
||||
# Values: CMD
|
||||
#
|
||||
actionstop = iptables -D <chain> -p <protocol> -m multiport --dports <port> -j fail2ban-<name>
|
||||
iptables -F fail2ban-<name>
|
||||
iptables -F fail2ban-<name>-log
|
||||
iptables -X fail2ban-<name>
|
||||
iptables -X fail2ban-<name>-log
|
||||
actionstop = iptables -D <chain> -p <protocol> -m multiport --dports <port> -j f2b-<name>
|
||||
iptables -F f2b-<name>
|
||||
iptables -F f2b-<name>-log
|
||||
iptables -X f2b-<name>
|
||||
iptables -X f2b-<name>-log
|
||||
|
||||
# Option: actioncheck
|
||||
# Notes.: command executed once before each actionban command
|
||||
# Values: CMD
|
||||
#
|
||||
actioncheck = iptables -n -L fail2ban-<name>-log >/dev/null
|
||||
actioncheck = iptables -n -L f2b-<name>-log >/dev/null
|
||||
|
||||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
|
|
@ -48,7 +48,7 @@ actioncheck = iptables -n -L fail2ban-<name>-log >/dev/null
|
|||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j fail2ban-<name>-log
|
||||
actionban = iptables -I f2b-<name> 1 -s <ip> -j f2b-<name>-log
|
||||
|
||||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
|
|
@ -56,7 +56,7 @@ actionban = iptables -I fail2ban-<name> 1 -s <ip> -j fail2ban-<name>-log
|
|||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban = iptables -D fail2ban-<name> -s <ip> -j fail2ban-<name>-log
|
||||
actionunban = iptables -D f2b-<name> -s <ip> -j f2b-<name>-log
|
||||
|
||||
[Init]
|
||||
|
||||
|
|
|
|||
|
|
@ -14,23 +14,23 @@ before = iptables-blocktype.conf
|
|||
# Notes.: command executed once at the start of Fail2Ban.
|
||||
# Values: CMD
|
||||
#
|
||||
actionstart = iptables -N fail2ban-<name>
|
||||
iptables -A fail2ban-<name> -j RETURN
|
||||
iptables -I <chain> -p <protocol> -m multiport --dports <port> -j fail2ban-<name>
|
||||
actionstart = iptables -N f2b-<name>
|
||||
iptables -A f2b-<name> -j RETURN
|
||||
iptables -I <chain> -p <protocol> -m multiport --dports <port> -j f2b-<name>
|
||||
|
||||
# Option: actionstop
|
||||
# Notes.: command executed once at the end of Fail2Ban
|
||||
# Values: CMD
|
||||
#
|
||||
actionstop = iptables -D <chain> -p <protocol> -m multiport --dports <port> -j fail2ban-<name>
|
||||
iptables -F fail2ban-<name>
|
||||
iptables -X fail2ban-<name>
|
||||
actionstop = iptables -D <chain> -p <protocol> -m multiport --dports <port> -j f2b-<name>
|
||||
iptables -F f2b-<name>
|
||||
iptables -X f2b-<name>
|
||||
|
||||
# Option: actioncheck
|
||||
# Notes.: command executed once before each actionban command
|
||||
# Values: CMD
|
||||
#
|
||||
actioncheck = iptables -n -L <chain> | grep -q 'fail2ban-<name>[ \t]'
|
||||
actioncheck = iptables -n -L <chain> | grep -q 'f2b-<name>[ \t]'
|
||||
|
||||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
|
|
@ -38,7 +38,7 @@ actioncheck = iptables -n -L <chain> | grep -q 'fail2ban-<name>[ \t]'
|
|||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j <blocktype>
|
||||
actionban = iptables -I f2b-<name> 1 -s <ip> -j <blocktype>
|
||||
|
||||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
|
|
@ -46,7 +46,7 @@ actionban = iptables -I fail2ban-<name> 1 -s <ip> -j <blocktype>
|
|||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban = iptables -D fail2ban-<name> -s <ip> -j <blocktype>
|
||||
actionunban = iptables -D f2b-<name> -s <ip> -j <blocktype>
|
||||
|
||||
[Init]
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
#
|
||||
# Author: Cyril Jaquier
|
||||
# Copied from iptables.conf and modified by Yaroslav Halchenko
|
||||
# to fullfill the needs of bugreporter dbts#350746.
|
||||
# to fulfill the needs of bugreporter dbts#350746.
|
||||
#
|
||||
#
|
||||
|
||||
|
|
@ -17,23 +17,23 @@ before = iptables-blocktype.conf
|
|||
# Notes.: command executed once at the start of Fail2Ban.
|
||||
# Values: CMD
|
||||
#
|
||||
actionstart = iptables -N fail2ban-<name>
|
||||
iptables -A fail2ban-<name> -j RETURN
|
||||
iptables -I <chain> -m state --state NEW -p <protocol> --dport <port> -j fail2ban-<name>
|
||||
actionstart = iptables -N f2b-<name>
|
||||
iptables -A f2b-<name> -j RETURN
|
||||
iptables -I <chain> -m state --state NEW -p <protocol> --dport <port> -j f2b-<name>
|
||||
|
||||
# Option: actionstop
|
||||
# Notes.: command executed once at the end of Fail2Ban
|
||||
# Values: CMD
|
||||
#
|
||||
actionstop = iptables -D <chain> -m state --state NEW -p <protocol> --dport <port> -j fail2ban-<name>
|
||||
iptables -F fail2ban-<name>
|
||||
iptables -X fail2ban-<name>
|
||||
actionstop = iptables -D <chain> -m state --state NEW -p <protocol> --dport <port> -j f2b-<name>
|
||||
iptables -F f2b-<name>
|
||||
iptables -X f2b-<name>
|
||||
|
||||
# Option: actioncheck
|
||||
# Notes.: command executed once before each actionban command
|
||||
# Values: CMD
|
||||
#
|
||||
actioncheck = iptables -n -L <chain> | grep -q 'fail2ban-<name>[ \t]'
|
||||
actioncheck = iptables -n -L <chain> | grep -q 'f2b-<name>[ \t]'
|
||||
|
||||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
|
|
@ -41,7 +41,7 @@ actioncheck = iptables -n -L <chain> | grep -q 'fail2ban-<name>[ \t]'
|
|||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j <blocktype>
|
||||
actionban = iptables -I f2b-<name> 1 -s <ip> -j <blocktype>
|
||||
|
||||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
|
|
@ -49,7 +49,7 @@ actionban = iptables -I fail2ban-<name> 1 -s <ip> -j <blocktype>
|
|||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban = iptables -D fail2ban-<name> -s <ip> -j <blocktype>
|
||||
actionunban = iptables -D f2b-<name> -s <ip> -j <blocktype>
|
||||
|
||||
[Init]
|
||||
|
||||
|
|
|
|||
|
|
@ -23,29 +23,30 @@ before = iptables-blocktype.conf
|
|||
# iptables-persistent package).
|
||||
#
|
||||
# Explanation of the rule below:
|
||||
# Check if any packets coming from an IP on the fail2ban-<name>
|
||||
# Check if any packets coming from an IP on the f2b-<name>
|
||||
# list have been seen in the last 3600 seconds. If yes, update the
|
||||
# timestamp for this IP and drop the packet. If not, let the packet
|
||||
# through.
|
||||
#
|
||||
# Fail2ban inserts blacklisted hosts into the fail2ban-<name> list
|
||||
# Fail2ban inserts blacklisted hosts into the f2b-<name> list
|
||||
# and removes them from the list after some time, according to its
|
||||
# own rules. The 3600 second timeout is independent and acts as a
|
||||
# safeguard in case the fail2ban process dies unexpectedly. The
|
||||
# shorter of the two timeouts actually matters.
|
||||
actionstart = iptables -I INPUT -m recent --update --seconds 3600 --name fail2ban-<name> -j <blocktype>
|
||||
actionstart = if [ `id -u` -eq 0 ];then iptables -I INPUT -m recent --update --seconds 3600 --name f2b-<name> -j <blocktype>;fi
|
||||
|
||||
# Option: actionstop
|
||||
# Notes.: command executed once at the end of Fail2Ban
|
||||
# Values: CMD
|
||||
#
|
||||
actionstop = echo / > /proc/net/xt_recent/fail2ban-<name>
|
||||
actionstop = echo / > /proc/net/xt_recent/f2b-<name>
|
||||
if [ `id -u` -eq 0 ];then iptables -D INPUT -m recent --update --seconds 3600 --name f2b-<name> -j <blocktype>;fi
|
||||
|
||||
# Option: actioncheck
|
||||
# Notes.: command executed once before each actionban command
|
||||
# Values: CMD
|
||||
#
|
||||
actioncheck = test -e /proc/net/xt_recent/fail2ban-<name>
|
||||
actioncheck = test -e /proc/net/xt_recent/f2b-<name>
|
||||
|
||||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
|
|
@ -53,7 +54,7 @@ actioncheck = test -e /proc/net/xt_recent/fail2ban-<name>
|
|||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = echo +<ip> > /proc/net/xt_recent/fail2ban-<name>
|
||||
actionban = echo +<ip> > /proc/net/xt_recent/f2b-<name>
|
||||
|
||||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
|
|
@ -61,7 +62,7 @@ actionban = echo +<ip> > /proc/net/xt_recent/fail2ban-<name>
|
|||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban = echo -<ip> > /proc/net/xt_recent/fail2ban-<name>
|
||||
actionunban = echo -<ip> > /proc/net/xt_recent/f2b-<name>
|
||||
|
||||
[Init]
|
||||
|
||||
|
|
|
|||
|
|
@ -14,23 +14,23 @@ before = iptables-blocktype.conf
|
|||
# Notes.: command executed once at the start of Fail2Ban.
|
||||
# Values: CMD
|
||||
#
|
||||
actionstart = iptables -N fail2ban-<name>
|
||||
iptables -A fail2ban-<name> -j RETURN
|
||||
iptables -I <chain> -p <protocol> --dport <port> -j fail2ban-<name>
|
||||
actionstart = iptables -N f2b-<name>
|
||||
iptables -A f2b-<name> -j RETURN
|
||||
iptables -I <chain> -p <protocol> --dport <port> -j f2b-<name>
|
||||
|
||||
# Option: actionstop
|
||||
# Notes.: command executed once at the end of Fail2Ban
|
||||
# Values: CMD
|
||||
#
|
||||
actionstop = iptables -D <chain> -p <protocol> --dport <port> -j fail2ban-<name>
|
||||
iptables -F fail2ban-<name>
|
||||
iptables -X fail2ban-<name>
|
||||
actionstop = iptables -D <chain> -p <protocol> --dport <port> -j f2b-<name>
|
||||
iptables -F f2b-<name>
|
||||
iptables -X f2b-<name>
|
||||
|
||||
# Option: actioncheck
|
||||
# Notes.: command executed once before each actionban command
|
||||
# Values: CMD
|
||||
#
|
||||
actioncheck = iptables -n -L <chain> | grep -q 'fail2ban-<name>[ \t]'
|
||||
actioncheck = iptables -n -L <chain> | grep -q 'f2b-<name>[ \t]'
|
||||
|
||||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
|
|
@ -38,7 +38,7 @@ actioncheck = iptables -n -L <chain> | grep -q 'fail2ban-<name>[ \t]'
|
|||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j <blocktype>
|
||||
actionban = iptables -I f2b-<name> 1 -s <ip> -j <blocktype>
|
||||
|
||||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
|
|
@ -46,7 +46,7 @@ actionban = iptables -I fail2ban-<name> 1 -s <ip> -j <blocktype>
|
|||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban = iptables -D fail2ban-<name> -s <ip> -j <blocktype>
|
||||
actionunban = iptables -D f2b-<name> -s <ip> -j <blocktype>
|
||||
|
||||
[Init]
|
||||
|
||||
|
|
|
|||
|
|
@ -39,10 +39,10 @@ actioncheck =
|
|||
actionban = printf %%b "Hi,\n
|
||||
The IP <ip> has just been banned by Fail2Ban after
|
||||
<failures> attempts against <name>.\n\n
|
||||
Here are more information about <ip>:\n
|
||||
`whois <ip>`\n\n
|
||||
Here is more information about <ip>:\n
|
||||
`whois <ip> || echo missing whois program`\n\n
|
||||
Lines containing IP:<ip> in <logpath>\n
|
||||
`grep '\<<ip>\>' <logpath>`\n\n
|
||||
`grep '[^0-9]<ip>[^0-9]' <logpath>`\n\n
|
||||
Regards,\n
|
||||
Fail2Ban"|mail -s "[Fail2Ban] <name>: banned <ip> from `uname -n`" <dest>
|
||||
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@ actioncheck =
|
|||
actionban = printf %%b "Hi,\n
|
||||
The IP <ip> has just been banned by Fail2Ban after
|
||||
<failures> attempts against <name>.\n\n
|
||||
Here are more information about <ip>:\n
|
||||
`whois <ip>`\n
|
||||
Here is more information about <ip>:\n
|
||||
`whois <ip> || echo missing whois program`\n
|
||||
Regards,\n
|
||||
Fail2Ban"|mail -s "[Fail2Ban] <name>: banned <ip> from `uname -n`" <dest>
|
||||
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ blocktype = unreach port
|
|||
setnum = 10
|
||||
|
||||
# Option: number for ipfw rule
|
||||
# Notes: This is meant to be automaticly generated and not overwritten
|
||||
# Notes: This is meant to be automatically generated and not overwritten
|
||||
# Values: Random value between 10000 and 12000
|
||||
rulenum="`echo $((RANDOM%%2000+10000))`"
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,56 @@
|
|||
|
||||
after = sendmail-common.local
|
||||
|
||||
[Definition]
|
||||
|
||||
# Option: actionstart
|
||||
# Notes.: command executed once at the start of Fail2Ban.
|
||||
# Values: CMD
|
||||
#
|
||||
actionstart = printf %%b "Subject: [Fail2Ban] <name>: started on `uname -n`
|
||||
Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"`
|
||||
From: <sendername> <<sender>>
|
||||
To: <dest>\n
|
||||
Hi,\n
|
||||
The jail <name> has been started successfully.\n
|
||||
Regards,\n
|
||||
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
|
||||
|
||||
# Option: actionstop
|
||||
# Notes.: command executed once at the end of Fail2Ban
|
||||
# Values: CMD
|
||||
#
|
||||
actionstop = printf %%b "Subject: [Fail2Ban] <name>: stopped on `uname -n`
|
||||
Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"`
|
||||
From: <sendername> <<sender>>
|
||||
To: <dest>\n
|
||||
Hi,\n
|
||||
The jail <name> has been stopped.\n
|
||||
Regards,\n
|
||||
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
|
||||
|
||||
# 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 =
|
||||
|
||||
# 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 =
|
||||
|
||||
[Init]
|
||||
|
||||
# Recipient mail address
|
||||
|
|
|
|||
37
config/action.d/sendmail-whois-ipjailmatches.conf
Normal file
37
config/action.d/sendmail-whois-ipjailmatches.conf
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# Fail2Ban configuration file
|
||||
#
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
#
|
||||
|
||||
[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.
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
||||
Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"`
|
||||
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 are more information about <ip>:\n
|
||||
`/usr/bin/whois <ip>`\n\n
|
||||
Matches for <name> with <ipjailfailures> failures IP:<ip>\n
|
||||
<ipjailmatches>\n\n
|
||||
Regards,\n
|
||||
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
|
||||
|
||||
[Init]
|
||||
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
37
config/action.d/sendmail-whois-ipmatches.conf
Normal file
37
config/action.d/sendmail-whois-ipmatches.conf
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# Fail2Ban configuration file
|
||||
#
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
#
|
||||
|
||||
[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.
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
||||
Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"`
|
||||
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 are more information about <ip>:\n
|
||||
`/usr/bin/whois <ip>`\n\n
|
||||
Matches with <ipfailures> failures IP:<ip>\n
|
||||
<ipmatches>\n\n
|
||||
Regards,\n
|
||||
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
|
||||
|
||||
[Init]
|
||||
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
|
|
@ -10,38 +10,6 @@ before = sendmail-common.conf
|
|||
|
||||
[Definition]
|
||||
|
||||
# Option: actionstart
|
||||
# Notes.: command executed once at the start of Fail2Ban.
|
||||
# Values: CMD
|
||||
#
|
||||
actionstart = printf %%b "Subject: [Fail2Ban] <name>: started on `uname -n`
|
||||
Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"`
|
||||
From: <sendername> <<sender>>
|
||||
To: <dest>\n
|
||||
Hi,\n
|
||||
The jail <name> has been started successfully.\n
|
||||
Regards,\n
|
||||
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
|
||||
|
||||
# Option: actionstop
|
||||
# Notes.: command executed once at the end of Fail2Ban
|
||||
# Values: CMD
|
||||
#
|
||||
actionstop = printf %%b "Subject: [Fail2Ban] <name>: stopped on `uname -n`
|
||||
Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"`
|
||||
From: <sendername> <<sender>>
|
||||
To: <dest>\n
|
||||
Hi,\n
|
||||
The jail <name> has been stopped.\n
|
||||
Regards,\n
|
||||
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
|
||||
|
||||
# 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.
|
||||
|
|
@ -55,21 +23,13 @@ actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
|||
Hi,\n
|
||||
The IP <ip> has just been banned by Fail2Ban after
|
||||
<failures> attempts against <name>.\n\n
|
||||
Here are more information about <ip>:\n
|
||||
`/usr/bin/whois <ip>`\n\n
|
||||
Here is more information about <ip>:\n
|
||||
`/usr/bin/whois <ip> || echo missing whois program`\n\n
|
||||
Lines containing IP:<ip> in <logpath>\n
|
||||
`grep '\<<ip>\>' <logpath>`\n\n
|
||||
`grep '[^0-9]<ip>[^0-9]' <logpath>`\n\n
|
||||
Regards,\n
|
||||
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
|
||||
|
||||
# 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 =
|
||||
|
||||
[Init]
|
||||
|
||||
# Default name of the chain
|
||||
|
|
|
|||
37
config/action.d/sendmail-whois-matches.conf
Normal file
37
config/action.d/sendmail-whois-matches.conf
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# Fail2Ban configuration file
|
||||
#
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
#
|
||||
|
||||
[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.
|
||||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
||||
Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"`
|
||||
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 are more information about <ip>:\n
|
||||
`/usr/bin/whois <ip>`\n\n
|
||||
Matches:\n
|
||||
<matches>\n\n
|
||||
Regards,\n
|
||||
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
|
||||
|
||||
[Init]
|
||||
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
|
|
@ -10,38 +10,6 @@ before = sendmail-common.conf
|
|||
|
||||
[Definition]
|
||||
|
||||
# Option: actionstart
|
||||
# Notes.: command executed once at the start of Fail2Ban.
|
||||
# Values: CMD
|
||||
#
|
||||
actionstart = printf %%b "Subject: [Fail2Ban] <name>: started on `uname -n`
|
||||
Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"`
|
||||
From: <sendername> <<sender>>
|
||||
To: <dest>\n
|
||||
Hi,\n
|
||||
The jail <name> has been started successfully.\n
|
||||
Regards,\n
|
||||
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
|
||||
|
||||
# Option: actionstop
|
||||
# Notes.: command executed once at the end of Fail2Ban
|
||||
# Values: CMD
|
||||
#
|
||||
actionstop = printf %%b "Subject: [Fail2Ban] <name>: stopped on `uname -n`
|
||||
Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"`
|
||||
From: <sendername> <<sender>>
|
||||
To: <dest>\n
|
||||
Hi,\n
|
||||
The jail <name> has been stopped.\n
|
||||
Regards,\n
|
||||
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
|
||||
|
||||
# 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.
|
||||
|
|
@ -55,19 +23,11 @@ actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
|||
Hi,\n
|
||||
The IP <ip> has just been banned by Fail2Ban after
|
||||
<failures> attempts against <name>.\n\n
|
||||
Here are more information about <ip>:\n
|
||||
`/usr/bin/whois <ip>`\n
|
||||
Here is more information about <ip>:\n
|
||||
`/usr/bin/whois <ip> || echo missing whois program`\n
|
||||
Regards,\n
|
||||
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
|
||||
|
||||
# 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 =
|
||||
|
||||
[Init]
|
||||
|
||||
# Default name of the chain
|
||||
|
|
|
|||
|
|
@ -10,38 +10,6 @@ before = sendmail-common.conf
|
|||
|
||||
[Definition]
|
||||
|
||||
# Option: actionstart
|
||||
# Notes.: command executed once at the start of Fail2Ban.
|
||||
# Values: CMD
|
||||
#
|
||||
actionstart = printf %%b "Subject: [Fail2Ban] <name>: started on `uname -n`
|
||||
Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"`
|
||||
From: <sendername> <<sender>>
|
||||
To: <dest>\n
|
||||
Hi,\n
|
||||
The jail <name> has been started successfully.\n
|
||||
Regards,\n
|
||||
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
|
||||
|
||||
# Option: actionstop
|
||||
# Notes.: command executed once at the end of Fail2Ban
|
||||
# Values: CMD
|
||||
#
|
||||
actionstop = printf %%b "Subject: [Fail2Ban] <name>: stopped on `uname -n`
|
||||
Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"`
|
||||
From: <sendername> <<sender>>
|
||||
To: <dest>\n
|
||||
Hi,\n
|
||||
The jail <name> has been stopped.\n
|
||||
Regards,\n
|
||||
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
|
||||
|
||||
# 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.
|
||||
|
|
@ -58,14 +26,6 @@ actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
|
|||
Regards,\n
|
||||
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
|
||||
|
||||
# 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 =
|
||||
|
||||
[Init]
|
||||
|
||||
# Default name of the chain
|
||||
|
|
|
|||
225
config/action.d/smtp.py
Normal file
225
config/action.d/smtp.py
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
|
||||
# vi: set ft=python sts=4 ts=4 sw=4 noet :
|
||||
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Fail2Ban; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
import sys
|
||||
import socket
|
||||
import smtplib
|
||||
from email.mime.text import MIMEText
|
||||
from email.utils import formatdate, formataddr
|
||||
|
||||
from fail2ban.server.actions import ActionBase, CallingMap
|
||||
|
||||
messages = {}
|
||||
messages['start'] = \
|
||||
"""Hi,
|
||||
|
||||
The jail %(jailname)s has been started successfully.
|
||||
|
||||
Regards,
|
||||
Fail2Ban"""
|
||||
|
||||
messages['stop'] = \
|
||||
"""Hi,
|
||||
|
||||
The jail %(jailname)s has been stopped.
|
||||
|
||||
Regards,
|
||||
Fail2Ban"""
|
||||
|
||||
messages['ban'] = {}
|
||||
messages['ban']['head'] = \
|
||||
"""Hi,
|
||||
|
||||
The IP %(ip)s has just been banned for %(bantime)s seconds
|
||||
by Fail2Ban after %(failures)i attempts against %(jailname)s.
|
||||
"""
|
||||
messages['ban']['tail'] = \
|
||||
"""
|
||||
Regards,
|
||||
Fail2Ban"""
|
||||
messages['ban']['matches'] = \
|
||||
"""
|
||||
Matches for this ban:
|
||||
%(matches)s
|
||||
"""
|
||||
messages['ban']['ipmatches'] = \
|
||||
"""
|
||||
Matches for %(ip)s:
|
||||
%(ipmatches)s
|
||||
"""
|
||||
messages['ban']['ipjailmatches'] = \
|
||||
"""
|
||||
Matches for %(ip)s for jail %(jailname)s:
|
||||
%(ipjailmatches)s
|
||||
"""
|
||||
|
||||
class SMTPAction(ActionBase):
|
||||
"""Fail2Ban action which sends emails to inform on jail starting,
|
||||
stopping and bans.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, jail, name, host="localhost", user=None, password=None,
|
||||
sendername="Fail2Ban", sender="fail2ban", dest="root", matches=None):
|
||||
"""Initialise action.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
jail : Jail
|
||||
The jail which the action belongs to.
|
||||
name : str
|
||||
Named assigned to the action.
|
||||
host : str, optional
|
||||
SMTP host, of host:port format. Default host "localhost" and
|
||||
port "25"
|
||||
user : str, optional
|
||||
Username used for authentication with SMTP server.
|
||||
password : str, optional
|
||||
Password used for authentication with SMTP server.
|
||||
sendername : str, optional
|
||||
Name to use for from address in email. Default "Fail2Ban".
|
||||
sender : str, optional
|
||||
Email address to use for from address in email.
|
||||
Default "fail2ban".
|
||||
dest : str, optional
|
||||
Email addresses of intended recipient(s) in comma space ", "
|
||||
delimited format. Default "root".
|
||||
matches : str, optional
|
||||
Type of matches to be included from ban in email. Can be one
|
||||
of "matches", "ipmatches" or "ipjailmatches". Default None
|
||||
(see man jail.conf.5).
|
||||
"""
|
||||
|
||||
super(SMTPAction, self).__init__(jail, name)
|
||||
|
||||
self.host = host
|
||||
#TODO: self.ssl = ssl
|
||||
|
||||
self.user = user
|
||||
self.password =password
|
||||
|
||||
self.fromname = sendername
|
||||
self.fromaddr = sender
|
||||
self.toaddr = dest
|
||||
|
||||
self.matches = matches
|
||||
|
||||
self.message_values = CallingMap(
|
||||
jailname = self._jail.name,
|
||||
hostname = socket.gethostname,
|
||||
bantime = self._jail.actions.getBanTime,
|
||||
)
|
||||
|
||||
def _sendMessage(self, subject, text):
|
||||
"""Sends message based on arguments and instance's properties.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
subject : str
|
||||
Subject of the email.
|
||||
text : str
|
||||
Body of the email.
|
||||
|
||||
Raises
|
||||
------
|
||||
SMTPConnectionError
|
||||
Error on connecting to host.
|
||||
SMTPAuthenticationError
|
||||
Error authenticating with SMTP server.
|
||||
SMTPException
|
||||
See Python `smtplib` for full list of other possible
|
||||
exceptions.
|
||||
"""
|
||||
msg = MIMEText(text)
|
||||
msg['Subject'] = subject
|
||||
msg['From'] = formataddr((self.fromname, self.fromaddr))
|
||||
msg['To'] = self.toaddr
|
||||
msg['Date'] = formatdate()
|
||||
|
||||
smtp = smtplib.SMTP()
|
||||
try:
|
||||
self._logSys.debug("Connected to SMTP '%s', response: %i: %s",
|
||||
self.host, *smtp.connect(self.host))
|
||||
if self.user and self.password:
|
||||
smtp.login(self.user, self.password)
|
||||
failed_recipients = smtp.sendmail(
|
||||
self.fromaddr, self.toaddr.split(", "), msg.as_string())
|
||||
except smtplib.SMTPConnectError:
|
||||
self._logSys.error("Error connecting to host '%s'", self.host)
|
||||
raise
|
||||
except smtplib.SMTPAuthenticationError:
|
||||
self._logSys.error(
|
||||
"Failed to authenticate with host '%s' user '%s'",
|
||||
self.host, self.user)
|
||||
raise
|
||||
except smtplib.SMTPException:
|
||||
self._logSys.error(
|
||||
"Error sending mail to host '%s' from '%s' to '%s'",
|
||||
self.host, self.fromaddr, self.toaddr)
|
||||
raise
|
||||
else:
|
||||
if failed_recipients:
|
||||
self._logSys.warning(
|
||||
"Email to '%s' failed to following recipients: %r",
|
||||
self.toaddr, failed_recipients)
|
||||
self._logSys.debug("Email '%s' successfully sent", subject)
|
||||
finally:
|
||||
try:
|
||||
self._logSys.debug("Disconnected from '%s', response %i: %s",
|
||||
self.host, *smtp.quit())
|
||||
except smtplib.SMTPServerDisconnected:
|
||||
pass # Not connected
|
||||
|
||||
def start(self):
|
||||
"""Sends email to recipients informing that the jail has started.
|
||||
"""
|
||||
self._sendMessage(
|
||||
"[Fail2Ban] %(jailname)s: started on %(hostname)s" %
|
||||
self.message_values,
|
||||
messages['start'] % self.message_values)
|
||||
|
||||
def stop(self):
|
||||
"""Sends email to recipients informing that the jail has stopped.
|
||||
"""
|
||||
self._sendMessage(
|
||||
"[Fail2Ban] %(jailname)s: stopped on %(hostname)s" %
|
||||
self.message_values,
|
||||
messages['stop'] % self.message_values)
|
||||
|
||||
def ban(self, aInfo):
|
||||
"""Sends email to recipients informing that ban has occurred.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
aInfo : dict
|
||||
Dictionary which includes information in relation to
|
||||
the ban.
|
||||
"""
|
||||
aInfo.update(self.message_values)
|
||||
message = "".join([
|
||||
messages['ban']['head'],
|
||||
messages['ban'].get(self.matches, ""),
|
||||
messages['ban']['tail']
|
||||
])
|
||||
self._sendMessage(
|
||||
"[Fail2Ban] %(jailname)s: banned %(ip)s from %(hostname)s" %
|
||||
aInfo,
|
||||
message % aInfo)
|
||||
|
||||
Action = SMTPAction
|
||||
40
config/action.d/ufw.conf
Normal file
40
config/action.d/ufw.conf
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# Fail2Ban action configuration file for ufw
|
||||
#
|
||||
# You are required to run "ufw enable" before this will have an effect.
|
||||
#
|
||||
# The insert position should be approprate to block the required traffic.
|
||||
# A number after an allow rule to the application won't be much use.
|
||||
|
||||
[Definition]
|
||||
|
||||
actionstart =
|
||||
|
||||
actionstop =
|
||||
|
||||
actioncheck =
|
||||
|
||||
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
|
||||
|
||||
[Init]
|
||||
# Option: insertpos
|
||||
# Notes.: The postition number in the firewall list to insert the block rule
|
||||
insertpos = 1
|
||||
|
||||
# Option: blocktype
|
||||
# Notes.: reject or deny
|
||||
blocktype = reject
|
||||
|
||||
# Option: destination
|
||||
# Notes.: The destination address to block in the ufw rule
|
||||
destination = any
|
||||
|
||||
# Option: application
|
||||
# Notes.: application from sudo ufw app list
|
||||
application =
|
||||
|
||||
# DEV NOTES:
|
||||
#
|
||||
# Author: Guilhem Lettron
|
||||
# Enhancements: Daniel Black
|
||||
124
config/action.d/xarf-login-attack.conf
Normal file
124
config/action.d/xarf-login-attack.conf
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
# Fail2Ban action for sending xarf Login-Attack messages to IP owner
|
||||
#
|
||||
# IMPORTANT:
|
||||
#
|
||||
# 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
|
||||
# action for:
|
||||
# * The recidive where the IP has been banned multiple times
|
||||
# * Where maxretry has been set quite high, beyond the normal user typing
|
||||
# password incorrectly.
|
||||
# * For filters that have a low likelyhood of receiving human errors
|
||||
#
|
||||
# DEPENDANCIES:
|
||||
#
|
||||
# This requires the dig command from bind-utils
|
||||
#
|
||||
# This uses the https://abusix.com/contactdb.html to lookup abuse contacts.
|
||||
#
|
||||
# XARF is a specification for sending a formatted response
|
||||
# for non-messaging based abuse including:
|
||||
#
|
||||
# Login-Attack, Malware-Attack, Fraud (Phishing, etc.), Info DNSBL
|
||||
#
|
||||
# For details see:
|
||||
# https://github.com/abusix/xarf-specification
|
||||
# http://www.x-arf.org/schemata.html
|
||||
#
|
||||
# Author: Daniel Black
|
||||
# Based on complain written by Russell Odom <russ@gloomytrousers.co.uk>
|
||||
#
|
||||
#
|
||||
|
||||
[Definition]
|
||||
|
||||
actionstart =
|
||||
|
||||
actionstop =
|
||||
|
||||
actioncheck =
|
||||
|
||||
actionban = oifs=${IFS}; IFS=.;SEP_IP=( <ip> ); set -- ${SEP_IP}; ADDRESSES=$(dig +short -t txt -q $4.$3.$2.$1.abuse-contacts.abusix.org); IFS=${oifs}
|
||||
IP=<ip>
|
||||
FROM=<sender>
|
||||
SERVICE=<service>
|
||||
FAILURES=<failures>
|
||||
REPORTID=<time>@`uname -n`
|
||||
TLP=<tlp>
|
||||
PORT=<port>
|
||||
DATE=`LC_TIME=C date -u --date=@<time> +"%%a, %%d %%h %%Y %%T +0000"`
|
||||
if [ ! -z "$ADDRESSES" ]; then
|
||||
(printf -- %%b "<header>\n<message>\n<report>\n";
|
||||
date '+Note: Local timezone is %%z (%%Z)';
|
||||
printf -- %%b "<ipmatches>\n\n<footer>") | <mailcmd> <mailargs> ${ADDRESSES//,/\" \"}
|
||||
fi
|
||||
|
||||
actionunban =
|
||||
|
||||
[Init]
|
||||
# Option: header
|
||||
# Notes: This is really a fixed value
|
||||
header = Subject: abuse report about $IP - $DATE\nAuto-Submitted: auto-generated\nX-XARF: PLAIN\nContent-Transfer-Encoding: 7bit\nContent-Type: multipart/mixed; charset=utf8;\n boundary=Abuse-bfbb0f920793ac03cb8634bde14d8a1e;\n\n--Abuse-bfbb0f920793ac03cb8634bde14d8a1e\nMIME-Version: 1.0\nContent-Transfer-Encoding: 7bit\nContent-Type: text/plain; charset=utf-8;\n
|
||||
|
||||
# Option: footer
|
||||
# Notes: This is really a fixed value and needs to match the report and header
|
||||
# mime delimiters
|
||||
footer = \n\n--Abuse-bfbb0f920793ac03cb8634bde14d8a1e--
|
||||
|
||||
# Option: report
|
||||
# Notes: Intended to be fixed
|
||||
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
|
||||
# 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
|
||||
|
||||
# Option: loglines
|
||||
# Notes.: The number of log lines to search for the IP for the report
|
||||
loglines = 9000
|
||||
|
||||
# Option: mailcmd
|
||||
# Notes.: Your system mail command. It is passed the recipient
|
||||
# Values: CMD
|
||||
#
|
||||
mailcmd = /usr/sbin/sendmail
|
||||
|
||||
# Option: mailargs
|
||||
# Notes.: Additional arguments to mail command. e.g. for standard Unix mail:
|
||||
# CC reports to another address:
|
||||
# -c me@example.com
|
||||
# Appear to come from a different address - the '--' indicates
|
||||
# arguments to be passed to Sendmail:
|
||||
# -- -f me@example.com
|
||||
# Values: [ STRING ]
|
||||
#
|
||||
mailargs = -f <sender>
|
||||
|
||||
# Option: tlp
|
||||
# Notes.: Traffic light protocol defining the sharing of this information.
|
||||
# http://www.trusted-introducer.org/ISTLPv11.pdf
|
||||
# green is share to those involved in network security but it is not
|
||||
# to be released to the public.
|
||||
tlp = green
|
||||
|
||||
# ALL of the following parameters should be set so the report contains
|
||||
# meaningful information
|
||||
|
||||
# Option: service
|
||||
# Notes.: This is the service type that was attacked. e.g. ssh, pop3
|
||||
service = unspecified
|
||||
|
||||
# Option: logpath
|
||||
# Notes: Path to the log files which contain relevant lines for the abuser IP
|
||||
# Values: Filename(s) space separated and can contain wildcards (these are
|
||||
# greped for the IP so make sure these aren't too long
|
||||
logpath = /dev/null
|
||||
|
||||
# Option: sender
|
||||
# Notes.: This is the sender that is included in the XARF report
|
||||
sender = fail2ban@`uname -n`
|
||||
|
||||
# Option: port
|
||||
# Notes.: This is the port number that received the login-attack
|
||||
port = 0
|
||||
|
||||
|
|
@ -6,20 +6,22 @@
|
|||
# file, but provide customizations in fail2ban.local file, e.g.:
|
||||
#
|
||||
# [Definition]
|
||||
# loglevel = 4
|
||||
# loglevel = DEBUG
|
||||
#
|
||||
|
||||
[Definition]
|
||||
|
||||
# Option: loglevel
|
||||
# Notes.: Set the log level output.
|
||||
# 1 = ERROR
|
||||
# 2 = WARN
|
||||
# 3 = INFO
|
||||
# 4 = DEBUG
|
||||
# Values: [ NUM ] Default: 1
|
||||
# CRITICAL
|
||||
# ERROR
|
||||
# WARNING
|
||||
# NOTICE
|
||||
# INFO
|
||||
# DEBUG
|
||||
# Values: [ LEVEL ] Default: ERROR
|
||||
#
|
||||
loglevel = 3
|
||||
loglevel = INFO
|
||||
|
||||
# Option: logtarget
|
||||
# Notes.: Set the log target. This could be a file, SYSLOG, STDERR or STDOUT.
|
||||
|
|
@ -47,3 +49,15 @@ socket = /var/run/fail2ban/fail2ban.sock
|
|||
#
|
||||
pidfile = /var/run/fail2ban/fail2ban.pid
|
||||
|
||||
# Options: dbfile
|
||||
# Notes.: Set the file for the fail2ban persistent data to be stored.
|
||||
# A value of ":memory:" means database is only stored in memory
|
||||
# and data is lost once fail2ban is stops.
|
||||
# A value of "None" disables the database.
|
||||
# Values: [ None :memory: FILE ] Default: /var/lib/fail2ban/fail2ban.sqlite3
|
||||
dbfile = /var/lib/fail2ban/fail2ban.sqlite3
|
||||
|
||||
# Options: dbpurgeage
|
||||
# Notes.: Sets age at which bans should be purged from the database
|
||||
# Values: [ SECONDS ] Default: 86400 (24hours)
|
||||
dbpurgeage = 86400
|
||||
|
|
|
|||
|
|
@ -10,19 +10,19 @@ before = apache-common.conf
|
|||
[Definition]
|
||||
|
||||
|
||||
failregex = ^%(_apache_error_client)s (AH01797: )?client denied by server configuration: (uri )?\S*\s*$
|
||||
^%(_apache_error_client)s (AH01617: )?user .* authentication failure for "\S*": Password Mismatch$
|
||||
^%(_apache_error_client)s (AH01618: )?user .* not found(: )?\S*\s*$
|
||||
^%(_apache_error_client)s (AH01614: )?client used wrong authentication scheme: \S*\s*$
|
||||
failregex = ^%(_apache_error_client)s (AH01797: )?client denied by server configuration: (uri )?\S*(, referer: \S+)?\s*$
|
||||
^%(_apache_error_client)s (AH01617: )?user .*? authentication failure for "\S*": Password Mismatch(, referer: \S+)?$
|
||||
^%(_apache_error_client)s (AH01618: )?user .*? not found(: )?\S*(, referer: \S+)?\s*$
|
||||
^%(_apache_error_client)s (AH01614: )?client used wrong authentication scheme: \S*(, referer: \S+)?\s*$
|
||||
^%(_apache_error_client)s (AH\d+: )?Authorization of user \S+ to access \S* failed, reason: .*$
|
||||
^%(_apache_error_client)s (AH0179[24]: )?(Digest: )?user .*: password mismatch: \S*\s*$
|
||||
^%(_apache_error_client)s (AH0179[01]: |Digest: )user `.*' in realm `.+' (not found|denied by provider): \S*\s*$
|
||||
^%(_apache_error_client)s (AH01631: )?user .*: authorization failure for "\S*":\s*$
|
||||
^%(_apache_error_client)s (AH01775: )?(Digest: )?invalid nonce .* received - length is not \S+\s*$
|
||||
^%(_apache_error_client)s (AH01788: )?(Digest: )?realm mismatch - got `.*' but expected `.+'\s*$
|
||||
^%(_apache_error_client)s (AH01789: )?(Digest: )?unknown algorithm `.*' received: \S*\s*$
|
||||
^%(_apache_error_client)s (AH01793: )?invalid qop `.*' received: \S*\s*$
|
||||
^%(_apache_error_client)s (AH01777: )?(Digest: )?invalid nonce .* received - user attempted time travel\s*$
|
||||
^%(_apache_error_client)s (AH0179[24]: )?(Digest: )?user .*?: password mismatch: \S*(, referer: \S+)?\s*$
|
||||
^%(_apache_error_client)s (AH0179[01]: |Digest: )user `.*?' in realm `.+' (not found|denied by provider): \S*(, referer: \S+)?\s*$
|
||||
^%(_apache_error_client)s (AH01631: )?user .*?: authorization failure for "\S*":(, referer: \S+)?\s*$
|
||||
^%(_apache_error_client)s (AH01775: )?(Digest: )?invalid nonce .* received - length is not \S+(, referer: \S+)?\s*$
|
||||
^%(_apache_error_client)s (AH01788: )?(Digest: )?realm mismatch - got `.*?' but expected `.+'(, referer: \S+)?\s*$
|
||||
^%(_apache_error_client)s (AH01789: )?(Digest: )?unknown algorithm `.*?' received: \S*(, referer: \S+)?\s*$
|
||||
^%(_apache_error_client)s (AH01793: )?invalid qop `.*?' received: \S*(, referer: \S+)?\s*$
|
||||
^%(_apache_error_client)s (AH01777: )?(Digest: )?invalid nonce .*? received - user attempted time travel(, referer: \S+)?\s*$
|
||||
|
||||
ignoreregex =
|
||||
|
||||
|
|
@ -50,5 +50,7 @@ ignoreregex =
|
|||
# ^%(_apache_error_client)s (AH01779: )?user .*: one-time-nonce mismatch - sending new nonce\s*$
|
||||
# ^%(_apache_error_client)s (AH02486: )?realm mismatch - got `.*' but no realm specified\s*$
|
||||
#
|
||||
# referer is always in error log messages if it exists added as per the log_error_core function in server/log.c
|
||||
#
|
||||
# Author: Cyril Jaquier
|
||||
# Major edits by Daniel Black
|
||||
|
|
|
|||
48
config/filter.d/apache-botsearch.conf
Normal file
48
config/filter.d/apache-botsearch.conf
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
# Fail2Ban filter to match web requests for selected URLs that don't exist
|
||||
#
|
||||
# This filter is aimed at blocking specific URLs that don't exist. This
|
||||
# could be a set of URLs places in a Disallow: directive in robots.txt or
|
||||
# just some web services that don't exist caused bots are searching for
|
||||
# exploitable content. This filter is designed to have a low false postitive
|
||||
# rate due.
|
||||
#
|
||||
# An alternative to this is the apache-noscript filter which blocks all
|
||||
# types of scripts that don't exist.
|
||||
#
|
||||
#
|
||||
# This is normally a predefined list of exploitable or valuable web services
|
||||
# that are hidden or aren't actually installed.
|
||||
#
|
||||
|
||||
[INCLUDES]
|
||||
|
||||
# overwrite with apache-common.local if _apache_error_client is incorrect.
|
||||
before = apache-common.conf
|
||||
|
||||
[Definition]
|
||||
|
||||
failregex = ^%(_apache_error_client)s ((AH001(28|30): )?File does not exist|(AH01264: )?script not found or unable to stat): <webroot><block>(, referer: \S+)?\s*$
|
||||
^%(_apache_error_client)s script '<webroot><block>' not found or unable to stat(, referer: \S+)?\s*$
|
||||
|
||||
ignoreregex =
|
||||
|
||||
|
||||
[Init]
|
||||
|
||||
# Webroot represents the webroot on which all other files are based
|
||||
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:
|
||||
#
|
||||
# Author: Daniel Black
|
||||
|
|
@ -8,12 +8,13 @@ after = apache-common.local
|
|||
|
||||
[DEFAULT]
|
||||
|
||||
_apache_error_client = \[[^]]*\] \[(error|\S+:\S+)\]( \[pid \d+:\S+ \d+\])? \[client <HOST>(:\d{1,5})?\]
|
||||
_apache_error_client = \[\] \[(:?error|\S+:\S+)\]( \[pid \d+(:\S+ \d+)?\])? \[client <HOST>(:\d{1,5})?\]
|
||||
|
||||
# Common prefix for [error] apache messages which also would include <HOST>
|
||||
# Depending on the version it could be
|
||||
# 2.2: [Sat Jun 01 11:23:08 2013] [error] [client 1.2.3.4]
|
||||
# 2.4: [Thu Jun 27 11:55:44.569531 2013] [core:info] [pid 4101:tid 2992634688] [client 1.2.3.4:46652]
|
||||
# 2.4 (perfork): [Mon Dec 23 07:49:01.981912 2013] [:error] [pid 3790] [client 204.232.202.107:46301] script '/var/www/timthumb.php' not found or unable to
|
||||
#
|
||||
# Reference: https://github.com/fail2ban/fail2ban/issues/268
|
||||
#
|
||||
|
|
|
|||
18
config/filter.d/apache-modsecurity.conf
Normal file
18
config/filter.d/apache-modsecurity.conf
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Fail2Ban apache-modsec filter
|
||||
#
|
||||
|
||||
[INCLUDES]
|
||||
|
||||
# Read common prefixes. If any customizations available -- read them from
|
||||
# apache-common.local
|
||||
before = apache-common.conf
|
||||
|
||||
[Definition]
|
||||
|
||||
|
||||
failregex = ^%(_apache_error_client)s ModSecurity: (\[.*?\] )*Access denied with code [45]\d\d.*$
|
||||
|
||||
ignoreregex =
|
||||
|
||||
# https://github.com/SpiderLabs/ModSecurity/wiki/ModSecurity-2-Data-Formats
|
||||
# Author: Daniel Black
|
||||
|
|
@ -1,5 +1,13 @@
|
|||
# Fail2Ban filter to block web requests for scripts (on non scripted websites)
|
||||
#
|
||||
# This matches many types of scripts that don't exist. This could generate a
|
||||
# lot of false positive matches in cases like wikis and forums where users
|
||||
# no affiliated with the website can insert links to missing files/scripts into
|
||||
# pages and cause non-malicious browsers of the site to trigger against this
|
||||
# filter.
|
||||
#
|
||||
# If you'd like to match specific URLs that don't exist see the
|
||||
# apache-botsearch filter.
|
||||
#
|
||||
|
||||
[INCLUDES]
|
||||
|
|
@ -9,8 +17,8 @@ before = apache-common.conf
|
|||
|
||||
[Definition]
|
||||
|
||||
failregex = ^%(_apache_error_client)s ((AH001(28|30): )?File does not exist|(AH01264: )?script not found or unable to stat): /\S*(\.php|\.asp|\.exe|\.pl)\s*$
|
||||
^%(_apache_error_client)s script '/\S*(\.php|\.asp|\.exe|\.pl)\S*' not found or unable to stat\s*$
|
||||
failregex = ^%(_apache_error_client)s ((AH001(28|30): )?File does not exist|(AH01264: )?script not found or unable to stat): /\S*(php([45]|[.-]cgi)?|\.asp|\.exe|\.pl)(, referer: \S+)?\s*$
|
||||
^%(_apache_error_client)s script '/\S*(php([45]|[.-]cgi)?|\.asp|\.exe|\.pl)\S*' not found or unable to stat(, referer: \S+)?\s*$
|
||||
|
||||
ignoreregex =
|
||||
|
||||
|
|
@ -19,6 +27,6 @@ ignoreregex =
|
|||
#
|
||||
# https://wiki.apache.org/httpd/ListOfErrors for apache error IDs
|
||||
#
|
||||
# Second regex, script '/\S*(\.php|\.asp|\.exe|\.pl)\S*' not found or unable to stat\s*$ is Before http-2.2
|
||||
# Second regex, script '/\S*(\.php|\.asp|\.exe|\.pl)\S*' not found or unable to stat\s*$ is in httpd-2.2
|
||||
#
|
||||
# Author: Cyril Jaquier
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ before = apache-common.conf
|
|||
|
||||
[Definition]
|
||||
|
||||
failregex = ^%(_apache_error_client)s ((AH0013[456]: )?Invalid (method|URI) in request .*( - possible attempt to establish SSL connection on non-SSL port)?|(AH00565: )?request failed: URI too long \(longer than \d+\)|request failed: erroneous characters after protocol string: .*|AH00566: request failed: invalid characters in URI)$
|
||||
failregex = ^%(_apache_error_client)s ((AH0013[456]: )?Invalid (method|URI) in request .*( - possible attempt to establish SSL connection on non-SSL port)?|(AH00565: )?request failed: URI too long \(longer than \d+\)|request failed: erroneous characters after protocol string: .*|AH00566: request failed: invalid characters in URI)(, referer: \S+)?$
|
||||
|
||||
ignoreregex =
|
||||
|
||||
|
|
|
|||
|
|
@ -1,24 +1,39 @@
|
|||
# Fail2Ban filter for asterisk authentication failures
|
||||
#
|
||||
|
||||
[INCLUDES]
|
||||
|
||||
# Read common prefixes. If any customizations available -- read them from
|
||||
# common.local
|
||||
before = common.conf
|
||||
|
||||
[Definition]
|
||||
|
||||
_daemon = asterisk
|
||||
|
||||
__pid_re = (?:\[\d+\])
|
||||
|
||||
# All Asterisk log messages begin like this:
|
||||
log_prefix= \[\]\s*(?:NOTICE|SECURITY)%(__pid_re)s:?(?:\[C-[\da-f]*\])? \S+:\d*
|
||||
log_prefix= (?:NOTICE|SECURITY)%(__pid_re)s:?(?:\[C-[\da-f]*\])? \S+:\d*( in \w+:)?
|
||||
|
||||
failregex = ^%(log_prefix)s Registration from '[^']*' failed for '<HOST>(:\d+)?' - (Wrong password|No matching peer found|Username/auth name mismatch|Device does not match ACL|Peer is not supposed to register|ACL error \(permit/deny\)|Not a local domain)$
|
||||
^%(log_prefix)s Call from '[^']*' \(<HOST>:\d+\) to extension '\d+' rejected because extension not found in context 'default'\.$
|
||||
^%(log_prefix)s Host <HOST> failed to authenticate as '[^']*'$
|
||||
^%(log_prefix)s No registration for peer '[^']*' \(from <HOST>\)$
|
||||
^%(log_prefix)s Host <HOST> failed MD5 authentication for '[^']*' \([^)]+\)$
|
||||
^%(log_prefix)s Failed to authenticate (user|device) [^@]+@<HOST>\S*$
|
||||
^%(log_prefix)s (?:handle_request_subscribe: )?Sending fake auth rejection for (device|user) \d*<sip:[^@]+@<HOST>>;tag=\w+\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]+")?$
|
||||
^\[\]\s*WARNING%(__pid_re)s:?(?:\[C-[\da-f]*\])? Ext\. s: "Rejecting unknown SIP connection from <HOST>"$
|
||||
failregex = ^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s Registration from '[^']*' failed for '<HOST>(:\d+)?' - (Wrong password|Username/auth name mismatch|No matching peer found|Not a local domain|Device does not match ACL|Peer is not supposed to register|ACL error \(permit/deny\)|Not a local domain)$
|
||||
^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s Call from '[^']*' \(<HOST>:\d+\) to extension '\d+' rejected because extension not found in context 'default'\.$
|
||||
^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s Host <HOST> failed to authenticate as '[^']*'$
|
||||
^(%(__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 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 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*WARNING%(__pid_re)s:?(?:\[C-[\da-f]*\])? )Ext\. s: "Rejecting unknown SIP connection from <HOST>"$
|
||||
|
||||
ignoreregex =
|
||||
|
||||
|
||||
# Author: Xavier Devlamynck
|
||||
# Author: Xavier Devlamynck / Daniel Black
|
||||
#
|
||||
# General log format - main/logger.c:ast_log
|
||||
# Address format - ast_sockaddr_stringify
|
||||
#
|
||||
# First regex: channels/chan_sip.c
|
||||
#
|
||||
# main/logger.c:ast_log_vsyslog - "in {functionname}:" only occurs in syslog
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ __daemon_combs_re = (?:%(__pid_re)s?:\s+%(__daemon_re)s|%(__daemon_re)s%(__pid_r
|
|||
|
||||
# Some messages have a kernel prefix with a timestamp
|
||||
# EXAMPLES: kernel: [769570.846956]
|
||||
__kernel_prefix = kernel: \[\d+\.\d+\]
|
||||
__kernel_prefix = kernel: \[ *\d+\.\d+\]
|
||||
|
||||
__hostname = \S+
|
||||
|
||||
|
|
|
|||
16
config/filter.d/counter-strike.conf
Normal file
16
config/filter.d/counter-strike.conf
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# Fail2Ban filter for failure attempts in Counter Strike-1.6
|
||||
#
|
||||
#
|
||||
|
||||
[Definition]
|
||||
|
||||
failregex = ^: Bad Rcon: "rcon \d+ "\S+" sv_contact ".*?"" from "<HOST>:\d+"$
|
||||
|
||||
|
||||
[Init]
|
||||
|
||||
datepattern = ^L %%d/%%m/%%Y - %%H:%%M:%%S
|
||||
|
||||
|
||||
# Author: Daniel Black
|
||||
|
||||
|
|
@ -10,14 +10,20 @@ before = common.conf
|
|||
_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*$
|
||||
^%(__prefix_line)s(pop3|imap)-login: (Info: )?(Aborted login|Disconnected)(: Inactivity)? \(((no auth attempts|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}(, session=<\w+>)?(, TLS( handshaking)?(: Disconnected)?)?\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*$
|
||||
|
||||
ignoreregex =
|
||||
|
||||
[Init]
|
||||
|
||||
journalmatch = _SYSTEMD_UNIT=dovecot.service
|
||||
|
||||
# DEV Notes:
|
||||
# * the first regex is essentially a copy of pam-generic.conf
|
||||
# * Probably doesn't do dovecot sql/ldap backends properly
|
||||
# * Removed the 'no auth attempts' log lines from the matches because produces
|
||||
# lots of false positives on misconfigured MTAs making regexp unuseable
|
||||
#
|
||||
# Author: Martin Waschbuesch
|
||||
# Daniel Black (rewrote with begin and end anchors)
|
||||
|
|
|
|||
37
config/filter.d/ejabberd-auth.conf
Normal file
37
config/filter.d/ejabberd-auth.conf
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# Fail2Ban configuration file
|
||||
#
|
||||
# Author: Steven Hiscocks
|
||||
#
|
||||
#
|
||||
|
||||
[Definition]
|
||||
|
||||
# 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\-.^_]+)
|
||||
# Multiline regexs should use tag "<SKIPLINES>" to separate lines.
|
||||
# This allows lines between the matching lines to continue to be
|
||||
# searched for other failures. This tag can be used multiple times.
|
||||
# Values: TEXT
|
||||
#
|
||||
failregex = ^=INFO REPORT==== ===\nI\(<0\.\d+\.0>:ejabberd_c2s:\d+\) : \([^)]+\) Failed authentication for .+ from IP <HOST> \({{(?:\d+,){3}\d+},\d+}\)$
|
||||
^(?:\.\d+)? \[info\] <0\.\d+\.\d>@ejabberd_c2s:wait_for_feature_request:\d+ \([^\)]+\) Failed authentication for \S+ from IP <HOST>$
|
||||
|
||||
# Option: ignoreregex
|
||||
# Notes.: regex to ignore. If this regex matches, the line is ignored.
|
||||
# Values: TEXT
|
||||
#
|
||||
ignoreregex =
|
||||
|
||||
[Init]
|
||||
|
||||
# "maxlines" is number of log lines to buffer for multi-line regex searches
|
||||
maxlines = 2
|
||||
|
||||
# Option: journalmatch
|
||||
# Notes.: systemd journalctl style match filter for journal based backend
|
||||
# Values: TEXT
|
||||
#
|
||||
journalmatch =
|
||||
|
|
@ -1,5 +1,22 @@
|
|||
# Fail2Ban filter for exim the spam rejection messages
|
||||
#
|
||||
# Honeypot traps are very useful for fighting spam. You just activate an email
|
||||
# address on your domain that you do not intend to use at all, and that normal
|
||||
# people do not risk to try for contacting you. It may be something that
|
||||
# spammers often test. You can also hide the address on a web page to be picked
|
||||
# by spam spiders. Or simply parse your mail logs for an invalid address
|
||||
# already being frequently targeted by spammers. Enable the address and
|
||||
# redirect it to the blackhole. In Exim's alias file, you would add the
|
||||
# following line (assuming the address is honeypot@yourdomain.com):
|
||||
#
|
||||
# honeypot: :blackhole:
|
||||
#
|
||||
# For the SA: Action: silently tossed message... to be logged exim's SAdevnull option needs to be used.
|
||||
#
|
||||
# To this filter use the jail.local should contain in the right jail:
|
||||
#
|
||||
# filter = exim-spam[honeypot=honeypot@yourdomain.com]
|
||||
#
|
||||
|
||||
[INCLUDES]
|
||||
|
||||
|
|
@ -12,9 +29,20 @@ before = exim-common.conf
|
|||
failregex = ^%(pid)s \S+ F=(<>|\S+@\S+) %(host_info)srejected by local_scan\(\): .{0,256}$
|
||||
^%(pid)s %(host_info)sF=(<>|[^@]+@\S+) rejected RCPT [^@]+@\S+: .*dnsbl.*\s*$
|
||||
^%(pid)s \S+ %(host_info)sF=(<>|[^@]+@\S+) rejected after DATA: This message contains a virus \(\S+\)\.\s*$
|
||||
^%(pid)s \S+ SA: Action: flagged as Spam but accepted: score=\d+\.\d+ required=\d+\.\d+ \(scanned in \d+/\d+ secs \| Message-Id: \S+\)\. From \S+ \(host=\S+ \[<HOST>\]\) for <honeypot>$
|
||||
^%(pid)s \S+ SA: Action: silently tossed message: score=\d+\.\d+ required=\d+\.\d+ trigger=\d+\.\d+ \(scanned in \d+/\d+ secs \| Message-Id: \S+\)\. From \S+ \(host=(\S+ )?\[<HOST>\]\) for \S+$
|
||||
|
||||
ignoreregex =
|
||||
|
||||
[Init]
|
||||
|
||||
# Option: honeypot
|
||||
# Notes.: honeypot is an email address that isn't published anywhere that a
|
||||
# legitimate email sender would send email too.
|
||||
# Values: email address
|
||||
|
||||
honeypot = trap@example.com
|
||||
|
||||
# DEV Notes:
|
||||
# The %(host_info) defination contains a <HOST> match
|
||||
#
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ before = exim-common.conf
|
|||
[Definition]
|
||||
|
||||
failregex = ^%(pid)s %(host_info)ssender verify fail for <\S+>: (?:Unknown user|Unrouteable address|all relevant MX records point to non-existent hosts)\s*$
|
||||
^%(pid)s (plain|login) authenticator failed for (\S+ )?\(\S+\) \[<HOST>\]: 535 Incorrect authentication data( \(set_id=.*\)|: \d+ Time\(s\))?\s*$
|
||||
^%(pid)s \w+ authenticator failed for (\S+ )?\(\S+\) \[<HOST>\]: 535 Incorrect authentication data( \(set_id=.*\)|: \d+ Time\(s\))?\s*$
|
||||
^%(pid)s %(host_info)sF=(<>|[^@]+@\S+) rejected RCPT [^@]+@\S+: (relay not permitted|Sender verify failed|Unknown user)\s*$
|
||||
^%(pid)s SMTP protocol synchronization error \([^)]*\): rejected (connection from|"\S+") %(host_info)s(next )?input=".*"\s*$
|
||||
^%(pid)s SMTP call from \S+ \[<HOST>\](:\d+)? (I=\[\S+\]:\d+ )?dropped: too many nonmail commands \(last was "\S+"\)\s*$
|
||||
|
|
|
|||
23
config/filter.d/freeswitch.conf
Normal file
23
config/filter.d/freeswitch.conf
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Fail2Ban configuration file
|
||||
#
|
||||
# Enable "log-auth-failures" on each Sofia profile to monitor
|
||||
# <param name="log-auth-failures" value="true"/>
|
||||
# -- this requires a high enough loglevel on your logs to save these messages.
|
||||
#
|
||||
# In the fail2ban jail.local file for this filter set ignoreip to the internal
|
||||
# IP addresses on your LAN.
|
||||
#
|
||||
|
||||
[Definition]
|
||||
|
||||
failregex = ^\.\d+ \[WARNING\] sofia_reg\.c:\d+ SIP auth (failure|challenge) \((REGISTER|INVITE)\) on sofia profile \'[^']+\' for \[.*\] from ip <HOST>$
|
||||
^\.\d+ \[WARNING\] sofia_reg\.c:\d+ Can't find user \[\d+@\d+\.\d+\.\d+\.\d+\] from <HOST>$
|
||||
|
||||
ignoreregex =
|
||||
|
||||
# Author: Rupa SChomaker, soapee01, Daniel Black
|
||||
# http://wiki.freeswitch.org/wiki/Fail2ban
|
||||
# Thanks to Jim on mailing list of samples and guidance
|
||||
#
|
||||
# No need to match the following. Its a duplicate of the SIP auth regex.
|
||||
# ^\.\d+ \[DEBUG\] sofia\.c:\d+ IP <HOST> Rejected by acl "\S+"\. Falling back to Digest auth\.$
|
||||
14
config/filter.d/groupoffice.conf
Normal file
14
config/filter.d/groupoffice.conf
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# Fail2Ban filter for Group-Office
|
||||
#
|
||||
# Enable logging with:
|
||||
# $config['info_log']='/home/groupoffice/log/info.log';
|
||||
#
|
||||
|
||||
[Definition]
|
||||
|
||||
failregex = ^\[\]LOGIN FAILED for user: "\S+" from IP: <HOST>$
|
||||
|
||||
|
||||
|
||||
# Author: Daniel Black
|
||||
|
||||
22
config/filter.d/guacamole.conf
Normal file
22
config/filter.d/guacamole.conf
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# Fail2Ban configuration file for guacamole
|
||||
#
|
||||
# Author: Steven Hiscocks
|
||||
#
|
||||
|
||||
[Definition]
|
||||
|
||||
# Option: failregex
|
||||
# Notes.: regex to match the password failures messages in the logfile.
|
||||
# Values: TEXT
|
||||
#
|
||||
failregex = ^.*\nWARNING: Authentication attempt from <HOST> for user "[^"]*" failed\.$
|
||||
|
||||
# Option: ignoreregex
|
||||
# Notes.: regex to ignore. If this regex matches, the line is ignored.
|
||||
# Values: TEXT
|
||||
#
|
||||
ignoreregex =
|
||||
|
||||
[Init]
|
||||
# "maxlines" is number of log lines to buffer for multi-line regex searches
|
||||
maxlines = 2
|
||||
16
config/filter.d/horde.conf
Normal file
16
config/filter.d/horde.conf
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# fail2ban filter configuration for horde
|
||||
|
||||
|
||||
[Definition]
|
||||
|
||||
|
||||
failregex = ^ HORDE \[error\] \[(horde|imp)\] FAILED LOGIN for \S+ \[<HOST>\](\(forwarded for \[\S+\]\))? to (Horde|{[^}]+}) \[(pid \d+ )?on line \d+ of \S+\]$
|
||||
|
||||
|
||||
ignoreregex =
|
||||
|
||||
# DEV NOTES:
|
||||
# https://github.com/horde/horde/blob/master/imp/lib/Auth.php#L132
|
||||
# https://github.com/horde/horde/blob/master/horde/login.php
|
||||
#
|
||||
# Author: Daniel Black
|
||||
17
config/filter.d/kerio.conf
Normal file
17
config/filter.d/kerio.conf
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# Fail2ban filter for kerio
|
||||
|
||||
[Definition]
|
||||
|
||||
failregex = ^ SMTP Spam attack detected from <HOST>,
|
||||
^ IP address <HOST> found in DNS blacklist \S+, mail from \S+ to \S+$
|
||||
^ Relay attempt from IP address <HOST>
|
||||
^ Attempt to deliver to unknown recipient \S+, from \S+, IP address <HOST>$
|
||||
[Init]
|
||||
|
||||
datepattern = ^\[%%d/%%b/%%Y %%H:%%M:%%S\]
|
||||
|
||||
# DEV NOTES:
|
||||
#
|
||||
# Author: A.P. Lawrence
|
||||
#
|
||||
# Based off: http://aplawrence.com/Kerio/fail2ban.html
|
||||
17
config/filter.d/nagios.conf
Normal file
17
config/filter.d/nagios.conf
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# Fail2Ban filter for Nagios Remote Plugin Executor (nrpe2)
|
||||
# Detecting unauthorized access to the nrpe2 daemon
|
||||
# typically logged in /var/log/messages syslog
|
||||
#
|
||||
|
||||
[INCLUDES]
|
||||
# Read syslog common prefixes
|
||||
before = common.conf
|
||||
|
||||
[Definition]
|
||||
_daemon = nrpe
|
||||
failregex = ^%(__prefix_line)sHost <HOST> is not allowed to talk to us!\s*$
|
||||
ignoreregex =
|
||||
|
||||
# DEV Notes:
|
||||
#
|
||||
# Author: Ivo Truxa - 2014/02/03
|
||||
|
|
@ -34,15 +34,13 @@ __daemon_combs_re=(?:%(__pid_re)s?:\s+%(__daemon_re)s|%(__daemon_re)s%(__pid_re)
|
|||
# this can be optional (for instance if we match named native log files)
|
||||
__line_prefix=(?:\s\S+ %(__daemon_combs_re)s\s+)?
|
||||
|
||||
failregex = ^%(__line_prefix)s(\.\d+)?( error:)?\s*client <HOST>#\S+( \([\S.]+\))?: (view (internal|external): )?query(?: \(cache\))? '.*' denied\s*$
|
||||
^%(__line_prefix)s(\.\d+)?( error:)?\s*client <HOST>#\S+( \([\S.]+\))?: zone transfer '\S+/AXFR/\w+' denied\s*$
|
||||
^%(__line_prefix)s(\.\d+)?( error:)?\s*client <HOST>#\S+( \([\S.]+\))?: bad zone transfer request: '\S+/IN': non-authoritative zone \(NOTAUTH\)\s*$
|
||||
failregex = ^%(__line_prefix)s( error:)?\s*client <HOST>#\S+( \([\S.]+\))?: (view (internal|external): )?query(?: \(cache\))? '.*' denied\s*$
|
||||
^%(__line_prefix)s( error:)?\s*client <HOST>#\S+( \([\S.]+\))?: zone transfer '\S+/AXFR/\w+' denied\s*$
|
||||
^%(__line_prefix)s( error:)?\s*client <HOST>#\S+( \([\S.]+\))?: bad zone transfer request: '\S+/IN': non-authoritative zone \(NOTAUTH\)\s*$
|
||||
|
||||
# DEV Notes:
|
||||
# Trying to generalize the
|
||||
# structure which is general to capture general patterns in log
|
||||
# lines to cover different configurations/distributions
|
||||
#
|
||||
# (\.\d+)? is a really ugly catch of the microseconds not captured in the date detector
|
||||
#
|
||||
# Author: Yaroslav Halchenko
|
||||
|
|
|
|||
26
config/filter.d/nsd.conf
Normal file
26
config/filter.d/nsd.conf
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# Fail2Ban configuration file
|
||||
#
|
||||
# Author: Bas van den Dikkenberg
|
||||
#
|
||||
#
|
||||
|
||||
[INCLUDES]
|
||||
|
||||
# Read common prefixes. If any customizations available -- read them from
|
||||
# common.local
|
||||
before = common.conf
|
||||
|
||||
|
||||
[Definition]
|
||||
|
||||
_daemon = nsd
|
||||
|
||||
# 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)sinfo: ratelimit block .* query <HOST> TYPE255$
|
||||
^\[\]%(__prefix_line)sinfo: .* <HOST> refused, no acl matches\.$
|
||||
15
config/filter.d/openwebmail.conf
Normal file
15
config/filter.d/openwebmail.conf
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# Fail2Ban filter for Openwebmail
|
||||
# banning hosts with authentication errors in /var/log/openwebmail.log
|
||||
# OpenWebMail http://openwebmail.org
|
||||
#
|
||||
|
||||
[Definition]
|
||||
|
||||
failregex = ^ - \[\d+\] \(<HOST>\) (?P<USER>\S+) - login error - (no such user - loginname=(?P=USER)|auth_unix.pl, ret -4, Password incorrect)$
|
||||
^ - \[\d+\] \(<HOST>\) (?P<USER>\S+) - userinfo error - auth_unix.pl, ret -4, User (?P=USER) doesn't exist$
|
||||
|
||||
ignoreregex =
|
||||
|
||||
# DEV Notes:
|
||||
#
|
||||
# Author: Ivo Truxa (c) 2013 truXoft.com
|
||||
|
|
@ -15,7 +15,12 @@ _daemon = postfix/smtpd
|
|||
failregex = ^%(__prefix_line)sNOQUEUE: reject: RCPT from \S+\[<HOST>\]: 554 5\.7\.1 .*$
|
||||
^%(__prefix_line)sNOQUEUE: reject: RCPT from \S+\[<HOST>\]: 450 4\.7\.1 : Helo command rejected: Host not found; from=<> to=<> proto=ESMTP helo= *$
|
||||
^%(__prefix_line)sNOQUEUE: reject: VRFY from \S+\[<HOST>\]: 550 5\.1\.1 .*$
|
||||
^%(__prefix_line)simproper command pipelining after \S+ from [^[]*\[<HOST>\]:?$
|
||||
|
||||
ignoreregex =
|
||||
|
||||
[Init]
|
||||
|
||||
journalmatch = _SYSTEMD_UNIT=postfix.service
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
# Fail2Ban fitler for the Proftpd FTP daemon
|
||||
#
|
||||
# Set "UseReverseDNS off" in proftpd.conf to avoid the need for DNS.
|
||||
# See: http://www.proftpd.org/docs/howto/DNS.html
|
||||
|
||||
[INCLUDES]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +1,30 @@
|
|||
# Fail2Ban filter for pureftp
|
||||
#
|
||||
# Disable hostname based logging by:
|
||||
#
|
||||
# Start pure-ftpd with the -H switch or on Ubuntu 'echo yes > /etc/pure-ftpd/conf/DontResolve'
|
||||
#
|
||||
#
|
||||
|
||||
[INCLUDES]
|
||||
|
||||
before = common.conf
|
||||
|
||||
[Definition]
|
||||
|
||||
# Error message specified in multiple languages
|
||||
__errmsg = (?:Authentication failed for user|Erreur d'authentification pour l'utilisateur)
|
||||
_daemon = pure-ftpd
|
||||
|
||||
failregex = ^%(__prefix_line)s\(.+?@<HOST>\) \[WARNING\] %(__errmsg)s \[.+\]\s*$
|
||||
# Error message specified in multiple languages
|
||||
__errmsg = (?:<3A>ϥΪ<CFA5>\[.*\]<5D><><EFBFBD>ҥ<EFBFBD><D2A5><EFBFBD>|ʹ<><CAB9><EFBFBD><EFBFBD>\[.*\]<5D><>֤ʧ<D6A4><CAA7>|\[.*\] kullan<61>c<EFBFBD>s<EFBFBD> i<>in giri<72> hatal<61>|<7C><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> \[.*\]|Godkjennelse mislyktes for \[.*\]|Beh<65>righetskontroll misslyckas f<>r anv<6E>ndare \[.*\]|Autentifikacia uzivatela zlyhala \[.*\]|Autentificare esuata pentru utilizatorul \[.*\]|Autentica<63><61>o falhou para usu<73>rio \[.*\]|Autentyfikacja nie powiod<6F>a si<73> dla u<>ytkownika \[.*\]|Autorisatie faalde voor gebruiker \[.*\]|\[.*\] <20><><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>|Autenticazione falita per l'utente \[.*\]|Azonos<6F>t<EFBFBD>s sikertelen \[.*\] felhaszn<7A>l<EFBFBD>nak|\[.*\] c'est un batard, il connait pas son code|Erreur d'authentification pour l'utilisateur \[.*\]|Autentificaci<63>n fallida para el usuario \[.*\]|Authentication failed for user \[.*\]|Authentifizierung fehlgeschlagen f<>r Benutzer \[.*\].|Godkendelse mislykkedes for \[.*\]|Autentifikace u<>ivatele selhala \[.*\])
|
||||
|
||||
failregex = ^%(__prefix_line)s\(.+?@<HOST>\) \[WARNING\] %(__errmsg)s\s*$
|
||||
|
||||
ignoreregex =
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
# Modified: Yaroslav Halchenko for pure-ftpd
|
||||
# Documentation thanks to Blake on http://www.fail2ban.org/wiki/index.php?title=Fail2ban:Community_Portal
|
||||
#
|
||||
# Only logs to syslog though facility can be changed configuration file/command line
|
||||
#
|
||||
# fgrep -r MSG_AUTH_FAILED_LOG pure-ftpd-1.0.36/src
|
||||
|
|
|
|||
|
|
@ -21,12 +21,16 @@ before = common.conf
|
|||
|
||||
[Definition]
|
||||
|
||||
_daemon = fail2ban\.actions
|
||||
_daemon = fail2ban\.server\.actions
|
||||
|
||||
# The name of the jail that this filter is used for. In jail.conf, name the
|
||||
# jail using this filter 'recidive', or change this line!
|
||||
_jailname = recidive
|
||||
|
||||
failregex = ^(%(__prefix_line)s|,\d{3} fail2ban.actions:\s+)WARNING\s+\[(?!%(_jailname)s\])(?:.*)\]\s+Ban\s+<HOST>\s*$
|
||||
failregex = ^(%(__prefix_line)s| %(_daemon)s%(__pid_re)s?:\s+)WARNING\s+\[(?!%(_jailname)s\])(?:.*)\]\s+Ban\s+<HOST>\s*$
|
||||
|
||||
[Init]
|
||||
|
||||
journalmatch = _SYSTEMD_UNIT=fail2ban.service PRIORITY=4
|
||||
|
||||
# Author: Tom Hendrikx, modifications by Amir Caspi
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ before = common.conf
|
|||
|
||||
[Definition]
|
||||
|
||||
failregex = ^\s*(\[(\s[+-][0-9]{4})?\])?(%(__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 roundcube: IMAP Error)?: (FAILED login|Login failed) for .*? from <HOST>(\. .* in .*?/rcube_imap\.php on line \d+ \(\S+ \S+\))?$
|
||||
|
||||
ignoreregex =
|
||||
# DEV Notes:
|
||||
|
|
@ -22,8 +22,8 @@ ignoreregex =
|
|||
# DoS resistance:
|
||||
#
|
||||
# Assume that the user can inject "from <HOST>" into the imap response
|
||||
# somehow. Write test cases around this to ensure that the combination of
|
||||
# arbitary user input and IMAP response doesn't inject the wrong IP for
|
||||
# somehow. Write test cases around this to ensure that the combination of
|
||||
# arbitrary user input and IMAP response doesn't inject the wrong IP for
|
||||
# fail2ban
|
||||
#
|
||||
# Author: Teodor Micu & Yaroslav Halchenko & terence namusonge & Daniel Black
|
||||
|
|
|
|||
18
config/filter.d/sendmail-auth.conf
Normal file
18
config/filter.d/sendmail-auth.conf
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Fail2Ban filter for sendmail authentication failures
|
||||
#
|
||||
|
||||
[INCLUDES]
|
||||
|
||||
before = common.conf
|
||||
|
||||
[Definition]
|
||||
|
||||
_daemon = (?:sm-(mta|acceptingconnections))
|
||||
|
||||
failregex = ^%(__prefix_line)s\w{14}: (\S+ )?\[<HOST>\]( \(may be forged\))?: possible SMTP attack: command=AUTH, count=\d+$
|
||||
|
||||
ignoreregex =
|
||||
|
||||
# DEV Notes:
|
||||
#
|
||||
# Author: Daniel Black
|
||||
50
config/filter.d/sendmail-reject.conf
Normal file
50
config/filter.d/sendmail-reject.conf
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
# Fail2Ban filter for sendmail spam/relay type failures
|
||||
#
|
||||
# Some of the below failregex will only work properly, when the following
|
||||
# options are set in the .mc file (see your Sendmail documentation on how
|
||||
# to modify it and generate the corresponding .cf file):
|
||||
#
|
||||
# FEATURE(`delay_checks')
|
||||
# FEATURE(`greet_pause', `500')
|
||||
# FEATURE(`ratecontrol', `nodelay', `terminate')
|
||||
# FEATURE(`conncontrol', `nodelay', `terminate')
|
||||
#
|
||||
# ratecontrol and conncontrol also need corresponding options ClientRate:
|
||||
# and ClientConn: in the access file, see documentation for ratecontrol and
|
||||
# conncontrol in the sendmail/cf/README file.
|
||||
|
||||
[INCLUDES]
|
||||
|
||||
before = common.conf
|
||||
|
||||
[Definition]
|
||||
|
||||
_daemon = (?:(sm-(mta|acceptingconnections)|sendmail))
|
||||
|
||||
failregex = ^%(__prefix_line)s\w{14}: ruleset=check_rcpt, arg1=(?P<email><\S+@\S+>), relay=(\S+ )?\[<HOST>\]( \(may be forged\))?, reject=(550 5\.7\.1 (?P=email)\.\.\. Relaying denied\. (IP name possibly forged \[(\d+\.){3}\d+\]|Proper authentication required\.|IP name lookup failed \[(\d+\.){3}\d+\])|553 5\.1\.8 (?P=email)\.\.\. Domain of sender address \S+ does not exist|550 5\.[71]\.1 (?P=email)\.\.\. (Rejected: .*|User unknown))$
|
||||
^%(__prefix_line)sruleset=check_relay, arg1=(?P<dom>\S+), arg2=<HOST>, relay=((?P=dom) )?\[(\d+\.){3}\d+\]( \(may be forged\))?, reject=421 4\.3\.2 (Connection rate limit exceeded\.|Too many open connections\.)$
|
||||
^%(__prefix_line)s\w{14}: rejecting commands from (\S+ )?\[<HOST>\] due to pre-greeting traffic after \d+ seconds$
|
||||
^%(__prefix_line)s\w{14}: (\S+ )?\[<HOST>\]: ((?i)expn|vrfy) \S+ \[rejected\]$
|
||||
^(?P<__prefix>%(__prefix_line)s\w+: )<[^@]+@[^>]+>\.\.\. No such user here<SKIPLINES>(?P=__prefix)from=<[^@]+@[^>]+>, size=\d+, class=\d+, nrcpts=\d+, bodytype=\w+, proto=E?SMTP, daemon=MTA, relay=\S+ \[<HOST>\]$
|
||||
|
||||
|
||||
ignoreregex =
|
||||
|
||||
|
||||
[Init]
|
||||
|
||||
# "maxlines" is number of log lines to buffer for multi-line regex searches
|
||||
maxlines = 10
|
||||
|
||||
# DEV NOTES:
|
||||
#
|
||||
# Regarding the last multiline regex:
|
||||
#
|
||||
# There can be a nunber of non-related lines between the first and second part
|
||||
# of this regex maxlines of 10 is quite generious. Only one of the
|
||||
# "No such user" lines needs to be matched before the line with the HOST.
|
||||
#
|
||||
# Note the capture __prefix, includes both the __prefix_lines (which includes
|
||||
# the sendmail PID), but also the \w+ which the the sendmail assigned mail ID.
|
||||
#
|
||||
# Author: Daniel Black and Fabian Wenk
|
||||
32
config/filter.d/solid-pop3d.conf
Normal file
32
config/filter.d/solid-pop3d.conf
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# Fail2Ban filter for unsuccesful solid-pop3 authentication attempts
|
||||
#
|
||||
# Doesn't currently provide PAM support as PAM log messages don't include rhost as
|
||||
# remote IP.
|
||||
#
|
||||
[INCLUDES]
|
||||
|
||||
before = common.conf
|
||||
|
||||
[Definition]
|
||||
|
||||
_daemon = solid-pop3d
|
||||
|
||||
failregex = ^%(__prefix_line)sauthentication failed: (no such user|can't map user name): .*? - <HOST>$
|
||||
^%(__prefix_line)s(APOP )?authentication failed for (mapped )?user .*? - <HOST>$
|
||||
^%(__prefix_line)sroot login not allowed - <HOST>$
|
||||
^%(__prefix_line)scan't find APOP secret for user .*? - <HOST>$
|
||||
|
||||
ignoreregex =
|
||||
|
||||
# DEV Notes:
|
||||
#
|
||||
# solid-pop3d needs to be compiled with --enable-logextend to support
|
||||
# IP addresses in log messages.
|
||||
#
|
||||
# solid-pop3d-0.15/src/main.c contains all authentication errors
|
||||
# except for PAM authentication messages ( src/authenticate.c )
|
||||
#
|
||||
# A pam authentication failure message (note no IP for rhost).
|
||||
# Nov 17 23:17:50 emf1pt2-2-35-70 solid-pop3d[17176]: pam_unix(solid-pop3d:auth): authentication failure; logname= uid=0 euid=0 tty= ruser= rhost= user=jacques
|
||||
#
|
||||
# Authors: Daniel Black
|
||||
13
config/filter.d/squid.conf
Normal file
13
config/filter.d/squid.conf
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# Fail2Ban filter for Squid attempted proxy bypasses
|
||||
#
|
||||
#
|
||||
|
||||
[Definition]
|
||||
|
||||
failregex = ^\s+\d\s<HOST>\s+[A-Z_]+_DENIED/403 .*$
|
||||
^\s+\d\s<HOST>\s+NONE/405 .*$
|
||||
|
||||
|
||||
|
||||
# Author: Daniel Black
|
||||
|
||||
13
config/filter.d/squirrelmail.conf
Normal file
13
config/filter.d/squirrelmail.conf
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
[Definition]
|
||||
|
||||
failregex = ^ \[LOGIN_ERROR\].*from <HOST>: Unknown user or password incorrect\.$
|
||||
|
||||
|
||||
[Init]
|
||||
|
||||
datepattern = ^%%m/%%d/%%Y %%H:%%M:%%S
|
||||
|
||||
# DEV NOTES:
|
||||
#
|
||||
# Author: Daniel Black
|
||||
|
|
@ -22,4 +22,8 @@ failregex = ^%(__prefix_line)sDid not receive identification string from <HOST>\
|
|||
|
||||
ignoreregex =
|
||||
|
||||
[Init]
|
||||
|
||||
journalmatch = _SYSTEMD_UNIT=sshd.service + _COMM=sshd
|
||||
|
||||
# Author: Yaroslav Halchenko
|
||||
|
|
|
|||
|
|
@ -1,5 +1,12 @@
|
|||
# Fail2Ban filter for openssh
|
||||
#
|
||||
# If you want to protect OpenSSH from being bruteforced by password
|
||||
# authentication then get public key authentication working before disabling
|
||||
# PasswordAuthentication in sshd_config.
|
||||
#
|
||||
#
|
||||
# "Connection from <HOST> port \d+" requires LogLevel VERBOSE in sshd_config
|
||||
#
|
||||
|
||||
[INCLUDES]
|
||||
|
||||
|
|
@ -7,7 +14,6 @@
|
|||
# common.local
|
||||
before = common.conf
|
||||
|
||||
|
||||
[Definition]
|
||||
|
||||
_daemon = sshd
|
||||
|
|
@ -21,11 +27,22 @@ failregex = ^%(__prefix_line)s(?:error: PAM: )?[aA]uthentication (?:failure|erro
|
|||
^%(__prefix_line)sUser .+ from <HOST> not allowed because listed in DenyUsers\s*$
|
||||
^%(__prefix_line)sUser .+ from <HOST> not allowed because not in any group\s*$
|
||||
^%(__prefix_line)srefused connect from \S+ \(<HOST>\)\s*$
|
||||
^%(__prefix_line)sReceived disconnect from <HOST>: 3: \S+: Auth fail$
|
||||
^%(__prefix_line)sUser .+ from <HOST> not allowed because a group is listed in DenyGroups\s*$
|
||||
^%(__prefix_line)sUser .+ from <HOST> not allowed because none of user's groups are listed in AllowGroups\s*$
|
||||
^(?P<__prefix>%(__prefix_line)s)User .+ not allowed because account is locked<SKIPLINES>(?P=__prefix)(?:error: )?Received disconnect from <HOST>: 11: Bye Bye \[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+<SKIPLINES>(?P=__prefix)Disconnecting: Too many authentication failures for .+? \[preauth\]$
|
||||
|
||||
ignoreregex =
|
||||
|
||||
[Init]
|
||||
|
||||
# "maxlines" is number of log lines to buffer for multi-line regex searches
|
||||
maxlines = 10
|
||||
|
||||
journalmatch = _SYSTEMD_UNIT=sshd.service + _COMM=sshd
|
||||
|
||||
# DEV Notes:
|
||||
#
|
||||
# "Failed \S+ for .*? from <HOST>..." failregex uses non-greedy catch-all because
|
||||
|
|
@ -34,3 +51,4 @@ ignoreregex =
|
|||
# matched away first.
|
||||
#
|
||||
# Author: Cyril Jaquier, Yaroslav Halchenko, Petr Voralek, Daniel Black
|
||||
|
||||
|
|
|
|||
11
config/filter.d/stunnel.conf
Normal file
11
config/filter.d/stunnel.conf
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# Fail2ban filter for stunnel
|
||||
|
||||
[Definition]
|
||||
|
||||
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$
|
||||
|
||||
# DEV NOTES:
|
||||
#
|
||||
# Author: Daniel Black
|
||||
#
|
||||
# Based off: http://www.fail2ban.org/wiki/index.php/Fail2ban:Community_Portal#stunnel4
|
||||
21
config/filter.d/tine20.conf
Normal file
21
config/filter.d/tine20.conf
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# Fail2Ban filter for Tine 2.0 authentication
|
||||
#
|
||||
# Enable logging with:
|
||||
# $config['info_log']='/var/log/tine20/tine20.log';
|
||||
#
|
||||
|
||||
[Definition]
|
||||
|
||||
failregex = ^[\da-f]{5,} [\da-f]{5,} (-- none --|.*?)( \d+(\.\d+)?(h|m|s|ms)){0,2} - WARN \(\d+\): Tinebase_Controller::login::\d+ Login with username .*? from <HOST> failed \(-[13]\)!$
|
||||
|
||||
ignoreregex =
|
||||
|
||||
# Author: Mika (mkl) from Tine20.org forum: https://www.tine20.org/forum/viewtopic.php?f=2&t=15688&p=54766
|
||||
# Editor: Daniel Black
|
||||
# Advisor: Lars Kneschke
|
||||
#
|
||||
# Usernames can contain spaces.
|
||||
#
|
||||
# Authentication: http://git.tine20.org/git?p=tine20;a=blob;f=tine20/Tinebase/Controller.php#l105
|
||||
# Logger: http://git.tine20.org/git?p=tine20;a=blob;f=tine20/Tinebase/Log/Formatter.php
|
||||
# formatMicrotimeDiff: http://git.tine20.org/git?p=tine20;a=blob;f=tine20/Tinebase/Helper.php#l276
|
||||
|
|
@ -1,5 +1,8 @@
|
|||
# Fail2Ban filter for vsftp
|
||||
#
|
||||
# Configure VSFTP for "dual_log_enable=YES", and have fail2ban watch
|
||||
# /var/log/vsftpd.log instead of /var/log/secure. vsftpd.log file shows the
|
||||
# incoming ip address rather than domain names.
|
||||
|
||||
[INCLUDES]
|
||||
|
||||
|
|
@ -16,3 +19,4 @@ failregex = ^%(__prefix_line)s%(__pam_re)s\s+authentication failure; logname=\S*
|
|||
ignoreregex =
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
# Documentation from fail2ban wiki
|
||||
|
|
|
|||
899
config/jail.conf
899
config/jail.conf
File diff suppressed because it is too large
Load diff
57
config/paths-common.conf
Normal file
57
config/paths-common.conf
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
# Common
|
||||
#
|
||||
|
||||
[INCLUDES]
|
||||
|
||||
after = paths-overrides.local
|
||||
|
||||
[DEFAULT]
|
||||
|
||||
|
||||
|
||||
sshd_log = %(syslog_authpriv)s
|
||||
|
||||
dropbear_log = %(syslog_authpriv)s
|
||||
|
||||
|
||||
# from /etc/audit/auditd.conf
|
||||
auditd_log = /var/log/audit/audit.log
|
||||
|
||||
|
||||
nginx_error_log = /var/log/nginx/error.log
|
||||
|
||||
nginx_access_log = /var/log/nginx/access.log
|
||||
|
||||
|
||||
lighttpd_error_log = /var/log/lighttpd/error.log
|
||||
|
||||
# http://www.hardened-php.net/suhosin/configuration.html#suhosin.log.syslog.facility
|
||||
# syslog_user is the default. Lighttpd also hooks errors into its log.
|
||||
|
||||
suhosin_log = %(syslog_user)s %(lighttpd_error_log)s
|
||||
|
||||
# defaults to ftp or local2 if ftp doesn't exist
|
||||
proftpd_log = %(syslog_ftp)s
|
||||
|
||||
# http://svnweb.freebsd.org/ports/head/ftp/proftpd/files/patch-src_proftpd.8.in?view=markup
|
||||
# defaults to ftp but can be overwritten.
|
||||
pureftpd_log = %(syslog_ftp)s
|
||||
|
||||
# ftp, daemon and then local7 are tried at configure time however it is overwriteable at configure time
|
||||
#
|
||||
wuftpd_log = %(syslog_ftp)s
|
||||
|
||||
# syslog_enable defaults to no. so it defaults to vsftpd_log_file setting of /var/log/vsftpd.log
|
||||
# No distro seems to set it to syslog by default
|
||||
# If syslog set it defaults to ftp facility if exists at compile time otherwise falls back to daemonlog.
|
||||
vsftpd_log = /var/log/vsftpd.log
|
||||
|
||||
# Technically syslog_facility in main.cf can overwrite but no-one sane does this.
|
||||
postfix_log = %(syslog_mail_warn)s
|
||||
|
||||
dovecot_log = %(syslog_mail_warn)s
|
||||
|
||||
# Seems to be set at compile time only to LOG_LOCAL0 (src/const.h) at Notice level
|
||||
solidpop3d_log = %(syslog_local0)s
|
||||
|
||||
mysql_log = %(syslog_daemon)s
|
||||
39
config/paths-debian.conf
Normal file
39
config/paths-debian.conf
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# Debian
|
||||
|
||||
[INCLUDES]
|
||||
|
||||
before = paths-common.conf
|
||||
|
||||
after = paths-overrides.local
|
||||
|
||||
|
||||
[DEFAULT]
|
||||
|
||||
syslog_mail = /var/log/mail.log
|
||||
|
||||
syslog_mail_warn = /var/log/mail.warn
|
||||
|
||||
syslog_authpriv = /var/log/auth.log
|
||||
|
||||
# syslog_auth = /var/log/auth.log
|
||||
#
|
||||
syslog_user = /var/log/user.log
|
||||
|
||||
syslog_ftp = /var/log/syslog
|
||||
|
||||
syslog_daemon = /var/log/daemon.log
|
||||
|
||||
syslog_local0 = /var/log/messages
|
||||
|
||||
|
||||
apache_error_log = /var/log/apache2/*error.log
|
||||
|
||||
apache_access_log = /var/log/apache2/*access.log
|
||||
|
||||
|
||||
# was in debian squeezy but not in wheezy
|
||||
# /etc/proftpd/proftpd.conf (SystemLog)
|
||||
proftpd_log = /var/log/proftpd/proftpd.log
|
||||
|
||||
|
||||
|
||||
35
config/paths-fedora.conf
Normal file
35
config/paths-fedora.conf
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# Fedora
|
||||
|
||||
[INCLUDES]
|
||||
|
||||
before = paths-common.conf
|
||||
|
||||
after = paths-overrides.local
|
||||
|
||||
|
||||
[DEFAULT]
|
||||
|
||||
syslog_mail = /var/log/maillog
|
||||
|
||||
syslog_mail_warn = /var/log/maillog
|
||||
|
||||
syslog_authpriv = /var/log/secure
|
||||
|
||||
syslog_user = /var/log/messages
|
||||
|
||||
syslog_ftp = /var/log/messages
|
||||
|
||||
syslog_daemon = /var/log/messages
|
||||
|
||||
syslog_local0 = /var/log/messages
|
||||
|
||||
|
||||
apache_error_log = /var/log/httpd/*error_log
|
||||
|
||||
apache_access_log = /var/log/httpd/*access_log
|
||||
|
||||
# /etc/proftpd/proftpd.conf (ExtendedLog for Anonymous)
|
||||
# proftpd_log = /var/log/proftpd/auth.log
|
||||
# Tested and it worked out in /var/log/messages so assuming syslog_ftp for now.
|
||||
|
||||
mysql_log = /var/lib/mysql/mysqld.log
|
||||
46
config/paths-freebsd.conf
Normal file
46
config/paths-freebsd.conf
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
# FreeBSD
|
||||
|
||||
[INCLUDES]
|
||||
|
||||
before = common-paths.conf
|
||||
|
||||
after = paths-overrides.local
|
||||
|
||||
|
||||
[DEFAULT]
|
||||
|
||||
# http://www.freebsd.org/doc/handbook/configtuning-syslog.html
|
||||
#
|
||||
syslog_mail = /var/log/maillog
|
||||
|
||||
syslog_mail_warn = /var/log/maillog
|
||||
|
||||
syslog_authpriv = /var/log/auth.log
|
||||
|
||||
# note - is only ftp.info - if notice /var/log/messages may be needed
|
||||
syslog_ftp = /var/log/xferlog
|
||||
|
||||
syslog_daemon = /var/log/messages
|
||||
|
||||
syslog_local0 = /var/log/messages
|
||||
|
||||
# Linux things
|
||||
# we fake to avoid parse error in startups
|
||||
|
||||
auditd_log = /dev/null
|
||||
|
||||
# http://svnweb.freebsd.org/ports/head/www/apache24/files/patch-docs__conf__extra__httpd-ssl.conf.in?view=markup
|
||||
# http://svnweb.freebsd.org/ports/head/www/apache22/files/patch-docs__conf__extra__httpd-ssl.conf.in?view=markup
|
||||
# http://svnweb.freebsd.org/ports/head/www/apache24/files/patch-config.layout
|
||||
# http://svnweb.freebsd.org/ports/head/www/apache22/files/patch-config.layout
|
||||
|
||||
apache_error_log = /usr/local/www/logs/*error[_.]log
|
||||
|
||||
apache_access_log = /usr/local/www/logs/*access[_.]log
|
||||
|
||||
# http://svnweb.freebsd.org/ports/head/www/nginx/Makefile?view=markup
|
||||
|
||||
nginx_error_log = /var/log/nginx-error.log
|
||||
|
||||
nginx_access_log = /var/log/nginx-access.log
|
||||
|
||||
27
config/paths-osx.conf
Normal file
27
config/paths-osx.conf
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# OSX
|
||||
#
|
||||
|
||||
[INCLUDES]
|
||||
|
||||
before = common-paths.conf
|
||||
|
||||
after = paths-overrides.local
|
||||
|
||||
|
||||
[DEFAULT]
|
||||
|
||||
syslog_mail = /var/log/mail.log
|
||||
|
||||
syslog_mail_warn = /var/log/mail.warn
|
||||
|
||||
syslog_authpriv = /var/log/secure.log
|
||||
#syslog_auth =
|
||||
|
||||
#syslog_user =
|
||||
|
||||
#syslog_ftp =
|
||||
|
||||
#syslog_daemon =
|
||||
|
||||
#syslog_local0 =
|
||||
|
||||
|
|
@ -74,12 +74,6 @@ further configuration. To run not as root, further setup is necessary:
|
|||
|
||||
with <name> suitably replaced.
|
||||
|
||||
- suppress actionstart for iptables-xt_recent-echo action by creating an override file
|
||||
iptables-xt_recent-echo.local to accompany iptables-xt_recent-echo.conf with
|
||||
|
||||
[Definition]
|
||||
actionstart =
|
||||
|
||||
- Permissions:
|
||||
|
||||
make sure that configuration files under /etc/fail2ban are readable by
|
||||
|
|
|
|||
14
fail2ban-2to3
Executable file
14
fail2ban-2to3
Executable file
|
|
@ -0,0 +1,14 @@
|
|||
#!/bin/bash
|
||||
# This script carries out conversion of fail2ban to python3
|
||||
# A backup of any converted files are created with ".bak"
|
||||
# extension
|
||||
|
||||
set -eu
|
||||
|
||||
if 2to3 -w --no-diffs bin/* fail2ban;then
|
||||
echo "Success!" >&2
|
||||
exit 0
|
||||
else
|
||||
echo "Fail!" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
|
@ -1,247 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
|
||||
# vi: set ft=python sts=4 ts=4 sw=4 noet :
|
||||
"""Script to run Fail2Ban tests battery
|
||||
"""
|
||||
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Fail2Ban; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2012- Yaroslav Halchenko"
|
||||
__license__ = "GPL"
|
||||
|
||||
|
||||
import unittest, logging, sys, time, os
|
||||
|
||||
if sys.version_info >= (2, 6):
|
||||
import json
|
||||
else: # pragma: no cover
|
||||
try:
|
||||
import simplejson as json
|
||||
except ImportError:
|
||||
json = None
|
||||
|
||||
from common.version import version
|
||||
from testcases import banmanagertestcase
|
||||
from testcases import clientreadertestcase
|
||||
from testcases import failmanagertestcase
|
||||
from testcases import filtertestcase
|
||||
from testcases import servertestcase
|
||||
from testcases import datedetectortestcase
|
||||
from testcases import actiontestcase
|
||||
from testcases import sockettestcase
|
||||
from testcases import misctestcase
|
||||
if json:
|
||||
from testcases import samplestestcase
|
||||
|
||||
from testcases.utils import FormatterWithTraceBack
|
||||
from testcases import actionstestcase
|
||||
from server.mytime import MyTime
|
||||
|
||||
from optparse import OptionParser, Option
|
||||
|
||||
def get_opt_parser():
|
||||
# use module docstring for help output
|
||||
p = OptionParser(
|
||||
usage="%s [OPTIONS] [regexps]\n" % sys.argv[0] + __doc__,
|
||||
version="%prog " + version)
|
||||
|
||||
p.add_options([
|
||||
Option('-l', "--log-level", type="choice",
|
||||
dest="log_level",
|
||||
choices=('heavydebug', 'debug', 'info', 'warning', 'error', 'fatal'),
|
||||
default=None,
|
||||
help="Log level for the logger to use during running tests"),
|
||||
Option('-n', "--no-network", action="store_true",
|
||||
dest="no_network",
|
||||
help="Do not run tests that require the network"),
|
||||
Option("-t", "--log-traceback", action='store_true',
|
||||
help="Enrich log-messages with compressed tracebacks"),
|
||||
Option("--full-traceback", action='store_true',
|
||||
help="Either to make the tracebacks full, not compressed (as by default)"),
|
||||
|
||||
])
|
||||
|
||||
return p
|
||||
|
||||
parser = get_opt_parser()
|
||||
(opts, regexps) = parser.parse_args()
|
||||
|
||||
#
|
||||
# Logging
|
||||
#
|
||||
logSys = logging.getLogger("fail2ban")
|
||||
|
||||
# Numerical level of verbosity corresponding to a log "level"
|
||||
verbosity = {'heavydebug': 4,
|
||||
'debug': 3,
|
||||
'info': 2,
|
||||
'warning': 1,
|
||||
'error': 1,
|
||||
'fatal': 0,
|
||||
None: 1}[opts.log_level]
|
||||
|
||||
if opts.log_level is not None: # pragma: no cover
|
||||
# so we had explicit settings
|
||||
logSys.setLevel(getattr(logging, opts.log_level.upper()))
|
||||
else: # pragma: no cover
|
||||
# suppress the logging but it would leave unittests' progress dots
|
||||
# ticking, unless like with '-l fatal' which would be silent
|
||||
# unless error occurs
|
||||
logSys.setLevel(getattr(logging, 'FATAL'))
|
||||
|
||||
# Add the default logging handler
|
||||
stdout = logging.StreamHandler(sys.stdout)
|
||||
|
||||
fmt = ' %(message)s'
|
||||
|
||||
if opts.log_traceback or opts.full_traceback:
|
||||
Formatter = FormatterWithTraceBack
|
||||
fmt = (opts.full_traceback and ' %(tb)s' or ' %(tbc)s') + fmt
|
||||
else:
|
||||
Formatter = logging.Formatter
|
||||
|
||||
# Custom log format for the verbose tests runs
|
||||
if verbosity > 1: # pragma: no cover
|
||||
stdout.setFormatter(Formatter(' %(asctime)-15s %(thread)s' + fmt))
|
||||
else: # pragma: no cover
|
||||
# just prefix with the space
|
||||
stdout.setFormatter(Formatter(fmt))
|
||||
logSys.addHandler(stdout)
|
||||
|
||||
#
|
||||
# Let know the version
|
||||
#
|
||||
if not opts.log_level or opts.log_level != 'fatal': # pragma: no cover
|
||||
print "Fail2ban %s test suite. Python %s. Please wait..." \
|
||||
% (version, str(sys.version).replace('\n', ''))
|
||||
|
||||
|
||||
#
|
||||
# Gather the tests
|
||||
#
|
||||
if not len(regexps): # pragma: no cover
|
||||
tests = unittest.TestSuite()
|
||||
else: # pragma: no cover
|
||||
import re
|
||||
class FilteredTestSuite(unittest.TestSuite):
|
||||
_regexps = [re.compile(r) for r in regexps]
|
||||
def addTest(self, suite):
|
||||
suite_str = str(suite)
|
||||
for r in self._regexps:
|
||||
if r.search(suite_str):
|
||||
super(FilteredTestSuite, self).addTest(suite)
|
||||
return
|
||||
|
||||
tests = FilteredTestSuite()
|
||||
|
||||
# Server
|
||||
#tests.addTest(unittest.makeSuite(servertestcase.StartStop))
|
||||
tests.addTest(unittest.makeSuite(servertestcase.Transmitter))
|
||||
tests.addTest(unittest.makeSuite(servertestcase.JailTests))
|
||||
tests.addTest(unittest.makeSuite(actiontestcase.ExecuteAction))
|
||||
tests.addTest(unittest.makeSuite(actionstestcase.ExecuteActions))
|
||||
# FailManager
|
||||
tests.addTest(unittest.makeSuite(failmanagertestcase.AddFailure))
|
||||
# BanManager
|
||||
tests.addTest(unittest.makeSuite(banmanagertestcase.AddFailure))
|
||||
# ClientReaders
|
||||
tests.addTest(unittest.makeSuite(clientreadertestcase.ConfigReaderTest))
|
||||
tests.addTest(unittest.makeSuite(clientreadertestcase.JailReaderTest))
|
||||
tests.addTest(unittest.makeSuite(clientreadertestcase.JailsReaderTest))
|
||||
# CSocket and AsyncServer
|
||||
tests.addTest(unittest.makeSuite(sockettestcase.Socket))
|
||||
# Misc helpers
|
||||
tests.addTest(unittest.makeSuite(misctestcase.HelpersTest))
|
||||
tests.addTest(unittest.makeSuite(misctestcase.SetupTest))
|
||||
tests.addTest(unittest.makeSuite(misctestcase.TestsUtilsTest))
|
||||
tests.addTest(unittest.makeSuite(misctestcase.CustomDateFormatsTest))
|
||||
|
||||
# Filter
|
||||
if not opts.no_network:
|
||||
tests.addTest(unittest.makeSuite(filtertestcase.IgnoreIP))
|
||||
tests.addTest(unittest.makeSuite(filtertestcase.LogFile))
|
||||
tests.addTest(unittest.makeSuite(filtertestcase.LogFileMonitor))
|
||||
if not opts.no_network:
|
||||
tests.addTest(unittest.makeSuite(filtertestcase.GetFailures))
|
||||
tests.addTest(unittest.makeSuite(filtertestcase.DNSUtilsTests))
|
||||
tests.addTest(unittest.makeSuite(filtertestcase.JailTests))
|
||||
|
||||
# DateDetector
|
||||
tests.addTest(unittest.makeSuite(datedetectortestcase.DateDetectorTest))
|
||||
if json:
|
||||
# Filter Regex tests with sample logs
|
||||
tests.addTest(unittest.makeSuite(samplestestcase.FilterSamplesRegex))
|
||||
else: # pragma: no cover
|
||||
print "I: Skipping filter samples testing. No simplejson/json module"
|
||||
|
||||
#
|
||||
# Extensive use-tests of different available filters backends
|
||||
#
|
||||
|
||||
from server.filterpoll import FilterPoll
|
||||
filters = [FilterPoll] # always available
|
||||
|
||||
# Additional filters available only if external modules are available
|
||||
# yoh: Since I do not know better way for parametric tests
|
||||
# with good old unittest
|
||||
try:
|
||||
from server.filtergamin import FilterGamin
|
||||
filters.append(FilterGamin)
|
||||
except Exception, e: # pragma: no cover
|
||||
print "I: Skipping gamin backend testing. Got exception '%s'" % e
|
||||
|
||||
try:
|
||||
from server.filterpyinotify import FilterPyinotify
|
||||
filters.append(FilterPyinotify)
|
||||
except Exception, e: # pragma: no cover
|
||||
print "I: Skipping pyinotify backend testing. Got exception '%s'" % e
|
||||
|
||||
for Filter_ in filters:
|
||||
tests.addTest(unittest.makeSuite(
|
||||
filtertestcase.get_monitor_failures_testcase(Filter_)))
|
||||
|
||||
# Server test for logging elements which break logging used to support
|
||||
# testcases analysis
|
||||
tests.addTest(unittest.makeSuite(servertestcase.TransmitterLogging))
|
||||
|
||||
#
|
||||
# Run the tests
|
||||
#
|
||||
testRunner = unittest.TextTestRunner(verbosity=verbosity)
|
||||
|
||||
try:
|
||||
# Set the time to a fixed, known value
|
||||
# Sun Aug 14 12:00:00 CEST 2005
|
||||
# yoh: we need to adjust TZ to match the one used by Cyril so all the timestamps match
|
||||
old_TZ = os.environ.get('TZ', None)
|
||||
os.environ['TZ'] = 'Europe/Zurich'
|
||||
time.tzset()
|
||||
MyTime.setTime(1124013600)
|
||||
|
||||
tests_results = testRunner.run(tests)
|
||||
|
||||
finally: # pragma: no cover
|
||||
# Just for the sake of it reset the TZ
|
||||
# yoh: move all this into setup/teardown methods within tests
|
||||
os.environ.pop('TZ')
|
||||
if old_TZ:
|
||||
os.environ['TZ'] = old_TZ
|
||||
time.tzset()
|
||||
|
||||
if not tests_results.wasSuccessful(): # pragma: no cover
|
||||
sys.exit(1)
|
||||
|
|
@ -9,7 +9,7 @@ for python in /usr/{,local/}bin/python2.[0-9]{,.*}{,-dbg}
|
|||
do
|
||||
[ -e "$python" ] || continue
|
||||
echo "Testing using $python"
|
||||
$python ./fail2ban-testcases "$@" || failed+=" $python"
|
||||
$python bin/fail2ban-testcases "$@" || failed+=" $python"
|
||||
done
|
||||
|
||||
if [ ! -z "$failed" ]; then
|
||||
|
|
|
|||
18
fail2ban-testcases-all-python3
Executable file
18
fail2ban-testcases-all-python3
Executable file
|
|
@ -0,0 +1,18 @@
|
|||
#!/bin/bash
|
||||
# Simple helper script to exercise unittests using all available
|
||||
# (under /usr/bin and /usr/local/bin python3.*)
|
||||
|
||||
set -eu
|
||||
|
||||
failed=
|
||||
for python in /usr/{,local/}bin/python3.[0-9]{,.*}{,-dbg}
|
||||
do
|
||||
[ -e "$python" ] || continue
|
||||
echo "Testing using $python"
|
||||
$python bin/fail2ban-testcases "$@" || failed+=" $python"
|
||||
done
|
||||
|
||||
if [ ! -z "$failed" ]; then
|
||||
echo "E: Failed with $failed"
|
||||
exit 1
|
||||
fi
|
||||
70
fail2ban/__init__.py
Normal file
70
fail2ban/__init__.py
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
|
||||
# vi: set ft=python sts=4 ts=4 sw=4 noet :
|
||||
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Fail2Ban; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
import logging.handlers
|
||||
|
||||
# Custom debug level
|
||||
logging.HEAVYDEBUG = 5
|
||||
|
||||
"""
|
||||
Below derived from:
|
||||
https://mail.python.org/pipermail/tutor/2007-August/056243.html
|
||||
"""
|
||||
|
||||
logging.NOTICE = logging.INFO + 5
|
||||
logging.addLevelName(logging.NOTICE, 'NOTICE')
|
||||
|
||||
# define a new logger function for notice
|
||||
# this is exactly like existing info, critical, debug...etc
|
||||
def _Logger_notice(self, msg, *args, **kwargs):
|
||||
"""
|
||||
Log 'msg % args' with severity 'NOTICE'.
|
||||
|
||||
To pass exception information, use the keyword argument exc_info with
|
||||
a true value, e.g.
|
||||
|
||||
logger.notice("Houston, we have a %s", "major disaster", exc_info=1)
|
||||
"""
|
||||
if self.isEnabledFor(logging.NOTICE):
|
||||
self._log(logging.NOTICE, msg, args, **kwargs)
|
||||
|
||||
logging.Logger.notice = _Logger_notice
|
||||
|
||||
# define a new root level notice function
|
||||
# this is exactly like existing info, critical, debug...etc
|
||||
def _root_notice(msg, *args, **kwargs):
|
||||
"""
|
||||
Log a message with severity 'NOTICE' on the root logger.
|
||||
"""
|
||||
if len(logging.root.handlers) == 0:
|
||||
logging.basicConfig()
|
||||
logging.root.notice(msg, *args, **kwargs)
|
||||
|
||||
# make the notice root level function known
|
||||
logging.notice = _root_notice
|
||||
|
||||
# add NOTICE to the priority map of all the levels
|
||||
logging.handlers.SysLogHandler.priority_map['NOTICE'] = 'notice'
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue