mirror of https://github.com/fail2ban/fail2ban
Merge commit '0.9.0a2-792-g1864f75' into debian-releases/experimental
* commit '0.9.0a2-792-g1864f75': (113 commits) Credits and notes from #806 fixed encoding fixed encoding ENH: Ignore errors while unbaning in symbiosis firewall ENH: just a bit more descriptive exception ;-) ENH/BF(TST): making permissions restrictive is not sufficient -- really remove file to test changelog entry for postfix-sasl fix added systemd configuration for postfix-sasl.conf 1.5 version of Fail2ban logwatch file minor typo Fxi jail.conf to use more syslog macros ENH: symbiosis-blacklist-allports action Fix typos. changelog and thanks for the preceding fix Added entry for Cloudflare action ChangeLog Added and entry about Cloudflare action Changed to Cloudflare JSON API Fix sieve filter to use correct option changelog entries for already merged and upcoming merge Update courier-smtp.conf ...debian-releases/experimental
commit
8b2f0678a7
|
@ -8,3 +8,4 @@ htmlcov
|
||||||
*.rej
|
*.rej
|
||||||
*.bak
|
*.bak
|
||||||
__pycache__
|
__pycache__
|
||||||
|
.vagrant/
|
||||||
|
|
|
@ -6,6 +6,7 @@ python:
|
||||||
- "2.7"
|
- "2.7"
|
||||||
- "3.2"
|
- "3.2"
|
||||||
- "3.3"
|
- "3.3"
|
||||||
|
- "3.4"
|
||||||
- "pypy"
|
- "pypy"
|
||||||
before_install:
|
before_install:
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then sudo apt-get update -qq; fi
|
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then sudo apt-get update -qq; fi
|
||||||
|
|
|
@ -0,0 +1,181 @@
|
||||||
|
#!/usr/bin/perl
|
||||||
|
##########################################################################
|
||||||
|
# $Id: fail2ban 150 2013-06-18 22:19:38Z mtremaine $
|
||||||
|
##########################################################################
|
||||||
|
# $Log: fail2ban,v $
|
||||||
|
# Revision 1.5 2008/08/18 16:07:46 mike
|
||||||
|
# Patches from Paul Gear <paul at libertysys.com> -mgt
|
||||||
|
#
|
||||||
|
# Revision 1.4 2008/06/30 23:07:51 kirk
|
||||||
|
# fixed copyright holders for files where I know who they should be
|
||||||
|
#
|
||||||
|
# Revision 1.3 2008/03/24 23:31:26 kirk
|
||||||
|
# added copyright/license notice to each script
|
||||||
|
#
|
||||||
|
# Revision 1.2 2006/12/15 04:53:59 bjorn
|
||||||
|
# Additional filtering, by Willi Mann.
|
||||||
|
#
|
||||||
|
# Revision 1.1 2006/05/30 19:04:26 bjorn
|
||||||
|
# Added fail2ban service, written by Yaroslav Halchenko.
|
||||||
|
#
|
||||||
|
# Written by Yaroslav Halchenko <debian@onerussian.com> for fail2ban
|
||||||
|
#
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
########################################################
|
||||||
|
## Copyright (c) 2008 Yaroslav Halchenko
|
||||||
|
## Covered under the included MIT/X-Consortium License:
|
||||||
|
## http://www.opensource.org/licenses/mit-license.php
|
||||||
|
## All modifications and contributions by other persons to
|
||||||
|
## this script are assumed to have been donated to the
|
||||||
|
## Logwatch project and thus assume the above copyright
|
||||||
|
## and licensing terms. If you want to make contributions
|
||||||
|
## under your own copyright or a different license this
|
||||||
|
## must be explicitly stated in the contribution an the
|
||||||
|
## Logwatch project reserves the right to not accept such
|
||||||
|
## contributions. If you have made significant
|
||||||
|
## contributions to this script and want to claim
|
||||||
|
## copyright please contact logwatch-devel@lists.sourceforge.net.
|
||||||
|
#########################################################
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use Logwatch ':all';
|
||||||
|
|
||||||
|
my $Debug = $ENV{'LOGWATCH_DEBUG'} || 0;
|
||||||
|
my $Detail = $ENV{'LOGWATCH_DETAIL_LEVEL'} || 0;
|
||||||
|
my $IgnoreHost = $ENV{'sshd_ignore_host'} || "";
|
||||||
|
my $DebugCounter = 0;
|
||||||
|
my $ReInitializations = 0;
|
||||||
|
my @IptablesErrors = ();
|
||||||
|
my @ActionErrors = ();
|
||||||
|
my $NotValidIP = 0; # reported invalid IPs number
|
||||||
|
my @OtherList = ();
|
||||||
|
|
||||||
|
my %ServicesBans = ();
|
||||||
|
|
||||||
|
if ( $Debug >= 5 ) {
|
||||||
|
print STDERR "\n\nDEBUG: Inside Fail2Ban Filter \n\n";
|
||||||
|
$DebugCounter = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (defined(my $ThisLine = <STDIN>)) {
|
||||||
|
if ( $Debug >= 5 ) {
|
||||||
|
print STDERR "DEBUG($DebugCounter): $ThisLine";
|
||||||
|
$DebugCounter++;
|
||||||
|
}
|
||||||
|
chomp($ThisLine);
|
||||||
|
if ( ($ThisLine =~ /..,... DEBUG: /) or
|
||||||
|
($ThisLine =~ /..,... \S*\s*: DEBUG /) or # syntax of 0.7.? fail2ban
|
||||||
|
($ThisLine =~ /..,... INFO: (Fail2Ban v.* is running|Exiting|Enabled sections:)/) or
|
||||||
|
($ThisLine =~ /INFO\s+Log rotation detected for/) or
|
||||||
|
($ThisLine =~ /INFO\s+Jail.+(?:stopped|started|uses poller)/) or
|
||||||
|
($ThisLine =~ /INFO\s+Changed logging target to/) or
|
||||||
|
($ThisLine =~ /INFO\s+Creating new jail/) or
|
||||||
|
($ThisLine =~ /..,... \S+\s*: INFO\s+(Set |Socket|Exiting|Gamin|Created|Added|Using)/) or # syntax of 0.7.? fail2ban
|
||||||
|
($ThisLine =~ /..,... WARNING: Verbose level is /) or
|
||||||
|
($ThisLine =~ /..,... WARNING: Restoring firewall rules/)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if ( $Debug >= 6 ) {
|
||||||
|
print STDERR "DEBUG($DebugCounter): line ignored\n";
|
||||||
|
}
|
||||||
|
} elsif ( my ($Service,$Action,$Host) = ($ThisLine =~ m/WARNING:?\s\[?(.*?)[]:]?\s(Ban|Unban)[^\.]* (\S+)/)) {
|
||||||
|
if ( $Debug >= 6 ) {
|
||||||
|
print STDERR "DEBUG($DebugCounter): Found $Action for $Service from $Host\n";
|
||||||
|
}
|
||||||
|
$ServicesBans{$Service}{$Host}{$Action}++;
|
||||||
|
$ServicesBans{$Service}{"(all)"}{$Action}++;
|
||||||
|
} elsif ( my ($Service,$Host,$NumFailures) = ($ThisLine =~ m/INFO: (\S+): (.+) has (\d+) login failure\(s\). Banned./)) {
|
||||||
|
if ($Debug >= 4) {
|
||||||
|
print STDERR "DEBUG: Found host $Host trying to access $Service - failed $NumFailures times\n";
|
||||||
|
}
|
||||||
|
push @{$ServicesBans{$Service}{$Host}{'Failures'}}, $NumFailures;
|
||||||
|
} elsif ( my ($Service,$Host) = ($ThisLine =~ m/ ERROR:\s(.*):\s(\S+)\salready in ban list/)) {
|
||||||
|
$ServicesBans{$Service}{$Host}{'AlreadyInTheList'}++;
|
||||||
|
} elsif ( my ($Service,$Host) = ($ThisLine =~ m/WARNING\s*\[(.*)\]\s*(\S+)\s*already banned/)) {
|
||||||
|
$ServicesBans{$Service}{$Host}{'AlreadyInTheList'}++;
|
||||||
|
} elsif ( my ($Service,$Host) = ($ThisLine =~ m/ WARNING:\s(.*):\sReBan (\S+)/)) {
|
||||||
|
$ServicesBans{$Service}{$Host}{'ReBan'}++;
|
||||||
|
} elsif ($ThisLine =~ / ERROR:?\s*(Execution of command )?\'?iptables/) {
|
||||||
|
push @IptablesErrors, "$ThisLine\n";
|
||||||
|
} elsif ($ThisLine =~ /ERROR.*returned \d+$/) {
|
||||||
|
push @ActionErrors, "$ThisLine\n";
|
||||||
|
} elsif (($ThisLine =~ /..,... WARNING: \#\S+ reinitialization of firewalls/) or
|
||||||
|
($ThisLine =~ / ERROR\s*Invariant check failed. Trying to restore a sane environment/)) {
|
||||||
|
$ReInitializations++;
|
||||||
|
} elsif ($ThisLine =~ /..,... WARNING: is not a valid IP address/) {
|
||||||
|
# just ignore - this will be fixed within fail2ban and is harmless warning
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
# Report any unmatched entries...
|
||||||
|
push @OtherList, "$ThisLine\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
###########################################################
|
||||||
|
|
||||||
|
|
||||||
|
if (keys %ServicesBans) {
|
||||||
|
printf("\nBanned services with Fail2Ban: Bans:Unbans\n");
|
||||||
|
foreach my $service (sort {$a cmp $b} keys %ServicesBans) {
|
||||||
|
printf(" %-55s [%3d:%-3d]\n", "$service:",
|
||||||
|
$ServicesBans{$service}{'(all)'}{'Ban'},
|
||||||
|
$ServicesBans{$service}{'(all)'}{'Unban'});
|
||||||
|
delete $ServicesBans{$service}{'(all)'};
|
||||||
|
my $totalSort = TotalCountOrder(%{$ServicesBans{$service}}, \&SortIP);
|
||||||
|
if ($Detail >= 5) {
|
||||||
|
foreach my $ip (sort $totalSort keys %{$ServicesBans{$service}}) {
|
||||||
|
my $name = LookupIP($ip);
|
||||||
|
printf(" %-53s %3d:%-3d\n",
|
||||||
|
$name,
|
||||||
|
$ServicesBans{$service}{$ip}{'Ban'},
|
||||||
|
$ServicesBans{$service}{$ip}{'Unban'});
|
||||||
|
if (($Detail >= 10) and ($ServicesBans{$service}{$ip}{'Failures'}>0)) {
|
||||||
|
print " Failed ";
|
||||||
|
foreach my $fails (@{$ServicesBans{$service}{$ip}{'Failures'}}) {
|
||||||
|
print " $fails";
|
||||||
|
}
|
||||||
|
print " times";
|
||||||
|
printf("\n %d Duplicate Ban attempts", $ServicesBans{$service}{$ip}{'AlreadyInTheList'}) ;
|
||||||
|
printf("\n %d ReBans due to rules reinitilizations", $ServicesBans{$service}{$ip}{'ReBan'}) ;
|
||||||
|
print "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ($Detail>0) {
|
||||||
|
if ($#IptablesErrors > 0) {
|
||||||
|
printf("\n%d faulty iptables invocation(s)", $#IptablesErrors);
|
||||||
|
if ($Detail > 5) {
|
||||||
|
print ":\n";
|
||||||
|
print @IptablesErrors ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($#ActionErrors > 0) {
|
||||||
|
printf("\n%d error(s) returned from actions", $#ActionErrors);
|
||||||
|
if ($Detail > 5) {
|
||||||
|
print ":\n";
|
||||||
|
print @ActionErrors ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($ReInitializations > 0) {
|
||||||
|
printf("\n%d fail2ban rules reinitialization(s)", $ReInitializations);
|
||||||
|
}
|
||||||
|
if ($#OtherList >= 0) {
|
||||||
|
print "\n**Unmatched Entries**\n";
|
||||||
|
print @OtherList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
|
||||||
|
# vi: shiftwidth=3 tabstop=3 syntax=perl et
|
||||||
|
# Local Variables:
|
||||||
|
# mode: perl
|
||||||
|
# perl-indent-level: 3
|
||||||
|
# indent-tabs-mode: nil
|
||||||
|
# End:
|
|
@ -0,0 +1,17 @@
|
||||||
|
Guidelines on Fail2Ban contributions
|
||||||
|
====================================
|
||||||
|
|
||||||
|
### You found a severe security vulnerability in Fail2Ban?
|
||||||
|
email details to fail2ban-vulnerabilities at lists dot sourceforge dot net .
|
||||||
|
|
||||||
|
### You need some new features, you found bugs?
|
||||||
|
visit [Issues](https://github.com/fail2ban/fail2ban/issues)
|
||||||
|
and if your issue is not yet known -- file a bug report. See
|
||||||
|
[Fail2Ban wiki](http://www.fail2ban.org/wiki/index.php/HOWTO_Seek_Help)
|
||||||
|
on further instructions.
|
||||||
|
|
||||||
|
### You would like to troubleshoot or discuss?
|
||||||
|
join the [mailing list](https://lists.sourceforge.net/lists/listinfo/fail2ban-users)
|
||||||
|
|
||||||
|
### You would like to contribute (new filters/actions/code/documentation)?
|
||||||
|
send a [pull request](https://github.com/fail2ban/fail2ban/pulls)
|
49
ChangeLog
49
ChangeLog
|
@ -10,24 +10,69 @@ Fail2Ban (version 0.9.0.dev) 2014/xx/xx
|
||||||
ver. 0.9.1 (2014/xx/xx) - better, faster, stronger
|
ver. 0.9.1 (2014/xx/xx) - better, faster, stronger
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
- Refactoring (IMPORTANT -- Please review your setup and configuration):
|
||||||
|
* iptables-common.conf replaced iptables-blocktype.conf
|
||||||
|
(iptables-blocktype.local should still be read) and now also
|
||||||
|
provides defaults for the chain, port, protocol and name tags
|
||||||
|
|
||||||
- Fixes:
|
- Fixes:
|
||||||
|
* UTF-8 fixes in pure-ftp thanks to Johannes Weberhofer. Closes gh-806.
|
||||||
* systemd backend error on bad utf-8 in python3
|
* systemd backend error on bad utf-8 in python3
|
||||||
* badips.py action error when logging HTTP error raised with badips request
|
* badips.py action error when logging HTTP error raised with badips request
|
||||||
* fail2ban-regex failed to work in python3 due to space/tab mix
|
* fail2ban-regex failed to work in python3 due to space/tab mix
|
||||||
|
* recidive regex samples incorrect log level
|
||||||
* journalmatch for recidive incorrect PRIORITY
|
* journalmatch for recidive incorrect PRIORITY
|
||||||
* loglevel couldn't be changed in fail2ban.conf
|
* loglevel couldn't be changed in fail2ban.conf
|
||||||
* Handle case when no sqlite library is available for persistent database
|
* Handle case when no sqlite library is available for persistent database
|
||||||
* Only reban once per IP from database on fail2ban restart
|
* Only reban once per IP from database on fail2ban restart
|
||||||
* Nginx filter to support missing server_name. Closes gh-676
|
* Nginx filter to support missing server_name. Closes gh-676
|
||||||
|
* fail2ban-regex assertion error caused by miscount missed lines with
|
||||||
|
multiline regex
|
||||||
|
* Fix actions failing to execute for Python 3.4.0. Workaround for
|
||||||
|
http://bugs.python.org/issue21207
|
||||||
|
* Database now returns persistent bans on restart (bantime < 0)
|
||||||
|
* Recursive action tags now fully processed. Fixes issue with bsd-ipfw
|
||||||
|
action
|
||||||
|
* Fixed TypeError with "ipfailures" and "ipjailfailures" action tags.
|
||||||
|
Thanks Serg G. Brester
|
||||||
|
* Correct times for non-timezone date times formats during DST
|
||||||
|
* Pass a copy of, not original, aInfo into actions to avoid side-effects
|
||||||
|
* Per-distribution paths to the exim's main log
|
||||||
|
* Ignored IPs are no longer banned when being restored from persistent
|
||||||
|
database
|
||||||
|
* Manually unbanned IPs are now removed from persistent database, such they
|
||||||
|
wont be banned again when Fail2Ban is restarted
|
||||||
|
* Pass "bantime" parameter to the actions in default jail's action
|
||||||
|
definition(s)
|
||||||
|
* filters.d/sieve.conf - fixed typo in _daemon. Thanks Jisoo Park
|
||||||
|
* cyrus-imap -- also catch also failed logins via secured (imaps/pop3s).
|
||||||
|
Regression was introduced while strengthening failregex in 0.8.11 (bd175f)
|
||||||
|
Debian bug #755173
|
||||||
|
* postfix-sasl -- added journalmatch. Thanks Luc Maisonobe
|
||||||
|
|
||||||
- New features:
|
- New features:
|
||||||
|
- New filters:
|
||||||
|
- monit Thanks Jason H Martin
|
||||||
|
- directadmin Thanks niorg
|
||||||
|
- New actions:
|
||||||
|
- symbiosis-blacklist-allports for Bytemark symbiosis firewall
|
||||||
|
- fail2ban-client can fetch the running server version
|
||||||
|
- Added Cloudflare API action
|
||||||
|
|
||||||
- Enhancements
|
- Enhancements
|
||||||
* Fail2ban-regex - add print-all-matched option. Closes gh-652
|
* Fail2ban-regex - add print-all-matched option. Closes gh-652
|
||||||
* Suppress fail2ban-client warnings for non-critical config options
|
* Suppress fail2ban-client warnings for non-critical config options
|
||||||
|
* Match non "Bye Bye" disconnect messages for sshd locked account regex
|
||||||
|
* courier-smtp filter:
|
||||||
|
- match lines with user names
|
||||||
|
- match lines containing "535 Authentication failed" attempts
|
||||||
|
* Add <chain> tag to iptables-ipsets
|
||||||
|
* Realign fail2ban log output with white space to improve readability. Does
|
||||||
|
not affect SYSLOG output
|
||||||
|
* Log unhandled exceptions
|
||||||
|
* cyrus-imap: catch "user not found" attempts
|
||||||
|
|
||||||
ver. 0.9.0 (2014/03/14 - beta
|
ver. 0.9.0 (2014/03/14) - beta
|
||||||
----------
|
----------
|
||||||
|
|
||||||
Carries all fixes, features and enhancements from 0.8.13 (unreleased) with
|
Carries all fixes, features and enhancements from 0.8.13 (unreleased) with
|
||||||
|
|
2
MANIFEST
2
MANIFEST
|
@ -258,7 +258,7 @@ config/action.d/dummy.conf
|
||||||
config/action.d/firewallcmd-new.conf
|
config/action.d/firewallcmd-new.conf
|
||||||
config/action.d/firewallcmd-ipset.conf
|
config/action.d/firewallcmd-ipset.conf
|
||||||
config/action.d/iptables-ipset-proto6-allports.conf
|
config/action.d/iptables-ipset-proto6-allports.conf
|
||||||
config/action.d/iptables-blocktype.conf
|
config/action.d/iptables-common.conf
|
||||||
config/action.d/iptables-ipset-proto4.conf
|
config/action.d/iptables-ipset-proto4.conf
|
||||||
config/action.d/iptables-ipset-proto6.conf
|
config/action.d/iptables-ipset-proto6.conf
|
||||||
config/action.d/iptables-xt_recent-echo.conf
|
config/action.d/iptables-xt_recent-echo.conf
|
||||||
|
|
|
@ -6,20 +6,20 @@ By Roy Sigurd Karlsbakk <roy@karlsbakk.net>
|
||||||
|
|
||||||
ABOUT
|
ABOUT
|
||||||
|
|
||||||
This readme is meant for those wanting to install fail2ban on Solaris 10,
|
This README is meant for those wanting to install fail2ban on Solaris 10,
|
||||||
OpenSolaris, OpenIndiana etc. To some degree it may as well be useful for
|
OpenSolaris, OpenIndiana etc. To some degree it may as well be useful for
|
||||||
users of older Solaris versions and Nexenta, but don't rely on it.
|
users of older Solaris versions and Nexenta, but don't rely on it.
|
||||||
|
|
||||||
READ ME FIRST
|
READ ME FIRST
|
||||||
|
|
||||||
If I use the term Solaris, I am talking about any Solaris dialect, that is, the
|
If I use the term Solaris, I am talking about any Solaris dialect, that is, the
|
||||||
official Sun/Oracle ones or derivates. If I describe an OS as
|
official Sun/Oracle ones or derivatives. If I describe an OS as
|
||||||
"OpenSolaris-based", it means it's either OpenSolaris, OpenIndiana or one of the
|
"OpenSolaris-based", it means it's either OpenSolaris, OpenIndiana or one of the
|
||||||
other, but /not/ the Nexenta family, since this only uses the OpenSolaris/
|
other, but /not/ the Nexenta family, since this only uses the OpenSolaris/
|
||||||
IllumOS kernel and not the userland. If I say Solaris 10, I mean Solaris 10 and
|
IllumOS kernel and not the userland. If I say Solaris 10, I mean Solaris 10 and
|
||||||
perhaps, if you're lucky and have some good gods on your side, it may also apply
|
perhaps, if you're lucky and have some good gods on your side, it may also apply
|
||||||
to Solaris 9 or even 8 and hopefully in the new Solaris 11 whenever that may be
|
to Solaris 9 or even 8 and hopefully in the new Solaris 11 whenever that may be
|
||||||
released. Quoted lines of code, settings et cetera are indented with two spaces.
|
released. Quoted lines of code, settings etc. are indented with two spaces.
|
||||||
This does _not_ mean you should use that indentation, especially in config files
|
This does _not_ mean you should use that indentation, especially in config files
|
||||||
where they can be harmful. Optional settings are prefixed with OPT: while
|
where they can be harmful. Optional settings are prefixed with OPT: while
|
||||||
required settings are prefixed with REQ:. If no prefix is found, regard it as a
|
required settings are prefixed with REQ:. If no prefix is found, regard it as a
|
||||||
|
|
20
README.md
20
README.md
|
@ -68,24 +68,12 @@ Code status:
|
||||||
Contact:
|
Contact:
|
||||||
--------
|
--------
|
||||||
|
|
||||||
### You found a severe security vulnerability in Fail2Ban?
|
### Bugs, feature requests, discussions?
|
||||||
email details to fail2ban-vulnerabilities at lists dot sourceforge dot net .
|
See [CONTRIBUTING.md](https://github.com/fail2ban/fail2ban/blob/master/CONTRIBUTING.md)
|
||||||
|
|
||||||
### You need some new features, you found bugs?
|
|
||||||
visit [Issues](https://github.com/fail2ban/fail2ban/issues)
|
|
||||||
and if your issue is not yet known -- file a bug report. See
|
|
||||||
[Fail2Ban wiki](http://www.fail2ban.org/wiki/index.php/HOWTO_Seek_Help)
|
|
||||||
on further instructions.
|
|
||||||
|
|
||||||
### You would like to troubleshoot or discuss?
|
|
||||||
join the [mailing list](https://lists.sourceforge.net/lists/listinfo/fail2ban-users)
|
|
||||||
|
|
||||||
### You would like to contribute (new filters/actions/code/documentation)?
|
|
||||||
send a pull request
|
|
||||||
|
|
||||||
### You just appreciate this program:
|
### You just appreciate this program:
|
||||||
send kudos to the original author ([Cyril Jaquier](mailto: Cyril Jaquier <cyril.jaquier@fail2ban.org>)
|
send kudos to the original author ([Cyril Jaquier](mailto: Cyril Jaquier <cyril.jaquier@fail2ban.org>))
|
||||||
or better to the [mailing list](https://lists.sourceforge.net/lists/listinfo/fail2ban-users)
|
or *better* to the [mailing list](https://lists.sourceforge.net/lists/listinfo/fail2ban-users)
|
||||||
since Fail2Ban is "community-driven" for years now.
|
since Fail2Ban is "community-driven" for years now.
|
||||||
|
|
||||||
Thanks:
|
Thanks:
|
||||||
|
|
12
THANKS
12
THANKS
|
@ -44,10 +44,14 @@ Hank Leininger
|
||||||
Hanno 'Rince' Wagner
|
Hanno 'Rince' Wagner
|
||||||
Helmut Grohne
|
Helmut Grohne
|
||||||
Iain Lea
|
Iain Lea
|
||||||
|
Ioan Indreias
|
||||||
Ivo Truxa
|
Ivo Truxa
|
||||||
John Thoe
|
John Thoe
|
||||||
Jacques Lav!gnotte
|
Jacques Lav!gnotte
|
||||||
Ioan Indreias
|
Johannes Weberhofer
|
||||||
|
Jason H Martin
|
||||||
|
Jisoo Park
|
||||||
|
Joel M Snyder
|
||||||
Jonathan Kamens
|
Jonathan Kamens
|
||||||
Jonathan Lanning
|
Jonathan Lanning
|
||||||
Jonathan Underwood
|
Jonathan Underwood
|
||||||
|
@ -60,6 +64,7 @@ kjohnsonecl
|
||||||
kojiro
|
kojiro
|
||||||
Lars Kneschke
|
Lars Kneschke
|
||||||
Lee Clemens
|
Lee Clemens
|
||||||
|
leftyfb (Mike Rushton)
|
||||||
Manuel Arostegui Ramirez
|
Manuel Arostegui Ramirez
|
||||||
Marcel Dopita
|
Marcel Dopita
|
||||||
Mark Edgington
|
Mark Edgington
|
||||||
|
@ -75,8 +80,10 @@ Michael Hanselmann
|
||||||
Mika (mkl)
|
Mika (mkl)
|
||||||
Nick Munger
|
Nick Munger
|
||||||
onorua
|
onorua
|
||||||
|
Paul Marrapese
|
||||||
Noel Butler
|
Noel Butler
|
||||||
Patrick Börjesson
|
Patrick Börjesson
|
||||||
|
Pressy
|
||||||
Raphaël Marichez
|
Raphaël Marichez
|
||||||
RealRancor
|
RealRancor
|
||||||
René Berber
|
René Berber
|
||||||
|
@ -84,7 +91,10 @@ Robert Edeker
|
||||||
Rolf Fokkens
|
Rolf Fokkens
|
||||||
Roman Gelfand
|
Roman Gelfand
|
||||||
Russell Odom
|
Russell Odom
|
||||||
|
SATO Kentaro
|
||||||
|
Sean DuBois
|
||||||
Sebastian Arcus
|
Sebastian Arcus
|
||||||
|
Serg G. Brester
|
||||||
Sireyessire
|
Sireyessire
|
||||||
silviogarbes
|
silviogarbes
|
||||||
Stefan Tatschner
|
Stefan Tatschner
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
Vagrant.configure("2") do |config|
|
||||||
|
|
||||||
|
config.vm.define "secure" do |secure|
|
||||||
|
secure.vm.box = "ubuntu/trusty64"
|
||||||
|
secure.vm.hostname = "secure.dev.fail2ban.org"
|
||||||
|
secure.vm.network "private_network", ip: "192.168.200.100"
|
||||||
|
|
||||||
|
# secure.vm.synced_folder 'salt/roots', '/srv/salt'
|
||||||
|
|
||||||
|
# secure.vm.provision :salt do |salt|
|
||||||
|
# salt.minion_config = 'salt/minion'
|
||||||
|
# salt.run_highstate = true
|
||||||
|
# salt.verbose = true
|
||||||
|
# end
|
||||||
|
end
|
||||||
|
|
||||||
|
config.vm.define "attacker" do |attacker|
|
||||||
|
attacker.vm.box = "ubuntu/trusty64"
|
||||||
|
attacker.vm.hostname = "attacker.dev.fail2ban.org"
|
||||||
|
attacker.vm.network "private_network", ip: "192.168.200.150"
|
||||||
|
|
||||||
|
# attacker.vm.synced_folder 'salt/roots', '/srv/salt'
|
||||||
|
|
||||||
|
# attacker.vm.provision :salt do |salt|
|
||||||
|
# salt.minion_config = 'salt/minion'
|
||||||
|
# salt.run_highstate = true
|
||||||
|
# salt.verbose = true
|
||||||
|
# end
|
||||||
|
end
|
||||||
|
end
|
|
@ -30,9 +30,10 @@ from fail2ban.protocol import printFormatted
|
||||||
from fail2ban.client.csocket import CSocket
|
from fail2ban.client.csocket import CSocket
|
||||||
from fail2ban.client.configurator import Configurator
|
from fail2ban.client.configurator import Configurator
|
||||||
from fail2ban.client.beautifier import Beautifier
|
from fail2ban.client.beautifier import Beautifier
|
||||||
|
from fail2ban.helpers import getLogger
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger("fail2ban.client")
|
logSys = getLogger("fail2ban")
|
||||||
|
|
||||||
##
|
##
|
||||||
#
|
#
|
||||||
|
@ -51,6 +52,7 @@ class Fail2banClient:
|
||||||
self.__conf["conf"] = "/etc/fail2ban"
|
self.__conf["conf"] = "/etc/fail2ban"
|
||||||
self.__conf["dump"] = False
|
self.__conf["dump"] = False
|
||||||
self.__conf["force"] = False
|
self.__conf["force"] = False
|
||||||
|
self.__conf["background"] = True
|
||||||
self.__conf["verbose"] = 1
|
self.__conf["verbose"] = 1
|
||||||
self.__conf["interactive"] = False
|
self.__conf["interactive"] = False
|
||||||
self.__conf["socket"] = None
|
self.__conf["socket"] = None
|
||||||
|
@ -83,6 +85,8 @@ class Fail2banClient:
|
||||||
print " -v increase verbosity"
|
print " -v increase verbosity"
|
||||||
print " -q decrease verbosity"
|
print " -q decrease verbosity"
|
||||||
print " -x force execution of the server (remove socket file)"
|
print " -x force execution of the server (remove socket file)"
|
||||||
|
print " -b start server in background (default)"
|
||||||
|
print " -f start server in foreground (note that the client forks once itself)"
|
||||||
print " -h, --help display this help message"
|
print " -h, --help display this help message"
|
||||||
print " -V, --version print the version"
|
print " -V, --version print the version"
|
||||||
print
|
print
|
||||||
|
@ -125,6 +129,10 @@ class Fail2banClient:
|
||||||
self.__conf["force"] = True
|
self.__conf["force"] = True
|
||||||
elif opt[0] == "-i":
|
elif opt[0] == "-i":
|
||||||
self.__conf["interactive"] = True
|
self.__conf["interactive"] = True
|
||||||
|
elif opt[0] == "-b":
|
||||||
|
self.__conf["background"] = True
|
||||||
|
elif opt[0] == "-f":
|
||||||
|
self.__conf["background"] = False
|
||||||
elif opt[0] in ["-h", "--help"]:
|
elif opt[0] in ["-h", "--help"]:
|
||||||
self.dispUsage()
|
self.dispUsage()
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
@ -194,7 +202,8 @@ class Fail2banClient:
|
||||||
# Start the server
|
# Start the server
|
||||||
self.__startServerAsync(self.__conf["socket"],
|
self.__startServerAsync(self.__conf["socket"],
|
||||||
self.__conf["pidfile"],
|
self.__conf["pidfile"],
|
||||||
self.__conf["force"])
|
self.__conf["force"],
|
||||||
|
self.__conf["background"])
|
||||||
try:
|
try:
|
||||||
# Wait for the server to start
|
# Wait for the server to start
|
||||||
self.__waitOnServer()
|
self.__waitOnServer()
|
||||||
|
@ -242,14 +251,12 @@ class Fail2banClient:
|
||||||
#
|
#
|
||||||
# Start the Fail2ban server in daemon mode.
|
# Start the Fail2ban server in daemon mode.
|
||||||
|
|
||||||
def __startServerAsync(self, socket, pidfile, force = False):
|
def __startServerAsync(self, socket, pidfile, force = False, background = True):
|
||||||
# Forks the current process.
|
# Forks the current process.
|
||||||
pid = os.fork()
|
pid = os.fork()
|
||||||
if pid == 0:
|
if pid == 0:
|
||||||
args = list()
|
args = list()
|
||||||
args.append(self.SERVER)
|
args.append(self.SERVER)
|
||||||
# Start in background mode.
|
|
||||||
args.append("-b")
|
|
||||||
# Set the socket path.
|
# Set the socket path.
|
||||||
args.append("-s")
|
args.append("-s")
|
||||||
args.append(socket)
|
args.append(socket)
|
||||||
|
@ -259,6 +266,12 @@ class Fail2banClient:
|
||||||
# Force the execution if needed.
|
# Force the execution if needed.
|
||||||
if force:
|
if force:
|
||||||
args.append("-x")
|
args.append("-x")
|
||||||
|
# Start in foreground mode if requested.
|
||||||
|
if background:
|
||||||
|
args.append("-b")
|
||||||
|
else:
|
||||||
|
args.append("-f")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Use the current directory.
|
# Use the current directory.
|
||||||
exe = os.path.abspath(os.path.join(sys.path[0], self.SERVER))
|
exe = os.path.abspath(os.path.join(sys.path[0], self.SERVER))
|
||||||
|
@ -312,7 +325,7 @@ class Fail2banClient:
|
||||||
|
|
||||||
# Reads the command line options.
|
# Reads the command line options.
|
||||||
try:
|
try:
|
||||||
cmdOpts = 'hc:s:p:xdviqV'
|
cmdOpts = 'hc:s:p:xfbdviqV'
|
||||||
cmdLongOpts = ['help', 'version']
|
cmdLongOpts = ['help', 'version']
|
||||||
optList, args = getopt.getopt(self.__argv[1:], cmdOpts, cmdLongOpts)
|
optList, args = getopt.getopt(self.__argv[1:], cmdOpts, cmdLongOpts)
|
||||||
except getopt.GetoptError:
|
except getopt.GetoptError:
|
||||||
|
|
|
@ -25,11 +25,11 @@ This tools can test regular expressions for "fail2ban".
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__author__ = "Cyril Jaquier, Yaroslav Halchenko"
|
__author__ = "Fail2Ban Developers"
|
||||||
__copyright__ = "Copyright (c) 2004-2008 Cyril Jaquier, 2012-2013 Yaroslav Halchenko"
|
__copyright__ = "Copyright (c) 2004-2008 Cyril Jaquier, 2012-2014 Yaroslav Halchenko"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import getopt, sys, time, logging, os, locale, shlex, urllib
|
import getopt, sys, time, logging, os, locale, shlex, time, urllib
|
||||||
from optparse import OptionParser, Option
|
from optparse import OptionParser, Option
|
||||||
|
|
||||||
from ConfigParser import NoOptionError, NoSectionError, MissingSectionHeaderError
|
from ConfigParser import NoOptionError, NoSectionError, MissingSectionHeaderError
|
||||||
|
@ -45,9 +45,9 @@ from fail2ban.client.filterreader import FilterReader
|
||||||
from fail2ban.server.filter import Filter
|
from fail2ban.server.filter import Filter
|
||||||
from fail2ban.server.failregex import RegexException
|
from fail2ban.server.failregex import RegexException
|
||||||
|
|
||||||
from fail2ban.tests.utils import FormatterWithTraceBack
|
from fail2ban.helpers import FormatterWithTraceBack, getLogger
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger("fail2ban")
|
logSys = getLogger("fail2ban")
|
||||||
|
|
||||||
def debuggexURL(sample, regex):
|
def debuggexURL(sample, regex):
|
||||||
q = urllib.urlencode({ 're': regex.replace('<HOST>', '(?&.ipv4)'),
|
q = urllib.urlencode({ 're': regex.replace('<HOST>', '(?&.ipv4)'),
|
||||||
|
@ -223,6 +223,7 @@ class Fail2banRegex(object):
|
||||||
self._filter = Filter(None)
|
self._filter = Filter(None)
|
||||||
self._ignoreregex = list()
|
self._ignoreregex = list()
|
||||||
self._failregex = list()
|
self._failregex = list()
|
||||||
|
self._time_elapsed = None
|
||||||
self._line_stats = LineStats()
|
self._line_stats = LineStats()
|
||||||
|
|
||||||
if opts.maxlines:
|
if opts.maxlines:
|
||||||
|
@ -344,10 +345,11 @@ class Fail2banRegex(object):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
self._line_stats.matched += 1
|
self._line_stats.matched += 1
|
||||||
|
self._line_stats.missed -= 1
|
||||||
return line, ret
|
return line, ret
|
||||||
|
|
||||||
def process(self, test_lines):
|
def process(self, test_lines):
|
||||||
|
t0 = time.time()
|
||||||
for line_no, line in enumerate(test_lines):
|
for line_no, line in enumerate(test_lines):
|
||||||
if isinstance(line, tuple):
|
if isinstance(line, tuple):
|
||||||
line_datetimestripped, ret = fail2banRegex.testRegex(
|
line_datetimestripped, ret = fail2banRegex.testRegex(
|
||||||
|
@ -382,6 +384,7 @@ class Fail2banRegex(object):
|
||||||
|
|
||||||
if line_no % 10 == 0 and self._filter.dateDetector is not None:
|
if line_no % 10 == 0 and self._filter.dateDetector is not None:
|
||||||
self._filter.dateDetector.sortTemplate()
|
self._filter.dateDetector.sortTemplate()
|
||||||
|
self._time_elapsed = time.time() - t0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -455,7 +458,10 @@ class Fail2banRegex(object):
|
||||||
template.hits, template.name))
|
template.hits, template.name))
|
||||||
pprint_list(out, "[# of hits] date format")
|
pprint_list(out, "[# of hits] date format")
|
||||||
|
|
||||||
print "\nLines: %s" % self._line_stats
|
print "\nLines: %s" % self._line_stats,
|
||||||
|
if self._time_elapsed is not None:
|
||||||
|
print "[processed in %.2f sec]" % self._time_elapsed,
|
||||||
|
print
|
||||||
|
|
||||||
if self._print_all_matched:
|
if self._print_all_matched:
|
||||||
self.printLines('matched')
|
self.printLines('matched')
|
||||||
|
|
|
@ -22,13 +22,14 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import getopt, sys, logging, os
|
import getopt, sys, os
|
||||||
|
|
||||||
from fail2ban.version import version
|
from fail2ban.version import version
|
||||||
from fail2ban.server.server import Server
|
from fail2ban.server.server import Server
|
||||||
|
from fail2ban.helpers import getLogger
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger("fail2ban")
|
logSys = getLogger("fail2ban")
|
||||||
|
|
||||||
##
|
##
|
||||||
# \mainpage Fail2Ban
|
# \mainpage Fail2Ban
|
||||||
|
|
|
@ -24,8 +24,8 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2012- Yaroslav Halchenko"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2012- Yaroslav Halchenko"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
import logging
|
||||||
import unittest, logging, sys, time, os
|
import unittest, sys, time, os
|
||||||
|
|
||||||
# Check if local fail2ban module exists, and use if it exists by
|
# Check if local fail2ban module exists, and use if it exists by
|
||||||
# modifying the path. This is such that tests can be used in dev
|
# modifying the path. This is such that tests can be used in dev
|
||||||
|
@ -34,7 +34,8 @@ if os.path.exists("fail2ban/__init__.py"):
|
||||||
sys.path.insert(0, ".")
|
sys.path.insert(0, ".")
|
||||||
from fail2ban.version import version
|
from fail2ban.version import version
|
||||||
|
|
||||||
from fail2ban.tests.utils import FormatterWithTraceBack, gatherTests
|
from fail2ban.tests.utils import gatherTests
|
||||||
|
from fail2ban.helpers import FormatterWithTraceBack, getLogger
|
||||||
from fail2ban.server.mytime import MyTime
|
from fail2ban.server.mytime import MyTime
|
||||||
|
|
||||||
from optparse import OptionParser, Option
|
from optparse import OptionParser, Option
|
||||||
|
@ -69,7 +70,7 @@ parser = get_opt_parser()
|
||||||
#
|
#
|
||||||
# Logging
|
# Logging
|
||||||
#
|
#
|
||||||
logSys = logging.getLogger("fail2ban")
|
logSys = getLogger("fail2ban")
|
||||||
|
|
||||||
# Numerical level of verbosity corresponding to a log "level"
|
# Numerical level of verbosity corresponding to a log "level"
|
||||||
verbosity = {'heavydebug': 4,
|
verbosity = {'heavydebug': 4,
|
||||||
|
|
|
@ -36,7 +36,7 @@ from fail2ban.server.actions import ActionBase
|
||||||
from fail2ban.version import version as f2bVersion
|
from fail2ban.version import version as f2bVersion
|
||||||
|
|
||||||
class BadIPsAction(ActionBase):
|
class BadIPsAction(ActionBase):
|
||||||
"""Fail2Ban action which resports bans to badips.com, and also
|
"""Fail2Ban action which reports bans to badips.com, and also
|
||||||
blacklist bad IPs listed on badips.com by using another action's
|
blacklist bad IPs listed on badips.com by using another action's
|
||||||
ban method.
|
ban method.
|
||||||
|
|
||||||
|
@ -161,7 +161,7 @@ class BadIPsAction(ActionBase):
|
||||||
"/".join([self._badips, "get", "list", category, str(score)]),
|
"/".join([self._badips, "get", "list", category, str(score)]),
|
||||||
urlencode({'age': age})])
|
urlencode({'age': age})])
|
||||||
if key:
|
if key:
|
||||||
url = "&".join([url, urlencode({"key", key})])
|
url = "&".join([url, urlencode({'key': key})])
|
||||||
response = urlopen(self._Request(url))
|
response = urlopen(self._Request(url))
|
||||||
except HTTPError as response:
|
except HTTPError as response:
|
||||||
messages = json.loads(response.read().decode('utf-8'))
|
messages = json.loads(response.read().decode('utf-8'))
|
||||||
|
@ -258,7 +258,7 @@ class BadIPsAction(ActionBase):
|
||||||
self._logSys.error(
|
self._logSys.error(
|
||||||
"Error banning IP %s for jail '%s' with action '%s': %s",
|
"Error banning IP %s for jail '%s' with action '%s': %s",
|
||||||
ip, self._jail.name, self.banaction, e,
|
ip, self._jail.name, self.banaction, e,
|
||||||
exc_info=self._logSys.getEffectiveLevel<=logging.DEBUG)
|
exc_info=self._logSys.getEffectiveLevel()<=logging.DEBUG)
|
||||||
else:
|
else:
|
||||||
self._bannedips.add(ip)
|
self._bannedips.add(ip)
|
||||||
self._logSys.info(
|
self._logSys.info(
|
||||||
|
@ -279,7 +279,7 @@ class BadIPsAction(ActionBase):
|
||||||
self._logSys.info(
|
self._logSys.info(
|
||||||
"Error unbanning IP %s for jail '%s' with action '%s': %s",
|
"Error unbanning IP %s for jail '%s' with action '%s': %s",
|
||||||
ip, self._jail.name, self.banaction, e,
|
ip, self._jail.name, self.banaction, e,
|
||||||
exc_info=self._logSys.getEffectiveLevel<=logging.DEBUG)
|
exc_info=self._logSys.getEffectiveLevel()<=logging.DEBUG)
|
||||||
else:
|
else:
|
||||||
self._logSys.info(
|
self._logSys.info(
|
||||||
"Unbanned IP %s for jail '%s' with action '%s'",
|
"Unbanned IP %s for jail '%s' with action '%s'",
|
||||||
|
@ -346,7 +346,7 @@ class BadIPsAction(ActionBase):
|
||||||
try:
|
try:
|
||||||
url = "/".join([self._badips, "add", self.category, aInfo['ip']])
|
url = "/".join([self._badips, "add", self.category, aInfo['ip']])
|
||||||
if self.key:
|
if self.key:
|
||||||
url = "?".join([url, urlencode({"key", self.key})])
|
url = "?".join([url, urlencode({'key': self.key})])
|
||||||
response = urlopen(self._Request(url))
|
response = urlopen(self._Request(url))
|
||||||
except HTTPError as response:
|
except HTTPError as response:
|
||||||
messages = json.loads(response.read().decode('utf-8'))
|
messages = json.loads(response.read().decode('utf-8'))
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
#
|
||||||
|
# Author: Mike Rushton
|
||||||
|
#
|
||||||
|
# Referenced from from http://www.normyee.net/blog/2012/02/02/adding-cloudflare-support-to-fail2ban by NORM YEE
|
||||||
|
#
|
||||||
|
# To get your Cloudflare API key: https://www.cloudflare.com/my-account
|
||||||
|
#
|
||||||
|
|
||||||
|
[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 = curl https://www.cloudflare.com/api_json.html -d 'a=ban' -d 'tkn=<cftoken>' -d 'email=<cfuser>' -d 'key=<ip>'
|
||||||
|
# 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 = curl https://www.cloudflare.com/api_json.html -d 'a=nul' -d 'tkn=<cftoken>' -d 'email=<cfuser>' -d 'key=<ip>'
|
||||||
|
|
||||||
|
|
||||||
|
[Init]
|
||||||
|
|
||||||
|
# Default Cloudflare API token
|
||||||
|
cftoken =
|
||||||
|
|
||||||
|
# Default Cloudflare username
|
||||||
|
cfuser =
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
[INCLUDES]
|
[INCLUDES]
|
||||||
|
|
||||||
before = iptables-blocktype.conf
|
before = iptables-common.conf
|
||||||
|
|
||||||
[Definition]
|
[Definition]
|
||||||
|
|
||||||
|
@ -31,22 +31,6 @@ actionunban = ipset del fail2ban-<name> <ip> -exist
|
||||||
|
|
||||||
[Init]
|
[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
|
# Option: chain
|
||||||
# Notes specifies the iptables chain to which the fail2ban rules should be
|
# Notes specifies the iptables chain to which the fail2ban rules should be
|
||||||
# added
|
# added
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
[INCLUDES]
|
[INCLUDES]
|
||||||
|
|
||||||
before = iptables-blocktype.conf
|
before = iptables-common.conf
|
||||||
|
|
||||||
[Definition]
|
[Definition]
|
||||||
|
|
||||||
|
@ -24,22 +24,6 @@ actionunban = firewall-cmd --direct --remove-rule ipv4 filter f2b-<name> 0 -s <i
|
||||||
|
|
||||||
[Init]
|
[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
|
# Option: chain
|
||||||
# Notes specifies the iptables chain to which the fail2ban rules should be
|
# Notes specifies the iptables chain to which the fail2ban rules should be
|
||||||
# added
|
# added
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
[INCLUDES]
|
[INCLUDES]
|
||||||
|
|
||||||
before = iptables-blocktype.conf
|
before = iptables-common.conf
|
||||||
|
|
||||||
|
|
||||||
[Definition]
|
[Definition]
|
||||||
|
@ -53,18 +53,3 @@ actionunban = iptables -D f2b-<name> -s <ip> -j <blocktype>
|
||||||
|
|
||||||
[Init]
|
[Init]
|
||||||
|
|
||||||
# Default name of the chain
|
|
||||||
#
|
|
||||||
name = default
|
|
||||||
|
|
||||||
# Option: protocol
|
|
||||||
# Notes.: internally used by config reader for interpolations.
|
|
||||||
# Values: [ tcp | udp | icmp | all ] Default: tcp
|
|
||||||
#
|
|
||||||
protocol = tcp
|
|
||||||
|
|
||||||
# Option: chain
|
|
||||||
# Notes specifies the iptables chain to which the fail2ban rules should be
|
|
||||||
# added
|
|
||||||
# Values: STRING Default: INPUT
|
|
||||||
chain = INPUT
|
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
# Fail2Ban configuration file
|
|
||||||
#
|
|
||||||
# Author: Daniel Black
|
|
||||||
#
|
|
||||||
# This is a included configuration file and includes the defination for the blocktype
|
|
||||||
# used in all iptables based actions by default.
|
|
||||||
#
|
|
||||||
# The user can override the default in iptables-blocktype.local
|
|
||||||
|
|
||||||
[INCLUDES]
|
|
||||||
|
|
||||||
after = iptables-blocktype.local
|
|
||||||
|
|
||||||
[Init]
|
|
||||||
|
|
||||||
# Option: blocktype
|
|
||||||
# Note: This is what the action does with rules. This can be any jump target
|
|
||||||
# as per the iptables man page (section 8). Common values are DROP
|
|
||||||
# REJECT, REJECT --reject-with icmp-port-unreachable
|
|
||||||
# Values: STRING
|
|
||||||
blocktype = REJECT --reject-with icmp-port-unreachable
|
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
# Fail2Ban configuration file
|
||||||
|
#
|
||||||
|
# Author: Daniel Black
|
||||||
|
#
|
||||||
|
# This is a included configuration file and includes the definitions for the iptables
|
||||||
|
# used in all iptables based actions by default.
|
||||||
|
#
|
||||||
|
# The user can override the defaults in iptables-common.local
|
||||||
|
|
||||||
|
[INCLUDES]
|
||||||
|
|
||||||
|
after = iptables-blocktype.local
|
||||||
|
iptables-common.local
|
||||||
|
# iptables-blocktype.local is obsolete
|
||||||
|
|
||||||
|
[Init]
|
||||||
|
|
||||||
|
# Option: chain
|
||||||
|
# Notes specifies the iptables chain to which the Fail2Ban rules should be
|
||||||
|
# added
|
||||||
|
# Values: STRING Default: INPUT
|
||||||
|
chain = INPUT
|
||||||
|
|
||||||
|
# Default name of the chain
|
||||||
|
#
|
||||||
|
name = default
|
||||||
|
|
||||||
|
# Option: port
|
||||||
|
# Notes.: specifies port to monitor
|
||||||
|
# Values: [ NUM | STRING ] Default:
|
||||||
|
#
|
||||||
|
port = ssh
|
||||||
|
|
||||||
|
# Option: protocol
|
||||||
|
# Notes.: internally used by config reader for interpolations.
|
||||||
|
# Values: [ tcp | udp | icmp | all ] Default: tcp
|
||||||
|
#
|
||||||
|
protocol = tcp
|
||||||
|
|
||||||
|
# Option: blocktype
|
||||||
|
# Note: This is what the action does with rules. This can be any jump target
|
||||||
|
# as per the iptables man page (section 8). Common values are DROP
|
||||||
|
# REJECT, REJECT --reject-with icmp-port-unreachable
|
||||||
|
# Values: STRING
|
||||||
|
blocktype = REJECT --reject-with icmp-port-unreachable
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
[INCLUDES]
|
[INCLUDES]
|
||||||
|
|
||||||
before = iptables-blocktype.conf
|
before = iptables-common.conf
|
||||||
|
|
||||||
[Definition]
|
[Definition]
|
||||||
|
|
||||||
|
@ -28,13 +28,13 @@ before = iptables-blocktype.conf
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstart = ipset --create f2b-<name> iphash
|
actionstart = ipset --create f2b-<name> iphash
|
||||||
iptables -I INPUT -p <protocol> -m multiport --dports <port> -m set --match-set f2b-<name> src -j <blocktype>
|
iptables -I <chain> -p <protocol> -m multiport --dports <port> -m set --match-set f2b-<name> src -j <blocktype>
|
||||||
|
|
||||||
# Option: actionstop
|
# Option: actionstop
|
||||||
# Notes.: command executed once at the end of Fail2Ban
|
# Notes.: command executed once at the end of Fail2Ban
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstop = iptables -D INPUT -p <protocol> -m multiport --dports <port> -m set --match-set f2b-<name> src -j <blocktype>
|
actionstop = iptables -D <chain> -p <protocol> -m multiport --dports <port> -m set --match-set f2b-<name> src -j <blocktype>
|
||||||
ipset --flush f2b-<name>
|
ipset --flush f2b-<name>
|
||||||
ipset --destroy f2b-<name>
|
ipset --destroy f2b-<name>
|
||||||
|
|
||||||
|
@ -56,18 +56,3 @@ actionunban = ipset --test f2b-<name> <ip> && ipset --del f2b-<name> <ip>
|
||||||
|
|
||||||
[Init]
|
[Init]
|
||||||
|
|
||||||
# Default name of the ipset
|
|
||||||
#
|
|
||||||
name = default
|
|
||||||
|
|
||||||
# Option: port
|
|
||||||
# Notes.: specifies port to monitor
|
|
||||||
# Values: [ NUM | STRING ] Default: ssh
|
|
||||||
#
|
|
||||||
port = ssh
|
|
||||||
|
|
||||||
# Option: protocol
|
|
||||||
# Notes.: internally used by config reader for interpolations.
|
|
||||||
# Values: [ tcp | udp | icmp | all ] Default: tcp
|
|
||||||
#
|
|
||||||
protocol = tcp
|
|
||||||
|
|
|
@ -15,8 +15,7 @@
|
||||||
|
|
||||||
[INCLUDES]
|
[INCLUDES]
|
||||||
|
|
||||||
before = iptables-blocktype.conf
|
before = iptables-common.conf
|
||||||
|
|
||||||
|
|
||||||
[Definition]
|
[Definition]
|
||||||
|
|
||||||
|
@ -25,13 +24,13 @@ before = iptables-blocktype.conf
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstart = ipset create f2b-<name> hash:ip timeout <bantime>
|
actionstart = ipset create f2b-<name> hash:ip timeout <bantime>
|
||||||
iptables -I INPUT -m set --match-set f2b-<name> src -j <blocktype>
|
iptables -I <chain> -m set --match-set f2b-<name> src -j <blocktype>
|
||||||
|
|
||||||
# Option: actionstop
|
# Option: actionstop
|
||||||
# Notes.: command executed once at the end of Fail2Ban
|
# Notes.: command executed once at the end of Fail2Ban
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstop = iptables -D INPUT -m set --match-set f2b-<name> src -j <blocktype>
|
actionstop = iptables -D <chain> -m set --match-set f2b-<name> src -j <blocktype>
|
||||||
ipset flush f2b-<name>
|
ipset flush f2b-<name>
|
||||||
ipset destroy f2b-<name>
|
ipset destroy f2b-<name>
|
||||||
|
|
||||||
|
@ -53,12 +52,8 @@ actionunban = ipset del f2b-<name> <ip> -exist
|
||||||
|
|
||||||
[Init]
|
[Init]
|
||||||
|
|
||||||
# Default name of the ipset
|
|
||||||
#
|
|
||||||
name = default
|
|
||||||
|
|
||||||
# Option: bantime
|
# Option: bantime
|
||||||
# Notes: specifies the bantime in seconds (handled internally rather than by fail2ban)
|
# Notes: specifies the bantime in seconds (handled internally rather than by fail2ban)
|
||||||
# Values: [ NUM ] Default: 600
|
# Values: [ NUM ] Default: 600
|
||||||
|
#
|
||||||
bantime = 600
|
bantime = 600
|
||||||
|
|
|
@ -15,8 +15,7 @@
|
||||||
|
|
||||||
[INCLUDES]
|
[INCLUDES]
|
||||||
|
|
||||||
before = iptables-blocktype.conf
|
before = iptables-common.conf
|
||||||
|
|
||||||
|
|
||||||
[Definition]
|
[Definition]
|
||||||
|
|
||||||
|
@ -25,13 +24,13 @@ before = iptables-blocktype.conf
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstart = ipset create f2b-<name> hash:ip timeout <bantime>
|
actionstart = ipset create f2b-<name> hash:ip timeout <bantime>
|
||||||
iptables -I INPUT -p <protocol> -m multiport --dports <port> -m set --match-set f2b-<name> src -j <blocktype>
|
iptables -I <chain> -p <protocol> -m multiport --dports <port> -m set --match-set f2b-<name> src -j <blocktype>
|
||||||
|
|
||||||
# Option: actionstop
|
# Option: actionstop
|
||||||
# Notes.: command executed once at the end of Fail2Ban
|
# Notes.: command executed once at the end of Fail2Ban
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstop = iptables -D INPUT -p <protocol> -m multiport --dports <port> -m set --match-set f2b-<name> src -j <blocktype>
|
actionstop = iptables -D <chain> -p <protocol> -m multiport --dports <port> -m set --match-set f2b-<name> src -j <blocktype>
|
||||||
ipset flush f2b-<name>
|
ipset flush f2b-<name>
|
||||||
ipset destroy f2b-<name>
|
ipset destroy f2b-<name>
|
||||||
|
|
||||||
|
@ -53,24 +52,8 @@ actionunban = ipset del f2b-<name> <ip> -exist
|
||||||
|
|
||||||
[Init]
|
[Init]
|
||||||
|
|
||||||
# Default name of the ipset
|
|
||||||
#
|
|
||||||
name = default
|
|
||||||
|
|
||||||
# Option: port
|
|
||||||
# Notes.: specifies port to monitor
|
|
||||||
# Values: [ NUM | STRING ] Default: ssh
|
|
||||||
#
|
|
||||||
port = ssh
|
|
||||||
|
|
||||||
# Option: protocol
|
|
||||||
# Notes.: internally used by config reader for interpolations.
|
|
||||||
# Values: [ tcp | udp | icmp | all ] Default: tcp
|
|
||||||
#
|
|
||||||
protocol = tcp
|
|
||||||
|
|
||||||
# Option: bantime
|
# Option: bantime
|
||||||
# Notes: specifies the bantime in seconds (handled internally rather than by fail2ban)
|
# Notes: specifies the bantime in seconds (handled internally rather than by fail2ban)
|
||||||
# Values: [ NUM ] Default: 600
|
# Values: [ NUM ] Default: 600
|
||||||
|
#
|
||||||
bantime = 600
|
bantime = 600
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
[INCLUDES]
|
[INCLUDES]
|
||||||
|
|
||||||
before = iptables-blocktype.conf
|
before = iptables-common.conf
|
||||||
|
|
||||||
[Definition]
|
[Definition]
|
||||||
|
|
||||||
|
@ -60,24 +60,3 @@ actionunban = iptables -D f2b-<name> -s <ip> -j f2b-<name>-log
|
||||||
|
|
||||||
[Init]
|
[Init]
|
||||||
|
|
||||||
# Default name of the chain
|
|
||||||
#
|
|
||||||
name = default
|
|
||||||
|
|
||||||
# Option: port
|
|
||||||
# Notes.: specifies port to monitor
|
|
||||||
# Values: [ NUM | STRING ] Default:
|
|
||||||
#
|
|
||||||
port = ssh
|
|
||||||
|
|
||||||
# Option: protocol
|
|
||||||
# Notes.: internally used by config reader for interpolations.
|
|
||||||
# Values: [ tcp | udp | icmp | all ] Default: tcp
|
|
||||||
#
|
|
||||||
protocol = tcp
|
|
||||||
|
|
||||||
# Option: chain
|
|
||||||
# Notes specifies the iptables chain to which the fail2ban rules should be
|
|
||||||
# added
|
|
||||||
# Values: STRING Default: INPUT
|
|
||||||
chain = INPUT
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
[INCLUDES]
|
[INCLUDES]
|
||||||
|
|
||||||
before = iptables-blocktype.conf
|
before = iptables-common.conf
|
||||||
|
|
||||||
[Definition]
|
[Definition]
|
||||||
|
|
||||||
|
@ -50,24 +50,3 @@ actionunban = iptables -D f2b-<name> -s <ip> -j <blocktype>
|
||||||
|
|
||||||
[Init]
|
[Init]
|
||||||
|
|
||||||
# Default name of the chain
|
|
||||||
#
|
|
||||||
name = default
|
|
||||||
|
|
||||||
# Option: port
|
|
||||||
# Notes.: specifies port to monitor
|
|
||||||
# Values: [ NUM | STRING ] Default:
|
|
||||||
#
|
|
||||||
port = ssh
|
|
||||||
|
|
||||||
# Option: protocol
|
|
||||||
# Notes.: internally used by config reader for interpolations.
|
|
||||||
# Values: [ tcp | udp | icmp | all ] Default: tcp
|
|
||||||
#
|
|
||||||
protocol = tcp
|
|
||||||
|
|
||||||
# Option: chain
|
|
||||||
# Notes specifies the iptables chain to which the fail2ban rules should be
|
|
||||||
# added
|
|
||||||
# Values: STRING Default: INPUT
|
|
||||||
chain = INPUT
|
|
||||||
|
|
|
@ -8,8 +8,7 @@
|
||||||
|
|
||||||
[INCLUDES]
|
[INCLUDES]
|
||||||
|
|
||||||
before = iptables-blocktype.conf
|
before = iptables-common.conf
|
||||||
|
|
||||||
|
|
||||||
[Definition]
|
[Definition]
|
||||||
|
|
||||||
|
@ -53,24 +52,3 @@ actionunban = iptables -D f2b-<name> -s <ip> -j <blocktype>
|
||||||
|
|
||||||
[Init]
|
[Init]
|
||||||
|
|
||||||
# Default name of the chain
|
|
||||||
#
|
|
||||||
name = default
|
|
||||||
|
|
||||||
# Option: port
|
|
||||||
# Notes.: specifies port to monitor
|
|
||||||
# Values: [ NUM | STRING ] Default:
|
|
||||||
#
|
|
||||||
port = ssh
|
|
||||||
|
|
||||||
# Option: protocol
|
|
||||||
# Notes.: internally used by config reader for interpolations.
|
|
||||||
# Values: [ tcp | udp | icmp | all ] Default: tcp
|
|
||||||
#
|
|
||||||
protocol = tcp
|
|
||||||
|
|
||||||
# Option: chain
|
|
||||||
# Notes specifies the iptables chain to which the fail2ban rules should be
|
|
||||||
# added
|
|
||||||
# Values: STRING Default: INPUT
|
|
||||||
chain = INPUT
|
|
||||||
|
|
|
@ -6,8 +6,7 @@
|
||||||
|
|
||||||
[INCLUDES]
|
[INCLUDES]
|
||||||
|
|
||||||
before = iptables-blocktype.conf
|
before = iptables-common.conf
|
||||||
|
|
||||||
|
|
||||||
[Definition]
|
[Definition]
|
||||||
|
|
||||||
|
@ -33,14 +32,14 @@ before = iptables-blocktype.conf
|
||||||
# own rules. The 3600 second timeout is independent and acts as a
|
# own rules. The 3600 second timeout is independent and acts as a
|
||||||
# safeguard in case the fail2ban process dies unexpectedly. The
|
# safeguard in case the fail2ban process dies unexpectedly. The
|
||||||
# shorter of the two timeouts actually matters.
|
# shorter of the two timeouts actually matters.
|
||||||
actionstart = if [ `id -u` -eq 0 ];then iptables -I INPUT -m recent --update --seconds 3600 --name f2b-<name> -j <blocktype>;fi
|
actionstart = if [ `id -u` -eq 0 ];then iptables -I <chain> -m recent --update --seconds 3600 --name f2b-<name> -j <blocktype>;fi
|
||||||
|
|
||||||
# Option: actionstop
|
# Option: actionstop
|
||||||
# Notes.: command executed once at the end of Fail2Ban
|
# Notes.: command executed once at the end of Fail2Ban
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstop = echo / > /proc/net/xt_recent/f2b-<name>
|
actionstop = echo / > /proc/net/xt_recent/f2b-<name>
|
||||||
if [ `id -u` -eq 0 ];then iptables -D INPUT -m recent --update --seconds 3600 --name f2b-<name> -j <blocktype>;fi
|
if [ `id -u` -eq 0 ];then iptables -D <chain> -m recent --update --seconds 3600 --name f2b-<name> -j <blocktype>;fi
|
||||||
|
|
||||||
# Option: actioncheck
|
# Option: actioncheck
|
||||||
# Notes.: command executed once before each actionban command
|
# Notes.: command executed once before each actionban command
|
||||||
|
@ -66,12 +65,3 @@ actionunban = echo -<ip> > /proc/net/xt_recent/f2b-<name>
|
||||||
|
|
||||||
[Init]
|
[Init]
|
||||||
|
|
||||||
# Default name of the chain
|
|
||||||
#
|
|
||||||
name = default
|
|
||||||
|
|
||||||
# Option: protocol
|
|
||||||
# Notes.: internally used by config reader for interpolations.
|
|
||||||
# Values: [ tcp | udp | icmp | all ] Default: tcp
|
|
||||||
#
|
|
||||||
protocol = tcp
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
[INCLUDES]
|
[INCLUDES]
|
||||||
|
|
||||||
before = iptables-blocktype.conf
|
before = iptables-common.conf
|
||||||
|
|
||||||
[Definition]
|
[Definition]
|
||||||
|
|
||||||
|
@ -50,24 +50,3 @@ actionunban = iptables -D f2b-<name> -s <ip> -j <blocktype>
|
||||||
|
|
||||||
[Init]
|
[Init]
|
||||||
|
|
||||||
# Default name of the chain
|
|
||||||
#
|
|
||||||
name = default
|
|
||||||
|
|
||||||
# Option: port
|
|
||||||
# Notes.: specifies port to monitor
|
|
||||||
# Values: [ NUM | STRING ] Default:
|
|
||||||
#
|
|
||||||
port = ssh
|
|
||||||
|
|
||||||
# Option: protocol
|
|
||||||
# Notes.: internally used by config reader for interpolations.
|
|
||||||
# Values: [ tcp | udp | icmp | all ] Default: tcp
|
|
||||||
#
|
|
||||||
protocol = tcp
|
|
||||||
|
|
||||||
# Option: chain
|
|
||||||
# Notes specifies the iptables chain to which the fail2ban rules should be
|
|
||||||
# added
|
|
||||||
# Values: STRING Default: INPUT
|
|
||||||
chain = INPUT
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ messages['ban'] = {}
|
||||||
messages['ban']['head'] = \
|
messages['ban']['head'] = \
|
||||||
"""Hi,
|
"""Hi,
|
||||||
|
|
||||||
The IP %(ip)s has just been banned for %(bantime)s seconds
|
The IP %(ip)s has just been banned for %(bantime)i seconds
|
||||||
by Fail2Ban after %(failures)i attempts against %(jailname)s.
|
by Fail2Ban after %(failures)i attempts against %(jailname)s.
|
||||||
"""
|
"""
|
||||||
messages['ban']['tail'] = \
|
messages['ban']['tail'] = \
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
# Fail2Ban configuration file for Bytemark Symbiosis firewall
|
||||||
|
#
|
||||||
|
# Author: Yaroslav Halchenko
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
[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 = iptables -n -L <chain>
|
||||||
|
|
||||||
|
# Option: actionban
|
||||||
|
# Notes.: command executed when banning an IP.
|
||||||
|
# Values: CMD
|
||||||
|
#
|
||||||
|
actionban = echo 'all' >| /etc/symbiosis/firewall/blacklist.d/<ip>.auto
|
||||||
|
iptables -I <chain> 1 -s <ip> -j <blocktype>
|
||||||
|
|
||||||
|
# Option: actionunban
|
||||||
|
# Notes.: command executed when unbanning an IP.
|
||||||
|
# Values: CMD
|
||||||
|
#
|
||||||
|
actionunban = rm -f /etc/symbiosis/firewall/blacklist.d/<ip>.auto
|
||||||
|
iptables -D <chain> -s <ip> -j <blocktype> || :
|
||||||
|
|
||||||
|
[Init]
|
||||||
|
|
||||||
|
# Option: chain
|
||||||
|
# Notes specifies the iptables chain to which the fail2ban rules should be
|
||||||
|
# added to. blacklist is a chain initiated by symbiosis firewall.
|
||||||
|
# Values: STRING Default: blacklist
|
||||||
|
chain = blacklist
|
||||||
|
|
||||||
|
# Option: blocktype
|
||||||
|
# Note: This is to match default symbiosis firewall type for blacklisted IPs
|
||||||
|
# Values: STRING
|
||||||
|
blocktype = DROP
|
|
@ -1,9 +1,9 @@
|
||||||
# Fail2Ban action configuration file for ufw
|
# Fail2Ban action configuration file for ufw
|
||||||
#
|
#
|
||||||
# You are required to run "ufw enable" before this will have an effect.
|
# You are required to run "ufw enable" before this will have any effect.
|
||||||
#
|
#
|
||||||
# The insert position should be approprate to block the required traffic.
|
# The insert position should be appropriate to block the required traffic.
|
||||||
# A number after an allow rule to the application won't be much use.
|
# A number after an allow rule to the application won't be of much use.
|
||||||
|
|
||||||
[Definition]
|
[Definition]
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ actionunban = [ -n "<application>" ] && app="app <application>" ; ufw delete <bl
|
||||||
|
|
||||||
[Init]
|
[Init]
|
||||||
# Option: insertpos
|
# Option: insertpos
|
||||||
# Notes.: The postition number in the firewall list to insert the block rule
|
# Notes.: The position number in the firewall list to insert the block rule
|
||||||
insertpos = 1
|
insertpos = 1
|
||||||
|
|
||||||
# Option: blocktype
|
# Option: blocktype
|
||||||
|
|
|
@ -12,7 +12,8 @@ before = common.conf
|
||||||
|
|
||||||
_daemon = courieresmtpd
|
_daemon = courieresmtpd
|
||||||
|
|
||||||
failregex = ^%(__prefix_line)serror,relay=<HOST>,.*: 550 User unknown\.$
|
failregex = ^%(__prefix_line)serror,relay=<HOST>,.*: 550 User (<.*> )?unknown\.?$
|
||||||
|
^%(__prefix_line)serror,relay=<HOST>,msg="535 Authentication failed\.",cmd:( AUTH \S+)?( [0-9a-zA-Z\+/=]+)?$
|
||||||
|
|
||||||
ignoreregex =
|
ignoreregex =
|
||||||
|
|
||||||
|
|
|
@ -11,9 +11,9 @@ before = common.conf
|
||||||
|
|
||||||
[Definition]
|
[Definition]
|
||||||
|
|
||||||
_daemon = (?:cyrus/)?(?:imapd?|pop3d?)
|
_daemon = (?:cyrus/)?(?:imap(d|s)?|pop3(d|s)?)
|
||||||
|
|
||||||
failregex = ^%(__prefix_line)sbadlogin: \S+ ?\[<HOST>\] \S+ .*?\[?SASL\(-13\): authentication failure: .*\]?$
|
failregex = ^%(__prefix_line)sbadlogin: \S+ ?\[<HOST>\] \S+ .*?\[?SASL\(-13\): (authentication failure|user not found): .*\]?$
|
||||||
|
|
||||||
ignoreregex =
|
ignoreregex =
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Fail2Ban configuration file for Directadmin
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
[INCLUDES]
|
||||||
|
|
||||||
|
before = common.conf
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
|
||||||
|
failregex = ^: \'<HOST>\' \d{1,3} failed login attempt(s)?. \s*
|
||||||
|
|
||||||
|
ignoreregex =
|
||||||
|
|
||||||
|
[Init]
|
||||||
|
datepattern = ^%%Y:%%m:%%d-%%H:%%M:%%S
|
||||||
|
|
||||||
|
#
|
||||||
|
# Requires Directadmin v1.45.3 or higher. http://www.directadmin.com/features.php?id=1590
|
||||||
|
#
|
||||||
|
# Author: Cyril Roos
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
# Fail2Ban filter for monit.conf, looks for failed access attempts
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
|
||||||
|
failregex = ^\[[A-Z]+\s+\]\s*error\s*:\s*Warning:\s+Client '<HOST>' supplied unknown user '\w+' accessing monit httpd$
|
||||||
|
^\[[A-Z]+\s+\]\s*error\s*:\s*Warning:\s+Client '<HOST>' supplied wrong password for user '\w+' accessing monit httpd$
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
# Fail2Ban configuration file
|
||||||
|
# for Oracle IMS with XML logging
|
||||||
|
#
|
||||||
|
# Author: Joel Snyder/jms@opus1.com/2014-June-01
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
[INCLUDES]
|
||||||
|
|
||||||
|
# Read common prefixes.
|
||||||
|
# If any customizations available -- read them from
|
||||||
|
# common.local
|
||||||
|
before = common.conf
|
||||||
|
|
||||||
|
|
||||||
|
[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\-.^_]+)
|
||||||
|
# Values: TEXT
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# CONFIGURATION REQUIREMENTS FOR ORACLE IMS v6 and ABOVE:
|
||||||
|
#
|
||||||
|
# In OPTION.DAT you must have LOG_FORMAT=4 and
|
||||||
|
# bit 5 of LOG_CONNECTION must be set.
|
||||||
|
#
|
||||||
|
# Many of these sub-fields are optional and can be turned on and off
|
||||||
|
# by the system manager. We need the "tr" field
|
||||||
|
# (transport information (present if bit 5 of LOG_CONNECTION is
|
||||||
|
# set and transport information is available)).
|
||||||
|
# "di" should be there by default if you have LOG_FORMAT=4.
|
||||||
|
# Do not use "mi" as this is not included by default.
|
||||||
|
#
|
||||||
|
# Typical line IF YOU ARE USING TAGGING ! ! ! is:
|
||||||
|
# <co ts="2014-06-02T09:45:50.29" pi="123f.3f8.4397"
|
||||||
|
# sc="tcp_local" dr="+" ac="U"
|
||||||
|
# tr="TCP|192.245.12.223|25|151.1.71.144|59762" ap="SMTP"
|
||||||
|
# mi="Bad password"
|
||||||
|
# us="01ko8hqnoif09qx0np@imap.opus1.com"
|
||||||
|
# di="535 5.7.8 Bad username or password (Authentication failed)."/>
|
||||||
|
# Format is generally documented in the PORT_ACCESS mapping
|
||||||
|
# at http://docs.oracle.com/cd/E19563-01/819-4428/bgaur/index.html
|
||||||
|
#
|
||||||
|
# All that would be on one line.
|
||||||
|
# Note that you MUST have LOG_FORMAT=4 for this to work!
|
||||||
|
#
|
||||||
|
|
||||||
|
failregex = ^.*tr="[A-Z]+\|[0-9.]+\|\d+\|<HOST>\|\d+" ap="[^"]*" mi="Bad password" us="[^"]*" di="535 5.7.8 Bad username or password( \(Authentication failed\))?\."/>$
|
||||||
|
|
||||||
|
# Option: ignoreregex
|
||||||
|
# Notes.: regex to ignore. If this regex matches, the line is ignored.
|
||||||
|
# Values: TEXT
|
||||||
|
#
|
||||||
|
ignoreregex =
|
|
@ -11,4 +11,9 @@ _daemon = postfix/smtpd
|
||||||
|
|
||||||
failregex = ^%(__prefix_line)swarning: [-._\w]+\[<HOST>\]: SASL (?:LOGIN|PLAIN|(?:CRAM|DIGEST)-MD5) authentication failed(: [ A-Za-z0-9+/]*={0,2})?\s*$
|
failregex = ^%(__prefix_line)swarning: [-._\w]+\[<HOST>\]: SASL (?:LOGIN|PLAIN|(?:CRAM|DIGEST)-MD5) authentication failed(: [ A-Za-z0-9+/]*={0,2})?\s*$
|
||||||
|
|
||||||
|
[Init]
|
||||||
|
|
||||||
|
journalmatch = _SYSTEMD_UNIT=postfix.service
|
||||||
|
|
||||||
|
|
||||||
# Author: Yaroslav Halchenko
|
# Author: Yaroslav Halchenko
|
||||||
|
|
|
@ -15,7 +15,7 @@ before = common.conf
|
||||||
_daemon = pure-ftpd
|
_daemon = pure-ftpd
|
||||||
|
|
||||||
# Error message specified in multiple languages
|
# Error message specified in multiple languages
|
||||||
__errmsg = (?:<EFBFBD>ϥΪ<EFBFBD>\[.*\]<5D><><EFBFBD>ҥ<EFBFBD><D2A5><EFBFBD>|ʹ<><CAB9><EFBFBD><EFBFBD>\[.*\]<5D><>֤ʧ<D6A4><CAA7>|\[.*\] kullan<61>c<EFBFBD>s<EFBFBD> i<>in giri<72> hatal<61>|<7C><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> \[.*\]|Godkjennelse mislyktes for \[.*\]|Beh<65>righetskontroll misslyckas f<>r anv<6E>ndare \[.*\]|Autentifikacia uzivatela zlyhala \[.*\]|Autentificare esuata pentru utilizatorul \[.*\]|Autentica<63><61>o falhou para usu<73>rio \[.*\]|Autentyfikacja nie powiod<6F>a si<73> dla u<>ytkownika \[.*\]|Autorisatie faalde voor gebruiker \[.*\]|\[.*\] <20><><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>|Autenticazione falita per l'utente \[.*\]|Azonos<6F>t<EFBFBD>s sikertelen \[.*\] felhaszn<7A>l<EFBFBD>nak|\[.*\] c'est un batard, il connait pas son code|Erreur d'authentification pour l'utilisateur \[.*\]|Autentificaci<63>n fallida para el usuario \[.*\]|Authentication failed for user \[.*\]|Authentifizierung fehlgeschlagen f<>r Benutzer \[.*\].|Godkendelse mislykkedes for \[.*\]|Autentifikace u<>ivatele selhala \[.*\])
|
__errmsg = (?:Godkendelse mislykkedes for \[.*\]|Authentifizierung fehlgeschlagen für Benutzer \[.*\].|Authentication failed for user \[.*\]|Autentificación fallida para el usuario \[.*\]|\[.*\] c'est un batard, il connait pas son code|Erreur d'authentification pour l'utilisateur \[.*\]|Azonosítás sikertelen \[.*\] felhasználónak|Autenticazione falita per l'utente \[.*\]|Autorisatie faalde voor gebruiker \[.*\]|Godkjennelse mislyktes for \[.*\]|\[.*\] kullanýcýsý için giriþ hatalý|Autenticação falhou para usuário \[.*\]|Autentificare esuata pentru utilizatorul \[.*\]|Autentifikace uživatele selhala \[.*\]|Autentyfikacja nie powiodła się dla użytkownika \[.*\]|Autentifikacia uzivatela zlyhala \[.*\]|Behörighetskontroll misslyckas för användare \[.*\]|Авторизация не удалась пользователю \[.*\]|\[.*\] 嶸盪 檣隸 褒ぬ|妏蚚氪\[.*\]桄痐囮啖|使用者\[.*\]驗證失敗)
|
||||||
|
|
||||||
failregex = ^%(__prefix_line)s\(.+?@<HOST>\) \[WARNING\] %(__errmsg)s\s*$
|
failregex = ^%(__prefix_line)s\(.+?@<HOST>\) \[WARNING\] %(__errmsg)s\s*$
|
||||||
|
|
||||||
|
@ -24,7 +24,13 @@ ignoreregex =
|
||||||
# Author: Cyril Jaquier
|
# Author: Cyril Jaquier
|
||||||
# Modified: Yaroslav Halchenko for pure-ftpd
|
# Modified: Yaroslav Halchenko for pure-ftpd
|
||||||
# Documentation thanks to Blake on http://www.fail2ban.org/wiki/index.php?title=Fail2ban:Community_Portal
|
# Documentation thanks to Blake on http://www.fail2ban.org/wiki/index.php?title=Fail2ban:Community_Portal
|
||||||
|
# UTF-8 editing and mechanism thanks to Johannes Weberhofer
|
||||||
#
|
#
|
||||||
# Only logs to syslog though facility can be changed configuration file/command line
|
# Only logs to syslog though facility can be changed configuration file/command line
|
||||||
#
|
#
|
||||||
# fgrep -r MSG_AUTH_FAILED_LOG pure-ftpd-1.0.36/src
|
# To get messages in the right encoding:
|
||||||
|
# grep MSG_AUTH_FAILED_LOG pure-ftpd-1.0.36/src/messages_[defhint]* | grep -Po '".?"' | recode latin1..utf-8 | tr -d '"' > messages
|
||||||
|
# grep MSG_AUTH_FAILED_LOG pure-ftpd-1.0.36/src/messages_[pr][to] | grep -Po '".?"' | recode latin1..utf-8 | tr -d '"' >> messages
|
||||||
|
# grep MSG_AUTH_FAILED_LOG pure-ftpd-1.0.36/src/messages_[cps][slkv] | grep -Po '".?"' | recode latin2..utf-8 | tr -d '"' >> messages
|
||||||
|
# grep MSG_AUTH_FAILED_LOG pure-ftpd-1.0.36/src/messages_ru | grep -Po '".?"' | recode KOI8-R..utf-8 | tr -d '"' >> messages
|
||||||
|
# grep MSG_AUTH_FAILED_LOG pure-ftpd-1.0.36/src/messages_[kz] | grep -Po '".*?"' | tr -d '"' | recode big5..utf-8 >> messages
|
||||||
|
|
|
@ -21,13 +21,13 @@ before = common.conf
|
||||||
|
|
||||||
[Definition]
|
[Definition]
|
||||||
|
|
||||||
_daemon = fail2ban\.server\.actions
|
_daemon = fail2ban\.actions\s*
|
||||||
|
|
||||||
# The name of the jail that this filter is used for. In jail.conf, name the
|
# The name of the jail that this filter is used for. In jail.conf, name the
|
||||||
# jail using this filter 'recidive', or change this line!
|
# jail using this filter 'recidive', or change this line!
|
||||||
_jailname = recidive
|
_jailname = recidive
|
||||||
|
|
||||||
failregex = ^(%(__prefix_line)s| %(_daemon)s%(__pid_re)s?:\s+)WARNING\s+\[(?!%(_jailname)s\])(?:.*)\]\s+Ban\s+<HOST>\s*$
|
failregex = ^(%(__prefix_line)s| %(_daemon)s%(__pid_re)s?:\s+)NOTICE\s+\[(?!%(_jailname)s\])(?:.*)\]\s+Ban\s+<HOST>\s*$
|
||||||
|
|
||||||
[Init]
|
[Init]
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ before = common.conf
|
||||||
|
|
||||||
[Definition]
|
[Definition]
|
||||||
|
|
||||||
_deamon = (?:cyrus/)?(?:tim)?sieved?
|
_daemon = (?:cyrus/)?(?:tim)?sieved?
|
||||||
|
|
||||||
failregex = ^%(__prefix_line)sbadlogin: \S+ ?\[<HOST>\] \S+ authentication failure$
|
failregex = ^%(__prefix_line)sbadlogin: \S+ ?\[<HOST>\] \S+ authentication failure$
|
||||||
|
|
||||||
|
|
|
@ -30,9 +30,9 @@ failregex = ^%(__prefix_line)s(?:error: PAM: )?[aA]uthentication (?:failure|erro
|
||||||
^%(__prefix_line)sReceived disconnect from <HOST>: 3: \S+: Auth fail$
|
^%(__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 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*$
|
^%(__prefix_line)sUser .+ from <HOST> not allowed because none of user's groups are listed in AllowGroups\s*$
|
||||||
^(?P<__prefix>%(__prefix_line)s)User .+ not allowed because account is locked<SKIPLINES>(?P=__prefix)(?:error: )?Received disconnect from <HOST>: 11: Bye Bye \[preauth\]$
|
^(?P<__prefix>%(__prefix_line)s)User .+ not allowed because account is locked<SKIPLINES>(?P=__prefix)(?:error: )?Received disconnect from <HOST>: 11: .+ \[preauth\]$
|
||||||
^(?P<__prefix>%(__prefix_line)s)Disconnecting: Too many authentication failures for .+? \[preauth\]<SKIPLINES>(?P=__prefix)(?:error: )?Connection closed by <HOST> \[preauth\]$
|
^(?P<__prefix>%(__prefix_line)s)Disconnecting: Too many authentication failures for .+? \[preauth\]<SKIPLINES>(?P=__prefix)(?:error: )?Connection closed by <HOST> \[preauth\]$
|
||||||
^(?P<__prefix>%(__prefix_line)s)Connection from <HOST> port \d+<SKIPLINES>(?P=__prefix)Disconnecting: Too many authentication failures for .+? \[preauth\]$
|
^(?P<__prefix>%(__prefix_line)s)Connection from <HOST> port \d+(?: on \S+ port \d+)?<SKIPLINES>(?P=__prefix)Disconnecting: Too many authentication failures for .+? \[preauth\]$
|
||||||
|
|
||||||
ignoreregex =
|
ignoreregex =
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# YOU SHOULD NOT MODIFY THIS FILE.
|
# YOU SHOULD NOT MODIFY THIS FILE.
|
||||||
#
|
#
|
||||||
# It will probably be overwitten or improved in a distribution update.
|
# It will probably be overwritten or improved in a distribution update.
|
||||||
#
|
#
|
||||||
# Provide customizations in a jail.local file or a jail.d/customisation.local.
|
# Provide customizations in a jail.local file or a jail.d/customisation.local.
|
||||||
# For example to change the default bantime for all jails and to enable the
|
# For example to change the default bantime for all jails and to enable the
|
||||||
|
@ -151,22 +151,22 @@ port = 0:65535
|
||||||
banaction = iptables-multiport
|
banaction = iptables-multiport
|
||||||
|
|
||||||
# The simplest action to take: ban only
|
# The simplest action to take: ban only
|
||||||
action_ = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
|
action_ = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
|
||||||
|
|
||||||
# ban & send an e-mail with whois report to the destemail.
|
# ban & send an e-mail with whois report to the destemail.
|
||||||
action_mw = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
|
action_mw = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
|
||||||
%(mta)s-whois[name=%(__name__)s, dest="%(destemail)s", protocol="%(protocol)s", chain="%(chain)s"]
|
%(mta)s-whois[name=%(__name__)s, dest="%(destemail)s", protocol="%(protocol)s", chain="%(chain)s"]
|
||||||
|
|
||||||
# ban & send an e-mail with whois report and relevant log lines
|
# ban & send an e-mail with whois report and relevant log lines
|
||||||
# to the destemail.
|
# to the destemail.
|
||||||
action_mwl = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
|
action_mwl = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
|
||||||
%(mta)s-whois-lines[name=%(__name__)s, dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s"]
|
%(mta)s-whois-lines[name=%(__name__)s, dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s"]
|
||||||
|
|
||||||
# See the IMPORTANT note in action.d/xarf-login-attack for when to use this action
|
# See the IMPORTANT note in action.d/xarf-login-attack for when to use this action
|
||||||
#
|
#
|
||||||
# ban & send a xarf e-mail to abuse contact of IP address and include relevant log lines
|
# ban & send a xarf e-mail to abuse contact of IP address and include relevant log lines
|
||||||
# to the destemail.
|
# to the destemail.
|
||||||
action_xarf = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
|
action_xarf = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
|
||||||
xarf-login-attack[service=%(__name__)s, sender="%(sender)s", logpath=%(logpath)s, port="%(port)s"]
|
xarf-login-attack[service=%(__name__)s, sender="%(sender)s", logpath=%(logpath)s, port="%(port)s"]
|
||||||
|
|
||||||
|
|
||||||
|
@ -366,11 +366,17 @@ maxretry = 5
|
||||||
port = http,https
|
port = http,https
|
||||||
logpath = /var/log/tomcat*/catalina.out
|
logpath = /var/log/tomcat*/catalina.out
|
||||||
|
|
||||||
|
[monit]
|
||||||
|
#Ban clients brute-forcing the monit gui login
|
||||||
|
filter = monit
|
||||||
|
port = 2812
|
||||||
|
logpath = /var/log/monit
|
||||||
|
|
||||||
|
|
||||||
[webmin-auth]
|
[webmin-auth]
|
||||||
|
|
||||||
port = 10000
|
port = 10000
|
||||||
logpath = /var/log/auth.log
|
logpath = %(syslog_authpriv)s
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -423,7 +429,7 @@ maxretry = 6
|
||||||
|
|
||||||
[vsftpd]
|
[vsftpd]
|
||||||
# or overwrite it in jails.local to be
|
# or overwrite it in jails.local to be
|
||||||
# logpath = /var/log/auth.log
|
# logpath = %(syslog_authpriv)s
|
||||||
# if you want to rely on PAM failed login attempts
|
# if you want to rely on PAM failed login attempts
|
||||||
# vsftpd's failregex should match both of those formats
|
# vsftpd's failregex should match both of those formats
|
||||||
port = ftp,ftp-data,ftps,ftps-data
|
port = ftp,ftp-data,ftps,ftps-data
|
||||||
|
@ -495,13 +501,13 @@ logpath = %(solidpop3d_log)s
|
||||||
[exim]
|
[exim]
|
||||||
|
|
||||||
port = smtp,465,submission
|
port = smtp,465,submission
|
||||||
logpath = /var/log/exim/mainlog
|
logpath = %(exim_main_log)s
|
||||||
|
|
||||||
|
|
||||||
[exim-spam]
|
[exim-spam]
|
||||||
|
|
||||||
port = smtp,465,submission
|
port = smtp,465,submission
|
||||||
logpath = /var/log/exim/mainlog
|
logpath = %(exim_main_log)s
|
||||||
|
|
||||||
|
|
||||||
[kerio]
|
[kerio]
|
||||||
|
@ -533,7 +539,7 @@ logpath = %(postfix_log)s
|
||||||
[perdition]
|
[perdition]
|
||||||
|
|
||||||
port = imap3,imaps,pop3,pop3s
|
port = imap3,imaps,pop3,pop3s
|
||||||
logpath = /var/log/maillog
|
logpath = %(syslog_mail)s
|
||||||
|
|
||||||
|
|
||||||
[squirrelmail]
|
[squirrelmail]
|
||||||
|
@ -657,13 +663,13 @@ maxretry = 5
|
||||||
[pam-generic]
|
[pam-generic]
|
||||||
# pam-generic filter can be customized to monitor specific subset of 'tty's
|
# pam-generic filter can be customized to monitor specific subset of 'tty's
|
||||||
banaction = iptables-allports
|
banaction = iptables-allports
|
||||||
logpath = /var/log/auth.log
|
logpath = %(syslog_authpriv)s
|
||||||
|
|
||||||
|
|
||||||
[xinetd-fail]
|
[xinetd-fail]
|
||||||
|
|
||||||
banaction = iptables-multiport-log
|
banaction = iptables-multiport-log
|
||||||
logpath = /var/log/daemon.log
|
logpath = %(syslog_daemon)s
|
||||||
maxretry = 2
|
maxretry = 2
|
||||||
|
|
||||||
|
|
||||||
|
@ -693,5 +699,18 @@ action = %(banaction)s[name=%(__name__)s-tcp, port="%(tcpport)s", protocol="tcp
|
||||||
[nagios]
|
[nagios]
|
||||||
|
|
||||||
enabled = false
|
enabled = false
|
||||||
logpath = /var/log/messages ; nrpe.cfg may define a different log_facility
|
logpath = %(syslog_daemon)s ; nrpe.cfg may define a different log_facility
|
||||||
maxretry = 1
|
maxretry = 1
|
||||||
|
|
||||||
|
|
||||||
|
[oracleims]
|
||||||
|
# see "oracleims" filter file for configuration requirement for Oracle IMS v6 and above
|
||||||
|
enabled = false
|
||||||
|
logpath = /opt/sun/comms/messaging64/log/mail.log_current
|
||||||
|
maxretry = 6
|
||||||
|
banaction = iptables-allports
|
||||||
|
|
||||||
|
[directadmin]
|
||||||
|
enabled = false
|
||||||
|
logpath = /var/log/directadmin/login.log
|
||||||
|
port = 2222
|
||||||
|
|
|
@ -7,16 +7,22 @@ after = paths-overrides.local
|
||||||
|
|
||||||
[DEFAULT]
|
[DEFAULT]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
sshd_log = %(syslog_authpriv)s
|
sshd_log = %(syslog_authpriv)s
|
||||||
|
|
||||||
dropbear_log = %(syslog_authpriv)s
|
dropbear_log = %(syslog_authpriv)s
|
||||||
|
|
||||||
|
# There is no sensible generic defaults for syslog log targets, thus
|
||||||
|
# leaving them empty here so that no errors while parsing/interpolating configs
|
||||||
|
syslog_daemon =
|
||||||
|
syslog_ftp =
|
||||||
|
syslog_local0 =
|
||||||
|
syslog_mail_warn =
|
||||||
|
syslog_user =
|
||||||
|
|
||||||
# from /etc/audit/auditd.conf
|
# from /etc/audit/auditd.conf
|
||||||
auditd_log = /var/log/audit/audit.log
|
auditd_log = /var/log/audit/audit.log
|
||||||
|
|
||||||
|
exim_main_log = /var/log/exim/mainlog
|
||||||
|
|
||||||
nginx_error_log = /var/log/nginx/error.log
|
nginx_error_log = /var/log/nginx/error.log
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ apache_error_log = /var/log/apache2/*error.log
|
||||||
|
|
||||||
apache_access_log = /var/log/apache2/*access.log
|
apache_access_log = /var/log/apache2/*access.log
|
||||||
|
|
||||||
|
exim_main_log = /var/log/exim4/mainlog
|
||||||
|
|
||||||
# was in debian squeezy but not in wheezy
|
# was in debian squeezy but not in wheezy
|
||||||
# /etc/proftpd/proftpd.conf (SystemLog)
|
# /etc/proftpd/proftpd.conf (SystemLog)
|
||||||
|
|
|
@ -32,4 +32,6 @@ apache_access_log = /var/log/httpd/*access_log
|
||||||
# proftpd_log = /var/log/proftpd/auth.log
|
# proftpd_log = /var/log/proftpd/auth.log
|
||||||
# Tested and it worked out in /var/log/messages so assuming syslog_ftp for now.
|
# Tested and it worked out in /var/log/messages so assuming syslog_ftp for now.
|
||||||
|
|
||||||
|
exim_main_log = /var/log/exim/main.log
|
||||||
|
|
||||||
mysql_log = /var/lib/mysql/mysqld.log
|
mysql_log = /var/lib/mysql/mysqld.log
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
[INCLUDES]
|
[INCLUDES]
|
||||||
|
|
||||||
before = common-paths.conf
|
before = paths-common.conf
|
||||||
|
|
||||||
after = paths-overrides.local
|
after = paths-overrides.local
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
[INCLUDES]
|
[INCLUDES]
|
||||||
|
|
||||||
before = common-paths.conf
|
before = paths-common.conf
|
||||||
|
|
||||||
after = paths-overrides.local
|
after = paths-overrides.local
|
||||||
|
|
||||||
|
|
|
@ -24,12 +24,13 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import logging, os
|
import os
|
||||||
|
|
||||||
from .configreader import ConfigReader, DefinitionInitConfigReader
|
from .configreader import ConfigReader, DefinitionInitConfigReader
|
||||||
|
from ..helpers import getLogger
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
class ActionReader(DefinitionInitConfigReader):
|
class ActionReader(DefinitionInitConfigReader):
|
||||||
|
|
||||||
|
|
|
@ -21,12 +21,11 @@ __author__ = "Cyril Jaquier, Yaroslav Halchenko"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2013- Yaroslav Halchenko"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2013- Yaroslav Halchenko"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from ..exceptions import UnknownJailException, DuplicateJailException
|
from ..exceptions import UnknownJailException, DuplicateJailException
|
||||||
|
from ..helpers import getLogger
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
##
|
##
|
||||||
# Beautify the output of the client.
|
# Beautify the output of the client.
|
||||||
|
@ -52,6 +51,8 @@ class Beautifier:
|
||||||
try:
|
try:
|
||||||
if inC[0] == "ping":
|
if inC[0] == "ping":
|
||||||
msg = "Server replied: " + response
|
msg = "Server replied: " + response
|
||||||
|
elif inC[0] == "version":
|
||||||
|
msg = response
|
||||||
elif inC[0] == "start":
|
elif inC[0] == "start":
|
||||||
msg = "Jail started"
|
msg = "Jail started"
|
||||||
elif inC[0] == "stop":
|
elif inC[0] == "stop":
|
||||||
|
|
|
@ -24,7 +24,8 @@ __author__ = 'Yaroslav Halhenko'
|
||||||
__copyright__ = 'Copyright (c) 2007 Yaroslav Halchenko'
|
__copyright__ = 'Copyright (c) 2007 Yaroslav Halchenko'
|
||||||
__license__ = 'GPL'
|
__license__ = 'GPL'
|
||||||
|
|
||||||
import logging, os, sys
|
import os, sys
|
||||||
|
from ..helpers import getLogger
|
||||||
|
|
||||||
if sys.version_info >= (3,2): # pragma: no cover
|
if sys.version_info >= (3,2): # pragma: no cover
|
||||||
|
|
||||||
|
@ -60,7 +61,7 @@ else: # pragma: no cover
|
||||||
from ConfigParser import SafeConfigParser
|
from ConfigParser import SafeConfigParser
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
__all__ = ['SafeConfigParserWithIncludes']
|
__all__ = ['SafeConfigParserWithIncludes']
|
||||||
|
|
||||||
|
|
|
@ -24,13 +24,14 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import glob, logging, os
|
import glob, os
|
||||||
from ConfigParser import NoOptionError, NoSectionError
|
from ConfigParser import NoOptionError, NoSectionError
|
||||||
|
|
||||||
from .configparserinc import SafeConfigParserWithIncludes
|
from .configparserinc import SafeConfigParserWithIncludes
|
||||||
|
from ..helpers import getLogger
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
class ConfigReader(SafeConfigParserWithIncludes):
|
class ConfigReader(SafeConfigParserWithIncludes):
|
||||||
|
|
||||||
|
|
|
@ -24,13 +24,12 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from .fail2banreader import Fail2banReader
|
from .fail2banreader import Fail2banReader
|
||||||
from .jailsreader import JailsReader
|
from .jailsreader import JailsReader
|
||||||
|
from ..helpers import getLogger
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
class Configurator:
|
class Configurator:
|
||||||
|
|
||||||
|
|
|
@ -24,12 +24,11 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from .configreader import ConfigReader
|
from .configreader import ConfigReader
|
||||||
|
from ..helpers import getLogger
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
class Fail2banReader(ConfigReader):
|
class Fail2banReader(ConfigReader):
|
||||||
|
|
||||||
|
|
|
@ -24,13 +24,14 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import logging, os, shlex
|
import os, shlex
|
||||||
|
|
||||||
from .configreader import ConfigReader, DefinitionInitConfigReader
|
from .configreader import ConfigReader, DefinitionInitConfigReader
|
||||||
from ..server.action import CommandAction
|
from ..server.action import CommandAction
|
||||||
|
from ..helpers import getLogger
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
class FilterReader(DefinitionInitConfigReader):
|
class FilterReader(DefinitionInitConfigReader):
|
||||||
|
|
||||||
|
|
|
@ -24,15 +24,16 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import logging, re, glob, os.path
|
import re, glob, os.path
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from .configreader import ConfigReader
|
from .configreader import ConfigReader
|
||||||
from .filterreader import FilterReader
|
from .filterreader import FilterReader
|
||||||
from .actionreader import ActionReader
|
from .actionreader import ActionReader
|
||||||
|
from ..helpers import getLogger
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
class JailReader(ConfigReader):
|
class JailReader(ConfigReader):
|
||||||
|
|
||||||
|
|
|
@ -24,13 +24,12 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from .configreader import ConfigReader
|
from .configreader import ConfigReader
|
||||||
from .jailreader import JailReader
|
from .jailreader import JailReader
|
||||||
|
from ..helpers import getLogger
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
class JailsReader(ConfigReader):
|
class JailsReader(ConfigReader):
|
||||||
|
|
||||||
|
|
|
@ -20,9 +20,104 @@
|
||||||
__author__ = "Cyril Jaquier, Arturo 'Buanzo' Busleiman, Yaroslav Halchenko"
|
__author__ = "Cyril Jaquier, Arturo 'Buanzo' Busleiman, Yaroslav Halchenko"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import traceback
|
||||||
|
import re
|
||||||
|
import logging
|
||||||
|
|
||||||
def formatExceptionInfo():
|
def formatExceptionInfo():
|
||||||
""" Consistently format exception information """
|
""" Consistently format exception information """
|
||||||
import sys
|
|
||||||
cla, exc = sys.exc_info()[:2]
|
cla, exc = sys.exc_info()[:2]
|
||||||
return (cla.__name__, str(exc))
|
return (cla.__name__, str(exc))
|
||||||
|
|
||||||
|
#
|
||||||
|
# Following "traceback" functions are adopted from PyMVPA distributed
|
||||||
|
# under MIT/Expat and copyright by PyMVPA developers (i.e. me and
|
||||||
|
# Michael). Hereby I re-license derivative work on these pieces under GPL
|
||||||
|
# to stay in line with the main Fail2Ban license
|
||||||
|
#
|
||||||
|
def mbasename(s):
|
||||||
|
"""Custom function to include directory name if filename is too common
|
||||||
|
|
||||||
|
Also strip .py at the end
|
||||||
|
"""
|
||||||
|
base = os.path.basename(s)
|
||||||
|
if base.endswith('.py'):
|
||||||
|
base = base[:-3]
|
||||||
|
if base in set(['base', '__init__']):
|
||||||
|
base = os.path.basename(os.path.dirname(s)) + '.' + base
|
||||||
|
return base
|
||||||
|
|
||||||
|
class TraceBack(object):
|
||||||
|
"""Customized traceback to be included in debug messages
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, compress=False):
|
||||||
|
"""Initialize TrackBack metric
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
compress : bool
|
||||||
|
if True then prefix common with previous invocation gets
|
||||||
|
replaced with ...
|
||||||
|
"""
|
||||||
|
self.__prev = ""
|
||||||
|
self.__compress = compress
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
ftb = traceback.extract_stack(limit=100)[:-2]
|
||||||
|
entries = [
|
||||||
|
[mbasename(x[0]), os.path.dirname(x[0]), str(x[1])] for x in ftb]
|
||||||
|
entries = [ [e[0], e[2]] for e in entries
|
||||||
|
if not (e[0] in ['unittest', 'logging.__init__']
|
||||||
|
or e[1].endswith('/unittest'))]
|
||||||
|
|
||||||
|
# lets make it more concise
|
||||||
|
entries_out = [entries[0]]
|
||||||
|
for entry in entries[1:]:
|
||||||
|
if entry[0] == entries_out[-1][0]:
|
||||||
|
entries_out[-1][1] += ',%s' % entry[1]
|
||||||
|
else:
|
||||||
|
entries_out.append(entry)
|
||||||
|
sftb = '>'.join(['%s:%s' % (mbasename(x[0]),
|
||||||
|
x[1]) for x in entries_out])
|
||||||
|
if self.__compress:
|
||||||
|
# lets remove part which is common with previous invocation
|
||||||
|
prev_next = sftb
|
||||||
|
common_prefix = os.path.commonprefix((self.__prev, sftb))
|
||||||
|
common_prefix2 = re.sub('>[^>]*$', '', common_prefix)
|
||||||
|
|
||||||
|
if common_prefix2 != "":
|
||||||
|
sftb = '...' + sftb[len(common_prefix2):]
|
||||||
|
self.__prev = prev_next
|
||||||
|
|
||||||
|
return sftb
|
||||||
|
|
||||||
|
class FormatterWithTraceBack(logging.Formatter):
|
||||||
|
"""Custom formatter which expands %(tb) and %(tbc) with tracebacks
|
||||||
|
|
||||||
|
TODO: might need locking in case of compressed tracebacks
|
||||||
|
"""
|
||||||
|
def __init__(self, fmt, *args, **kwargs):
|
||||||
|
logging.Formatter.__init__(self, fmt=fmt, *args, **kwargs)
|
||||||
|
compress = '%(tbc)s' in fmt
|
||||||
|
self._tb = TraceBack(compress=compress)
|
||||||
|
|
||||||
|
def format(self, record):
|
||||||
|
record.tbc = record.tb = self._tb()
|
||||||
|
return logging.Formatter.format(self, record)
|
||||||
|
|
||||||
|
def getLogger(name):
|
||||||
|
"""Get logging.Logger instance with Fail2Ban logger name convention
|
||||||
|
"""
|
||||||
|
if "." in name:
|
||||||
|
name = "fail2ban.%s" % name.rpartition(".")[-1]
|
||||||
|
return logging.getLogger(name)
|
||||||
|
|
||||||
|
def excepthook(exctype, value, traceback):
|
||||||
|
"""Except hook used to log unhandled exceptions to Fail2Ban log
|
||||||
|
"""
|
||||||
|
getLogger("fail2ban").critical(
|
||||||
|
"Unhandled exception in Fail2Ban:", exc_info=True)
|
||||||
|
return sys.__excepthook__(exctype, value, traceback)
|
||||||
|
|
|
@ -38,6 +38,7 @@ protocol = [
|
||||||
["status", "gets the current status of the server"],
|
["status", "gets the current status of the server"],
|
||||||
["ping", "tests if the server is alive"],
|
["ping", "tests if the server is alive"],
|
||||||
["help", "return this output"],
|
["help", "return this output"],
|
||||||
|
["version", "return the server version"],
|
||||||
['', "LOGGING", ""],
|
['', "LOGGING", ""],
|
||||||
["set loglevel <LEVEL>", "sets logging level to <LEVEL>. Levels: CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG"],
|
["set loglevel <LEVEL>", "sets logging level to <LEVEL>. Levels: CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG"],
|
||||||
["get loglevel", "gets the logging level"],
|
["get loglevel", "gets the logging level"],
|
||||||
|
|
|
@ -25,10 +25,11 @@ import logging, os, subprocess, time, signal, tempfile
|
||||||
import threading, re
|
import threading, re
|
||||||
from abc import ABCMeta
|
from abc import ABCMeta
|
||||||
from collections import MutableMapping
|
from collections import MutableMapping
|
||||||
#from subprocess import call
|
|
||||||
|
from ..helpers import getLogger
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
# Create a lock for running system commands
|
# Create a lock for running system commands
|
||||||
_cmd_lock = threading.Lock()
|
_cmd_lock = threading.Lock()
|
||||||
|
@ -68,6 +69,9 @@ class CallingMap(MutableMapping):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.data = dict(*args, **kwargs)
|
self.data = dict(*args, **kwargs)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "%s(%r)" % (self.__class__.__name__, self.data)
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
value = self.data[key]
|
value = self.data[key]
|
||||||
if callable(value):
|
if callable(value):
|
||||||
|
@ -87,6 +91,9 @@ class CallingMap(MutableMapping):
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(self.data)
|
return len(self.data)
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
return self.__class__(self.data.copy())
|
||||||
|
|
||||||
class ActionBase(object):
|
class ActionBase(object):
|
||||||
"""An abstract base class for actions in Fail2Ban.
|
"""An abstract base class for actions in Fail2Ban.
|
||||||
|
|
||||||
|
@ -135,8 +142,7 @@ class ActionBase(object):
|
||||||
def __init__(self, jail, name):
|
def __init__(self, jail, name):
|
||||||
self._jail = jail
|
self._jail = jail
|
||||||
self._name = name
|
self._name = name
|
||||||
self._logSys = logging.getLogger(
|
self._logSys = getLogger("fail2ban.%s" % self.__class__.__name__)
|
||||||
'%s.%s' % (__name__, self.__class__.__name__))
|
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
"""Executed when the jail/action is started.
|
"""Executed when the jail/action is started.
|
||||||
|
@ -194,6 +200,8 @@ class CommandAction(ActionBase):
|
||||||
timeout
|
timeout
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
_escapedTags = set(('matches', 'ipmatches', 'ipjailmatches'))
|
||||||
|
|
||||||
def __init__(self, jail, name):
|
def __init__(self, jail, name):
|
||||||
super(CommandAction, self).__init__(jail, name)
|
super(CommandAction, self).__init__(jail, name)
|
||||||
self.timeout = 60
|
self.timeout = 60
|
||||||
|
@ -351,8 +359,8 @@ class CommandAction(ActionBase):
|
||||||
if not self.executeCmd(stopCmd, self.timeout):
|
if not self.executeCmd(stopCmd, self.timeout):
|
||||||
raise RuntimeError("Error stopping action")
|
raise RuntimeError("Error stopping action")
|
||||||
|
|
||||||
@staticmethod
|
@classmethod
|
||||||
def substituteRecursiveTags(tags):
|
def substituteRecursiveTags(cls, tags):
|
||||||
"""Sort out tag definitions within other tags.
|
"""Sort out tag definitions within other tags.
|
||||||
|
|
||||||
so: becomes:
|
so: becomes:
|
||||||
|
@ -371,8 +379,11 @@ class CommandAction(ActionBase):
|
||||||
within the values recursively replaced.
|
within the values recursively replaced.
|
||||||
"""
|
"""
|
||||||
t = re.compile(r'<([^ >]+)>')
|
t = re.compile(r'<([^ >]+)>')
|
||||||
for tag, value in tags.iteritems():
|
for tag in tags.iterkeys():
|
||||||
value = str(value)
|
if tag in cls._escapedTags:
|
||||||
|
# Escaped so won't match
|
||||||
|
continue
|
||||||
|
value = str(tags[tag])
|
||||||
m = t.search(value)
|
m = t.search(value)
|
||||||
done = []
|
done = []
|
||||||
#logSys.log(5, 'TAG: %s, value: %s' % (tag, value))
|
#logSys.log(5, 'TAG: %s, value: %s' % (tag, value))
|
||||||
|
@ -383,6 +394,9 @@ class CommandAction(ActionBase):
|
||||||
# recursive definitions are bad
|
# recursive definitions are bad
|
||||||
#logSys.log(5, 'recursion fail tag: %s value: %s' % (tag, value) )
|
#logSys.log(5, 'recursion fail tag: %s value: %s' % (tag, value) )
|
||||||
return False
|
return False
|
||||||
|
elif found_tag in cls._escapedTags:
|
||||||
|
# Escaped so won't match
|
||||||
|
continue
|
||||||
else:
|
else:
|
||||||
if tags.has_key(found_tag):
|
if tags.has_key(found_tag):
|
||||||
value = value.replace('<%s>' % found_tag , tags[found_tag])
|
value = value.replace('<%s>' % found_tag , tags[found_tag])
|
||||||
|
@ -441,10 +455,11 @@ class CommandAction(ActionBase):
|
||||||
`query` string with tags replaced.
|
`query` string with tags replaced.
|
||||||
"""
|
"""
|
||||||
string = query
|
string = query
|
||||||
|
aInfo = cls.substituteRecursiveTags(aInfo)
|
||||||
for tag in aInfo:
|
for tag in aInfo:
|
||||||
if "<%s>" % tag in query:
|
if "<%s>" % tag in query:
|
||||||
value = str(aInfo[tag]) # assure string
|
value = str(aInfo[tag]) # assure string
|
||||||
if tag.endswith('matches'):
|
if tag in cls._escapedTags:
|
||||||
# That one needs to be escaped since its content is
|
# That one needs to be escaped since its content is
|
||||||
# out of our control
|
# out of our control
|
||||||
value = cls.escapeTag(value)
|
value = cls.escapeTag(value)
|
||||||
|
|
|
@ -41,9 +41,10 @@ from .banmanager import BanManager
|
||||||
from .jailthread import JailThread
|
from .jailthread import JailThread
|
||||||
from .action import ActionBase, CommandAction, CallingMap
|
from .action import ActionBase, CommandAction, CallingMap
|
||||||
from .mytime import MyTime
|
from .mytime import MyTime
|
||||||
|
from ..helpers import getLogger
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
class Actions(JailThread, Mapping):
|
class Actions(JailThread, Mapping):
|
||||||
"""Handles jail actions.
|
"""Handles jail actions.
|
||||||
|
@ -196,6 +197,8 @@ class Actions(JailThread, Mapping):
|
||||||
if ticket is not None:
|
if ticket is not None:
|
||||||
# Unban the IP.
|
# Unban the IP.
|
||||||
self.__unBan(ticket)
|
self.__unBan(ticket)
|
||||||
|
if self._jail.database is not None:
|
||||||
|
self._jail.database.delBan(self._jail, ticket)
|
||||||
else:
|
else:
|
||||||
raise ValueError("IP %s is not banned" % ip)
|
raise ValueError("IP %s is not banned" % ip)
|
||||||
|
|
||||||
|
@ -215,7 +218,8 @@ class Actions(JailThread, Mapping):
|
||||||
action.start()
|
action.start()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logSys.error("Failed to start jail '%s' action '%s': %s",
|
logSys.error("Failed to start jail '%s' action '%s': %s",
|
||||||
self._jail.name, name, e)
|
self._jail.name, name, e,
|
||||||
|
exc_info=logSys.getEffectiveLevel()<=logging.DEBUG)
|
||||||
while self.active:
|
while self.active:
|
||||||
if not self.idle:
|
if not self.idle:
|
||||||
#logSys.debug(self._jail.name + ": action")
|
#logSys.debug(self._jail.name + ": action")
|
||||||
|
@ -234,7 +238,8 @@ class Actions(JailThread, Mapping):
|
||||||
action.stop()
|
action.stop()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logSys.error("Failed to stop jail '%s' action '%s': %s",
|
logSys.error("Failed to stop jail '%s' action '%s': %s",
|
||||||
self._jail.name, name, e)
|
self._jail.name, name, e,
|
||||||
|
exc_info=logSys.getEffectiveLevel()<=logging.DEBUG)
|
||||||
logSys.debug(self._jail.name + ": action terminated")
|
logSys.debug(self._jail.name + ": action terminated")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -253,32 +258,31 @@ class Actions(JailThread, Mapping):
|
||||||
if ticket != False:
|
if ticket != False:
|
||||||
aInfo = CallingMap()
|
aInfo = CallingMap()
|
||||||
bTicket = BanManager.createBanTicket(ticket)
|
bTicket = BanManager.createBanTicket(ticket)
|
||||||
aInfo["ip"] = bTicket.getIP()
|
ip = bTicket.getIP()
|
||||||
|
aInfo["ip"] = ip
|
||||||
aInfo["failures"] = bTicket.getAttempt()
|
aInfo["failures"] = bTicket.getAttempt()
|
||||||
aInfo["time"] = bTicket.getTime()
|
aInfo["time"] = bTicket.getTime()
|
||||||
aInfo["matches"] = "\n".join(bTicket.getMatches())
|
aInfo["matches"] = "\n".join(bTicket.getMatches())
|
||||||
if self._jail.database is not None:
|
if self._jail.database is not None:
|
||||||
aInfo["ipmatches"] = lambda: "\n".join(
|
aInfo["ipmatches"] = lambda jail=self._jail: "\n".join(
|
||||||
self._jail.database.getBansMerged(
|
jail.database.getBansMerged(ip=ip).getMatches())
|
||||||
ip=bTicket.getIP()).getMatches())
|
aInfo["ipjailmatches"] = lambda jail=self._jail: "\n".join(
|
||||||
aInfo["ipjailmatches"] = lambda: "\n".join(
|
jail.database.getBansMerged(ip=ip, jail=jail).getMatches())
|
||||||
self._jail.database.getBansMerged(
|
aInfo["ipfailures"] = lambda jail=self._jail: \
|
||||||
ip=bTicket.getIP(), jail=self._jail).getMatches())
|
jail.database.getBansMerged(ip=ip).getAttempt()
|
||||||
aInfo["ipfailures"] = lambda: "\n".join(
|
aInfo["ipjailfailures"] = lambda jail=self._jail: \
|
||||||
self._jail.database.getBansMerged(
|
jail.database.getBansMerged(ip=ip, jail=jail).getAttempt()
|
||||||
ip=bTicket.getIP()).getAttempt())
|
|
||||||
aInfo["ipjailfailures"] = lambda: "\n".join(
|
|
||||||
self._jail.database.getBansMerged(
|
|
||||||
ip=bTicket.getIP(), jail=self._jail).getAttempt())
|
|
||||||
if self.__banManager.addBanTicket(bTicket):
|
if self.__banManager.addBanTicket(bTicket):
|
||||||
logSys.notice("[%s] Ban %s" % (self._jail.name, aInfo["ip"]))
|
logSys.notice("[%s] Ban %s" % (self._jail.name, aInfo["ip"]))
|
||||||
for name, action in self._actions.iteritems():
|
for name, action in self._actions.iteritems():
|
||||||
try:
|
try:
|
||||||
action.ban(aInfo)
|
action.ban(aInfo.copy())
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logSys.error(
|
logSys.error(
|
||||||
"Failed to execute ban jail '%s' action '%s': %s",
|
"Failed to execute ban jail '%s' action '%s' "
|
||||||
self._jail.name, name, e)
|
"info '%r': %s",
|
||||||
|
self._jail.name, name, aInfo, e,
|
||||||
|
exc_info=logSys.getEffectiveLevel()<=logging.DEBUG)
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
logSys.notice("[%s] %s already banned" % (self._jail.name,
|
logSys.notice("[%s] %s already banned" % (self._jail.name,
|
||||||
|
@ -321,11 +325,13 @@ class Actions(JailThread, Mapping):
|
||||||
logSys.notice("[%s] Unban %s" % (self._jail.name, aInfo["ip"]))
|
logSys.notice("[%s] Unban %s" % (self._jail.name, aInfo["ip"]))
|
||||||
for name, action in self._actions.iteritems():
|
for name, action in self._actions.iteritems():
|
||||||
try:
|
try:
|
||||||
action.unban(aInfo)
|
action.unban(aInfo.copy())
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logSys.error(
|
logSys.error(
|
||||||
"Failed to execute unban jail '%s' action '%s': %s",
|
"Failed to execute unban jail '%s' action '%s' "
|
||||||
self._jail.name, name, e)
|
"info '%r': %s",
|
||||||
|
self._jail.name, name, aInfo, e,
|
||||||
|
exc_info=logSys.getEffectiveLevel()<=logging.DEBUG)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def status(self):
|
def status(self):
|
||||||
|
|
|
@ -25,12 +25,12 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
from pickle import dumps, loads, HIGHEST_PROTOCOL
|
from pickle import dumps, loads, HIGHEST_PROTOCOL
|
||||||
import asyncore, asynchat, socket, os, logging, sys, traceback, fcntl
|
import asyncore, asynchat, socket, os, sys, traceback, fcntl
|
||||||
|
|
||||||
from .. import helpers
|
from ..helpers import getLogger,formatExceptionInfo
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
if sys.version_info >= (3,):
|
if sys.version_info >= (3,):
|
||||||
# b"" causes SyntaxError in python <= 2.5, so below implements equivalent
|
# b"" causes SyntaxError in python <= 2.5, so below implements equivalent
|
||||||
|
@ -81,7 +81,7 @@ class RequestHandler(asynchat.async_chat):
|
||||||
self.close_when_done()
|
self.close_when_done()
|
||||||
|
|
||||||
def handle_error(self):
|
def handle_error(self):
|
||||||
e1, e2 = helpers.formatExceptionInfo()
|
e1, e2 = formatExceptionInfo()
|
||||||
logSys.error("Unexpected communication error: %s" % str(e2))
|
logSys.error("Unexpected communication error: %s" % str(e2))
|
||||||
logSys.error(traceback.format_exc().splitlines())
|
logSys.error(traceback.format_exc().splitlines())
|
||||||
self.close()
|
self.close()
|
||||||
|
|
|
@ -24,14 +24,14 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import logging
|
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
|
|
||||||
from .ticket import BanTicket
|
from .ticket import BanTicket
|
||||||
from .mytime import MyTime
|
from .mytime import MyTime
|
||||||
|
from ..helpers import getLogger
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
##
|
##
|
||||||
# Banning Manager.
|
# Banning Manager.
|
||||||
|
|
|
@ -21,7 +21,6 @@ __author__ = "Steven Hiscocks"
|
||||||
__copyright__ = "Copyright (c) 2013 Steven Hiscocks"
|
__copyright__ = "Copyright (c) 2013 Steven Hiscocks"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import logging
|
|
||||||
import sys
|
import sys
|
||||||
import shutil, time
|
import shutil, time
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
@ -32,9 +31,10 @@ from threading import Lock
|
||||||
|
|
||||||
from .mytime import MyTime
|
from .mytime import MyTime
|
||||||
from .ticket import FailTicket
|
from .ticket import FailTicket
|
||||||
|
from ..helpers import getLogger
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
if sys.version_info >= (3,):
|
if sys.version_info >= (3,):
|
||||||
sqlite3.register_adapter(
|
sqlite3.register_adapter(
|
||||||
|
@ -368,10 +368,25 @@ class Fail2BanDb(object):
|
||||||
#TODO: Implement data parts once arbitrary match keys completed
|
#TODO: Implement data parts once arbitrary match keys completed
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"INSERT INTO bans(jail, ip, timeofban, data) VALUES(?, ?, ?, ?)",
|
"INSERT INTO bans(jail, ip, timeofban, data) VALUES(?, ?, ?, ?)",
|
||||||
(jail.name, ticket.getIP(), ticket.getTime(),
|
(jail.name, ticket.getIP(), int(round(ticket.getTime())),
|
||||||
{"matches": ticket.getMatches(),
|
{"matches": ticket.getMatches(),
|
||||||
"failures": ticket.getAttempt()}))
|
"failures": ticket.getAttempt()}))
|
||||||
|
|
||||||
|
@commitandrollback
|
||||||
|
def delBan(self, cur, jail, ticket):
|
||||||
|
"""Delete a ban from the database.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
jail : Jail
|
||||||
|
Jail in which the ban has occurred.
|
||||||
|
ticket : BanTicket
|
||||||
|
Ticket of the ban to be removed.
|
||||||
|
"""
|
||||||
|
cur.execute(
|
||||||
|
"DELETE FROM bans WHERE jail = ? AND ip = ? AND timeofban = ?",
|
||||||
|
(jail.name, ticket.getIP(), int(round(ticket.getTime()))))
|
||||||
|
|
||||||
@commitandrollback
|
@commitandrollback
|
||||||
def _getBans(self, cur, jail=None, bantime=None, ip=None):
|
def _getBans(self, cur, jail=None, bantime=None, ip=None):
|
||||||
query = "SELECT ip, timeofban, data FROM bans WHERE 1"
|
query = "SELECT ip, timeofban, data FROM bans WHERE 1"
|
||||||
|
@ -380,7 +395,7 @@ class Fail2BanDb(object):
|
||||||
if jail is not None:
|
if jail is not None:
|
||||||
query += " AND jail=?"
|
query += " AND jail=?"
|
||||||
queryArgs.append(jail.name)
|
queryArgs.append(jail.name)
|
||||||
if bantime is not None:
|
if bantime is not None and bantime >= 0:
|
||||||
query += " AND timeofban > ?"
|
query += " AND timeofban > ?"
|
||||||
queryArgs.append(MyTime.time() - bantime)
|
queryArgs.append(MyTime.time() - bantime)
|
||||||
if ip is not None:
|
if ip is not None:
|
||||||
|
@ -399,7 +414,8 @@ class Fail2BanDb(object):
|
||||||
Jail that the ban belongs to. Default `None`; all jails.
|
Jail that the ban belongs to. Default `None`; all jails.
|
||||||
bantime : int
|
bantime : int
|
||||||
Ban time in seconds, such that bans returned would still be
|
Ban time in seconds, such that bans returned would still be
|
||||||
valid now. Default `None`; no limit.
|
valid now. Negative values are equivalent to `None`.
|
||||||
|
Default `None`; no limit.
|
||||||
ip : str
|
ip : str
|
||||||
IP Address to filter bans by. Default `None`; all IPs.
|
IP Address to filter bans by. Default `None`; all IPs.
|
||||||
|
|
||||||
|
@ -427,7 +443,8 @@ class Fail2BanDb(object):
|
||||||
Jail that the ban belongs to. Default `None`; all jails.
|
Jail that the ban belongs to. Default `None`; all jails.
|
||||||
bantime : int
|
bantime : int
|
||||||
Ban time in seconds, such that bans returned would still be
|
Ban time in seconds, such that bans returned would still be
|
||||||
valid now. Default `None`; no limit.
|
valid now. Negative values are equivalent to `None`.
|
||||||
|
Default `None`; no limit.
|
||||||
ip : str
|
ip : str
|
||||||
IP Address to filter bans by. Default `None`; all IPs.
|
IP Address to filter bans by. Default `None`; all IPs.
|
||||||
|
|
||||||
|
@ -438,7 +455,8 @@ class Fail2BanDb(object):
|
||||||
in a list. When `ip` argument passed, a single `Ticket` is
|
in a list. When `ip` argument passed, a single `Ticket` is
|
||||||
returned.
|
returned.
|
||||||
"""
|
"""
|
||||||
if bantime is None:
|
cacheKey = None
|
||||||
|
if bantime is None or bantime < 0:
|
||||||
cacheKey = (ip, jail)
|
cacheKey = (ip, jail)
|
||||||
if cacheKey in self._bansMergedCache:
|
if cacheKey in self._bansMergedCache:
|
||||||
return self._bansMergedCache[cacheKey]
|
return self._bansMergedCache[cacheKey]
|
||||||
|
@ -468,7 +486,7 @@ class Fail2BanDb(object):
|
||||||
ticket.setAttempt(failures)
|
ticket.setAttempt(failures)
|
||||||
tickets.append(ticket)
|
tickets.append(ticket)
|
||||||
|
|
||||||
if bantime is None:
|
if cacheKey:
|
||||||
self._bansMergedCache[cacheKey] = tickets if ip is None else ticket
|
self._bansMergedCache[cacheKey] = tickets if ip is None else ticket
|
||||||
return tickets if ip is None else ticket
|
return tickets if ip is None else ticket
|
||||||
|
|
||||||
|
|
|
@ -21,13 +21,13 @@ __author__ = "Cyril Jaquier and Fail2Ban Contributors"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import logging
|
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
|
|
||||||
from .datetemplate import DatePatternRegex, DateTai64n, DateEpoch
|
from .datetemplate import DatePatternRegex, DateTai64n, DateEpoch
|
||||||
|
from ..helpers import getLogger
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
class DateDetector(object):
|
class DateDetector(object):
|
||||||
"""Manages one or more date templates to find a date within a log line.
|
"""Manages one or more date templates to find a date within a log line.
|
||||||
|
|
|
@ -25,12 +25,12 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import logging
|
|
||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
|
|
||||||
from .strptime import reGroupDictStrptime, timeRE
|
from .strptime import reGroupDictStrptime, timeRE
|
||||||
|
from ..helpers import getLogger
|
||||||
|
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class DateTemplate(object):
|
class DateTemplate(object):
|
||||||
|
|
|
@ -24,10 +24,10 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import logging
|
from ..helpers import getLogger
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
class FailData:
|
class FailData:
|
||||||
|
|
||||||
|
|
|
@ -29,9 +29,10 @@ import logging
|
||||||
|
|
||||||
from .faildata import FailData
|
from .faildata import FailData
|
||||||
from .ticket import FailTicket
|
from .ticket import FailTicket
|
||||||
|
from ..helpers import getLogger
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
class FailManager:
|
class FailManager:
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ __author__ = "Cyril Jaquier and Fail2Ban Contributors"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2013 Yaroslav Halchenko"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2013 Yaroslav Halchenko"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import logging, re, os, fcntl, sys, locale, codecs
|
import re, os, fcntl, sys, locale, codecs
|
||||||
|
|
||||||
from .failmanager import FailManagerEmpty, FailManager
|
from .failmanager import FailManagerEmpty, FailManager
|
||||||
from .ticket import FailTicket
|
from .ticket import FailTicket
|
||||||
|
@ -31,9 +31,10 @@ from .datetemplate import DatePatternRegex, DateEpoch, DateTai64n
|
||||||
from .mytime import MyTime
|
from .mytime import MyTime
|
||||||
from .failregex import FailRegex, Regex, RegexException
|
from .failregex import FailRegex, Regex, RegexException
|
||||||
from .action import CommandAction
|
from .action import CommandAction
|
||||||
|
from ..helpers import getLogger
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
##
|
##
|
||||||
# Log reader class.
|
# Log reader class.
|
||||||
|
@ -790,8 +791,10 @@ class FileContainer:
|
||||||
try:
|
try:
|
||||||
line = line.decode(self.getEncoding(), 'strict')
|
line = line.decode(self.getEncoding(), 'strict')
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
logSys.warning("Error decoding line from '%s' with '%s': %s" %
|
logSys.warning(
|
||||||
(self.getFileName(), self.getEncoding(), `line`))
|
"Error decoding line from '%s' with '%s'. Continuing "
|
||||||
|
" to process line ignoring invalid characters: %r" %
|
||||||
|
(self.getFileName(), self.getEncoding(), line))
|
||||||
if sys.version_info >= (3,): # In python3, must be decoded
|
if sys.version_info >= (3,): # In python3, must be decoded
|
||||||
line = line.decode(self.getEncoding(), 'ignore')
|
line = line.decode(self.getEncoding(), 'ignore')
|
||||||
return line
|
return line
|
||||||
|
|
|
@ -23,16 +23,17 @@ __author__ = "Cyril Jaquier, Yaroslav Halchenko"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2012 Yaroslav Halchenko"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2012 Yaroslav Halchenko"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import time, logging, fcntl
|
import time, fcntl
|
||||||
|
|
||||||
import gamin
|
import gamin
|
||||||
|
|
||||||
from .failmanager import FailManagerEmpty
|
from .failmanager import FailManagerEmpty
|
||||||
from .filter import FileFilter
|
from .filter import FileFilter
|
||||||
from .mytime import MyTime
|
from .mytime import MyTime
|
||||||
|
from ..helpers import getLogger
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
##
|
##
|
||||||
# Log reader class.
|
# Log reader class.
|
||||||
|
|
|
@ -24,14 +24,15 @@ __author__ = "Cyril Jaquier, Yaroslav Halchenko"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier; 2012 Yaroslav Halchenko"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier; 2012 Yaroslav Halchenko"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import time, logging, os
|
import time, os
|
||||||
|
|
||||||
from .failmanager import FailManagerEmpty
|
from .failmanager import FailManagerEmpty
|
||||||
from .filter import FileFilter
|
from .filter import FileFilter
|
||||||
from .mytime import MyTime
|
from .mytime import MyTime
|
||||||
|
from ..helpers import getLogger
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
##
|
##
|
||||||
# Log reader class.
|
# Log reader class.
|
||||||
|
|
|
@ -32,6 +32,7 @@ import pyinotify
|
||||||
from .failmanager import FailManagerEmpty
|
from .failmanager import FailManagerEmpty
|
||||||
from .filter import FileFilter
|
from .filter import FileFilter
|
||||||
from .mytime import MyTime
|
from .mytime import MyTime
|
||||||
|
from ..helpers import getLogger
|
||||||
|
|
||||||
|
|
||||||
if not hasattr(pyinotify, '__version__') \
|
if not hasattr(pyinotify, '__version__') \
|
||||||
|
@ -48,7 +49,7 @@ except Exception, e:
|
||||||
% str(e))
|
% str(e))
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
##
|
##
|
||||||
# Log reader class.
|
# Log reader class.
|
||||||
|
|
|
@ -22,7 +22,7 @@ __author__ = "Steven Hiscocks"
|
||||||
__copyright__ = "Copyright (c) 2013 Steven Hiscocks"
|
__copyright__ = "Copyright (c) 2013 Steven Hiscocks"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import logging, datetime, time
|
import datetime, time
|
||||||
from distutils.version import LooseVersion
|
from distutils.version import LooseVersion
|
||||||
|
|
||||||
from systemd import journal
|
from systemd import journal
|
||||||
|
@ -32,10 +32,10 @@ if LooseVersion(getattr(journal, '__version__', "0")) < '204':
|
||||||
from .failmanager import FailManagerEmpty
|
from .failmanager import FailManagerEmpty
|
||||||
from .filter import JournalFilter
|
from .filter import JournalFilter
|
||||||
from .mytime import MyTime
|
from .mytime import MyTime
|
||||||
|
from ..helpers import getLogger
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger("fail2ban.filter")
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
##
|
##
|
||||||
# Journal reader class.
|
# Journal reader class.
|
||||||
|
@ -167,8 +167,9 @@ class FilterSystemd(JournalFilter): # pragma: systemd no cover
|
||||||
logelements.append(logentry['_HOSTNAME'])
|
logelements.append(logentry['_HOSTNAME'])
|
||||||
if logentry.get('SYSLOG_IDENTIFIER'):
|
if logentry.get('SYSLOG_IDENTIFIER'):
|
||||||
logelements.append(logentry['SYSLOG_IDENTIFIER'])
|
logelements.append(logentry['SYSLOG_IDENTIFIER'])
|
||||||
if logentry.get('_PID'):
|
if logentry.get('SYSLOG_PID') or logentry.get('_PID'):
|
||||||
logelements[-1] += ("[%i]" % logentry['_PID'])
|
logelements[-1] += ("[%i]" % logentry.get(
|
||||||
|
'SYSLOG_PID', logentry['_PID']))
|
||||||
logelements[-1] += ":"
|
logelements[-1] += ":"
|
||||||
elif logentry.get('_COMM'):
|
elif logentry.get('_COMM'):
|
||||||
logelements.append(logentry['_COMM'])
|
logelements.append(logentry['_COMM'])
|
||||||
|
@ -212,6 +213,12 @@ class FilterSystemd(JournalFilter): # pragma: systemd no cover
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
|
||||||
|
if not self.getJournalMatch():
|
||||||
|
logSys.notice(
|
||||||
|
"Jail started without 'journalmatch' set. "
|
||||||
|
"Jail regexs will be checked against all journal entries, "
|
||||||
|
"which is not advised for performance reasons.")
|
||||||
|
|
||||||
# Seek to now - findtime in journal
|
# Seek to now - findtime in journal
|
||||||
start_time = datetime.datetime.now() - \
|
start_time = datetime.datetime.now() - \
|
||||||
datetime.timedelta(seconds=int(self.getFindTime()))
|
datetime.timedelta(seconds=int(self.getFindTime()))
|
||||||
|
|
|
@ -26,9 +26,10 @@ __license__ = "GPL"
|
||||||
import Queue, logging
|
import Queue, logging
|
||||||
|
|
||||||
from .actions import Actions
|
from .actions import Actions
|
||||||
|
from ..helpers import getLogger
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
class Jail:
|
class Jail:
|
||||||
"""Fail2Ban jail, which manages a filter and associated actions.
|
"""Fail2Ban jail, which manages a filter and associated actions.
|
||||||
|
|
|
@ -24,9 +24,12 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
import sys
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from abc import abstractproperty, abstractmethod
|
from abc import abstractproperty, abstractmethod
|
||||||
|
|
||||||
|
from ..helpers import excepthook
|
||||||
|
|
||||||
class JailThread(Thread):
|
class JailThread(Thread):
|
||||||
"""Abstract class for threading elements in Fail2Ban.
|
"""Abstract class for threading elements in Fail2Ban.
|
||||||
|
|
||||||
|
@ -53,6 +56,16 @@ class JailThread(Thread):
|
||||||
## The time the thread sleeps in the loop.
|
## The time the thread sleeps in the loop.
|
||||||
self.sleeptime = 1
|
self.sleeptime = 1
|
||||||
|
|
||||||
|
# excepthook workaround for threads, derived from:
|
||||||
|
# http://bugs.python.org/issue1230540#msg91244
|
||||||
|
run = self.run
|
||||||
|
def run_with_except_hook(*args, **kwargs):
|
||||||
|
try:
|
||||||
|
run(*args, **kwargs)
|
||||||
|
except:
|
||||||
|
excepthook(*sys.exc_info())
|
||||||
|
self.run = run_with_except_hook
|
||||||
|
|
||||||
@abstractproperty
|
@abstractproperty
|
||||||
def status(self): # pragma: no cover - abstract
|
def status(self): # pragma: no cover - abstract
|
||||||
"""Abstract - Should provide status information.
|
"""Abstract - Should provide status information.
|
||||||
|
|
|
@ -32,9 +32,10 @@ from .filter import FileFilter, JournalFilter
|
||||||
from .transmitter import Transmitter
|
from .transmitter import Transmitter
|
||||||
from .asyncserver import AsyncServer, AsyncServerException
|
from .asyncserver import AsyncServer, AsyncServerException
|
||||||
from .. import version
|
from .. import version
|
||||||
|
from ..helpers import getLogger, excepthook
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from .database import Fail2BanDb
|
from .database import Fail2BanDb
|
||||||
|
@ -69,6 +70,9 @@ class Server:
|
||||||
signal.signal(signal.SIGTERM, self.__sigTERMhandler)
|
signal.signal(signal.SIGTERM, self.__sigTERMhandler)
|
||||||
signal.signal(signal.SIGINT, self.__sigTERMhandler)
|
signal.signal(signal.SIGINT, self.__sigTERMhandler)
|
||||||
|
|
||||||
|
# Ensure unhandled exceptions are logged
|
||||||
|
sys.excepthook = excepthook
|
||||||
|
|
||||||
# First set the mask to only allow access to owner
|
# First set the mask to only allow access to owner
|
||||||
os.umask(0077)
|
os.umask(0077)
|
||||||
if self.__daemon: # pragma: no cover
|
if self.__daemon: # pragma: no cover
|
||||||
|
@ -335,7 +339,7 @@ class Server:
|
||||||
def setLogLevel(self, value):
|
def setLogLevel(self, value):
|
||||||
try:
|
try:
|
||||||
self.__loggingLock.acquire()
|
self.__loggingLock.acquire()
|
||||||
logging.getLogger(__name__).parent.parent.setLevel(
|
getLogger("fail2ban").setLevel(
|
||||||
getattr(logging, value.upper()))
|
getattr(logging, value.upper()))
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
raise ValueError("Invalid log level")
|
raise ValueError("Invalid log level")
|
||||||
|
@ -367,7 +371,7 @@ class Server:
|
||||||
try:
|
try:
|
||||||
self.__loggingLock.acquire()
|
self.__loggingLock.acquire()
|
||||||
# set a format which is simpler for console use
|
# set a format which is simpler for console use
|
||||||
formatter = logging.Formatter("%(asctime)s %(name)-16s[%(process)d]: %(levelname)-7s %(message)s")
|
formatter = logging.Formatter("%(asctime)s %(name)-24s[%(process)d]: %(levelname)-7s %(message)s")
|
||||||
if target == "SYSLOG":
|
if target == "SYSLOG":
|
||||||
# Syslog daemons already add date to the message.
|
# Syslog daemons already add date to the message.
|
||||||
formatter = logging.Formatter("%(name)s[%(process)d]: %(levelname)s %(message)s")
|
formatter = logging.Formatter("%(name)s[%(process)d]: %(levelname)s %(message)s")
|
||||||
|
@ -388,7 +392,7 @@ class Server:
|
||||||
return False
|
return False
|
||||||
# Removes previous handlers -- in reverse order since removeHandler
|
# Removes previous handlers -- in reverse order since removeHandler
|
||||||
# alter the list in-place and that can confuses the iterable
|
# alter the list in-place and that can confuses the iterable
|
||||||
logger = logging.getLogger(__name__).parent.parent
|
logger = getLogger("fail2ban")
|
||||||
for handler in logger.handlers[::-1]:
|
for handler in logger.handlers[::-1]:
|
||||||
# Remove the handler.
|
# Remove the handler.
|
||||||
logger.removeHandler(handler)
|
logger.removeHandler(handler)
|
||||||
|
@ -425,7 +429,7 @@ class Server:
|
||||||
|
|
||||||
def flushLogs(self):
|
def flushLogs(self):
|
||||||
if self.__logTarget not in ['STDERR', 'STDOUT', 'SYSLOG']:
|
if self.__logTarget not in ['STDERR', 'STDOUT', 'SYSLOG']:
|
||||||
for handler in logging.getLogger(__name__).parent.parent.handlers:
|
for handler in getLogger("fail2ban").handlers:
|
||||||
try:
|
try:
|
||||||
handler.doRollover()
|
handler.doRollover()
|
||||||
logSys.info("rollover performed on %s" % self.__logTarget)
|
logSys.info("rollover performed on %s" % self.__logTarget)
|
||||||
|
@ -434,7 +438,7 @@ class Server:
|
||||||
logSys.info("flush performed on %s" % self.__logTarget)
|
logSys.info("flush performed on %s" % self.__logTarget)
|
||||||
return "rolled over"
|
return "rolled over"
|
||||||
else:
|
else:
|
||||||
for handler in logging.getLogger(__name__).parent.parent.handlers:
|
for handler in getLogger("fail2ban").handlers:
|
||||||
handler.flush()
|
handler.flush()
|
||||||
logSys.info("flush performed on %s" % self.__logTarget)
|
logSys.info("flush performed on %s" % self.__logTarget)
|
||||||
return "flushed"
|
return "flushed"
|
||||||
|
@ -523,11 +527,19 @@ class Server:
|
||||||
except (AttributeError, ValueError):
|
except (AttributeError, ValueError):
|
||||||
maxfd = 256 # default maximum
|
maxfd = 256 # default maximum
|
||||||
|
|
||||||
|
# urandom should not be closed in Python 3.4.0. Fixed in 3.4.1
|
||||||
|
# http://bugs.python.org/issue21207
|
||||||
|
if sys.version_info[0:3] == (3, 4, 0): # pragma: no cover
|
||||||
|
urandom_fd = os.open("/dev/urandom", os.O_RDONLY)
|
||||||
for fd in range(0, maxfd):
|
for fd in range(0, maxfd):
|
||||||
try:
|
try:
|
||||||
|
if not os.path.sameopenfile(urandom_fd, fd):
|
||||||
os.close(fd)
|
os.close(fd)
|
||||||
except OSError: # ERROR (ignore)
|
except OSError: # ERROR (ignore)
|
||||||
pass
|
pass
|
||||||
|
os.close(urandom_fd)
|
||||||
|
else:
|
||||||
|
os.closerange(0, maxfd)
|
||||||
|
|
||||||
# Redirect the standard file descriptors to /dev/null.
|
# Redirect the standard file descriptors to /dev/null.
|
||||||
os.open("/dev/null", os.O_RDONLY) # standard input (0)
|
os.open("/dev/null", os.O_RDONLY) # standard input (0)
|
||||||
|
|
|
@ -190,5 +190,5 @@ def reGroupDictStrptime(found_dict):
|
||||||
if gmtoff is not None:
|
if gmtoff is not None:
|
||||||
return calendar.timegm(date_result.utctimetuple())
|
return calendar.timegm(date_result.utctimetuple())
|
||||||
else:
|
else:
|
||||||
return time.mktime(date_result.utctimetuple())
|
return time.mktime(date_result.timetuple())
|
||||||
|
|
||||||
|
|
|
@ -24,10 +24,10 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import logging
|
from ..helpers import getLogger
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
class Ticket:
|
class Ticket:
|
||||||
|
|
||||||
|
|
|
@ -24,11 +24,14 @@ __author__ = "Cyril Jaquier"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import logging, time
|
import time
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from ..helpers import getLogger
|
||||||
|
from .. import version
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
class Transmitter:
|
class Transmitter:
|
||||||
|
|
||||||
|
@ -101,6 +104,8 @@ class Transmitter:
|
||||||
return self.__commandGet(command[1:])
|
return self.__commandGet(command[1:])
|
||||||
elif command[0] == "status":
|
elif command[0] == "status":
|
||||||
return self.status(command[1:])
|
return self.status(command[1:])
|
||||||
|
elif command[0] == "version":
|
||||||
|
return version.version
|
||||||
raise Exception("Invalid command")
|
raise Exception("Invalid command")
|
||||||
|
|
||||||
def __commandSet(self, command):
|
def __commandSet(self, command):
|
||||||
|
|
|
@ -29,6 +29,7 @@ import os
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
from ..server.actions import Actions
|
from ..server.actions import Actions
|
||||||
|
from ..server.ticket import FailTicket
|
||||||
from .dummyjail import DummyJail
|
from .dummyjail import DummyJail
|
||||||
from .utils import LogCaptureTestCase
|
from .utils import LogCaptureTestCase
|
||||||
|
|
||||||
|
@ -140,3 +141,28 @@ class ExecuteActions(LogCaptureTestCase):
|
||||||
self.__actions.stop()
|
self.__actions.stop()
|
||||||
self.__actions.join()
|
self.__actions.join()
|
||||||
self.assertTrue(self._is_logged("Failed to stop"))
|
self.assertTrue(self._is_logged("Failed to stop"))
|
||||||
|
|
||||||
|
def testBanActionsAInfo(self):
|
||||||
|
# Action which deletes IP address from aInfo
|
||||||
|
self.__actions.add(
|
||||||
|
"action1",
|
||||||
|
os.path.join(TEST_FILES_DIR, "action.d/action_modifyainfo.py"),
|
||||||
|
{})
|
||||||
|
self.__actions.add(
|
||||||
|
"action2",
|
||||||
|
os.path.join(TEST_FILES_DIR, "action.d/action_modifyainfo.py"),
|
||||||
|
{})
|
||||||
|
self.__jail.putFailTicket(FailTicket("1.2.3.4", 0))
|
||||||
|
self.__actions._Actions__checkBan()
|
||||||
|
# Will fail if modification of aInfo from first action propagates
|
||||||
|
# to second action, as both delete same key
|
||||||
|
self.assertFalse(self._is_logged("Failed to execute ban"))
|
||||||
|
self.assertTrue(self._is_logged("action1 ban deleted aInfo IP"))
|
||||||
|
self.assertTrue(self._is_logged("action2 ban deleted aInfo IP"))
|
||||||
|
|
||||||
|
self.__actions._Actions__flushBan()
|
||||||
|
# Will fail if modification of aInfo from first action propagates
|
||||||
|
# to second action, as both delete same key
|
||||||
|
self.assertFalse(self._is_logged("Failed to execute unban"))
|
||||||
|
self.assertTrue(self._is_logged("action1 unban deleted aInfo IP"))
|
||||||
|
self.assertTrue(self._is_logged("action2 unban deleted aInfo IP"))
|
||||||
|
|
|
@ -100,17 +100,24 @@ class CommandActionTest(LogCaptureTestCase):
|
||||||
{'ipjailmatches': "some >char< should \< be[ escap}ed&\n"}),
|
{'ipjailmatches': "some >char< should \< be[ escap}ed&\n"}),
|
||||||
"some \\>char\\< should \\\\\\< be\\[ escap\\}ed\\&\n")
|
"some \\>char\\< should \\\\\\< be\\[ escap\\}ed\\&\n")
|
||||||
|
|
||||||
|
|
||||||
|
# Recursive
|
||||||
|
aInfo["ABC"] = "<xyz>"
|
||||||
|
self.assertEqual(
|
||||||
|
self.__action.replaceTag("Text <xyz> text <ABC> ABC", aInfo),
|
||||||
|
"Text 890 text 890 ABC")
|
||||||
|
|
||||||
# Callable
|
# Callable
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.__action.replaceTag("09 <callme> 11",
|
self.__action.replaceTag("09 <matches> 11",
|
||||||
CallingMap(callme=lambda: str(10))),
|
CallingMap(matches=lambda: str(10))),
|
||||||
"09 10 11")
|
"09 10 11")
|
||||||
|
|
||||||
# As tag not present, therefore callable should not be called
|
# As tag not present, therefore callable should not be called
|
||||||
# Will raise ValueError if it is
|
# Will raise ValueError if it is
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.__action.replaceTag("abc",
|
self.__action.replaceTag("abc",
|
||||||
CallingMap(callme=lambda: int("a"))), "abc")
|
CallingMap(matches=lambda: int("a"))), "abc")
|
||||||
|
|
||||||
def testExecuteActionBan(self):
|
def testExecuteActionBan(self):
|
||||||
self.__action.actionstart = "touch /tmp/fail2ban.test"
|
self.__action.actionstart = "touch /tmp/fail2ban.test"
|
||||||
|
|
|
@ -173,14 +173,25 @@ class DatabaseTest(unittest.TestCase):
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
isinstance(self.db.getBans(jail=self.jail)[0], FailTicket))
|
isinstance(self.db.getBans(jail=self.jail)[0], FailTicket))
|
||||||
|
|
||||||
|
def testDelBan(self):
|
||||||
|
self.testAddBan()
|
||||||
|
ticket = self.db.getBans(jail=self.jail)[0]
|
||||||
|
self.db.delBan(self.jail, ticket)
|
||||||
|
self.assertEqual(len(self.db.getBans(jail=self.jail)), 0)
|
||||||
|
|
||||||
def testGetBansWithTime(self):
|
def testGetBansWithTime(self):
|
||||||
if Fail2BanDb is None: # pragma: no cover
|
if Fail2BanDb is None: # pragma: no cover
|
||||||
return
|
return
|
||||||
self.testAddJail()
|
self.testAddJail()
|
||||||
ticket = FailTicket("127.0.0.1", MyTime.time() - 40, ["abc\n"])
|
self.db.addBan(
|
||||||
self.db.addBan(self.jail, ticket)
|
self.jail, FailTicket("127.0.0.1", MyTime.time() - 60, ["abc\n"]))
|
||||||
|
self.db.addBan(
|
||||||
|
self.jail, FailTicket("127.0.0.1", MyTime.time() - 40, ["abc\n"]))
|
||||||
self.assertEqual(len(self.db.getBans(jail=self.jail,bantime=50)), 1)
|
self.assertEqual(len(self.db.getBans(jail=self.jail,bantime=50)), 1)
|
||||||
self.assertEqual(len(self.db.getBans(jail=self.jail,bantime=20)), 0)
|
self.assertEqual(len(self.db.getBans(jail=self.jail,bantime=20)), 0)
|
||||||
|
# Negative values are for persistent bans, and such all bans should
|
||||||
|
# be returned
|
||||||
|
self.assertEqual(len(self.db.getBans(jail=self.jail,bantime=-1)), 2)
|
||||||
|
|
||||||
def testGetBansMerged(self):
|
def testGetBansMerged(self):
|
||||||
if Fail2BanDb is None: # pragma: no cover
|
if Fail2BanDb is None: # pragma: no cover
|
||||||
|
@ -251,6 +262,10 @@ class DatabaseTest(unittest.TestCase):
|
||||||
self.assertEqual(len(tickets), 1)
|
self.assertEqual(len(tickets), 1)
|
||||||
tickets = self.db.getBansMerged(bantime=5)
|
tickets = self.db.getBansMerged(bantime=5)
|
||||||
self.assertEqual(len(tickets), 0)
|
self.assertEqual(len(tickets), 0)
|
||||||
|
# Negative values are for persistent bans, and such all bans should
|
||||||
|
# be returned
|
||||||
|
tickets = self.db.getBansMerged(bantime=-1)
|
||||||
|
self.assertEqual(len(tickets), 2)
|
||||||
|
|
||||||
def testPurge(self):
|
def testPurge(self):
|
||||||
if Fail2BanDb is None: # pragma: no cover
|
if Fail2BanDb is None: # pragma: no cover
|
||||||
|
|
|
@ -131,7 +131,7 @@ class DateDetectorTest(unittest.TestCase):
|
||||||
# see https://github.com/fail2ban/fail2ban/pull/130
|
# see https://github.com/fail2ban/fail2ban/pull/130
|
||||||
# yoh: unfortunately this test is not really effective to reproduce the
|
# yoh: unfortunately this test is not really effective to reproduce the
|
||||||
# situation but left in place to assure consistent behavior
|
# situation but left in place to assure consistent behavior
|
||||||
mu = time.mktime(datetime.datetime(2012, 10, 11, 2, 37, 17).utctimetuple())
|
mu = time.mktime(datetime.datetime(2012, 10, 11, 2, 37, 17).timetuple())
|
||||||
logdate = self.__datedetector.getTime('2012/10/11 02:37:17 [error] 18434#0')
|
logdate = self.__datedetector.getTime('2012/10/11 02:37:17 [error] 18434#0')
|
||||||
self.assertNotEqual(logdate, None)
|
self.assertNotEqual(logdate, None)
|
||||||
( logTime, logMatch ) = logdate
|
( logTime, logMatch ) = logdate
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
|
||||||
|
from fail2ban.server.action import ActionBase
|
||||||
|
|
||||||
|
class TestAction(ActionBase):
|
||||||
|
|
||||||
|
def ban(self, aInfo):
|
||||||
|
del aInfo['ip']
|
||||||
|
self._logSys.info("%s ban deleted aInfo IP", self._name)
|
||||||
|
|
||||||
|
def unban(self, aInfo):
|
||||||
|
del aInfo['ip']
|
||||||
|
self._logSys.info("%s unban deleted aInfo IP", self._name)
|
||||||
|
|
||||||
|
Action = TestAction
|
|
@ -1,5 +1,9 @@
|
||||||
# failJSON: { "time": "2005-04-10T03:47:57", "match": true , "host": "1.2.3.4" }
|
# failJSON: { "time": "2005-04-10T03:47:57", "match": true , "host": "1.2.3.4" }
|
||||||
Apr 10 03:47:57 web courieresmtpd: error,relay=::ffff:1.2.3.4,ident=tmf,from=<tmf@example.com>,to=<mailman-subscribe@example.com>: 550 User unknown.
|
Apr 10 03:47:57 web courieresmtpd: error,relay=::ffff:1.2.3.4,ident=tmf,from=<tmf@example.com>,to=<mailman-subscribe@example.com>: 550 User unknown.
|
||||||
|
# failJSON: { "time": "2005-07-03T23:07:20", "match": true , "host": "1.2.3.4" }
|
||||||
|
Jul 3 23:07:20 szerver courieresmtpd: error,relay=::ffff:1.2.3.4,msg="535 Authentication failed.",cmd: YWRvYmVhZG9iZQ==
|
||||||
|
# failJSON: { "time": "2005-07-04T18:39:39", "match": true , "host": "1.2.3.4" }
|
||||||
|
Jul 4 18:39:39 mail courieresmtpd: error,relay=::ffff:1.2.3.4,from=<picaro@astroboymail.com>,to=<user@update.net>: 550 User <benny> unknown
|
||||||
# failJSON: { "time": "2005-07-06T03:42:28", "match": true , "host": "1.2.3.4" }
|
# failJSON: { "time": "2005-07-06T03:42:28", "match": true , "host": "1.2.3.4" }
|
||||||
Jul 6 03:42:28 whistler courieresmtpd: error,relay=::ffff:1.2.3.4,from=<>,to=<admin at memcpy>: 550 User unknown.
|
Jul 6 03:42:28 whistler courieresmtpd: error,relay=::ffff:1.2.3.4,from=<>,to=<admin at memcpy>: 550 User unknown.
|
||||||
# failJSON: { "time": "2004-11-21T23:16:17", "match": true , "host": "1.2.3.4" }
|
# failJSON: { "time": "2004-11-21T23:16:17", "match": true , "host": "1.2.3.4" }
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
# failJSON: { "time": "2005-01-04T21:51:05", "match": true , "host": "127.0.0.1" }
|
# failJSON: { "time": "2005-01-04T21:51:05", "match": true , "host": "127.0.0.1" }
|
||||||
Jan 4 21:51:05 hostname cyrus/imap[5355]: badlogin: localhost.localdomain [127.0.0.1] plaintext cyrus@localdomain SASL(-13): authentication failure: checkpass failed
|
Jan 4 21:51:05 hostname cyrus/imap[5355]: badlogin: localhost.localdomain [127.0.0.1] plaintext cyrus@localdomain SASL(-13): authentication failure: checkpass failed
|
||||||
|
# failJSON: { "time": "2005-01-04T21:51:05", "match": true , "host": "127.0.0.1", "desc": "For secure imaps" }
|
||||||
|
Jan 4 21:51:05 hostname cyrus/imaps[5355]: badlogin: localhost.localdomain [127.0.0.1] plaintext cyrus@localdomain SASL(-13): authentication failure: checkpass failed
|
||||||
# failJSON: { "time": "2005-02-20T17:23:32", "match": true , "host": "198.51.100.23" }
|
# failJSON: { "time": "2005-02-20T17:23:32", "match": true , "host": "198.51.100.23" }
|
||||||
Feb 20 17:23:32 domain cyrus/pop3[18635]: badlogin: localhost [198.51.100.23] plaintext administrator SASL(-13): authentication failure: checkpass failed
|
Feb 20 17:23:32 domain cyrus/pop3[18635]: badlogin: localhost [198.51.100.23] plaintext administrator SASL(-13): authentication failure: checkpass failed
|
||||||
# failJSON: { "time": "2005-02-20T17:23:32", "match": true , "host": "1.2.3.4" }
|
# failJSON: { "time": "2005-02-20T17:23:32", "match": true , "host": "1.2.3.4" }
|
||||||
|
@ -10,4 +12,7 @@ Jun 8 18:11:13 lampserver imap[4480]: badlogin: example.com [198.51.100.45] DIGE
|
||||||
Dec 21 10:01:57 hostname imapd[18454]: badlogin: example.com [198.51.100.57] CRAM-MD5 [SASL(-13): authentication failure: incorrect digest response]
|
Dec 21 10:01:57 hostname imapd[18454]: badlogin: example.com [198.51.100.57] CRAM-MD5 [SASL(-13): authentication failure: incorrect digest response]
|
||||||
# failJSON: { "time": "2004-12-30T16:03:27", "match": true , "host": "1.2.3.4" }
|
# failJSON: { "time": "2004-12-30T16:03:27", "match": true , "host": "1.2.3.4" }
|
||||||
Dec 30 16:03:27 somehost imapd[2517]: badlogin: local-somehost[1.2.3.4] OTP [SASL(-13): authentication failure: External SSF not good enough]
|
Dec 30 16:03:27 somehost imapd[2517]: badlogin: local-somehost[1.2.3.4] OTP [SASL(-13): authentication failure: External SSF not good enough]
|
||||||
|
# failJSON: { "time": "2005-07-17T22:55:56", "match": true , "host": "1.2.3.4" }
|
||||||
|
Jul 17 22:55:56 derry cyrus/imaps[7568]: badlogin: serafinat.xxxxxx [1.2.3.4] plain [SASL(-13): user not found: user: pressy@derry property: cmusaslsecretPLAIN not found in sasldb]
|
||||||
|
# failJSON: { "time": "2005-07-18T16:46:42", "match": true , "host": "1.2.3.4" }
|
||||||
|
Jul 18 16:46:42 derry cyrus/imaps[27449]: badlogin: serafinat.xxxxxx [1.2.3.4] PLAIN [SASL(-13): user not found: Password verification failed]
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
# failJSON: { "time": "2014-07-02T00:17:45", "match": true , "host": "3.2.1.4" }
|
||||||
|
2014:07:02-00:17:45: '3.2.1.4' 2 failed login attempts. Account 'test'
|
||||||
|
|
||||||
|
# failJSON: { "time": "2014-07-02T13:07:40", "match": true , "host": "40.40.123.231" }
|
||||||
|
2014:07:02-13:07:40: '40.40.123.231' 13 failed login attempts. Account 'admin'
|
||||||
|
|
||||||
|
# failJSON: { "time": "2014-07-02T13:07:50", "match": true , "host": "40.40.123.231" }
|
||||||
|
2014:07:02-13:07:50: '40.40.123.231' 5 failed login attempt. Invalid account 'user%2Ename'
|
||||||
|
|
||||||
|
# failJSON: { "time": "2014-07-02T13:28:39", "match": false , "host": "12.12.123.231" }
|
||||||
|
2014:07:02-13:28:39: '12.12.123.231' successful login to 'nobody' after 1 attempts
|
||||||
|
|
||||||
|
# failJSON: { "time": "2014-07-02T13:29:38", "match": true , "host": "1.2.3.4" }
|
||||||
|
2014:07:02-13:29:38: '1.2.3.4' 2 failed login attempts. Account 'user' via 'admin'
|
|
@ -1,12 +1,12 @@
|
||||||
# failJSON: { "time": "2010-09-16T06:51:00", "match": true , "host": "80.187.101.33" }
|
# failJSON: { "time": "2010-09-16T07:51:00", "match": true , "host": "80.187.101.33" }
|
||||||
@400000004c91b044077a9e94 imap-login: Info: Aborted login (auth failed, 1 attempts): user=<martin@waschbuesch.de>, method=CRAM-MD5, rip=80.187.101.33, lip=80.254.129.240, TLS
|
@400000004c91b044077a9e94 imap-login: Info: Aborted login (auth failed, 1 attempts): user=<martin@waschbuesch.de>, method=CRAM-MD5, rip=80.187.101.33, lip=80.254.129.240, TLS
|
||||||
|
|
||||||
# failJSON: { "time": "2010-09-16T06:51:00", "match": true , "host": "176.61.140.224" }
|
# failJSON: { "time": "2010-09-16T07:51:00", "match": true , "host": "176.61.140.224" }
|
||||||
@400000004c91b044077a9e94 dovecot-auth: pam_unix(dovecot:auth): authentication failure; logname= uid=0 euid=0 tty=dovecot ruser=web rhost=176.61.140.224
|
@400000004c91b044077a9e94 dovecot-auth: pam_unix(dovecot:auth): authentication failure; logname= uid=0 euid=0 tty=dovecot ruser=web rhost=176.61.140.224
|
||||||
# Above example with injected rhost into ruser -- should not match for 1.2.3.4
|
# Above example with injected rhost into ruser -- should not match for 1.2.3.4
|
||||||
# failJSON: { "time": "2010-09-16T06:51:00", "match": true , "host": "192.0.43.10" }
|
# failJSON: { "time": "2010-09-16T07:51:00", "match": true , "host": "192.0.43.10" }
|
||||||
@400000004c91b044077a9e94 dovecot-auth: pam_unix(dovecot:auth): authentication failure; logname= uid=0 euid=0 tty=dovecot ruser=rhost=1.2.3.4 rhost=192.0.43.10
|
@400000004c91b044077a9e94 dovecot-auth: pam_unix(dovecot:auth): authentication failure; logname= uid=0 euid=0 tty=dovecot ruser=rhost=1.2.3.4 rhost=192.0.43.10
|
||||||
# failJSON: { "time": "2010-09-16T06:51:00", "match": true , "host": "176.61.140.225" }
|
# failJSON: { "time": "2010-09-16T07:51:00", "match": true , "host": "176.61.140.225" }
|
||||||
@400000004c91b044077a9e94 dovecot-auth: pam_unix(dovecot:auth): authentication failure; logname= uid=0 euid=0 tty=dovecot ruser=root rhost=176.61.140.225 user=root
|
@400000004c91b044077a9e94 dovecot-auth: pam_unix(dovecot:auth): authentication failure; logname= uid=0 euid=0 tty=dovecot ruser=root rhost=176.61.140.225 user=root
|
||||||
|
|
||||||
# failJSON: { "time": "2004-12-12T11:19:11", "match": true , "host": "190.210.136.21" }
|
# failJSON: { "time": "2004-12-12T11:19:11", "match": true , "host": "190.210.136.21" }
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
# failJSON: { "time": "2005-04-16T21:05:29", "match": true , "host": "69.93.127.111" }
|
||||||
|
[PDT Apr 16 21:05:29] error : Warning: Client '69.93.127.111' supplied unknown user 'foo' accessing monit httpd
|
||||||
|
|
||||||
|
# failJSON: { "time": "2005-04-16T20:59:33", "match": true , "host": "97.113.189.111" }
|
||||||
|
[PDT Apr 16 20:59:33] error : Warning: Client '97.113.189.111' supplied wrong password for user 'admin' accessing monit httpd
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
# CONFIGURATION REQUIREMENTS FOR ORACLE IMS v6.3 and ABOVE:
|
||||||
|
#
|
||||||
|
# In OPTION.DAT you must have LOG_FORMAT=4 and
|
||||||
|
# bit 5 of LOG_CONNECTION must be set.
|
||||||
|
#
|
||||||
|
# Many of these sub-fields are optional and can be turned on and off
|
||||||
|
# by the system manager. We need the "tr" field
|
||||||
|
# (transport information (present if bit 5 of LOG_CONNECTION is
|
||||||
|
# set and transport information is available)).
|
||||||
|
# "di" should be there by default if you have LOG_FORMAT=4.
|
||||||
|
#
|
||||||
|
# failJSON: { "time": "2014-06-02T22:02:13", "match": false , "host": "23.122.129.179" }
|
||||||
|
<co ts="2014-06-02T22:02:13.94" pi="72a9.3b4.3774" sc="tcp_submit" dr="+" ac="U" tr="TCP|192.245.12.223|465|23.122.129.179|60766" ap="SMTP/TLS-128-RC4" mi="Authentication successful - switched to channel tcp_submit" us="jaugustine@example.org" di="235 2.7.0 LOGIN authentication successful."/>
|
||||||
|
# failJSON: { "time": "2014-06-02T16:06:33", "match": true , "host": "89.96.245.78" }
|
||||||
|
<co ts="2014-06-02T16:06:33.99" pi="72aa.17f0.25622" sc="tcp_local" dr="+" ac="U" tr="TCP|192.245.12.223|25|89.96.245.78|4299" ap="SMTP" mi="Bad password" us="nic@transcend.com" di="535 5.7.8 Bad username or password (Authentication failed)."/>
|
||||||
|
# failJSON: { "time": "2014-06-02T10:08:07", "match": true , "host": "71.95.206.106" }
|
||||||
|
<co ts="2014-06-02T10:08:07.56" pi="123f.8e2.9022" sc="tcp_local" dr="+" ac="U" tr="TCP|192.245.12.223|25|71.95.206.106|56591" ap="SMTP" mi="Bad password" us="romeo.julieta@opus1.com" di="535 5.7.8 Bad username or password (Authentication failed)."/>
|
||||||
|
# failJSON: { "time": "2014-06-02T09:54:58", "match": true , "host": "151.1.71.144" }
|
||||||
|
<co ts="2014-06-02T09:54:58.82" pi="123f.715.7116" sc="tcp_local" dr="+" ac="U" tr="TCP|192.245.12.223|25|151.1.71.144|58406" ap="SMTP" mi="Bad password" us="01ko8hqnoif09qx0np@imap.opus1.com" di="535 5.7.8 Bad username or password (Authentication failed)."/>
|
|
@ -1,12 +1,14 @@
|
||||||
# failJSON: { "time": "2006-02-13T15:52:30", "match": true , "host": "1.2.3.4" }
|
# failJSON: { "time": "2006-02-13T15:52:30", "match": true , "host": "1.2.3.4" }
|
||||||
2006-02-13 15:52:30,388 fail2ban.server.actions: WARNING [sendmail] Ban 1.2.3.4
|
2006-02-13 15:52:30,388 fail2ban.actions: NOTICE [sendmail] Ban 1.2.3.4
|
||||||
# failJSON: { "time": "2006-02-13T15:52:30", "match": true , "host": "1.2.3.4", "desc": "Extended with [PID]" }
|
# failJSON: { "time": "2006-02-13T15:52:30", "match": true , "host": "1.2.3.4", "desc": "Extended with [PID]" }
|
||||||
2006-02-13 15:52:30,388 fail2ban.server.actions[123]: WARNING [sendmail] Ban 1.2.3.4
|
2006-02-13 15:52:30,388 fail2ban.actions[123]: NOTICE [sendmail] Ban 1.2.3.4
|
||||||
# failJSON: { "match": false }
|
# failJSON: { "match": false }
|
||||||
2006-02-13 16:07:31,183 fail2ban.server.actions: WARNING [sendmail] Unban 1.2.3.4
|
2006-02-13 16:07:31,183 fail2ban.actions: NOTICE [sendmail] Unban 1.2.3.4
|
||||||
# failJSON: { "match": false }
|
# failJSON: { "match": false }
|
||||||
2006-02-13 15:52:30,388 fail2ban.server.actions: WARNING [recidive] Ban 1.2.3.4
|
2006-02-13 15:52:30,388 fail2ban.actions: NOTICE [recidive] Ban 1.2.3.4
|
||||||
# syslog example
|
# syslog example
|
||||||
# failJSON: { "time": "2004-09-16T00:44:55", "match": true , "host": "10.0.0.7" }
|
# failJSON: { "time": "2004-09-16T00:44:55", "match": true , "host": "10.0.0.7" }
|
||||||
Sep 16 00:44:55 spaceman fail2ban.server.actions: WARNING [jail] Ban 10.0.0.7
|
Sep 16 00:44:55 spaceman fail2ban.actions: NOTICE [jail] Ban 10.0.0.7
|
||||||
|
|
||||||
|
# failJSON: { "time": "2006-02-13T15:52:30", "match": true , "host": "1.2.3.4", "desc": "Extended with [PID] and padding" }
|
||||||
|
2006-02-13 15:52:30,388 fail2ban.actions [123]: NOTICE [sendmail] Ban 1.2.3.4
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
# failJSON: { "time": "2013-07-09T01:45:16", "match": false , "host": "173.242.116.187" }
|
# failJSON: { "time": "2013-07-09T02:45:16", "match": false , "host": "173.242.116.187" }
|
||||||
type=USER_LOGIN msg=audit(1373330716.415:4063): user pid=11998 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=login acct="root" exe="/usr/sbin/sshd" hostname=? addr=173.242.116.187 terminal=ssh res=failed'
|
type=USER_LOGIN msg=audit(1373330716.415:4063): user pid=11998 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=login acct="root" exe="/usr/sbin/sshd" hostname=? addr=173.242.116.187 terminal=ssh res=failed'
|
||||||
|
|
||||||
# failJSON: { "time": "2013-07-09T01:45:17", "match": false , "host": "173.242.116.187" }
|
# failJSON: { "time": "2013-07-09T02:45:17", "match": false , "host": "173.242.116.187" }
|
||||||
type=USER_LOGIN msg=audit(1373330717.000:4068): user pid=12000 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=login acct=28756E6B6E6F776E207573657229 exe="/usr/sbin/sshd" hostname=? addr=173.242.116.187 terminal=ssh res=failed'
|
type=USER_LOGIN msg=audit(1373330717.000:4068): user pid=12000 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=login acct=28756E6B6E6F776E207573657229 exe="/usr/sbin/sshd" hostname=? addr=173.242.116.187 terminal=ssh res=failed'
|
||||||
|
|
||||||
# failJSON: { "time": "2013-07-09T01:45:17", "match": true , "host": "173.242.116.187" }
|
# failJSON: { "time": "2013-07-09T02:45:17", "match": true , "host": "173.242.116.187" }
|
||||||
type=USER_ERR msg=audit(1373330717.000:4070): user pid=12000 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=PAM:bad_ident acct="?" exe="/usr/sbin/sshd" hostname=173.242.116.187 addr=173.242.116.187 terminal=ssh res=failed'
|
type=USER_ERR msg=audit(1373330717.000:4070): user pid=12000 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=PAM:bad_ident acct="?" exe="/usr/sbin/sshd" hostname=173.242.116.187 addr=173.242.116.187 terminal=ssh res=failed'
|
||||||
|
|
||||||
# failJSON: { "time": "2013-07-09T01:45:17", "match": false , "host": "173.242.116.187" }
|
# failJSON: { "time": "2013-07-09T02:45:17", "match": false , "host": "173.242.116.187" }
|
||||||
type=USER_LOGIN msg=audit(1373330717.000:4073): user pid=12000 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=login acct=28696E76616C6964207573657229 exe="/usr/sbin/sshd" hostname=? addr=173.242.116.187 terminal=ssh res=failed'
|
type=USER_LOGIN msg=audit(1373330717.000:4073): user pid=12000 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=login acct=28696E76616C6964207573657229 exe="/usr/sbin/sshd" hostname=? addr=173.242.116.187 terminal=ssh res=failed'
|
||||||
|
|
||||||
# failJSON: { "time": "2013-06-30T01:02:08", "match": false , "host": "113.240.248.18" }
|
# failJSON: { "time": "2013-06-30T02:02:08", "match": false , "host": "113.240.248.18" }
|
||||||
type=USER_LOGIN msg=audit(1372546928.000:52008): user pid=21569 uid=0 auid=0 ses=76 subj=unconfined_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=login acct="sshd" exe="/usr/sbin/sshd" hostname=? addr=113.240.248.18 terminal=ssh res=failed'
|
type=USER_LOGIN msg=audit(1372546928.000:52008): user pid=21569 uid=0 auid=0 ses=76 subj=unconfined_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=login acct="sshd" exe="/usr/sbin/sshd" hostname=? addr=113.240.248.18 terminal=ssh res=failed'
|
||||||
|
|
||||||
# failJSON: { "time": "2013-06-30T02:58:20", "match": true , "host": "113.240.248.18" }
|
# failJSON: { "time": "2013-06-30T03:58:20", "match": true , "host": "113.240.248.18" }
|
||||||
type=USER_ERR msg=audit(1372557500.000:61747): user pid=23684 uid=0 auid=0 ses=76 subj=unconfined_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=PAM:bad_ident acct="?" exe="/usr/sbin/sshd" hostname=113.240.248.18 addr=113.240.248.18 terminal=ssh res=failed'
|
type=USER_ERR msg=audit(1372557500.000:61747): user pid=23684 uid=0 auid=0 ses=76 subj=unconfined_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=PAM:bad_ident acct="?" exe="/usr/sbin/sshd" hostname=113.240.248.18 addr=113.240.248.18 terminal=ssh res=failed'
|
||||||
|
|
||||||
# failJSON: { "time": "2013-06-30T03:58:20", "match": false , "host": "113.240.248.18" }
|
# failJSON: { "time": "2013-06-30T04:58:20", "match": false , "host": "113.240.248.18" }
|
||||||
type=USER_LOGIN msg=audit(1372557500.000:61750): user pid=23684 uid=0 auid=0 ses=76 subj=unconfined_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=login acct=28696E76616C6964207573657229 exe="/usr/sbin/sshd" hostname=? addr=113.240.248.18 terminal=ssh res=failed'
|
type=USER_LOGIN msg=audit(1372557500.000:61750): user pid=23684 uid=0 auid=0 ses=76 subj=unconfined_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=login acct=28696E76616C6964207573657229 exe="/usr/sbin/sshd" hostname=? addr=113.240.248.18 terminal=ssh res=failed'
|
||||||
|
|
||||||
# failJSON: { "time": "2013-07-06T17:48:00", "match": true , "host": "194.228.20.113" }
|
# failJSON: { "time": "2013-07-06T18:48:00", "match": true , "host": "194.228.20.113" }
|
||||||
type=USER_AUTH msg=audit(1373129280.000:9): user pid=1277 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=pubkey acct="root" exe="/usr/sbin/sshd" hostname=? addr=194.228.20.113 terminal=ssh res=failed'
|
type=USER_AUTH msg=audit(1373129280.000:9): user pid=1277 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=pubkey acct="root" exe="/usr/sbin/sshd" hostname=? addr=194.228.20.113 terminal=ssh res=failed'
|
||||||
|
|
||||||
# failJSON: { "time": "2013-10-30T07:57:43", "match": true , "host": "192.168.3.100" }
|
# failJSON: { "time": "2013-10-30T07:57:43", "match": true , "host": "192.168.3.100" }
|
||||||
|
|
|
@ -136,3 +136,15 @@ Jul 13 18:44:28 mdop sshd[4931]: Received disconnect from 89.24.13.192: 3: com.j
|
||||||
Feb 12 04:09:18 localhost sshd[26713]: Connection from 115.249.163.77 port 51353
|
Feb 12 04:09:18 localhost sshd[26713]: Connection from 115.249.163.77 port 51353
|
||||||
# failJSON: { "time": "2005-02-12T04:09:21", "match": true , "host": "115.249.163.77", "desc": "from gh-457" }
|
# failJSON: { "time": "2005-02-12T04:09:21", "match": true , "host": "115.249.163.77", "desc": "from gh-457" }
|
||||||
Feb 12 04:09:21 localhost sshd[26713]: Disconnecting: Too many authentication failures for root [preauth]
|
Feb 12 04:09:21 localhost sshd[26713]: Disconnecting: Too many authentication failures for root [preauth]
|
||||||
|
|
||||||
|
# failJSON: { "match": false }
|
||||||
|
Feb 12 04:09:18 localhost sshd[26713]: Connection from 115.249.163.77 port 51353 on 127.0.0.1 port 22
|
||||||
|
# failJSON: { "time": "2005-02-12T04:09:21", "match": true , "host": "115.249.163.77", "desc": "Multiline match with interface address" }
|
||||||
|
Feb 12 04:09:21 localhost sshd[26713]: Disconnecting: Too many authentication failures for root [preauth]
|
||||||
|
|
||||||
|
# failJSON: { "match": false }
|
||||||
|
Apr 27 13:02:04 host sshd[29116]: User root not allowed because account is locked
|
||||||
|
# failJSON: { "match": false }
|
||||||
|
Apr 27 13:02:04 host sshd[29116]: input_userauth_request: invalid user root [preauth]
|
||||||
|
# failJSON: { "time": "2005-04-27T13:02:04", "match": true , "host": "1.2.3.4", "desc": "No Bye-Bye" }
|
||||||
|
Apr 27 13:02:04 host sshd[29116]: Received disconnect from 1.2.3.4: 11: Normal Shutdown, Thank you for playing [preauth]
|
||||||
|
|
|
@ -24,6 +24,7 @@ __license__ = "GPL"
|
||||||
|
|
||||||
from __builtin__ import open as fopen
|
from __builtin__ import open as fopen
|
||||||
import unittest
|
import unittest
|
||||||
|
import getpass
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
@ -349,10 +350,20 @@ class LogFileMonitor(LogCaptureTestCase):
|
||||||
# shorter wait time for not modified status
|
# shorter wait time for not modified status
|
||||||
return not self.isModified(0.4)
|
return not self.isModified(0.4)
|
||||||
|
|
||||||
def testNoLogFile(self):
|
def testUnaccessibleLogFile(self):
|
||||||
os.chmod(self.name, 0)
|
os.chmod(self.name, 0)
|
||||||
self.filter.getFailures(self.name)
|
self.filter.getFailures(self.name)
|
||||||
self.assertTrue(self._is_logged('Unable to open %s' % self.name))
|
failure_was_logged = self._is_logged('Unable to open %s' % self.name)
|
||||||
|
is_root = getpass.getuser() == 'root'
|
||||||
|
# If ran as root, those restrictive permissions would not
|
||||||
|
# forbid log to be read.
|
||||||
|
self.assertTrue(failure_was_logged != is_root)
|
||||||
|
|
||||||
|
def testNoLogFile(self):
|
||||||
|
_killfile(self.file, self.name)
|
||||||
|
self.filter.getFailures(self.name)
|
||||||
|
failure_was_logged = self._is_logged('Unable to open %s' % self.name)
|
||||||
|
self.assertTrue(failure_was_logged)
|
||||||
|
|
||||||
def testRemovingFailRegex(self):
|
def testRemovingFailRegex(self):
|
||||||
self.filter.delFailRegex(0)
|
self.filter.delFailRegex(0)
|
||||||
|
@ -794,7 +805,7 @@ class GetFailures(unittest.TestCase):
|
||||||
FILENAME_MULTILINE = os.path.join(TEST_FILES_DIR, "testcase-multiline.log")
|
FILENAME_MULTILINE = os.path.join(TEST_FILES_DIR, "testcase-multiline.log")
|
||||||
|
|
||||||
# so that they could be reused by other tests
|
# so that they could be reused by other tests
|
||||||
FAILURES_01 = ('193.168.0.128', 3, 1124017199.0,
|
FAILURES_01 = ('193.168.0.128', 3, 1124013599.0,
|
||||||
[u'Aug 14 11:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128']*3)
|
[u'Aug 14 11:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128']*3)
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -844,7 +855,7 @@ class GetFailures(unittest.TestCase):
|
||||||
|
|
||||||
|
|
||||||
def testGetFailures02(self):
|
def testGetFailures02(self):
|
||||||
output = ('141.3.81.106', 4, 1124017139.0,
|
output = ('141.3.81.106', 4, 1124013539.0,
|
||||||
[u'Aug 14 11:%d:59 i60p295 sshd[12365]: Failed publickey for roehl from ::ffff:141.3.81.106 port 51332 ssh2'
|
[u'Aug 14 11:%d:59 i60p295 sshd[12365]: Failed publickey for roehl from ::ffff:141.3.81.106 port 51332 ssh2'
|
||||||
% m for m in 53, 54, 57, 58])
|
% m for m in 53, 54, 57, 58])
|
||||||
|
|
||||||
|
@ -854,7 +865,7 @@ class GetFailures(unittest.TestCase):
|
||||||
_assert_correct_last_attempt(self, self.filter, output)
|
_assert_correct_last_attempt(self, self.filter, output)
|
||||||
|
|
||||||
def testGetFailures03(self):
|
def testGetFailures03(self):
|
||||||
output = ('203.162.223.135', 7, 1124017144.0)
|
output = ('203.162.223.135', 7, 1124013544.0)
|
||||||
|
|
||||||
self.filter.addLogPath(GetFailures.FILENAME_03)
|
self.filter.addLogPath(GetFailures.FILENAME_03)
|
||||||
self.filter.addFailRegex("error,relay=<HOST>,.*550 User unknown")
|
self.filter.addFailRegex("error,relay=<HOST>,.*550 User unknown")
|
||||||
|
@ -862,7 +873,7 @@ class GetFailures(unittest.TestCase):
|
||||||
_assert_correct_last_attempt(self, self.filter, output)
|
_assert_correct_last_attempt(self, self.filter, output)
|
||||||
|
|
||||||
def testGetFailures04(self):
|
def testGetFailures04(self):
|
||||||
output = [('212.41.96.186', 4, 1124017200.0),
|
output = [('212.41.96.186', 4, 1124013600.0),
|
||||||
('212.41.96.185', 4, 1124017198.0)]
|
('212.41.96.185', 4, 1124017198.0)]
|
||||||
|
|
||||||
self.filter.addLogPath(GetFailures.FILENAME_04)
|
self.filter.addLogPath(GetFailures.FILENAME_04)
|
||||||
|
@ -877,11 +888,11 @@ class GetFailures(unittest.TestCase):
|
||||||
|
|
||||||
def testGetFailuresUseDNS(self):
|
def testGetFailuresUseDNS(self):
|
||||||
# We should still catch failures with usedns = no ;-)
|
# We should still catch failures with usedns = no ;-)
|
||||||
output_yes = ('93.184.216.119', 2, 1124017139.0,
|
output_yes = ('93.184.216.119', 2, 1124013539.0,
|
||||||
[u'Aug 14 11:54:59 i60p295 sshd[12365]: Failed publickey for roehl from example.com port 51332 ssh2',
|
[u'Aug 14 11:54:59 i60p295 sshd[12365]: Failed publickey for roehl from example.com port 51332 ssh2',
|
||||||
u'Aug 14 11:58:59 i60p295 sshd[12365]: Failed publickey for roehl from ::ffff:93.184.216.119 port 51332 ssh2'])
|
u'Aug 14 11:58:59 i60p295 sshd[12365]: Failed publickey for roehl from ::ffff:93.184.216.119 port 51332 ssh2'])
|
||||||
|
|
||||||
output_no = ('93.184.216.119', 1, 1124017139.0,
|
output_no = ('93.184.216.119', 1, 1124013539.0,
|
||||||
[u'Aug 14 11:58:59 i60p295 sshd[12365]: Failed publickey for roehl from ::ffff:93.184.216.119 port 51332 ssh2'])
|
[u'Aug 14 11:58:59 i60p295 sshd[12365]: Failed publickey for roehl from ::ffff:93.184.216.119 port 51332 ssh2'])
|
||||||
|
|
||||||
# Actually no exception would be raised -- it will be just set to 'no'
|
# Actually no exception would be raised -- it will be just set to 'no'
|
||||||
|
@ -904,7 +915,7 @@ class GetFailures(unittest.TestCase):
|
||||||
|
|
||||||
|
|
||||||
def testGetFailuresMultiRegex(self):
|
def testGetFailuresMultiRegex(self):
|
||||||
output = ('141.3.81.106', 8, 1124017141.0)
|
output = ('141.3.81.106', 8, 1124013541.0)
|
||||||
|
|
||||||
self.filter.addLogPath(GetFailures.FILENAME_02)
|
self.filter.addLogPath(GetFailures.FILENAME_02)
|
||||||
self.filter.addFailRegex("Failed .* from <HOST>")
|
self.filter.addFailRegex("Failed .* from <HOST>")
|
||||||
|
@ -923,8 +934,8 @@ class GetFailures(unittest.TestCase):
|
||||||
self.assertRaises(FailManagerEmpty, self.filter.failManager.toBan)
|
self.assertRaises(FailManagerEmpty, self.filter.failManager.toBan)
|
||||||
|
|
||||||
def testGetFailuresMultiLine(self):
|
def testGetFailuresMultiLine(self):
|
||||||
output = [("192.0.43.10", 2, 1124017199.0),
|
output = [("192.0.43.10", 2, 1124013599.0),
|
||||||
("192.0.43.11", 1, 1124017198.0)]
|
("192.0.43.11", 1, 1124013598.0)]
|
||||||
self.filter.addLogPath(GetFailures.FILENAME_MULTILINE)
|
self.filter.addLogPath(GetFailures.FILENAME_MULTILINE)
|
||||||
self.filter.addFailRegex("^.*rsyncd\[(?P<pid>\d+)\]: connect from .+ \(<HOST>\)$<SKIPLINES>^.+ rsyncd\[(?P=pid)\]: rsync error: .*$")
|
self.filter.addFailRegex("^.*rsyncd\[(?P<pid>\d+)\]: connect from .+ \(<HOST>\)$<SKIPLINES>^.+ rsyncd\[(?P=pid)\]: rsync error: .*$")
|
||||||
self.filter.setMaxLines(100)
|
self.filter.setMaxLines(100)
|
||||||
|
@ -942,7 +953,7 @@ class GetFailures(unittest.TestCase):
|
||||||
self.assertEqual(sorted(foundList), sorted(output))
|
self.assertEqual(sorted(foundList), sorted(output))
|
||||||
|
|
||||||
def testGetFailuresMultiLineIgnoreRegex(self):
|
def testGetFailuresMultiLineIgnoreRegex(self):
|
||||||
output = [("192.0.43.10", 2, 1124017199.0)]
|
output = [("192.0.43.10", 2, 1124013599.0)]
|
||||||
self.filter.addLogPath(GetFailures.FILENAME_MULTILINE)
|
self.filter.addLogPath(GetFailures.FILENAME_MULTILINE)
|
||||||
self.filter.addFailRegex("^.*rsyncd\[(?P<pid>\d+)\]: connect from .+ \(<HOST>\)$<SKIPLINES>^.+ rsyncd\[(?P=pid)\]: rsync error: .*$")
|
self.filter.addFailRegex("^.*rsyncd\[(?P<pid>\d+)\]: connect from .+ \(<HOST>\)$<SKIPLINES>^.+ rsyncd\[(?P=pid)\]: rsync error: .*$")
|
||||||
self.filter.addIgnoreRegex("rsync error: Received SIGINT")
|
self.filter.addIgnoreRegex("rsync error: Received SIGINT")
|
||||||
|
@ -956,9 +967,9 @@ class GetFailures(unittest.TestCase):
|
||||||
self.assertRaises(FailManagerEmpty, self.filter.failManager.toBan)
|
self.assertRaises(FailManagerEmpty, self.filter.failManager.toBan)
|
||||||
|
|
||||||
def testGetFailuresMultiLineMultiRegex(self):
|
def testGetFailuresMultiLineMultiRegex(self):
|
||||||
output = [("192.0.43.10", 2, 1124017199.0),
|
output = [("192.0.43.10", 2, 1124013599.0),
|
||||||
("192.0.43.11", 1, 1124017198.0),
|
("192.0.43.11", 1, 1124013598.0),
|
||||||
("192.0.43.15", 1, 1124017198.0)]
|
("192.0.43.15", 1, 1124013598.0)]
|
||||||
self.filter.addLogPath(GetFailures.FILENAME_MULTILINE)
|
self.filter.addLogPath(GetFailures.FILENAME_MULTILINE)
|
||||||
self.filter.addFailRegex("^.*rsyncd\[(?P<pid>\d+)\]: connect from .+ \(<HOST>\)$<SKIPLINES>^.+ rsyncd\[(?P=pid)\]: rsync error: .*$")
|
self.filter.addFailRegex("^.*rsyncd\[(?P<pid>\d+)\]: connect from .+ \(<HOST>\)$<SKIPLINES>^.+ rsyncd\[(?P=pid)\]: rsync error: .*$")
|
||||||
self.filter.addFailRegex("^.* sendmail\[.*, msgid=<(?P<msgid>[^>]+).*relay=\[<HOST>\].*$<SKIPLINES>^.+ spamd: result: Y \d+ .*,mid=<(?P=msgid)>(,bayes=[.\d]+)?(,autolearn=\S+)?\s*$")
|
self.filter.addFailRegex("^.* sendmail\[.*, msgid=<(?P<msgid>[^>]+).*relay=\[<HOST>\].*$<SKIPLINES>^.+ spamd: result: Y \d+ .*,mid=<(?P=msgid)>(,bayes=[.\d]+)?(,autolearn=\S+)?\s*$")
|
||||||
|
|
|
@ -32,8 +32,7 @@ import datetime
|
||||||
from glob import glob
|
from glob import glob
|
||||||
from StringIO import StringIO
|
from StringIO import StringIO
|
||||||
|
|
||||||
from .utils import mbasename, TraceBack, FormatterWithTraceBack
|
from ..helpers import formatExceptionInfo, mbasename, TraceBack, FormatterWithTraceBack, getLogger
|
||||||
from ..helpers import formatExceptionInfo
|
|
||||||
from ..server.datetemplate import DatePatternRegex
|
from ..server.datetemplate import DatePatternRegex
|
||||||
|
|
||||||
|
|
||||||
|
@ -160,7 +159,7 @@ class TestsUtilsTest(unittest.TestCase):
|
||||||
|
|
||||||
# and both types of traceback at once
|
# and both types of traceback at once
|
||||||
fmt = ' %(tb)s | %(tbc)s : %(message)s'
|
fmt = ' %(tb)s | %(tbc)s : %(message)s'
|
||||||
logSys = logging.getLogger("fail2ban_tests")
|
logSys = getLogger("fail2ban_tests")
|
||||||
out = logging.StreamHandler(strout)
|
out = logging.StreamHandler(strout)
|
||||||
out.setFormatter(Formatter(fmt))
|
out.setFormatter(Formatter(fmt))
|
||||||
logSys.addHandler(out)
|
logSys.addHandler(out)
|
||||||
|
|
|
@ -129,7 +129,7 @@ def testSampleRegexsFactory(name):
|
||||||
jsonTimeLocal = datetime.datetime.strptime(t, "%Y-%m-%dT%H:%M:%S.%f")
|
jsonTimeLocal = datetime.datetime.strptime(t, "%Y-%m-%dT%H:%M:%S.%f")
|
||||||
|
|
||||||
|
|
||||||
jsonTime = time.mktime(jsonTimeLocal.utctimetuple())
|
jsonTime = time.mktime(jsonTimeLocal.timetuple())
|
||||||
|
|
||||||
jsonTime += jsonTimeLocal.microsecond / 1000000
|
jsonTime += jsonTimeLocal.microsecond / 1000000
|
||||||
|
|
||||||
|
|
|
@ -30,11 +30,14 @@ import tempfile
|
||||||
import os
|
import os
|
||||||
import locale
|
import locale
|
||||||
import sys
|
import sys
|
||||||
import logging
|
|
||||||
|
|
||||||
from ..server.failregex import Regex, FailRegex, RegexException
|
from ..server.failregex import Regex, FailRegex, RegexException
|
||||||
from ..server.server import Server
|
from ..server.server import Server
|
||||||
from ..server.jail import Jail
|
from ..server.jail import Jail
|
||||||
|
from ..server.jailthread import JailThread
|
||||||
|
from .utils import LogCaptureTestCase
|
||||||
|
from ..helpers import getLogger
|
||||||
|
from .. import version
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from ..server import filtersystemd
|
from ..server import filtersystemd
|
||||||
|
@ -146,15 +149,18 @@ class Transmitter(TransmitterBase):
|
||||||
def testPing(self):
|
def testPing(self):
|
||||||
self.assertEqual(self.transm.proceed(["ping"]), (0, "pong"))
|
self.assertEqual(self.transm.proceed(["ping"]), (0, "pong"))
|
||||||
|
|
||||||
|
def testVersion(self):
|
||||||
|
self.assertEqual(self.transm.proceed(["version"]), (0, version.version))
|
||||||
|
|
||||||
def testSleep(self):
|
def testSleep(self):
|
||||||
t0 = time.time()
|
t0 = time.time()
|
||||||
self.assertEqual(self.transm.proceed(["sleep", "1"]), (0, None))
|
self.assertEqual(self.transm.proceed(["sleep", "1"]), (0, None))
|
||||||
t1 = time.time()
|
t1 = time.time()
|
||||||
# Approx 1 second delay
|
# Approx 1 second delay
|
||||||
self.assertAlmostEqual(t1 - t0, 1, places=2)
|
self.assertAlmostEqual(t1 - t0, 1, places=1)
|
||||||
|
|
||||||
def testDatabase(self):
|
def testDatabase(self):
|
||||||
_, tmpFilename = tempfile.mkstemp(".db", "Fail2Ban_")
|
tmp, tmpFilename = tempfile.mkstemp(".db", "fail2ban_")
|
||||||
# Jails present, can't change database
|
# Jails present, can't change database
|
||||||
self.setGetTestNOK("dbfile", tmpFilename)
|
self.setGetTestNOK("dbfile", tmpFilename)
|
||||||
self.server.delJail(self.jailName)
|
self.server.delJail(self.jailName)
|
||||||
|
@ -175,6 +181,8 @@ class Transmitter(TransmitterBase):
|
||||||
self.assertEqual(self.transm.proceed(
|
self.assertEqual(self.transm.proceed(
|
||||||
["get", "dbpurgeage"]),
|
["get", "dbpurgeage"]),
|
||||||
(0, None))
|
(0, None))
|
||||||
|
os.close(tmp)
|
||||||
|
os.unlink(tmpFilename)
|
||||||
|
|
||||||
def testAddJail(self):
|
def testAddJail(self):
|
||||||
jail2 = "TestJail2"
|
jail2 = "TestJail2"
|
||||||
|
@ -528,11 +536,27 @@ class Transmitter(TransmitterBase):
|
||||||
|
|
||||||
def testPythonActionMethodsAndProperties(self):
|
def testPythonActionMethodsAndProperties(self):
|
||||||
action = "TestCaseAction"
|
action = "TestCaseAction"
|
||||||
self.assertEqual(
|
try:
|
||||||
self.transm.proceed(["set", self.jailName, "addaction", action,
|
out = self.transm.proceed(
|
||||||
|
["set", self.jailName, "addaction", action,
|
||||||
os.path.join(TEST_FILES_DIR, "action.d", "action.py"),
|
os.path.join(TEST_FILES_DIR, "action.d", "action.py"),
|
||||||
'{"opt1": "value"}']),
|
'{"opt1": "value"}'])
|
||||||
(0, action))
|
self.assertEqual(out, (0, action))
|
||||||
|
except AssertionError:
|
||||||
|
if ((2, 6) <= sys.version_info < (2, 6, 5)) \
|
||||||
|
and '__init__() keywords must be strings' in out[1]:
|
||||||
|
# known issue http://bugs.python.org/issue2646 in 2.6 series
|
||||||
|
# since general Fail2Ban warnings are suppressed in normal
|
||||||
|
# operation -- let's issue Python's native warning here
|
||||||
|
import warnings
|
||||||
|
warnings.warn(
|
||||||
|
"Your version of Python %s seems to experience a known "
|
||||||
|
"issue forbidding correct operation of Fail2Ban: "
|
||||||
|
"http://bugs.python.org/issue2646 Upgrade your Python and "
|
||||||
|
"meanwhile other intestPythonActionMethodsAndProperties will "
|
||||||
|
"be skipped" % (sys.version))
|
||||||
|
return
|
||||||
|
raise
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
sorted(self.transm.proceed(["get", self.jailName,
|
sorted(self.transm.proceed(["get", self.jailName,
|
||||||
"actionproperties", action])[1]),
|
"actionproperties", action])[1]),
|
||||||
|
@ -704,7 +728,7 @@ class TransmitterLogging(TransmitterBase):
|
||||||
os.close(f)
|
os.close(f)
|
||||||
self.server.setLogLevel("WARNING")
|
self.server.setLogLevel("WARNING")
|
||||||
self.assertEqual(self.transm.proceed(["set", "logtarget", fn]), (0, fn))
|
self.assertEqual(self.transm.proceed(["set", "logtarget", fn]), (0, fn))
|
||||||
l = logging.getLogger('fail2ban.server.server').parent.parent
|
l = getLogger('fail2ban')
|
||||||
l.warning("Before file moved")
|
l.warning("Before file moved")
|
||||||
try:
|
try:
|
||||||
f2, fn2 = tempfile.mkstemp("fail2ban.log")
|
f2, fn2 = tempfile.mkstemp("fail2ban.log")
|
||||||
|
@ -778,5 +802,19 @@ class RegexTests(unittest.TestCase):
|
||||||
self.assertTrue(fr.hasMatched())
|
self.assertTrue(fr.hasMatched())
|
||||||
self.assertRaises(RegexException, fr.getHost)
|
self.assertRaises(RegexException, fr.getHost)
|
||||||
|
|
||||||
|
class _BadThread(JailThread):
|
||||||
|
def run(self):
|
||||||
|
int("ignore this exception -- raised for testing")
|
||||||
|
|
||||||
|
class LoggingTests(LogCaptureTestCase):
|
||||||
|
|
||||||
|
def testGetF2BLogger(self):
|
||||||
|
testLogSys = getLogger("fail2ban.some.string.with.name")
|
||||||
|
self.assertEqual(testLogSys.parent.name, "fail2ban")
|
||||||
|
self.assertEqual(testLogSys.name, "fail2ban.name")
|
||||||
|
|
||||||
|
def testFail2BanExceptHook(self):
|
||||||
|
badThread = _BadThread()
|
||||||
|
badThread.start()
|
||||||
|
badThread.join()
|
||||||
|
self.assertTrue(self._is_logged("Unhandled exception"))
|
||||||
|
|
|
@ -22,89 +22,17 @@ __author__ = "Yaroslav Halchenko"
|
||||||
__copyright__ = "Copyright (c) 2013 Yaroslav Halchenko"
|
__copyright__ = "Copyright (c) 2013 Yaroslav Halchenko"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import logging, os, re, traceback, time, unittest
|
import logging
|
||||||
from os.path import basename, dirname
|
import os
|
||||||
|
import re
|
||||||
|
import time
|
||||||
|
import unittest
|
||||||
from StringIO import StringIO
|
from StringIO import StringIO
|
||||||
|
|
||||||
from ..server.mytime import MyTime
|
from ..server.mytime import MyTime
|
||||||
|
from ..helpers import getLogger
|
||||||
|
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = getLogger(__name__)
|
||||||
|
|
||||||
#
|
|
||||||
# Following "traceback" functions are adopted from PyMVPA distributed
|
|
||||||
# under MIT/Expat and copyright by PyMVPA developers (i.e. me and
|
|
||||||
# Michael). Hereby I re-license derivative work on these pieces under GPL
|
|
||||||
# to stay in line with the main Fail2Ban license
|
|
||||||
#
|
|
||||||
def mbasename(s):
|
|
||||||
"""Custom function to include directory name if filename is too common
|
|
||||||
|
|
||||||
Also strip .py at the end
|
|
||||||
"""
|
|
||||||
base = basename(s)
|
|
||||||
if base.endswith('.py'):
|
|
||||||
base = base[:-3]
|
|
||||||
if base in set(['base', '__init__']):
|
|
||||||
base = basename(dirname(s)) + '.' + base
|
|
||||||
return base
|
|
||||||
|
|
||||||
class TraceBack(object):
|
|
||||||
"""Customized traceback to be included in debug messages
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, compress=False):
|
|
||||||
"""Initialize TrackBack metric
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
compress : bool
|
|
||||||
if True then prefix common with previous invocation gets
|
|
||||||
replaced with ...
|
|
||||||
"""
|
|
||||||
self.__prev = ""
|
|
||||||
self.__compress = compress
|
|
||||||
|
|
||||||
def __call__(self):
|
|
||||||
ftb = traceback.extract_stack(limit=100)[:-2]
|
|
||||||
entries = [[mbasename(x[0]), dirname(x[0]), str(x[1])] for x in ftb]
|
|
||||||
entries = [ [e[0], e[2]] for e in entries
|
|
||||||
if not (e[0] in ['unittest', 'logging.__init__']
|
|
||||||
or e[1].endswith('/unittest'))]
|
|
||||||
|
|
||||||
# lets make it more concise
|
|
||||||
entries_out = [entries[0]]
|
|
||||||
for entry in entries[1:]:
|
|
||||||
if entry[0] == entries_out[-1][0]:
|
|
||||||
entries_out[-1][1] += ',%s' % entry[1]
|
|
||||||
else:
|
|
||||||
entries_out.append(entry)
|
|
||||||
sftb = '>'.join(['%s:%s' % (mbasename(x[0]),
|
|
||||||
x[1]) for x in entries_out])
|
|
||||||
if self.__compress:
|
|
||||||
# lets remove part which is common with previous invocation
|
|
||||||
prev_next = sftb
|
|
||||||
common_prefix = os.path.commonprefix((self.__prev, sftb))
|
|
||||||
common_prefix2 = re.sub('>[^>]*$', '', common_prefix)
|
|
||||||
|
|
||||||
if common_prefix2 != "":
|
|
||||||
sftb = '...' + sftb[len(common_prefix2):]
|
|
||||||
self.__prev = prev_next
|
|
||||||
|
|
||||||
return sftb
|
|
||||||
|
|
||||||
class FormatterWithTraceBack(logging.Formatter):
|
|
||||||
"""Custom formatter which expands %(tb) and %(tbc) with tracebacks
|
|
||||||
|
|
||||||
TODO: might need locking in case of compressed tracebacks
|
|
||||||
"""
|
|
||||||
def __init__(self, fmt, *args, **kwargs):
|
|
||||||
logging.Formatter.__init__(self, fmt=fmt, *args, **kwargs)
|
|
||||||
compress = '%(tbc)s' in fmt
|
|
||||||
self._tb = TraceBack(compress=compress)
|
|
||||||
|
|
||||||
def format(self, record):
|
|
||||||
record.tbc = record.tb = self._tb()
|
|
||||||
return logging.Formatter.format(self, record)
|
|
||||||
|
|
||||||
def mtimesleep():
|
def mtimesleep():
|
||||||
# no sleep now should be necessary since polling tracks now not only
|
# no sleep now should be necessary since polling tracks now not only
|
||||||
|
@ -146,7 +74,6 @@ def gatherTests(regexps=None, no_network=False):
|
||||||
if not regexps: # pragma: no cover
|
if not regexps: # pragma: no cover
|
||||||
tests = unittest.TestSuite()
|
tests = unittest.TestSuite()
|
||||||
else: # pragma: no cover
|
else: # pragma: no cover
|
||||||
import re
|
|
||||||
class FilteredTestSuite(unittest.TestSuite):
|
class FilteredTestSuite(unittest.TestSuite):
|
||||||
_regexps = [re.compile(r) for r in regexps]
|
_regexps = [re.compile(r) for r in regexps]
|
||||||
def addTest(self, suite):
|
def addTest(self, suite):
|
||||||
|
@ -163,6 +90,7 @@ def gatherTests(regexps=None, no_network=False):
|
||||||
tests.addTest(unittest.makeSuite(servertestcase.Transmitter))
|
tests.addTest(unittest.makeSuite(servertestcase.Transmitter))
|
||||||
tests.addTest(unittest.makeSuite(servertestcase.JailTests))
|
tests.addTest(unittest.makeSuite(servertestcase.JailTests))
|
||||||
tests.addTest(unittest.makeSuite(servertestcase.RegexTests))
|
tests.addTest(unittest.makeSuite(servertestcase.RegexTests))
|
||||||
|
tests.addTest(unittest.makeSuite(servertestcase.LoggingTests))
|
||||||
tests.addTest(unittest.makeSuite(actiontestcase.CommandActionTest))
|
tests.addTest(unittest.makeSuite(actiontestcase.CommandActionTest))
|
||||||
tests.addTest(unittest.makeSuite(actionstestcase.ExecuteActions))
|
tests.addTest(unittest.makeSuite(actionstestcase.ExecuteActions))
|
||||||
# FailManager
|
# FailManager
|
||||||
|
@ -259,7 +187,7 @@ class LogCaptureTestCase(unittest.TestCase):
|
||||||
|
|
||||||
# For extended testing of what gets output into logging
|
# For extended testing of what gets output into logging
|
||||||
# system, we will redirect it to a string
|
# system, we will redirect it to a string
|
||||||
logSys = logging.getLogger("fail2ban")
|
logSys = getLogger("fail2ban")
|
||||||
|
|
||||||
# Keep old settings
|
# Keep old settings
|
||||||
self._old_level = logSys.level
|
self._old_level = logSys.level
|
||||||
|
@ -272,7 +200,7 @@ class LogCaptureTestCase(unittest.TestCase):
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
"""Call after every test case."""
|
"""Call after every test case."""
|
||||||
# print "O: >>%s<<" % self._log.getvalue()
|
# print "O: >>%s<<" % self._log.getvalue()
|
||||||
logSys = logging.getLogger("fail2ban")
|
logSys = getLogger("fail2ban")
|
||||||
logSys.handlers = self._old_handlers
|
logSys.handlers = self._old_handlers
|
||||||
logSys.level = self._old_level
|
logSys.level = self._old_level
|
||||||
|
|
||||||
|
|
|
@ -24,4 +24,4 @@ __author__ = "Cyril Jaquier, Yaroslav Halchenko, Steven Hiscocks, Daniel Black"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2014 Yaroslav Halchenko, 2013-2013 Steven Hiscocks, Daniel Black"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2014 Yaroslav Halchenko, 2013-2013 Steven Hiscocks, Daniel Black"
|
||||||
__license__ = "GPL-v2+"
|
__license__ = "GPL-v2+"
|
||||||
|
|
||||||
version = "0.9.0"
|
version = "0.9.0.dev"
|
||||||
|
|
|
@ -154,7 +154,7 @@ _fail2ban () {
|
||||||
fi
|
fi
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
delfailregex|delignoregex)
|
delfailregex|delignoreregex)
|
||||||
COMPREPLY=( $( compgen -W \
|
COMPREPLY=( $( compgen -W \
|
||||||
"$( "$1" get "$jail" "${prev/del/}" 2>/dev/null | awk -F"[][]" '{print $2}')" \
|
"$( "$1" get "$jail" "${prev/del/}" 2>/dev/null | awk -F"[][]" '{print $2}')" \
|
||||||
-- "$cur" ) )
|
-- "$cur" ) )
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue