Merge branch 'master' into badips

pull/588/head
Daniel Black 2014-01-15 09:37:11 +11:00
commit f566cab766
68 changed files with 1634 additions and 668 deletions

View File

@ -18,7 +18,7 @@ ver. 0.8.12 (2013/12/XX) - things-can-only-get-better
- 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
- recidive jail to block all protocols. Closes gh-440. Thanksg Ioan Indreias
- smtps not a IANA standard and has been removed from Arch. Replaced with
465. Thanks Stefan. Closes gh-447
- mysqld-syslog-iptables rule was too long. Part of gh-447.
@ -26,21 +26,46 @@ ver. 0.8.12 (2013/12/XX) - things-can-only-get-better
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.
- Enhancements:
- added firewallcmd-ipset action
- 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.
- added squid filter. Thanks Roman Gelfand.
- updated check_fail2ban to return performance data for all jails.
- filter apache-noscript now includes php cgi scripts.
Thanks dani. Closes gh-503
- added ufw action. Thanks Guilhem Lettron. lp-#701522
- exim-spam filter to match spamassassin log entry for option SAdevnull.
Thanks Ivo Truxa. Closes gh-533
- 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 to sshd filter expression for "Received disconnect from <HOST>: 3:
...: Auth fail". Thanks Marcel Dopita. Closes gh-289
- 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:
Daniel Black
* filter.d/solid-pop3d -- added thanks to Jacques Lav!gnotte on mailinglist.
- Enhancements:
- loglines now also report "[PID]" after the name portion
- 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 filter.d/openwebmail filter thanks Ivo Truxa. Closes gh-543
- Added filter.d/horde
- Added filter for freeswitch. Thanks Jim and editors and authors of
http://wiki.freeswitch.org/wiki/Fail2ban
ver. 0.8.11 (2013/11/13) - loves-unittests-and-tight-DoS-free-filter-regexes

460
DEVELOP
View File

@ -34,465 +34,7 @@ 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
============

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.

View File

@ -5,6 +5,7 @@ TODO
THANKS
COPYING
DEVELOP
FILTERS
fail2ban-client
fail2ban-server
fail2ban-testcases
@ -48,6 +49,7 @@ server/mytime.py
server/failregex.py
testcases/actionstestcase.py
testcases/dummyjail.py
testcases/files/ignorecommand.py
testcases/files/testcase-usedns.log
testcases/files/logs/bsd/syslog-plain.txt
testcases/files/logs/bsd/syslog-v.txt
@ -57,10 +59,12 @@ testcases/files/logs/assp
testcases/files/logs/asterisk
testcases/files/logs/dovecot
testcases/files/logs/exim
testcases/files/logs/groupoffice
testcases/files/logs/suhosin
testcases/files/logs/mysqld-auth
testcases/files/logs/named-refused
testcases/files/logs/nginx-http-auth
testcases/files/logs/openwebmail
testcases/files/logs/pam-generic
testcases/files/logs/postfix
testcases/files/logs/proftpd
@ -148,6 +152,7 @@ config/filter.d/exim.conf
config/filter.d/gssftpd.conf
config/filter.d/suhosin.conf
config/filter.d/named-refused.conf
config/filter.d/openwebmail.conf
config/filter.d/postfix.conf
config/filter.d/proftpd.conf
config/filter.d/pure-ftpd.conf
@ -177,6 +182,7 @@ 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/groupoffice.conf
config/filter.d/perdition.conf
config/filter.d/uwimap-auth.conf
config/action.d/apf.conf
@ -242,3 +248,7 @@ files/fail2ban-tmpfiles.conf
files/fail2ban.service
files/ipmasq-ZZZzzz_fail2ban.rul
files/gen_badbots
testcases/config/jail.conf
testcases/config/fail2ban.conf
testcases/config/filter.d/simple.conf
testcases/config/action.d/brokenaction.conf

10
THANKS
View File

@ -6,14 +6,17 @@ 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
Andrey G. Grozin
Andy Fragen
Arturo 'Buanzo' Busleiman
Axel Thimm
Bas van den Dikkenberg
Beau Raines
Bill Heaton
Carlos Alberto Lopez Perez
@ -23,6 +26,7 @@ Christoph Haas
Christos Psonis
Cyril Jaquier
Daniel B. Cid
Daniel B.
Daniel Black
David Nutter
Eric Gerbier
@ -31,9 +35,11 @@ ftoppi
François Boulogne
Frédéric
Georgiy Mernov
Guilhem Lettron
Guillaume Delvit
Hanno 'Rince' Wagner
Iain Lea
Ivo Truxa
Jacques Lav!gnotte
Ioan Indreias
Jonathan Kamens
@ -45,14 +51,17 @@ Justin Shore
Kévin Drapel
kjohnsonecl
kojiro
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
Nick Munger
@ -71,6 +80,7 @@ Stefan Tatschner
Stephen Gildea
Steven Hiscocks
Tom Pike
Tomas Pihl
Tyler
Vaclav Misek
Vincent Deffontaines

View File

@ -112,7 +112,7 @@ class ConfigReader(SafeConfigParserWithIncludes):
except NoSectionError, e:
# No "Definition" section or wrong basedir
logSys.error(e)
values[option[1]] = option[2]
return False
except NoOptionError:
if not option[2] is None:
logSys.warn("'%s' not defined in '%s'. Using default one: %r"

View File

@ -24,6 +24,7 @@ __author__ = "Cyril Jaquier"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import os
import logging
from configreader import ConfigReader
@ -34,27 +35,31 @@ class FilterReader(ConfigReader):
def __init__(self, fileName, name, **kwargs):
ConfigReader.__init__(self, **kwargs)
self.__file = fileName
self.__name = name
# Defer initialization to the set Methods
self.__file = self.__name = self.__opts = None
self.setFile(fileName)
self.setName(name)
def setFile(self, fileName):
self.__file = fileName
self.__opts = None
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)
return ConfigReader.read(self, os.path.join("filter.d", self.__file))
def getOptions(self, pOpts):
opts = [["string", "ignoreregex", ""],
["string", "failregex", ""]]
["string", "failregex", ""],
]
self.__opts = ConfigReader.getOptions(self, "Definition", opts, pOpts)
def convert(self):

View File

@ -35,7 +35,7 @@ logSys = logging.getLogger("fail2ban.client.config")
class JailReader(ConfigReader):
actionCRE = re.compile("^((?:\w|-|_|\.)+)(?:\[(.*)\])?$")
actionCRE = re.compile("^([\w_.-]+)(?:\[(.*)\])?$")
def __init__(self, name, force_enable=False, **kwargs):
ConfigReader.__init__(self, **kwargs)
@ -43,7 +43,11 @@ class JailReader(ConfigReader):
self.__filter = None
self.__force_enable = force_enable
self.__actions = list()
self.__opts = None
def getRawOptions(self):
return self.__opts
def setName(self, value):
self.__name = value
@ -54,7 +58,7 @@ class JailReader(ConfigReader):
return ConfigReader.read(self, "jail")
def isEnabled(self):
return self.__force_enable or self.__opts["enabled"]
return self.__force_enable or ( self.__opts and self.__opts["enabled"] )
@staticmethod
def _glob(path):
@ -64,12 +68,10 @@ class JailReader(ConfigReader):
"""
pathList = []
for p in glob.glob(path):
if not os.path.exists(p):
logSys.warning("File %s doesn't even exist, thus cannot be monitored" % p)
elif not os.path.lexists(p):
logSys.warning("File %s is a dangling link, thus cannot be monitored" % p)
else:
if os.path.exists(p):
pathList.append(p)
else:
logSys.warning("File %s is a dangling link, thus cannot be monitored" % p)
return pathList
def getOptions(self):
@ -82,22 +84,29 @@ class JailReader(ConfigReader):
["string", "usedns", "warn"],
["string", "failregex", None],
["string", "ignoreregex", None],
["string", "ignorecommand", None],
["string", "ignoreip", None],
["string", "filter", ""],
["string", "action", ""]]
self.__opts = ConfigReader.getOptions(self, self.__name, opts)
if not self.__opts:
return False
if self.isEnabled():
# Read filter
self.__filter = FilterReader(self.__opts["filter"], self.__name,
basedir=self.getBaseDir())
ret = self.__filter.read()
if ret:
self.__filter.getOptions(self.__opts)
if self.__opts["filter"]:
self.__filter = FilterReader(self.__opts["filter"], self.__name,
basedir=self.getBaseDir())
ret = self.__filter.read()
if ret:
self.__filter.getOptions(self.__opts)
else:
logSys.error("Unable to read the filter")
return False
else:
logSys.error("Unable to read the filter")
return False
self.__filter = None
logSys.warn("No filter set for jail %s" % self.__name)
# Read action
for act in self.__opts["action"].split('\n'):
try:
@ -160,12 +169,15 @@ class JailReader(ConfigReader):
stream.append(["set", self.__name, "usedns", self.__opts[opt]])
elif opt == "failregex":
stream.append(["set", self.__name, "addfailregex", self.__opts[opt]])
elif opt == "ignorecommand":
stream.append(["set", self.__name, "ignorecommand", self.__opts[opt]])
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])
stream.extend(self.__filter.convert())
if self.__filter:
stream.extend(self.__filter.convert())
for action in self.__actions:
stream.extend(action.convert())
stream.insert(0, ["add", self.__name, backend])
@ -175,12 +187,16 @@ class JailReader(ConfigReader):
def splitAction(action):
m = JailReader.actionCRE.match(action)
d = dict()
mgroups = m.groups()
try:
mgroups = m.groups()
except AttributeError:
raise ValueError("While reading action %s we should have got 1 or "
"2 groups. Got: 0" % action)
if len(mgroups) == 2:
action_name, action_opts = mgroups
elif len(mgroups) == 1:
elif len(mgroups) == 1: # pragma: nocover - unreachable - .* on second group always matches
action_name, action_opts = mgroups[0], None
else:
else: # pragma: nocover - unreachable - regex only can capture 2 groups
raise ValueError("While reading action %s we should have got up to "
"2 groups. Got: %r" % (action, mgroups))
if not action_opts is None:

View File

@ -45,6 +45,9 @@ class JailsReader(ConfigReader):
self.__jails = list()
self.__force_enable = force_enable
def getJails(self):
return self.__jails
def read(self):
return ConfigReader.read(self, "jail")
@ -60,6 +63,7 @@ class JailsReader(ConfigReader):
sections = [ section ]
# Get the options of all jails.
parse_status = True
for sec in sections:
jail = JailReader(sec, basedir=self.getBaseDir(),
force_enable=self.__force_enable)
@ -71,8 +75,8 @@ class JailsReader(ConfigReader):
self.__jails.append(jail)
else:
logSys.error("Errors in jail %r. Skipping..." % sec)
return False
return True
parse_status = False
return parse_status
def convert(self, allow_no_files=False):
"""Convert read before __opts and jails to the commands stream

