Merge tag '0.9.0' into debian

This commit is contained in:
Daniel Schaal 2014-03-15 12:45:45 +01:00
commit e2caa2b220
281 changed files with 11902 additions and 5225 deletions

2
.gitignore vendored
View file

@ -6,3 +6,5 @@ htmlcov
.coverage
*.orig
*.rej
*.bak
__pycache__

View file

@ -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

View file

@ -4,3 +4,4 @@ branch = True
omit =
/usr/*
/home/travis/virtualenv/*
fail2ban/server/filtersystemd.py

191
ChangeLog
View file

@ -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
View file

@ -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
View 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
View file

@ -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

View file

@ -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
View file

@ -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
View file

@ -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

View file

@ -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:

View file

@ -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 ]

View file

@ -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
View 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)

View file

@ -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

View file

@ -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

View file

@ -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

View 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
View 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

View 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 =

View file

@ -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
#

View file

@ -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

View 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

View 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

View file

@ -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]

View file

@ -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]

View file

@ -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]

View file

@ -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]

View file

@ -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]

View file

@ -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]

View file

@ -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]

View file

@ -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]

View file

@ -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]

View file

@ -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]

View file

@ -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>

View file

@ -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>

View file

@ -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))`"

View file

@ -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

View 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

View 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

View file

@ -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

View 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

View file

@ -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

View file

@ -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
View 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
View 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

View 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

View file

@ -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

View file

@ -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

View 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

View file

@ -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
#

View 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

View file

@ -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

View file

@ -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 =

View file

@ -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

View file

@ -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+

View 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

View file

@ -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)

View 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 =

View file

@ -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
#

View file

@ -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*$

View 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\.$

View 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

View 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

View 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

View 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

View 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

View file

@ -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
View 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\.$

View 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

View file

@ -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

View file

@ -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]

View file

@ -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

View file

@ -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

View file

@ -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

View 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

View 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

View 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

View 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

View 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

View file

@ -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

View file

@ -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

View 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

View 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

View file

@ -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

File diff suppressed because it is too large Load diff

57
config/paths-common.conf Normal file
View 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
View 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
View 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
View 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
View 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 =

View file

@ -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
View 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

View file

@ -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)

View file

@ -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
View 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
View 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