View File

@ -57,6 +57,7 @@ protocol = [
["set <JAIL> dellogpath <FILE>", "removes <FILE> from the monitoring list of <JAIL>"],
["set <JAIL> addfailregex <REGEX>", "adds the regular expression <REGEX> which must match failures for <JAIL>"],
["set <JAIL> delfailregex <INDEX>", "removes the regular expression at <INDEX> for failregex"],
["set <JAIL> ignorecommand <VALUE>", "sets ignorecommand of <JAIL>"],
["set <JAIL> addignoreregex <REGEX>", "adds the regular expression <REGEX> which should match pattern to exclude for <JAIL>"],
["set <JAIL> delignoreregex <INDEX>", "removes the regular expression at <INDEX> for ignoreregex"],
["set <JAIL> findtime <TIME>", "sets the number of seconds <TIME> for which the filter will look back for <JAIL>"],
@ -77,6 +78,7 @@ protocol = [
['', "JAIL INFORMATION", ""],
["get <JAIL> logpath", "gets the list of the monitored files for <JAIL>"],
["get <JAIL> ignoreip", "gets the list of ignored IP addresses for <JAIL>"],
["get <JAIL> ignorecommand", "gets ignorecommand of <JAIL>"],
["get <JAIL> failregex", "gets the list of regular expressions which matches the failures for <JAIL>"],
["get <JAIL> ignoreregex", "gets the list of regular expressions which matches patterns to ignore for <JAIL>"],
["get <JAIL> findtime", "gets the time for which the filter will look back for failures for <JAIL>"],

View File

@ -1,45 +1,17 @@
# 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]
@ -48,3 +20,6 @@ actionunban = apf --remove <ip>
#
name = default
# DEV NOTES:
#
# Author: Mark McKinstry

View File

@ -0,0 +1,69 @@
# 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>
actioncheck = firewall-cmd --direct --get-chains ipv4 filter | grep -q '^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

@ -1,9 +1,5 @@
# 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]
@ -20,7 +16,7 @@ actionstop = firewall-cmd --direct --remove-rule ipv4 filter <chain> 0 -m state
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]'
actioncheck = firewall-cmd --direct --get-chains ipv4 filter | grep -q '^fail2ban-<name>$'
actionban = firewall-cmd --direct --add-rule ipv4 filter fail2ban-<name> 0 -s <ip> -j <blocktype>
@ -50,3 +46,27 @@ protocol = tcp
# 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

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

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

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

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

@ -9,8 +9,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)(, referer: \S+)?\s*$
^%(_apache_error_client)s script '/\S*(\.php|\.asp|\.exe|\.pl)\S*' not found or unable to stat(, referer: \S+)?\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 =

View File

@ -15,7 +15,7 @@ failregex = ^%(log_prefix)s Registration from '[^']*' failed for '<HOST>(:\d+)?'
^%(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]+")?$
^%(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+")?$
^\[\]\s*WARNING%(__pid_re)s:?(?:\[C-[\da-f]*\])? Ext\. s: "Rejecting unknown SIP connection from <HOST>"$
ignoreregex =

View File

@ -0,0 +1,19 @@
# 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 = ^(?:\.\d+)? \[info\] <0\.\d+\.\d>@ejabberd_c2s:wait_for_feature_request:\d+ \([^\)]+\) Failed authentication for \S+ from IP <HOST>$

View File

@ -1,5 +1,6 @@
# Fail2Ban filter for exim the spam rejection messages
#
## For the SA: Action: silently tossed message... to be logged exim's SAdevnull option needs to be used.
[INCLUDES]
@ -12,6 +13,7 @@ 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: 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 =

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

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,6 +15,7 @@ _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 =

View File

@ -1,7 +1,11 @@
# 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
@ -17,3 +21,4 @@ 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

View File

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

@ -21,6 +21,7 @@ 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*$

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

View File

@ -31,6 +31,12 @@
# defined using space separator.
ignoreip = 127.0.0.1/8
# External command that will take an tagged arguments to ignore, e.g. <ip>,
# and return true if the IP is to be ignored. False otherwise.
#
# ignorecommand = /path/to/command <ip>
ignorecommand =
# "bantime" is the number of seconds that a host is banned.
bantime = 600
@ -69,6 +75,22 @@ usedns = warn
# The mail-whois action send a notification e-mail with a whois request
# in the body.
[pam-generic]
enabled = false
filter = pam-generic
action = iptables-allports[name=pam,protocol=all]
logpath = /var/log/secure
[xinetd-fail]
enabled = false
filter = xinetd-fail
action = iptables-allports[name=xinetd,protocol=all]
logpath = /var/log/daemon*log
[ssh-iptables]
enabled = false
@ -78,6 +100,25 @@ action = iptables[name=SSH, port=ssh, protocol=tcp]
logpath = /var/log/sshd.log
maxretry = 5
[ssh-ddos]
enabled = false
filter = sshd-ddos
action = iptables[name=SSHDDOS, port=ssh, protocol=tcp]
logpath = /var/log/sshd.log
maxretry = 2
[dropbear]
enabled = false
filter = dropbear
action = iptables[name=dropbear, port=ssh, protocol=tcp]
logpath = /var/log/messages
maxretry = 5
[proftpd-iptables]
enabled = false
@ -88,6 +129,35 @@ logpath = /var/log/proftpd/proftpd.log
maxretry = 6
[gssftpd-iptables]
enabled = false
filter = gssftpd
action = iptables[name=GSSFTPd, port=ftp, protocol=tcp]
sendmail-whois[name=GSSFTPd, dest=you@example.com]
logpath = /var/log/daemon.log
maxretry = 6
[pure-ftpd]
enabled = false
filter = pure-ftpd
action = iptables[name=pureftpd, port=ftp, protocol=tcp]
logpath = /var/log/pureftpd.log
maxretry = 6
[wuftpd]
enabled = false
filter = wuftpd
action = iptables[name=wuftpd, port=ftp, protocol=tcp]
logpath = /var/log/daemon.log
maxretry = 6
# This jail forces the backend to "polling".
[sasl-iptables]
@ -181,6 +251,36 @@ logpath = /var/log/apache*/*error.log
maxretry = 6
[apache-modsecurity]
enabled = false
filter = apache-modsecurity
action = iptables-multiport[name=apache-modsecurity,port="80,443"]
logpath = /var/log/apache*/*error.log
/home/www/myhomepage/error.log
maxretry = 2
[apache-overflows]
enabled = false
filter = apache-overflows
action = iptables-multiport[name=apache-overflows,port="80,443"]
logpath = /var/log/apache*/*error.log
/home/www/myhomepage/error.log
maxretry = 2
[apache-nohome]
enabled = false
filter = apache-nohome
action = iptables-multiport[name=apache-nohome,port="80,443"]
logpath = /var/log/apache*/*error.log
/home/www/myhomepage/error.log
maxretry = 2
[nginx-http-auth]
enabled = false
@ -189,6 +289,14 @@ action = iptables-multiport[name=nginx-http-auth,port="80,443"]
logpath = /var/log/nginx/error.log
[squid]
enabled = false
filter = squid
action = iptables-multiport[name=squid,port="80,443,8080"]
logpath = /var/log/squid/access.log
# The hosts.deny path can be defined with the "file" argument if it is
# not in /etc.
[postfix-tcpwrapper]
@ -201,6 +309,46 @@ logpath = /var/log/postfix.log
bantime = 300
[cyrus-imap]
enabled = false
filter = cyrus-imap
action = iptables-multiport[name=cyrus-imap,port="143,993"]
logpath = /var/log/mail*log
[courierlogin]
enabled = false
filter = courierlogin
action = iptables-multiport[name=courierlogin,port="25,110,143,465,587,993,995"]
logpath = /var/log/mail*log
[couriersmtp]
enabled = false
filter = couriersmtp
action = iptables-multiport[name=couriersmtp,port="25,465,587"]
logpath = /var/log/mail*log
[qmail-rbl]
enabled = false
filter = qmail
action = iptables-multiport[name=qmail-rbl,port="25,465,587"]
logpath = /service/qmail/log/main/current
[sieve]
enabled = false
filter = sieve
action = iptables-multiport[name=sieve,port="25,465,587"]
logpath = /var/log/mail*log
# Do not ban anybody. Just report information about the remote host.
# A notification is sent at most every 600 seconds (bantime).
[vsftpd-notification]
@ -268,6 +416,43 @@ action = iptables-multiport[name=SOGo, port="http,https"]
logpath = /var/log/sogo/sogo.log
[groupoffice]
enabled = false
filter = groupoffice
action = iptables-multiport[name=groupoffice, port="http,https"]
logpath = /home/groupoffice/log/info.log
[openwebmail]
enabled = false
filter = openwebmail
logpath = /var/log/openwebmail.log
action = ipfw
sendmail-whois[name=openwebmail, dest=you@example.com]
maxretry = 5
[horde]
enabled = false
filter = horde
logpath = /var/log/horde/horde.log
action = iptables-multiport[name=horde, port="http,https"]
maxretry = 5
# Ban attackers that try to use PHP's URL-fopen() functionality
# through GET/POST variables. - Experimental, with more than a year
# of usage in production environments.
[php-url-fopen]
enabled = false
action = iptables-multiport[name=php-url-open, port="http,https"]
filter = php-url-fopen
logpath = /var/www/*/logs/access_log
maxretry = 1
# Ban attackers that try to use PHP's URL-fopen() functionality
# through GET/POST variables. - Experimental, with more than a year
# of usage in production environments.
@ -345,6 +530,15 @@ logpath = /var/log/named/security.log
ignoreip = 168.192.0.1
[nsd]
enabled = false
filter = nsd
action = iptables-multiport[name=nsd-tcp, port="domain", protocol=tcp]
iptables-multiport[name=nsd-udp, port="domain", protocol=udp]
logpath = /var/log/nsd.log
[asterisk]
enabled = false
@ -355,6 +549,23 @@ action = iptables-multiport[name=asterisk-tcp, port="5060,5061", protocol=tcp]
logpath = /var/log/asterisk/messages
maxretry = 10
[freeswitch]
enabled = false
filter = freeswitch
logpath = /var/log/freeswitch.log
maxretry = 10
action = iptables-multiport[name=freeswitch-tcp, port="5060,5061,5080,5081", protocol=tcp]
iptables-multiport[name=freeswitch-udp, port="5060,5061,5080,5081", protocol=udp]
[ejabberd-auth]
enabled = false
filter = ejabberd-auth
logpath = /var/log/ejabberd/ejabberd.log
action = iptables[name=ejabberd, port=xmpp-client, protocol=tcp]
# Historical support (before https://github.com/fail2ban/fail2ban/issues/37 was fixed )
# use [asterisk] for new jails
[asterisk-tcp]
@ -527,9 +738,9 @@ logpath = /var/log/mail.log
[selinux-ssh]
enabled = false
filter = selinux-ssh
action = iptables[name=SELINUX-SSH, port=ssh, protocol=tcp]
enabled = false
filter = selinux-ssh
action = iptables[name=SELINUX-SSH, port=ssh, protocol=tcp]
logpath = /var/log/audit/audit.log
maxretry = 5

View File

@ -153,6 +153,7 @@ else: # pragma: no cover
#tests.addTest(unittest.makeSuite(servertestcase.StartStop))
tests.addTest(unittest.makeSuite(servertestcase.Transmitter))
tests.addTest(unittest.makeSuite(servertestcase.JailTests))
tests.addTest(unittest.makeSuite(servertestcase.RegexTests))
tests.addTest(unittest.makeSuite(actiontestcase.ExecuteAction))
tests.addTest(unittest.makeSuite(actionstestcase.ExecuteActions))
# FailManager
@ -177,6 +178,7 @@ if not opts.no_network:
tests.addTest(unittest.makeSuite(filtertestcase.IgnoreIP))
tests.addTest(unittest.makeSuite(filtertestcase.BasicFilter))
tests.addTest(unittest.makeSuite(filtertestcase.LogFile))
tests.addTest(unittest.makeSuite(filtertestcase.LogFileFilterPoll))
tests.addTest(unittest.makeSuite(filtertestcase.LogFileMonitor))
if not opts.no_network:
tests.addTest(unittest.makeSuite(filtertestcase.GetFailures))

View File

@ -108,6 +108,7 @@ my ($critical,$warning) = (2,1);
my $fail2ban_client_path = '/usr/bin/fail2ban-client';
my $fail2ban_socket = '';
my $jail_specific = '';
my $jail_name = '';
GetOptions (
'P=s' => \ $fail2ban_client_path,
@ -165,7 +166,7 @@ if (($critical < 0) or ($warning < 0) or ($critical < $warning)) {
# Core script
# -----------
my ($how_many_jail,$how_many_banned,$return_print,$plugstate) = (0,0,"","OK");
my ($how_many_jail,$how_many_banned,$return_print,$perf_print,$plugstate) = (0,0,"","","OK");
### Test the connection to the fail2ban server
@ -190,6 +191,7 @@ if ($jail_specific) {
else {
$how_many_banned = int($current_ban_number);
$return_print = $how_many_banned.' current banned IP(s) for the specific jail '.$jail_specific;
$perf_print .= "$current_ban_number " if ($perfdata_value);
}
}
### To analyze all the jail
@ -214,6 +216,7 @@ else {
else {
print "DEBUG : the jail $jail_name has currently $current_ban_number banned IPs\n" if ($verbose_value);
$how_many_banned += int($current_ban_number);
$perf_print .= "$jail_name.currentBannedIP=$current_ban_number " if ($perfdata_value);
}
}
$return_print = $how_many_jail.' detected jails with '.$how_many_banned.' current banned IP(s)';
@ -224,7 +227,7 @@ $plugstate = "CRITICAL" if ($how_many_banned >= $critical);
$plugstate = "WARNING" if (($how_many_banned >= $warning) && ($how_many_banned < $critical));
$return_print = $display." - ".$plugstate." - ".$return_print;
$return_print .= " | currentBannedIP=$how_many_banned" if ($perfdata_value);
$return_print .= " | $perf_print" if ($perfdata_value);
print $return_print;
exit $ERRORS{"$plugstate"};

View File

@ -128,6 +128,9 @@ for <JAIL>
removes the regular expression at
<INDEX> for failregex
.TP
\fBset <JAIL> ignorecommand <VALUE>\fR
sets ignorecommand of <JAIL>
.TP
\fBset <JAIL> addignoreregex <REGEX>\fR
adds the regular expression
<REGEX> which should match pattern
@ -206,6 +209,9 @@ files for <JAIL>
gets the list of ignored IP
addresses for <JAIL>
.TP
\fBget <JAIL> ignorecommand\fR
gets ignorecommand of <JAIL>
.TP
\fBget <JAIL> failregex\fR
gets the list of regular
expressions which matches the

View File

@ -3,22 +3,34 @@
jail.conf \- configuration for the fail2ban server
.SH SYNOPSIS
.I fail2ban.conf fail2ban.d/*.conf fail2ban.d/*.local
.I fail2ban.conf fail2ban.d/*.conf fail2ban.local fail2ban.d/*.local
.I jail.conf / jail.local
.I jail.conf jail.d/*.conf jail.local jail.d/*.local
.I action.d/*.conf action.d/*.local
.I filter.d/*.conf filter.d/*.local
.SH DESCRIPTION
Fail2ban has three configuration file types. Action files are the commands for banning and unbanning of IP address,
Filter files tell fail2ban how to detect authentication failures, and Jail configurations combine filters with actions into jails.
There are *.conf files that are distributed by fail2ban and *.local file that contain user customizations.
It is recommended that *.conf files should remain unchanged. If needed, customizations should be provided in *.local files.
For instance, if you would like to customize the [ssh-iptables-ipset] jail, create a jail.local to extend jail.conf
(the configuration for the fail2ban server). The jail.local file will be the following if you only need to enable
it:
.SH DESCRIPTION
Fail2ban has four configuration file types:
.TP
\fIfail2ban.conf\fR
Fail2Ban global configuration (such as logging)
.TP
\fIfilter.d/*.conf\fR
Filters specifying how to detect authentication failures
.TP
\fIaction.d/*.conf\fR
Actions defining the commands for banning and unbanning of IP address
.TP
\fIjail.conf\fR
Jails defining combinations of Filters with Actions.
.SH "CONFIGURATION FILES FORMAT"
\fI*.conf\fR files are distributed by Fail2Ban. It is recommended that *.conf files should remain unchanged to ease upgrades. If needed, customizations should be provided in \fI*.local\fR files. For example, if you would like to enable the [ssh-iptables-ipset] jail specified in jail.conf, create jail.local containing
.TP
\fIjail.local\fR
@ -27,81 +39,156 @@ it:
enabled = true
.PP
Override only the settings you need to change and the rest of the configuration will come from the corresponding
*.conf file.
In .local files specify only the settings you would like to change and the rest of the configuration will then come from the corresponding .conf file which is parsed first.
.TP
\fIjail.d/\fR and \fIfail2ban.d/\fR
In addition to .local, for jail.conf or fail2ban.conf file there can
be a corresponding \fI.d/\fR directory containing additional .conf
files. The order e.g. for \fIjail\fR configuration would be:
\fI*.d/\fR
.RS
In addition to .local, for any .conf file there can be a corresponding
\fI.d/\fR directory to contain additional .conf files that will be read after the
appropriate .local file. Last parsed file will take precidence over
identical entries, parsed alphabetically, e.g.
jail.conf
.RE
.RS
jail.d/*.conf (in alphabetical order)
.RE
.RS
jail.local
.RE
.RS
jail.d/*.local (in alphabetical order).
i.e. all .local files are parsed after .conf files in the original
configuration file and files under .d directory. Settings in the file
parsed later take precedence over identical entries in previously
parsed files. Files are ordered alphabetically, e.g.
\fIfail2ban.d/01_custom_log.conf\fR - to use a different log path
.RE
.RS
\fIjail.d/01_enable.conf\fR - to enable a specific jail
.RE
.RS
\fIjail.d/02_custom_port.conf\fR - containing specific configuration entry to change the port of the jail specified in the configuration
\fIjail.d/02_custom_port.conf\fR - to change the port(s) of a jail.
.RE
.RE
Configuration files have sections, those specified with [section name], and name = value pairs. For those name items that can accept multiple values, specify the values separated by spaces, or in separate lines space indented at the beginning of the line before the second value.
.PP
Configuration files can include other (defining common variables) configuration files, which is often used in Filters and Actions. Such inclusions are defined in a section called [INCLUDES]:
.TP
.B before
indicates that the specified file is to be parsed before the current file.
.TP
.B after
indicates that the specified file is to be parsed after the current file.
.RE
Using Python "string interpolation" mechanisms, other definitions are allowed and can later be used within other definitions as %(name)s. For example.
.RS
baduseragents = IE|wget
.RE
.RS
\fIfail2ban.d/01_custom_log.conf\fR - containing specific configuration entry to use a different log path.
.RE
failregex = useragent=%(baduseragents)s
.RE
The order \fIjail\fR configuration is parsed is:
Comments: use '#' for comment lines and '; ' (space is important) for inline comments. When using Python2.X '; ' can only be used on the first line due to an Python library bug.
jail.conf ,
jail.d/*.conf (in alphabetical order),
jail.local, followed by
jail.d/*.local (in alphabetical order).
.SH "FAIL2BAN CONFIGURATION FILE(S) (\fIfail2ban.conf\fB)"
Likewise for fail2ban configuration.
These files have one section, [Definition].
Comments: use '#' for comment lines and ';' (following a space) for inline comments
.SH DEFAULT
The following options are applicable to all jails. Their meaning is described in the default \fIjail.conf\fR file.
The items that can be set are:
.TP
\fBignoreip\fR
.B loglevel
verbosity level of log output: 1 = ERROR, 2 = WARN, 3 = INFO, 4 = DEBUG. Default: 1
.TP
\fBbantime\fR
.B logtarget
log target: filename, SYSLOG, STDERR or STDOUT. Default: STDERR . Only a single log target can be specified.
If you change logtarget from the default value and you are using logrotate -- also adjust or disable rotation in the
corresponding configuration file (e.g. /etc/logrotate.d/fail2ban on Debian systems).
.TP
\fBfindtime\fR
.B socket
socket filename. Default: /var/run/fail2ban/fail2ban.sock .
This is used for communication with the fail2ban server daemon. Do not remove this file when Fail2ban is running. It will not be possible to communicate with the server afterwards.
.TP
\fBmaxretry\fR
.B pidfile
PID filename. Default: /var/run/fail2ban/fail2ban.pid.
This is used to store the process ID of the fail2ban server.
.SH "JAIL CONFIGURATION FILE(S) (\fIjail.conf\fB)"
The following options are applicable to any jail. They appear in a section specifying the jail name or in the \fI[DEFAULT]\fR section which defines default values to be used if not specified in the individual section.
.TP
\fBbackend\fR
.B filter
name of the filter -- filename of the filter in /etc/fail2ban/filter.d/ without the .conf/.local extension. Only one filter can be specified.
.TP
\fBusedns\fR
.B logpath
filename(s) of the log files to be monitored. Globs -- paths containing * and ? or [0-9] -- can be used however only the files that exist at start up matching this glob pattern will be considered.
.TP
.B action
action(s) from \fI/etc/fail2ban/action.d/\fR without the \fI.conf\fR/\fI.local\fR extension. Arguments can be passed to actions to override the default values from the [Init] section in the action file. Arguments are specified by [name=value,name2=value]. Values can also be quoted. More that one action can be specified (in separate lines).
.TP
.B ignoreip
list of IPs not to ban. They can include a CIDR mask too.
.TP
.B ignorecommand
command that is executed to determine if the current candidate IP for banning should not be banned. IP will not be banned if command returns successfully (exit code 0).
Like ACTION FILES, tags like <ip> are can be included in the ignorecommand value and will be substituted before execution. Currently only <ip> is supported however more will be added later.
.TP
.B bantime
effective ban duration (in seconds).
.TP
.B findtime
time interval (in seconds) before the current time where failures will count towards a ban.
.TP
.B maxretry
number of failures that have to occur in the last \fBfindtime\fR seconds to ban then IP.
.TP
.B backend
backend to be used to detect changes in the logpath. It defaults to "auto" which will try "pyinotify", "gamin" before "polling". Any of these can be specified. "pyinotify" is only valid on Linux systems with the "pyinotify" Python libraries. "gamin" requires the "gamin" libraries.
.TP
.B usedns
use DNS to resolve HOST names that appear in the logs. By default it is "warn" which will resolve hostnames to IPs however it will also log a warning. If you are using DNS here you could be blocking the wrong IPs due to the asymmetric nature of reverse DNS (that the application used to write the domain name to log) compared to forward DNS that fail2ban uses to resolve this back to an IP (but not necessarily the same one). Ideally you should configure your applications to log a real IP. This can be set to "yes" to prevent warnings in the log or "no" to disable DNS resolution altogether (thus ignoring entries where hostname, not an IP is logged)..
.TP
.B failregex
regex (Python \fBreg\fRular \fBex\fRpression) to be added to the filter's failregexes. If this is useful for others using your application please share you regular expression with the fail2ban developers by reporting an issue (see REPORTING BUGS below).
.TP
.B ignoreregex
regex which, if the log line matches, would cause Fail2Ban not consider that line. This line will be ignored even if it matches a failregex of the jail or any of its filters.
.SH "ACTION FILES"
Action files specify which commands are executed to ban and unban an IP address. They are located under \fI/etc/fail2ban/action.d\fR.
.SH "ACTION CONFIGURATION FILES (\fIaction.d/*.conf\fB)"
Action files specify which commands are executed to ban and unban an IP address.
Like with jail.conf files, if you desire local changes create an \fI[actionname].local\fR file in the \fI/etc/fail2ban/action.d\fR directory
and override the required settings.
Action files are ini files that have two sections, \fBDefinition\fR and \fBInit\fR .
Action files have two sections, \fBDefinition\fR and \fBInit\fR .
The [Init] section allows for action-specific settings. In \fIjail.conf/jail.local\fR these can be overwritten for a particular jail as options to the jail.
The [Init] section enables action-specific settings. In \fIjail.conf/jail.local\fR these can be overridden for a particular jail as options of the action's specification in that jail.
The following commands can be present in the [Definition] section.
.TP
\fBactionstart\fR
.B actionstart
command(s) executed when the jail starts.
.TP
\fBactionstop\fR
.B actionstop
command(s) executed when the jail stops.
.TP
\fBactioncheck\fR
the command ran before any other action. It aims to verify if the environment is still ok.
.B actioncheck
command(s) ran before any other action. It aims to verify if the environment is still ok.
.TP
\fBactionban\fR
.B actionban
command(s) that bans the IP address after \fBmaxretry\fR log lines matches within last \fBfindtime\fR seconds.
.TP
\fBactionunban\fR
.B actionunban
command(s) that unbans the IP address after \fBbantime\fR.
.RE
Commands specified in the [Definition] section are executed through a system shell so shell redirection and process control is allowed. The commands should
return 0, otherwise error would be logged. Moreover if \fBactioncheck\fR exits with non-0 status, it is taken as indication that firewall status has changed and fail2ban needs to reinitialize itself (i.e. issue \fBactionstop\fR and \fBactionstart\fR commands).
@ -109,7 +196,7 @@ return 0, otherwise error would be logged. Moreover if \fBactioncheck\fR exits
Tags are enclosed in <>. All the elements of [Init] are tags that are replaced in all action commands. Tags can be added by the
\fBfail2ban-client\fR using the setctag command. \fB<br>\fR is a tag that is always a new line (\\n).
More than a single command is allowed to be specified. Each command needs to be on a separate line and indented with whitespaces without blank lines. The following example defines
More than a single command is allowed to be specified. Each command needs to be on a separate line and indented with whitespace(s) without blank lines. The following example defines
two commands to be executed.
actionban = iptables -I fail2ban-<name> --source <ip> -j DROP
@ -118,66 +205,50 @@ two commands to be executed.
.SS "Action Tags"
The following tags are substituted in the actionban, actionunban and actioncheck (when called before actionban/actionunban) commands.
.TP
\fBip\fR
An IPv4 ip address to be banned. e.g. 192.168.0.2
.B ip
IPv4 IP address to be banned. e.g. 192.168.0.2
.TP
\fBfailures\fR
The number of times the failure occurred in the log file. e.g. 3
.B failures
number of times the failure occurred in the log file. e.g. 3
.TP
\fBtime\fR
The unix time of the ban. e.g. 1357508484
.B time
UNIX (epoch) time of the ban. e.g. 1357508484
.TP
\fBmatches\fR
The concatenated string of the log file lines of the matches that generated the ban. Many characters interpreted by shell get escaped.
.B matches
concatenated string of the log file lines of the matches that generated the ban. Many characters interpreted by shell get escaped to prevent injection, nevertheless use with caution.
.SH FILTER FILES
.SH "FILTER FILES (\fIfilter.d/*.conf\fB)"
Filter definitions are those in \fI/etc/fail2ban/filter.d/*.conf\fR and \fIfilter.d/*.local\fR.
These are used to identify failed authentication attempts in logs and to extract the host IP address (or hostname if \fBusedns\fR is \fBtrue\fR).
These are used to identify failed authentication attempts in log files and to extract the host IP address (or hostname if \fBusedns\fR is \fBtrue\fR).
Like action files, filter files are ini files. The main section is the [Definition] section.
There are two filter definitions used in the [Definition] section:
.TP
\fBfailregex\fR
is the regex (\fBreg\fRular \fBex\fRpression) that will match failed attempts. The tag <HOST> is used as part of the regex and is itself a regex
for IPv4 addresses and hostnames. fail2ban will work out which one of these it actually is.
.B failregex
regex that will match failed attempts. The tag <HOST> is used as part of the regex and is itself a regex
for IPv4 addresses (and hostnames if \fBusedns\fR). Fail2Ban will work out which one of these it actually is.
.TP
\fBignoreregex\fR
is the regex to identify log entries that should be ignored by fail2ban, even if they match failregex.
Using Python "string interpolation" mechanisms, other definitions are allowed and can later be used within other definitions as %(defnname)s. For example.
baduseragents = IE|wget
failregex = useragent=%(baduseragents)s
.PP
Filters can also have a section called [INCLUDES]. This is used to read other configuration files.
.TP
\fBbefore\fR
indicates that this file is read before the [Definition] section.
.TP
\fBafter\fR
indicates that this file is read after the [Definition] section.
.B ignoreregex
regex to identify log entries that should be ignored by Fail2Ban, even if they match failregex.
.SH AUTHOR
Fail2ban was originally written by Cyril Jaquier <cyril.jaquier@fail2ban.org>.
At the moment it is maintained and further developed by Yaroslav O. Halchenko <debian@onerussian.com> and a number of contributors. See \fBTHANKS\fR file shipped with Fail2Ban for a full list.
At the moment it is maintained and further developed by Yaroslav O. Halchenko <debian@onerussian.com>, Daniel Black <daniel.subs@internode.on.net> and Steven Hiscocks <steven-fail2ban@hiscocks.me.uk> along with a number of contributors. See \fBTHANKS\fR file shipped with Fail2Ban for a full list.
.
Manual page written by Daniel Black and Yaroslav Halchenko.
.SH "REPORTING BUGS"
Report bugs to https://github.com/fail2ban/fail2ban/issues
.SH COPYRIGHT
Copyright \(co 2013 Daniel Black
Copyright \(co 2013 the Fail2Ban Team
.br
Copyright of modifications held by their respective authors.
Licensed under the GNU General Public License v2 (GPL).
.br
Licensed under the GNU General Public License v2 (GPL) or
(at your option) any later version.
.
.SH "SEE ALSO"
.br
fail2ban-server(1)

View File

@ -65,7 +65,7 @@ class RequestHandler(asynchat.async_chat):
# Serializes the response.
message = dumps(message, HIGHEST_PROTOCOL)
# Sends the response to the client.
self.send(message + RequestHandler.END_STRING)
self.push(message + RequestHandler.END_STRING)
# Closes the channel.
self.close_when_done()

View File

@ -21,7 +21,7 @@ __author__ = "Cyril Jaquier and Fail2Ban Contributors"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import time, logging
import sys, time, logging
from datetemplate import DateStrptime, DateTai64n, DateEpoch, DateISO8601
from threading import Lock
@ -46,12 +46,13 @@ class DateDetector:
def addDefaultTemplate(self):
self.__lock.acquire()
try:
# asctime with subsecond
template = DateStrptime()
template.setName("WEEKDAY MONTH Day Hour:Minute:Second[.subsecond] Year")
template.setRegex("\S{3} \S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}\.\d+ \d{4}")
template.setPattern("%a %b %d %H:%M:%S.%f %Y")
self._appendTemplate(template)
if sys.version_info >= (2, 5): # because of '%.f'
# asctime with subsecond
template = DateStrptime()
template.setName("WEEKDAY MONTH Day Hour:Minute:Second[.subsecond] Year")
template.setRegex("\S{3} \S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}\.\d+ \d{4}")
template.setPattern("%a %b %d %H:%M:%S.%f %Y")
self._appendTemplate(template)
# asctime without no subsecond
template = DateStrptime()
template.setName("WEEKDAY MONTH Day Hour:Minute:Second Year")
@ -101,13 +102,14 @@ class DateDetector:
template.setRegex("\d{2}/\d{2}/\d{4}:\d{2}:\d{2}:\d{2}")
template.setPattern("%m/%d/%Y:%H:%M:%S")
self._appendTemplate(template)
# proftpd 2013-11-16 21:43:03,296
# So like Exim below but with ,subsecond
template = DateStrptime()
template.setName("Year-Month-Day Hour:Minute:Second[,subsecond]")
template.setRegex("\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d+")
template.setPattern("%Y-%m-%d %H:%M:%S,%f")
self._appendTemplate(template)
if sys.version_info >= (2, 5): # because of '%.f'
# proftpd 2013-11-16 21:43:03,296
# So like Exim below but with ,subsecond
template = DateStrptime()
template.setName("Year-Month-Day Hour:Minute:Second[,subsecond]")
template.setRegex("\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d+")
template.setPattern("%Y-%m-%d %H:%M:%S,%f")
self._appendTemplate(template)
# Exim 2006-12-21 06:43:20
template = DateStrptime()
template.setName("Year-Month-Day Hour:Minute:Second")

View File

@ -78,7 +78,7 @@ class DateEpoch(DateTemplate):
def __init__(self):
DateTemplate.__init__(self)
self.setRegex("(?:^|(?P<selinux>(?<=audit\()))\d{10}(?:\.\d{3,6})?(?(selinux)(?=:\d+\)))")
self.setRegex("(?:^|(?P<square>(?<=^\[))|(?P<selinux>(?<=audit\()))\d{10}(?:\.\d{3,6})?(?(selinux)(?=:\d+\))(?(square)(?=\])))")
def getDate(self, line):
date = None

View File

@ -30,8 +30,9 @@ from jailthread import JailThread
from datedetector import DateDetector
from mytime import MyTime
from failregex import FailRegex, Regex, RegexException
from action import Action
import logging, re, os, fcntl, time
import logging, re, os, fcntl, time, shlex, subprocess
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.filter")
@ -67,6 +68,8 @@ class Filter(JailThread):
self.__findTime = 6000
## The ignore IP list.
self.__ignoreIpList = []
## External command
self.__ignoreCommand = False
self.dateDetector = DateDetector()
self.dateDetector.addDefaultTemplate()
@ -212,6 +215,20 @@ class Filter(JailThread):
def run(self): # pragma: no cover
raise Exception("run() is abstract")
##
# Set external command, for ignoredips
#
def setIgnoreCommand(self, command):
self.__ignoreCommand = command
##
# Get external command, for ignoredips
#
def getIgnoreCommand(self):
return self.__ignoreCommand
##
# Ban an IP - http://blogs.buanzo.com.ar/2009/04/fail2ban-patch-ban-ip-address-manually.html
# Arturo 'Buanzo' Busleiman <buanzo@buanzo.com.ar>
@ -284,6 +301,12 @@ class Filter(JailThread):
continue
if a == b:
return True
if self.__ignoreCommand:
command = Action.replaceTag(self.__ignoreCommand, { 'ip': ip } )
logSys.debug('ignore command: ' + command)
return Action.executeCmd(command)
return False

View File

@ -115,9 +115,6 @@ class FilterPyinotify(FileFilter):
wd = self.__monitor.add_watch(path, pyinotify.IN_MODIFY)
self.__watches.update(wd)
logSys.debug("Added file watcher for %s", path)
# process the file since we did get even
self._process_file(path)
def _delFileWatcher(self, path):
wdInt = self.__watches[path]
@ -143,6 +140,7 @@ class FilterPyinotify(FileFilter):
logSys.debug("Added monitor for the parent directory %s", path_dir)
self._addFileWatcher(path)
self._process_file(path)
##

View File

@ -184,6 +184,12 @@ class Server:
def getFindTime(self, name):
return self.__jails.getFilter(name).getFindTime()
def setIgnoreCommand(self, name, value):
self.__jails.getFilter(name).setIgnoreCommand(value)
def getIgnoreCommand(self, name):
return self.__jails.getFilter(name).getIgnoreCommand()
def addFailRegex(self, name, value):
self.__jails.getFilter(name).addFailRegex(value)

View File

@ -142,6 +142,10 @@ class Transmitter:
value = command[2]
self.__server.delLogPath(name, value)
return self.__server.getLogPath(name)
elif command[1] == "ignorecommand":
value = command[2]
self.__server.setIgnoreCommand(name, value)
return self.__server.getIgnoreCommand(name)
elif command[1] == "addfailregex":
value = command[2]
self.__server.addFailRegex(name, value)
@ -239,6 +243,8 @@ class Transmitter:
return self.__server.getLogPath(name)
elif command[1] == "ignoreip":
return self.__server.getIgnoreIP(name)
elif command[1] == "ignorecommand":
return self.__server.getIgnoreCommand(name)
elif command[1] == "failregex":
return self.__server.getFailRegex(name)
elif command[1] == "ignoreregex":

View File

@ -60,6 +60,13 @@ class ExecuteAction(LogCaptureTestCase):
self.assertEqual(Action.substituteRecursiveTags({'A': '<C>'}), {'A': '<C>'})
self.assertEqual(Action.substituteRecursiveTags({'A': '<C> <D> <X>','X':'fun'}), {'A': '<C> <D> fun', 'X':'fun'})
self.assertEqual(Action.substituteRecursiveTags({'A': '<C> <B>', 'B': 'cool'}), {'A': '<C> cool', 'B': 'cool'})
# Multiple stuff on same line is ok
self.assertEqual(Action.substituteRecursiveTags({'failregex': 'to=<honeypot> fromip=<IP> evilperson=<honeypot>', 'honeypot': 'pokie', 'ignoreregex': ''}),
{ 'failregex': "to=pokie fromip=<IP> evilperson=pokie",
'honeypot': 'pokie',
'ignoreregex': '',
})
# rest is just cool
self.assertEqual(Action.substituteRecursiveTags(aInfo),
{ 'HOST': "192.0.2.0",

View File

@ -21,12 +21,13 @@ __author__ = "Cyril Jaquier, Yaroslav Halchenko"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2013 Yaroslav Halchenko"
__license__ = "GPL"
import os, tempfile, shutil, unittest
import os, glob, tempfile, shutil, unittest
from client.configreader import ConfigReader
from client.jailreader import JailReader
from client.jailsreader import JailsReader
from client.configurator import Configurator
from utils import LogCaptureTestCase
class ConfigReaderTest(unittest.TestCase):
@ -106,7 +107,31 @@ option = %s
self.assertEqual(self._getoption(), 1)
class JailReaderTest(unittest.TestCase):
class JailReaderTest(LogCaptureTestCase):
def testJailActionEmpty(self):
jail = JailReader('emptyaction', basedir=os.path.join('testcases','config'))
self.assertTrue(jail.read())
self.assertTrue(jail.getOptions())
self.assertTrue(jail.isEnabled())
self.assertTrue(self._is_logged('No filter set for jail emptyaction'))
self.assertTrue(self._is_logged('No actions were defined for emptyaction'))
def testJailActionFilterMissing(self):
jail = JailReader('missingbitsjail', basedir=os.path.join('testcases','config'))
self.assertTrue(jail.read())
self.assertFalse(jail.getOptions())
self.assertTrue(jail.isEnabled())
self.assertTrue(self._is_logged("Found no accessible config files for 'filter.d/catchallthebadies' under testcases/config"))
self.assertTrue(self._is_logged('Unable to read the filter'))
def testJailActionBrokenDef(self):
jail = JailReader('brokenactiondef', basedir=os.path.join('testcases','config'))
self.assertTrue(jail.read())
self.assertFalse(jail.getOptions())
self.assertTrue(jail.isEnabled())
self.assertTrue(self._is_logged('Error in action definition joho[foo'))
self.assertTrue(self._is_logged('Caught exception: While reading action joho[foo we should have got 1 or 2 groups. Got: 0'))
def testStockSSHJail(self):
jail = JailReader('ssh-iptables', basedir='config') # we are running tests from root project dir atm
@ -114,33 +139,111 @@ class JailReaderTest(unittest.TestCase):
self.assertTrue(jail.getOptions())
self.assertFalse(jail.isEnabled())
self.assertEqual(jail.getName(), 'ssh-iptables')
jail.setName('ssh-funky-blocker')
self.assertEqual(jail.getName(), 'ssh-funky-blocker')
def testSplitAction(self):
action = "mail-whois[name=SSH]"
expected = ['mail-whois', {'name': 'SSH'}]
result = JailReader.splitAction(action)
self.assertEqual(expected, result)
self.assertEqual(['mail.who_is', {}], JailReader.splitAction("mail.who_is"))
self.assertEqual(['mail.who_is', {'a':'cat', 'b':'dog'}], JailReader.splitAction("mail.who_is[a=cat,b=dog]"))
self.assertEqual(['mail--ho_is', {}], JailReader.splitAction("mail--ho_is"))
self.assertEqual(['mail--ho_is', {}], JailReader.splitAction("mail--ho_is['s']"))
self.assertTrue(self._is_logged("Invalid argument ['s'] in ''s''"))
self.assertEqual(['mail', {'a': ','}], JailReader.splitAction("mail[a=',']"))
self.assertRaises(ValueError, JailReader.splitAction ,'mail-how[')
def testGlob(self):
d = tempfile.mkdtemp(prefix="f2b-temp")
# Generate few files
# regular file
open(os.path.join(d, 'f1'), 'w').close()
f1 = os.path.join(d, 'f1')
open(f1, 'w').close()
# dangling link
os.symlink('nonexisting', os.path.join(d, 'f2'))
f2 = os.path.join(d, 'f2')
os.symlink('nonexisting',f2)
# must be only f1
self.assertEqual(JailReader._glob(os.path.join(d, '*')), [os.path.join(d, 'f1')])
self.assertEqual(JailReader._glob(os.path.join(d, '*')), [f1])
# since f2 is dangling -- empty list
self.assertEqual(JailReader._glob(os.path.join(d, 'f2')), [])
self.assertEqual(JailReader._glob(f2), [])
self.assertTrue(self._is_logged('File %s is a dangling link, thus cannot be monitored' % f2))
self.assertEqual(JailReader._glob(os.path.join(d, 'nonexisting')), [])
os.remove(f1)
os.remove(f2)
os.rmdir(d)
class JailsReaderTest(unittest.TestCase):
class JailsReaderTest(LogCaptureTestCase):
def testProvidingBadBasedir(self):
if not os.path.exists('/XXX'):
reader = JailsReader(basedir='/XXX')
self.assertRaises(ValueError, reader.read)
def testReadTestJailConf(self):
jails = JailsReader(basedir=os.path.join('testcases','config'))
self.assertTrue(jails.read())
self.assertFalse(jails.getOptions())
self.assertRaises(ValueError, jails.convert)
comm_commands = jails.convert(allow_no_files=True)
self.maxDiff = None
self.assertEqual(sorted(comm_commands),
sorted([['add', 'emptyaction', 'auto'],
['set', 'emptyaction', 'usedns', 'warn'],
['set', 'emptyaction', 'maxretry', 3],
['set', 'emptyaction', 'findtime', 600],
['set', 'emptyaction', 'bantime', 600],
['add', 'special', 'auto'],
['set', 'special', 'usedns', 'warn'],
['set', 'special', 'maxretry', 3],
['set', 'special', 'addfailregex', '<IP>'],
['set', 'special', 'findtime', 600],
['set', 'special', 'bantime', 600],
['add', 'missinglogfiles', 'auto'],
['set', 'missinglogfiles', 'usedns', 'warn'],
['set', 'missinglogfiles', 'maxretry', 3],
['set', 'missinglogfiles', 'findtime', 600],
['set', 'missinglogfiles', 'bantime', 600],
['set', 'missinglogfiles', 'addfailregex', '<IP>'],
['add', 'brokenaction', 'auto'],
['set', 'brokenaction', 'usedns', 'warn'],
['set', 'brokenaction', 'maxretry', 3],
['set', 'brokenaction', 'findtime', 600],
['set', 'brokenaction', 'bantime', 600],
['set', 'brokenaction', 'addfailregex', '<IP>'],
['set', 'brokenaction', 'addaction', 'brokenaction'],
['set',
'brokenaction',
'actionban',
'brokenaction',
'hit with big stick <ip>'],
['set', 'brokenaction', 'actionstop', 'brokenaction', ''],
['set', 'brokenaction', 'actionstart', 'brokenaction', ''],
['set', 'brokenaction', 'actionunban', 'brokenaction', ''],
['set', 'brokenaction', 'actioncheck', 'brokenaction', ''],
['add', 'parse_to_end_of_jail.conf', 'auto'],
['set', 'parse_to_end_of_jail.conf', 'usedns', 'warn'],
['set', 'parse_to_end_of_jail.conf', 'maxretry', 3],
['set', 'parse_to_end_of_jail.conf', 'findtime', 600],
['set', 'parse_to_end_of_jail.conf', 'bantime', 600],
['set', 'parse_to_end_of_jail.conf', 'addfailregex', '<IP>'],
['start', 'emptyaction'],
['start', 'special'],
['start', 'missinglogfiles'],
['start', 'brokenaction'],
['start', 'parse_to_end_of_jail.conf'],]))
self.assertTrue(self._is_logged("Errors in jail 'missingbitsjail'. Skipping..."))
self.assertTrue(self._is_logged("No file(s) found for glob /weapons/of/mass/destruction"))
def testReadStockJailConf(self):
jails = JailsReader(basedir='config') # we are running tests from root project dir atm
self.assertTrue(jails.read()) # opens fine
@ -148,14 +251,30 @@ class JailsReaderTest(unittest.TestCase):
comm_commands = jails.convert()
# by default None of the jails is enabled and we get no
# commands to communicate to the server
self.maxDiff = None
self.assertEqual(comm_commands, [])
# We should not "read" some bogus jail
old_comm_commands = comm_commands[:] # make a copy
self.assertFalse(jails.getOptions("BOGUS"))
self.assertTrue(self._is_logged("No section: 'BOGUS'"))
# and there should be no side-effects
self.assertEqual(jails.convert(), old_comm_commands)
def testReadSockJailConfComplete(self):
jails = JailsReader(basedir='config', force_enable=True)
self.assertTrue(jails.read()) # opens fine
self.assertTrue(jails.getOptions()) # reads fine
# grab all filter names
filters = set(os.path.splitext(os.path.split(a)[1])[0]
for a in glob.glob(os.path.join('config', 'filter.d', '*.conf'))
if not a.endswith('common.conf'))
filters_jail = set(jail.getRawOptions()['filter'] for jail in jails.getJails())
self.maxDiff = None
self.assertTrue(filters.issubset(filters_jail),
"More filters exists than are referenced in stock jail.conf %r" % filters.difference(filters_jail))
self.assertTrue(filters_jail.issubset(filters),
"Stock jail.conf references non-existent filters %r" % filters_jail.difference(filters))
def testReadStockJailConfForceEnabled(self):
# more of a smoke test to make sure that no obvious surprises

View File

@ -0,0 +1,4 @@
[Definition]
actionban = hit with big stick <ip>

View File

@ -0,0 +1,5 @@
[Definition]
# 3 = INFO
loglevel = 3

View File

@ -0,0 +1,4 @@
[Definition]
failregex = <IP>

View File

@ -0,0 +1,33 @@
[DEFAULT]
filter = simple
logpath = /non/exist
[emptyaction]
enabled = true
filter =
action =
[special]
failregex = <IP>
ignoreregex =
ignoreip =
[missinglogfiles]
logpath = /weapons/of/mass/destruction
[brokenactiondef]
enabled = true
action = joho[foo
[brokenaction]
enabled = true
action = brokenaction
[missingbitsjail]
filter = catchallthebadies
action = thefunkychickendance
[parse_to_end_of_jail.conf]
enabled = true
action =

View File

@ -0,0 +1,5 @@
#!/usr/bin/python
import sys
if sys.argv[1] == "10.0.0.1":
exit(0)
exit(1)

View File

@ -0,0 +1,5 @@
# failJSON: { "time": "2013-12-23T13:12:31", "match": true , "host": "173.255.225.101" }
[Mon Dec 23 13:12:31 2013] [error] [client 173.255.225.101] ModSecurity: [file "/etc/httpd/modsecurity.d/activated_rules/modsecurity_crs_21_protocol_anomalies.conf"] [line "47"] [id "960015"] [rev "1"] [msg "Request Missing an Accept Header"] [severity "NOTICE"] [ver "OWASP_CRS/2.2.8"] [maturity "9"] [accuracy "9"] [tag "OWASP_CRS/PROTOCOL_VIOLATION/MISSING_HEADER_ACCEPT"] [tag "WASCTC/WASC-21"][tag "OWASP_TOP_10/A7"] [tag "PCI/6.5.10"] Access denied with code 403 (phase 2). Operator EQ matched 0 at REQUEST_HEADERS. [hostname "www.mysite.net"] [uri "/"] [unique_id "Urf@f12qgHIAACrFOlgAAABA"]
# failJSON: { "time": "2013-12-28T09:18:05", "match": true , "host": "32.65.254.69" }
[Sat Dec 28 09:18:05 2013] [error] [client 32.65.254.69] ModSecurity: [file "/etc/httpd/modsecurity.d/10_asl_rules.conf"] [line "635"] [id "340069"] [rev "4"] [msg "Atomicorp.com UNSUPPORTED DELAYED Rules: Web vulnerability scanner"] [severity "CRITICAL"] Access denied with code 403 (phase 2). Pattern match "(?:nessus(?:_is_probing_you_|test)|^/w00tw00t\\\\.at\\\\.)" at REQUEST_URI. [hostname "192.81.249.191"] [uri "/w00tw00t.at.blackhats.romanian.anti-sec:)"] [unique_id "4Q6RdsBR@b4AAA65LRUAAAAA"]

View File

@ -2,3 +2,17 @@
[Sun Jun 09 07:57:47 2013] [error] [client 192.0.43.10] script '/usr/lib/cgi-bin/gitweb.cgiwp-login.php' not found or unable to stat
# failJSON: { "time": "2008-07-22T06:48:30", "match": true , "host": "198.51.100.86" }
[Tue Jul 22 06:48:30 2008] [error] [client 198.51.100.86] File does not exist: /home/southern/public_html/azenv.php
# failJSON: { "time": "2008-07-22T06:48:30", "match": true , "host": "198.51.100.86" }
[Tue Jul 22 06:48:30 2008] [error] [client 198.51.100.86] script not found or unable to stat: /home/e-smith/files/ibays/Primary/cgi-bin/php
# failJSON: { "time": "2008-07-22T06:48:30", "match": true , "host": "198.51.100.86" }
[Tue Jul 22 06:48:30 2008] [error] [client 198.51.100.86] script not found or unable to stat: /home/e-smith/files/ibays/Primary/cgi-bin/php5
# failJSON: { "time": "2008-07-22T06:48:30", "match": true , "host": "198.51.100.86" }
[Tue Jul 22 06:48:30 2008] [error] [client 198.51.100.86] script not found or unable to stat: /home/e-smith/files/ibays/Primary/cgi-bin/php-cgi
# failJSON: { "time": "2008-07-22T06:48:30", "match": true , "host": "198.51.100.86" }
[Tue Jul 22 06:48:30 2008] [error] [client 198.51.100.86] script not found or unable to stat: /home/e-smith/files/ibays/Primary/cgi-bin/php.cgi
# failJSON: { "time": "2008-07-22T06:48:30", "match": true , "host": "198.51.100.86" }
[Tue Jul 22 06:48:30 2008] [error] [client 198.51.100.86] script not found or unable to stat: /home/e-smith/files/ibays/Primary/cgi-bin/php4
# apache 2.4
# failJSON: { "time": "2013-12-23T07:49:01", "match": true , "host": "204.232.202.107" }
[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 stat

View File

@ -40,6 +40,8 @@
[2009-12-22 16:35:24] NOTICE[14916]: chan_sip.c:15644 handle_request_subscribe: Sending fake auth rejection for user <sip:CS@192.168.2.102>;tag=6pwd6erg54
# failJSON: { "time": "2013-07-06T09:09:25", "match": true , "host": "141.255.164.106" }
[2013-07-06 09:09:25] SECURITY[3308] res_security_log.c: SecurityEvent="InvalidPassword",EventTV="1373098165-824497",Severity="Error",Service="SIP",EventVersion="2",AccountID="972592891005",SessionID="0x88aab6c",LocalAddress="IPV4/UDP/92.28.73.180/5060",RemoteAddress="IPV4/UDP/141.255.164.106/5084",Challenge="41d26de5",ReceivedChallenge="41d26de5",ReceivedHash="7a6a3a2e95a05260aee612896e1b4a39"
# failJSON: { "time": "2014-01-10T16:39:06", "match": true , "host": "50.30.42.14" }
[2014-01-10 16:39:06] SECURITY[1503] res_security_log.c: SecurityEvent="FailedACL",EventTV="1389368346-880526",Severity="Error",Service="SIP",EventVersion="1",AccountID="",SessionID="0x7ff408103b18",LocalAddress="IPV4/UDP/83.11.20.23/5060",RemoteAddress="IPV4/UDP/50.30.42.14/5066",ACLName="domain_must_match"
# failJSON: { "time": "2013-11-11T14:33:38", "match": true , "host": "192.168.55.152" }
[2013-11-11 14:33:38] WARNING[6756][C-0000001d] Ext. s: "Rejecting unknown SIP connection from 192.168.55.152"

View File

@ -0,0 +1,2 @@
# failJSON: { "time": "2014-01-07T18:09:08", "match": true , "host": "1.2.3.4" }
2014-01-07 18:09:08.512 [info] <0.22741.1>@ejabberd_c2s:wait_for_feature_request:662 ({socket_state,p1_tls,{tlssock,#Port<0.24718>,#Port<0.24720>},<0.22740.1>}) Failed authentication for test@example.com from IP 1.2.3.4

View File

@ -37,3 +37,6 @@
# failJSON: { "time": "2013-09-02T09:19:07", "match": true , "host": "118.233.20.68" }
2013-09-02 09:19:07 login authenticator failed for (gkzwsoju) [118.233.20.68]: 535 Incorrect authentication data
# failJSON: { "time": "2014-01-12T02:07:48", "match": true , "host": "85.214.85.40" }
2014-01-12 02:07:48 dovecot_login authenticator failed for h1832461.stratoserver.net (User) [85.214.85.40]: 535 Incorrect authentication data (set_id=scanner)

View File

@ -14,4 +14,9 @@
2013-06-15 11:20:36 [2516] 1Unmew-0000ea-SE H=egeftech.static.otenet.gr [83.235.177.148]:32706 I=[1.2.3.4]:25 F=auguriesvbd40@google.com rejected after DATA: This message contains a virus (Sanesecurity.Junk.39934.UNOFFICIAL).
# failJSON: { "time": "2013-06-16T02:50:43", "match": true , "host": "111.67.203.114" }
2013-06-16 02:50:43 H=dbs.marsukov.com [111.67.203.114] F=<trudofspiori@mail.ru> rejected RCPT <info@nanomedtech.ua>: rejected because 111.67.203.114 is in a black list at dnsbl.sorbs.net\nCurrently Sending Spam See: http://www.sorbs.net/lookup.shtml?111.67.203.114
# https://github.com/fail2ban/fail2ban/issues/533
# failJSON: { "time": "2013-12-29T15:34:12", "match": true , "host": "188.76.45.72" }
2013-12-29 15:34:12 1VxHRO-000NiI-Ly SA: Action: silently tossed message: score=31.0 required=5.0 trigger=30.0 (scanned in 6/6 secs | Message-Id: etPan.09bd0c40.c3d5f675.fdf7@server.local). From <Flossiedpd@jazztel.es> (host=72.45.76.188.dynamic.jazztel.es [188.76.45.72]) for me@my.com
# failJSON: { "time": "2013-12-29T15:39:11", "match": true , "host": "178.123.108.196" }
2013-12-29 15:39:11 1VxHWD-000NuW-83 SA: Action: silently tossed message: score=35.8 required=5.0 trigger=30.0 (scanned in 6/6 secs | Message-Id: 1VxHWD-000NuW-83). From <> (host=NULL [178.123.108.196]) for me@my.com

View File

@ -0,0 +1,11 @@
# failJSON: { "time": "2013-12-31T17:39:54", "match": true, "host": "81.94.202.251" }
2013-12-31 17:39:54.767815 [WARNING] sofia_reg.c:1533 SIP auth challenge (INVITE) on sofia profile 'internal' for [011448708752617@192.168.2.51] from ip 81.94.202.251
# failJSON: { "time": "2013-12-31T17:39:54", "match": true, "host": "5.11.47.236" }
2013-12-31 17:39:54.767815 [WARNING] sofia_reg.c:1478 SIP auth failure (INVITE) on sofia profile 'internal' for [000972543480510@192.168.2.51] from ip 5.11.47.236
# failJSON: { "time": "2013-12-31T17:39:54", "match": false }
2013-12-31 17:39:54.767815 [DEBUG] sofia.c:7954 IP 185.24.234.141 Rejected by acl "domains". Falling back to Digest auth.
# failJSON: { "time": "2013-12-31T17:39:54", "match": true, "host": "5.11.47.236" }
2013-12-31 17:39:54.767815 [WARNING] sofia_reg.c:2531 Can't find user [1001@192.168.2.51] from 5.11.47.236
# failJSON: { "time": "2013-12-31T17:39:54", "match": true, "host": "185.24.234.141" }
2013-12-31 17:39:54.767815 [WARNING] sofia_reg.c:2531 Can't find user [100@192.168.2.51] from 185.24.234.141

View File

@ -0,0 +1,4 @@
# failJSON: { "time": "2014-01-06T10:59:38", "match": true, "host": "127.0.0.1" }
[2014-01-06 10:59:38]LOGIN FAILED for user: "asdsad" from IP: 127.0.0.1
# failJSON: { "time": "2014-01-06T10:59:49", "match": false, "host": "127.0.0.1" }
[2014-01-06 10:59:49]LOGIN SUCCESS for user: "admin" from IP: 127.0.0.1

View File

@ -0,0 +1,6 @@
# failJSON: { "time": "2004-11-11T18:57:57", "match": true , "host": "203.16.208.190" }
Nov 11 18:57:57 HORDE [error] [horde] FAILED LOGIN for graham [203.16.208.190] to Horde [on line 116 of "/home/ace-hosting/public_html/horde/login.php"]
# failJSON: { "time": "2004-12-15T08:59:59", "match": true , "host": "1.2.3.4" }
Dec 15 08:59:59 HORDE [error] [imp] FAILED LOGIN for emai.user@somedomain.com [1.2.3.4] to {mx.somedomain.com:993 [imap/ssl/novalidate-cert]} [pid 68394 on line 139 of /usr/local/www/www.somedomain.com/public_html/horde/imp/lib/Auth/imp.php"]

4
testcases/files/logs/nsd Normal file
View File

@ -0,0 +1,4 @@
# failJSON: { "time": "2013-12-17T14:58:14", "match": true , "host": "192.0.2.105" }
[1387288694] nsd[7745]: info: ratelimit block example.com. type any target 192.0.2.0/24 query 192.0.2.105 TYPE255
# failJSON: { "time": "2013-12-18T07:42:15", "match": true , "host": "192.0.2.115" }
[1387348935] nsd[23600]: info: axfr for zone domain.nl. from client 192.0.2.115 refused, no acl matches.

View File

@ -0,0 +1,6 @@
# failJSON: { "time": "2013-12-28T19:03:53", "match": true , "host": "178.123.108.196" }
Sat Dec 28 19:03:53 2013 - [72926] (178.123.108.196) gsdfg - userinfo error - auth_unix.pl, ret -4, User gsdfg doesn't exist
# failJSON: { "time": "2013-12-28T19:04:03", "match": true , "host": "178.123.108.196" }
Sat Dec 28 19:04:03 2013 - [72926] (178.123.108.196) gsdfg - login error - no such user - loginname=gsdfg
# failJSON: { "time": "2013-12-28T19:05:38", "match": true , "host": "178.123.108.196" }
Sat Dec 28 19:05:38 2013 - [73540] (178.123.108.196) myname - login error - auth_unix.pl, ret -4, Password incorrect

View File

@ -10,3 +10,13 @@ Jul 18 23:12:56 xxx postfix/smtpd[8738]: NOQUEUE: reject: RCPT from foo[192.51.1
Jul 18 23:12:56 xxx postfix/smtpd[8738]: NOQUEUE: reject: RCPT from foo[192.51.100.43]: 554 5.7.1 <foo@bad.domain>: Sender address rejected: match bad.domain; from=<foo@bad.domain> to=<foo@porcupine.org> proto=SMTP helo=<192.51.100.43>
# failJSON: { "time": "2005-08-10T10:55:38", "match": true , "host": "72.53.132.234" }
Aug 10 10:55:38 f-vanier-bourgeois postfix/smtpd[2162]: NOQUEUE: reject: VRFY from 72-53-132-234.cpe.distributel.net[72.53.132.234]: 550 5.1.1 : Recipient address rejected: User unknown in local recipient tab
# failJSON: { "time": "2005-01-12T11:07:49", "match": true , "host": "181.21.131.88" }
Jan 12 11:07:49 emf1pt2-2-35-70 postfix/smtpd[13767]: improper command pipelining after DATA from unknown[181.21.131.88]:
# failJSON: { "time": "2004-12-25T02:35:54", "match": true , "host": "173.10.140.217" }
Dec 25 02:35:54 platypus postfix/smtpd[9144]: improper command pipelining after RSET from 173-10-140-217-BusName-washingtonDC.hfc.comcastbusiness.net[173.10.140.217]
# failJSON: { "time": "2004-12-18T02:05:46", "match": true , "host": "216.245.198.245" }
Dec 18 02:05:46 platypus postfix/smtpd[16349]: improper command pipelining after NOOP from unknown[216.245.198.245]

View File

@ -103,3 +103,7 @@ Sep 29 17:15:02 spaceman sshd[12946]: Failed password for user from 127.0.0.1 po
# failJSON: { "time": "2004-11-11T08:04:51", "match": true , "host": "127.0.0.1", "desc": "Injecting on username ssh 'from 10.10.1.1'@localhost" }
Nov 11 08:04:51 redbamboo sshd[2737]: Failed password for invalid user from 10.10.1.1 from 127.0.0.1 port 58946 ssh2
# failJSON: { "time": "2005-07-13T18:44:28", "match": true , "host": "89.24.13.192", "desc": "from gh-289" }
Jul 13 18:44:28 mdop sshd[4931]: Received disconnect from 89.24.13.192: 3: com.jcraft.jsch.JSchException: Auth fail

View File

@ -171,7 +171,6 @@ class IgnoreIP(LogCaptureTestCase):
ipList = "127.0.0.1", "192.168.0.1", "255.255.255.255", "99.99.99.99"
for ip in ipList:
self.filter.addIgnoreIP(ip)
self.assertTrue(self.filter.inIgnoreIPList(ip))
def testIgnoreIPNOK(self):
@ -201,6 +200,11 @@ class IgnoreIP(LogCaptureTestCase):
self.assertFalse(self._is_logged('Ignore 192.168.1.32'))
self.assertTrue(self._is_logged('Requested to manually ban an ignored IP 192.168.1.32. User knows best. Proceeding to ban it.'))
def testIgnoreCommand(self):
self.filter.setIgnoreCommand("testcases/files/ignorecommand.py <ip>")
self.assertTrue(self.filter.inIgnoreIPList("10.0.0.1"))
self.assertFalse(self.filter.inIgnoreIPList("10.0.0.0"))
class IgnoreIPDNS(IgnoreIP):
@ -215,15 +219,28 @@ class IgnoreIPDNS(IgnoreIP):
self.assertFalse(self.filter.inIgnoreIPList("128.178.50.11"))
self.assertFalse(self.filter.inIgnoreIPList("128.178.50.13"))
class LogFile(LogCaptureTestCase):
class LogFile(unittest.TestCase):
MISSING = 'testcases/missingLogFile'
def setUp(self):
LogCaptureTestCase.setUp(self)
def tearDown(self):
LogCaptureTestCase.tearDown(self)
def testMissingLogFiles(self):
self.filter = FilterPoll(None)
self.assertRaises(IOError, self.filter.addLogPath, LogFile.MISSING)
class LogFileFilterPoll(unittest.TestCase):
FILENAME = "testcases/files/testcase01.log"
def setUp(self):
"""Call before every test case."""
self.filter = FilterPoll(None)
self.filter.addLogPath(LogFile.FILENAME)
self.filter.addLogPath(LogFileFilterPoll.FILENAME)
def tearDown(self):
"""Call after every test case."""
@ -233,7 +250,8 @@ class LogFile(unittest.TestCase):
# self.filter.openLogFile(LogFile.FILENAME)
def testIsModified(self):
self.assertTrue(self.filter.isModified(LogFile.FILENAME))
self.assertTrue(self.filter.isModified(LogFileFilterPoll.FILENAME))
self.assertFalse(self.filter.isModified(LogFileFilterPoll.FILENAME))
class LogFileMonitor(LogCaptureTestCase):
@ -604,11 +622,11 @@ class GetFailures(unittest.TestCase):
"""Call after every test case."""
def testTail(self):
self.filter.addLogPath(LogFile.FILENAME, tail=True)
self.filter.addLogPath(GetFailures.FILENAME_01, tail=True)
self.assertEqual(self.filter.getLogPath()[-1].getPos(), 1653)
self.filter.getLogPath()[-1].close()
self.assertEqual(self.filter.getLogPath()[-1].readline(), "")
self.filter.delLogPath(LogFile.FILENAME)
self.filter.delLogPath(GetFailures.FILENAME_01)
self.assertEqual(self.filter.getLogPath(),[])
def testGetFailures01(self, filename=None, failures=None):

View File

@ -60,6 +60,8 @@ def testSampleRegexsFactory(name):
# Check filter exists
filterConf = FilterReader(name, "jail", basedir=CONFIG_DIR)
self.assertEqual(filterConf.getFile(), name)
self.assertEqual(filterConf.getName(), "jail")
filterConf.read()
filterConf.getOptions({})
@ -69,10 +71,6 @@ def testSampleRegexsFactory(name):
elif opt[2] == "addignoreregex":
self.filter.addIgnoreRegex(opt[3])
if not self.filter.getFailRegex():
# No fail regexs set: likely just common file for includes.
return
self.assertTrue(
os.path.isfile(os.path.join(TEST_FILES_DIR, "logs", name)),
"No sample log file available for '%s' filter" % name)

View File

@ -25,6 +25,7 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import unittest, socket, time, tempfile, os, sys
from server.failregex import Regex, FailRegex, RegexException
from server.server import Server, logSys
from server.jail import Jail
from common.exceptions import UnknownJailException
@ -337,6 +338,9 @@ class Transmitter(TransmitterBase):
self.transm.proceed(["set", self.jailName, "delignoreip", value]),
(0, [value]))
def testJailIgnoreCommand(self):
self.setGetTest("ignorecommand", "bin ", jail=self.jailName)
def testJailRegex(self):
self.jailAddDelRegexTest("failregex",
[
@ -536,13 +540,19 @@ class TransmitterLogging(TransmitterBase):
logSys.warn("After file moved")
self.assertEqual(self.transm.proceed(["flushlogs"]), (0, "rolled over"))
logSys.warn("After flushlogs")
with open(fn2,'r') as f:
# >py2.4: with open(fn2, 'r') as f:
f = open(fn2, 'r');
if True:
self.assertTrue(f.next().endswith("Before file moved\n"))
self.assertTrue(f.next().endswith("After file moved\n"))
self.assertRaises(StopIteration, f.next)
with open(fn,'r') as f:
f.close()
# >py2.4: with open(fn, 'r') as f:
f = open(fn, 'r');
if True:
self.assertTrue(f.next().endswith("After flushlogs\n"))
self.assertRaises(StopIteration, f.next)
f.close()
finally:
os.remove(fn2)
finally:
@ -558,3 +568,30 @@ class JailTests(unittest.TestCase):
longname = "veryveryverylongname"
jail = Jail(longname)
self.assertEqual(jail.getName(), longname)
class RegexTests(unittest.TestCase):
def testInit(self):
# Should raise an Exception upon empty regex
self.assertRaises(RegexException, Regex, '')
self.assertRaises(RegexException, Regex, ' ')
self.assertRaises(RegexException, Regex, '\t')
def testStr(self):
# .replace just to guarantee uniform use of ' or " in the %r
self.assertEqual(str(Regex('a')).replace('"', "'"), "Regex('a')")
# Class name should be proper
self.assertTrue(str(FailRegex('<HOST>')).startswith("FailRegex("))
def testHost(self):
self.assertRaises(RegexException, FailRegex, '')
# Testing obscure case when host group might be missing in the matched pattern,
# e.g. if we made it optional.
fr = FailRegex('%%<HOST>?')
self.assertFalse(fr.hasMatched())
fr.search("%%")
self.assertTrue(fr.hasMatched())
self.assertRaises(RegexException, fr.getHost)