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
Yaroslav Halchenko 2014-09-12 14:14:24 -04:00
commit 8b2f0678a7
102 changed files with 1238 additions and 548 deletions

1
.gitignore vendored
View File

@ -8,3 +8,4 @@ htmlcov
*.rej
*.bak
__pycache__
.vagrant/

View File

@ -6,6 +6,7 @@ python:
- "2.7"
- "3.2"
- "3.3"
- "3.4"
- "pypy"
before_install:
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then sudo apt-get update -qq; fi

181
3rdparty/logwatch/fail2ban vendored Executable file
View File

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

17
CONTRIBUTING.md Normal file
View File

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

View File

@ -10,24 +10,69 @@ Fail2Ban (version 0.9.0.dev) 2014/xx/xx
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:
* UTF-8 fixes in pure-ftp thanks to Johannes Weberhofer. Closes gh-806.
* systemd backend error on bad utf-8 in python3
* badips.py action error when logging HTTP error raised with badips request
* fail2ban-regex failed to work in python3 due to space/tab mix
* recidive regex samples incorrect log level
* journalmatch for recidive incorrect PRIORITY
* loglevel couldn't be changed in fail2ban.conf
* Handle case when no sqlite library is available for persistent database
* Only reban once per IP from database on fail2ban restart
* 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 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
* Fail2ban-regex - add print-all-matched option. Closes gh-652
* 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

View File

@ -258,7 +258,7 @@ config/action.d/dummy.conf
config/action.d/firewallcmd-new.conf
config/action.d/firewallcmd-ipset.conf
config/action.d/iptables-ipset-proto6-allports.conf
config/action.d/iptables-blocktype.conf
config/action.d/iptables-common.conf
config/action.d/iptables-ipset-proto4.conf
config/action.d/iptables-ipset-proto6.conf
config/action.d/iptables-xt_recent-echo.conf

View File

@ -6,20 +6,20 @@ By Roy Sigurd Karlsbakk <roy@karlsbakk.net>
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
users of older Solaris versions and Nexenta, but don't rely on it.
READ ME FIRST
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
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
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
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
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
@ -111,7 +111,7 @@ GOTCHAS AND FIXMES
svcadm enable fail2ban
* If svcs -xv says that fail2ban failed to start or svcs says it's in maintenance mode
check /var/svc/log/network-fail2ban:default.log for clues.
check /var/svc/log/network-fail2ban:default.log for clues.
Check permissions on /var/adm, /var/adm/auth.log /var/adm/fail2ban.log and /var/run/fail2ban
You may need to:

View File

@ -68,24 +68,12 @@ Code status:
Contact:
--------
### 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
### Bugs, feature requests, discussions?
See [CONTRIBUTING.md](https://github.com/fail2ban/fail2ban/blob/master/CONTRIBUTING.md)
### You just appreciate this program:
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)
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)
since Fail2Ban is "community-driven" for years now.
Thanks:

12
THANKS
View File

@ -44,10 +44,14 @@ Hank Leininger
Hanno 'Rince' Wagner
Helmut Grohne
Iain Lea
Ioan Indreias
Ivo Truxa
John Thoe
Jacques Lav!gnotte
Ioan Indreias
Johannes Weberhofer
Jason H Martin
Jisoo Park
Joel M Snyder
Jonathan Kamens
Jonathan Lanning
Jonathan Underwood
@ -60,6 +64,7 @@ kjohnsonecl
kojiro
Lars Kneschke
Lee Clemens
leftyfb (Mike Rushton)
Manuel Arostegui Ramirez
Marcel Dopita
Mark Edgington
@ -75,8 +80,10 @@ Michael Hanselmann
Mika (mkl)
Nick Munger
onorua
Paul Marrapese
Noel Butler
Patrick Börjesson
Pressy
Raphaël Marichez
RealRancor
René Berber
@ -84,7 +91,10 @@ Robert Edeker
Rolf Fokkens
Roman Gelfand
Russell Odom
SATO Kentaro
Sean DuBois
Sebastian Arcus
Serg G. Brester
Sireyessire
silviogarbes
Stefan Tatschner

30
Vagrantfile vendored Normal file
View File

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

View File

@ -30,9 +30,10 @@ from fail2ban.protocol import printFormatted
from fail2ban.client.csocket import CSocket
from fail2ban.client.configurator import Configurator
from fail2ban.client.beautifier import Beautifier
from fail2ban.helpers import getLogger
# 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["dump"] = False
self.__conf["force"] = False
self.__conf["background"] = True
self.__conf["verbose"] = 1
self.__conf["interactive"] = False
self.__conf["socket"] = None
@ -83,6 +85,8 @@ class Fail2banClient:
print " -v increase verbosity"
print " -q decrease verbosity"
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 " -V, --version print the version"
print
@ -125,6 +129,10 @@ class Fail2banClient:
self.__conf["force"] = True
elif opt[0] == "-i":
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"]:
self.dispUsage()
sys.exit(0)
@ -194,7 +202,8 @@ class Fail2banClient:
# Start the server
self.__startServerAsync(self.__conf["socket"],
self.__conf["pidfile"],
self.__conf["force"])
self.__conf["force"],
self.__conf["background"])
try:
# Wait for the server to start
self.__waitOnServer()
@ -242,14 +251,12 @@ class Fail2banClient:
#
# 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.
pid = os.fork()
if pid == 0:
args = list()
args.append(self.SERVER)
# Start in background mode.
args.append("-b")
# Set the socket path.
args.append("-s")
args.append(socket)
@ -259,6 +266,12 @@ class Fail2banClient:
# Force the execution if needed.
if force:
args.append("-x")
# Start in foreground mode if requested.
if background:
args.append("-b")
else:
args.append("-f")
try:
# Use the current directory.
exe = os.path.abspath(os.path.join(sys.path[0], self.SERVER))
@ -312,7 +325,7 @@ class Fail2banClient:
# Reads the command line options.
try:
cmdOpts = 'hc:s:p:xdviqV'
cmdOpts = 'hc:s:p:xfbdviqV'
cmdLongOpts = ['help', 'version']
optList, args = getopt.getopt(self.__argv[1:], cmdOpts, cmdLongOpts)
except getopt.GetoptError:

View File

@ -25,11 +25,11 @@ This tools can test regular expressions for "fail2ban".
"""
__author__ = "Cyril Jaquier, Yaroslav Halchenko"
__copyright__ = "Copyright (c) 2004-2008 Cyril Jaquier, 2012-2013 Yaroslav Halchenko"
__author__ = "Fail2Ban Developers"
__copyright__ = "Copyright (c) 2004-2008 Cyril Jaquier, 2012-2014 Yaroslav Halchenko"
__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 ConfigParser import NoOptionError, NoSectionError, MissingSectionHeaderError
@ -45,9 +45,9 @@ from fail2ban.client.filterreader import FilterReader
from fail2ban.server.filter import Filter
from fail2ban.server.failregex import RegexException
from fail2ban.tests.utils import FormatterWithTraceBack
from fail2ban.helpers import FormatterWithTraceBack, getLogger
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban")
logSys = getLogger("fail2ban")
def debuggexURL(sample, regex):
q = urllib.urlencode({ 're': regex.replace('<HOST>', '(?&.ipv4)'),
@ -223,6 +223,7 @@ class Fail2banRegex(object):
self._filter = Filter(None)
self._ignoreregex = list()
self._failregex = list()
self._time_elapsed = None
self._line_stats = LineStats()
if opts.maxlines:
@ -344,10 +345,11 @@ class Fail2banRegex(object):
pass
else:
self._line_stats.matched += 1
self._line_stats.missed -= 1
return line, ret
def process(self, test_lines):
t0 = time.time()
for line_no, line in enumerate(test_lines):
if isinstance(line, tuple):
line_datetimestripped, ret = fail2banRegex.testRegex(
@ -382,6 +384,7 @@ class Fail2banRegex(object):
if line_no % 10 == 0 and self._filter.dateDetector is not None:
self._filter.dateDetector.sortTemplate()
self._time_elapsed = time.time() - t0
@ -455,7 +458,10 @@ class Fail2banRegex(object):
template.hits, template.name))
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:
self.printLines('matched')

View File

@ -22,13 +22,14 @@ __author__ = "Cyril Jaquier"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import getopt, sys, logging, os
import getopt, sys, os
from fail2ban.version import version
from fail2ban.server.server import Server
from fail2ban.helpers import getLogger
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban")
logSys = getLogger("fail2ban")
##
# \mainpage Fail2Ban

View File

@ -24,8 +24,8 @@ __author__ = "Cyril Jaquier"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2012- Yaroslav Halchenko"
__license__ = "GPL"
import unittest, logging, sys, time, os
import logging
import unittest, sys, time, os
# Check if local fail2ban module exists, and use if it exists by
# modifying the path. This is such that tests can be used in dev
@ -34,7 +34,8 @@ if os.path.exists("fail2ban/__init__.py"):
sys.path.insert(0, ".")
from fail2ban.version import version
from fail2ban.tests.utils import FormatterWithTraceBack, gatherTests
from fail2ban.tests.utils import gatherTests
from fail2ban.helpers import FormatterWithTraceBack, getLogger
from fail2ban.server.mytime import MyTime
from optparse import OptionParser, Option
@ -69,7 +70,7 @@ parser = get_opt_parser()
#
# Logging
#
logSys = logging.getLogger("fail2ban")
logSys = getLogger("fail2ban")
# Numerical level of verbosity corresponding to a log "level"
verbosity = {'heavydebug': 4,

View File

@ -36,7 +36,7 @@ from fail2ban.server.actions import ActionBase
from fail2ban.version import version as f2bVersion
class BadIPsAction(ActionBase):
"""Fail2Ban action which resports bans to badips.com, and also
"""Fail2Ban action which reports bans to badips.com, and also
blacklist bad IPs listed on badips.com by using another action's
ban method.
@ -53,7 +53,7 @@ class BadIPsAction(ActionBase):
age : str, optional
Age of last report for bad IPs, per badips.com syntax.
Default "24h" (24 hours)
key : str, optional
key : str, optional
Key issued by badips.com to report bans, for later retrieval
of personalised content.
banaction : str, optional
@ -65,7 +65,7 @@ class BadIPsAction(ActionBase):
from category used for reporting. e.g. may want to report
"postfix", but want to use whole "mail" category for blacklist.
Default `category`.
bankey : str, optional
bankey : str, optional
Key issued by badips.com to blacklist IPs reported with the
associated key.
updateperiod : int, optional
@ -161,7 +161,7 @@ class BadIPsAction(ActionBase):
"/".join([self._badips, "get", "list", category, str(score)]),
urlencode({'age': age})])
if key:
url = "&".join([url, urlencode({"key", key})])
url = "&".join([url, urlencode({'key': key})])
response = urlopen(self._Request(url))
except HTTPError as response:
messages = json.loads(response.read().decode('utf-8'))
@ -258,7 +258,7 @@ class BadIPsAction(ActionBase):
self._logSys.error(
"Error banning IP %s for jail '%s' with action '%s': %s",
ip, self._jail.name, self.banaction, e,
exc_info=self._logSys.getEffectiveLevel<=logging.DEBUG)
exc_info=self._logSys.getEffectiveLevel()<=logging.DEBUG)
else:
self._bannedips.add(ip)
self._logSys.info(
@ -279,7 +279,7 @@ class BadIPsAction(ActionBase):
self._logSys.info(
"Error unbanning IP %s for jail '%s' with action '%s': %s",
ip, self._jail.name, self.banaction, e,
exc_info=self._logSys.getEffectiveLevel<=logging.DEBUG)
exc_info=self._logSys.getEffectiveLevel()<=logging.DEBUG)
else:
self._logSys.info(
"Unbanned IP %s for jail '%s' with action '%s'",
@ -346,7 +346,7 @@ class BadIPsAction(ActionBase):
try:
url = "/".join([self._badips, "add", self.category, aInfo['ip']])
if self.key:
url = "?".join([url, urlencode({"key", self.key})])
url = "?".join([url, urlencode({'key': self.key})])
response = urlopen(self._Request(url))
except HTTPError as response:
messages = json.loads(response.read().decode('utf-8'))

View File

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

View File

@ -14,7 +14,7 @@
[INCLUDES]
before = iptables-blocktype.conf
before = iptables-common.conf
[Definition]
@ -31,22 +31,6 @@ actionunban = ipset del fail2ban-<name> <ip> -exist
[Init]
# Default name of the chain
#
name = default
# Option: port
# Notes.: specifies port to monitor
# Values: [ NUM | STRING ]
#
port = ssh
# Option: protocol
# Notes.: internally used by config reader for interpolations.
# Values: [ tcp | udp | icmp | all ]
#
protocol = tcp
# Option: chain
# Notes specifies the iptables chain to which the fail2ban rules should be
# added

View File

@ -4,7 +4,7 @@
[INCLUDES]
before = iptables-blocktype.conf
before = iptables-common.conf
[Definition]
@ -24,22 +24,6 @@ actionunban = firewall-cmd --direct --remove-rule ipv4 filter f2b-<name> 0 -s <i
[Init]
# Default name of the chain
#
name = default
# Option: port
# Notes.: specifies port to monitor
# Values: [ NUM | STRING ]
#
port = ssh
# Option: protocol
# Notes.: internally used by config reader for interpolations.
# Values: [ tcp | udp | icmp | all ]
#
protocol = tcp
# Option: chain
# Notes specifies the iptables chain to which the fail2ban rules should be
# added

View File

@ -8,7 +8,7 @@
[INCLUDES]
before = iptables-blocktype.conf
before = iptables-common.conf
[Definition]
@ -53,18 +53,3 @@ actionunban = iptables -D f2b-<name> -s <ip> -j <blocktype>
[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

View File

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

View File

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

View File

@ -19,7 +19,7 @@
[INCLUDES]
before = iptables-blocktype.conf
before = iptables-common.conf
[Definition]
@ -28,13 +28,13 @@ before = iptables-blocktype.conf
# Values: CMD
#
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
# Notes.: command executed once at the end of Fail2Ban
# Values: CMD
#
actionstop = iptables -D INPUT -p <protocol> -m multiport --dports <port> -m set --match-set 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 --destroy f2b-<name>
@ -56,18 +56,3 @@ actionunban = ipset --test f2b-<name> <ip> && ipset --del f2b-<name> <ip>
[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

View File

@ -15,8 +15,7 @@
[INCLUDES]
before = iptables-blocktype.conf
before = iptables-common.conf
[Definition]
@ -25,13 +24,13 @@ before = iptables-blocktype.conf
# Values: CMD
#
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
# Notes.: command executed once at the end of Fail2Ban
# 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 destroy f2b-<name>
@ -53,12 +52,8 @@ actionunban = ipset del f2b-<name> <ip> -exist
[Init]
# Default name of the ipset
#
name = default
# Option: bantime
# Notes: specifies the bantime in seconds (handled internally rather than by fail2ban)
# Values: [ NUM ] Default: 600
#
bantime = 600

View File

@ -15,8 +15,7 @@
[INCLUDES]
before = iptables-blocktype.conf
before = iptables-common.conf
[Definition]
@ -25,13 +24,13 @@ before = iptables-blocktype.conf
# Values: CMD
#
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
# Notes.: command executed once at the end of Fail2Ban
# Values: CMD
#
actionstop = iptables -D INPUT -p <protocol> -m multiport --dports <port> -m set --match-set 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 destroy f2b-<name>
@ -53,24 +52,8 @@ actionunban = ipset del f2b-<name> <ip> -exist
[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
# Notes: specifies the bantime in seconds (handled internally rather than by fail2ban)
# Values: [ NUM ] Default: 600
#
bantime = 600

View File

@ -11,7 +11,7 @@
[INCLUDES]
before = iptables-blocktype.conf
before = iptables-common.conf
[Definition]
@ -60,24 +60,3 @@ actionunban = iptables -D f2b-<name> -s <ip> -j f2b-<name>-log
[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

View File

@ -6,7 +6,7 @@
[INCLUDES]
before = iptables-blocktype.conf
before = iptables-common.conf
[Definition]
@ -50,24 +50,3 @@ actionunban = iptables -D f2b-<name> -s <ip> -j <blocktype>
[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

View File

@ -8,8 +8,7 @@
[INCLUDES]
before = iptables-blocktype.conf
before = iptables-common.conf
[Definition]
@ -53,24 +52,3 @@ actionunban = iptables -D f2b-<name> -s <ip> -j <blocktype>
[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

View File

@ -6,8 +6,7 @@
[INCLUDES]
before = iptables-blocktype.conf
before = iptables-common.conf
[Definition]
@ -33,14 +32,14 @@ before = iptables-blocktype.conf
# own rules. The 3600 second timeout is independent and acts as a
# safeguard in case the fail2ban process dies unexpectedly. The
# shorter of the two timeouts actually matters.
actionstart = 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
# Notes.: command executed once at the end of Fail2Ban
# Values: CMD
#
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
# Notes.: command executed once before each actionban command
@ -66,12 +65,3 @@ actionunban = echo -<ip> > /proc/net/xt_recent/f2b-<name>
[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

View File

@ -6,7 +6,7 @@
[INCLUDES]
before = iptables-blocktype.conf
before = iptables-common.conf
[Definition]
@ -50,24 +50,3 @@ actionunban = iptables -D f2b-<name> -s <ip> -j <blocktype>
[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

View File

@ -45,7 +45,7 @@ messages['ban'] = {}
messages['ban']['head'] = \
"""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.
"""
messages['ban']['tail'] = \

View File

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

View File

@ -1,9 +1,9 @@
# 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.
# A number after an allow rule to the application won't be much use.
# The insert position should be appropriate to block the required traffic.
# A number after an allow rule to the application won't be of much use.
[Definition]
@ -19,7 +19,7 @@ actionunban = [ -n "<application>" ] && app="app <application>" ; ufw delete <bl
[Init]
# 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
# Option: blocktype

View File

@ -12,7 +12,8 @@ before = common.conf
_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 =

View File

@ -11,9 +11,9 @@ before = common.conf
[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 =

View File

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

View File

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

View File

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

View File

@ -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*$
[Init]
journalmatch = _SYSTEMD_UNIT=postfix.service
# Author: Yaroslav Halchenko

View File

@ -15,7 +15,7 @@ before = common.conf
_daemon = pure-ftpd
# 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*$
@ -24,7 +24,13 @@ ignoreregex =
# Author: Cyril Jaquier
# Modified: Yaroslav Halchenko for pure-ftpd
# Documentation thanks to Blake on http://www.fail2ban.org/wiki/index.php?title=Fail2ban:Community_Portal
# UTF-8 editing and mechanism thanks to Johannes Weberhofer
#
# 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

View File

@ -21,13 +21,13 @@ before = common.conf
[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
# jail using this filter 'recidive', or change this line!
_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]

View File

@ -9,7 +9,7 @@ before = common.conf
[Definition]
_deamon = (?:cyrus/)?(?:tim)?sieved?
_daemon = (?:cyrus/)?(?:tim)?sieved?
failregex = ^%(__prefix_line)sbadlogin: \S+ ?\[<HOST>\] \S+ authentication failure$

View File

@ -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)sUser .+ from <HOST> not allowed because a group is listed in DenyGroups\s*$
^%(__prefix_line)sUser .+ from <HOST> not allowed because none of user's groups are listed in AllowGroups\s*$
^(?P<__prefix>%(__prefix_line)s)User .+ not allowed because account is locked<SKIPLINES>(?P=__prefix)(?:error: )?Received disconnect from <HOST>: 11: Bye Bye \[preauth\]$
^(?P<__prefix>%(__prefix_line)s)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)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 =

View File

@ -10,7 +10,7 @@
#
# 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.
# For example to change the default bantime for all jails and to enable the
@ -151,22 +151,22 @@ port = 0:65535
banaction = iptables-multiport
# 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.
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"]
# ban & send an e-mail with whois report and relevant log lines
# 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"]
# 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
# 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"]
@ -366,11 +366,17 @@ maxretry = 5
port = http,https
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]
port = 10000
logpath = /var/log/auth.log
logpath = %(syslog_authpriv)s
#
@ -423,7 +429,7 @@ maxretry = 6
[vsftpd]
# 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
# vsftpd's failregex should match both of those formats
port = ftp,ftp-data,ftps,ftps-data
@ -495,13 +501,13 @@ logpath = %(solidpop3d_log)s
[exim]
port = smtp,465,submission
logpath = /var/log/exim/mainlog
logpath = %(exim_main_log)s
[exim-spam]
port = smtp,465,submission
logpath = /var/log/exim/mainlog
logpath = %(exim_main_log)s
[kerio]
@ -533,7 +539,7 @@ logpath = %(postfix_log)s
[perdition]
port = imap3,imaps,pop3,pop3s
logpath = /var/log/maillog
logpath = %(syslog_mail)s
[squirrelmail]
@ -657,13 +663,13 @@ maxretry = 5
[pam-generic]
# pam-generic filter can be customized to monitor specific subset of 'tty's
banaction = iptables-allports
logpath = /var/log/auth.log
logpath = %(syslog_authpriv)s
[xinetd-fail]
banaction = iptables-multiport-log
logpath = /var/log/daemon.log
logpath = %(syslog_daemon)s
maxretry = 2
@ -693,5 +699,18 @@ action = %(banaction)s[name=%(__name__)s-tcp, port="%(tcpport)s", protocol="tcp
[nagios]
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
[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

View File

@ -7,16 +7,22 @@ after = paths-overrides.local
[DEFAULT]
sshd_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
auditd_log = /var/log/audit/audit.log
exim_main_log = /var/log/exim/mainlog
nginx_error_log = /var/log/nginx/error.log
@ -31,7 +37,7 @@ lighttpd_error_log = /var/log/lighttpd/error.log
suhosin_log = %(syslog_user)s %(lighttpd_error_log)s
# defaults to ftp or local2 if ftp doesn't exist
proftpd_log = %(syslog_ftp)s
proftpd_log = %(syslog_ftp)s
# http://svnweb.freebsd.org/ports/head/ftp/proftpd/files/patch-src_proftpd.8.in?view=markup
# defaults to ftp but can be overwritten.

View File

@ -30,6 +30,7 @@ apache_error_log = /var/log/apache2/*error.log
apache_access_log = /var/log/apache2/*access.log
exim_main_log = /var/log/exim4/mainlog
# was in debian squeezy but not in wheezy
# /etc/proftpd/proftpd.conf (SystemLog)

View File

@ -32,4 +32,6 @@ apache_access_log = /var/log/httpd/*access_log
# proftpd_log = /var/log/proftpd/auth.log
# 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

View File

@ -2,7 +2,7 @@
[INCLUDES]
before = common-paths.conf
before = paths-common.conf
after = paths-overrides.local

View File

@ -3,7 +3,7 @@
[INCLUDES]
before = common-paths.conf
before = paths-common.conf
after = paths-overrides.local

View File

@ -24,12 +24,13 @@ __author__ = "Cyril Jaquier"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import logging, os
import os
from .configreader import ConfigReader, DefinitionInitConfigReader
from ..helpers import getLogger
# Gets the instance of the logger.
logSys = logging.getLogger(__name__)
logSys = getLogger(__name__)
class ActionReader(DefinitionInitConfigReader):

View File

@ -21,12 +21,11 @@ __author__ = "Cyril Jaquier, Yaroslav Halchenko"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2013- Yaroslav Halchenko"
__license__ = "GPL"
import logging
from ..exceptions import UnknownJailException, DuplicateJailException
from ..helpers import getLogger
# Gets the instance of the logger.
logSys = logging.getLogger(__name__)
logSys = getLogger(__name__)
##
# Beautify the output of the client.
@ -52,6 +51,8 @@ class Beautifier:
try:
if inC[0] == "ping":
msg = "Server replied: " + response
elif inC[0] == "version":
msg = response
elif inC[0] == "start":
msg = "Jail started"
elif inC[0] == "stop":

View File

@ -24,7 +24,8 @@ __author__ = 'Yaroslav Halhenko'
__copyright__ = 'Copyright (c) 2007 Yaroslav Halchenko'
__license__ = 'GPL'
import logging, os, sys
import os, sys
from ..helpers import getLogger
if sys.version_info >= (3,2): # pragma: no cover
@ -60,7 +61,7 @@ else: # pragma: no cover
from ConfigParser import SafeConfigParser
# Gets the instance of the logger.
logSys = logging.getLogger(__name__)
logSys = getLogger(__name__)
__all__ = ['SafeConfigParserWithIncludes']

View File

@ -24,13 +24,14 @@ __author__ = "Cyril Jaquier"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import glob, logging, os
import glob, os
from ConfigParser import NoOptionError, NoSectionError
from .configparserinc import SafeConfigParserWithIncludes
from ..helpers import getLogger
# Gets the instance of the logger.
logSys = logging.getLogger(__name__)
logSys = getLogger(__name__)
class ConfigReader(SafeConfigParserWithIncludes):

View File

@ -24,13 +24,12 @@ __author__ = "Cyril Jaquier"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import logging
from .fail2banreader import Fail2banReader
from .jailsreader import JailsReader
from ..helpers import getLogger
# Gets the instance of the logger.
logSys = logging.getLogger(__name__)
logSys = getLogger(__name__)
class Configurator:

View File

@ -24,12 +24,11 @@ __author__ = "Cyril Jaquier"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import logging
from .configreader import ConfigReader
from ..helpers import getLogger
# Gets the instance of the logger.
logSys = logging.getLogger(__name__)
logSys = getLogger(__name__)
class Fail2banReader(ConfigReader):

View File

@ -24,13 +24,14 @@ __author__ = "Cyril Jaquier"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import logging, os, shlex
import os, shlex
from .configreader import ConfigReader, DefinitionInitConfigReader
from ..server.action import CommandAction
from ..helpers import getLogger
# Gets the instance of the logger.
logSys = logging.getLogger(__name__)
logSys = getLogger(__name__)
class FilterReader(DefinitionInitConfigReader):

View File

@ -24,15 +24,16 @@ __author__ = "Cyril Jaquier"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import logging, re, glob, os.path
import re, glob, os.path
import json
from .configreader import ConfigReader
from .filterreader import FilterReader
from .actionreader import ActionReader
from ..helpers import getLogger
# Gets the instance of the logger.
logSys = logging.getLogger(__name__)
logSys = getLogger(__name__)
class JailReader(ConfigReader):

View File

@ -24,13 +24,12 @@ __author__ = "Cyril Jaquier"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import logging
from .configreader import ConfigReader
from .jailreader import JailReader
from ..helpers import getLogger
# Gets the instance of the logger.
logSys = logging.getLogger(__name__)
logSys = getLogger(__name__)
class JailsReader(ConfigReader):

View File

@ -20,9 +20,104 @@
__author__ = "Cyril Jaquier, Arturo 'Buanzo' Busleiman, Yaroslav Halchenko"
__license__ = "GPL"
import sys
import os
import traceback
import re
import logging
def formatExceptionInfo():
""" Consistently format exception information """
import sys
cla, exc = sys.exc_info()[:2]
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)

View File

@ -38,6 +38,7 @@ protocol = [
["status", "gets the current status of the server"],
["ping", "tests if the server is alive"],
["help", "return this output"],
["version", "return the server version"],
['', "LOGGING", ""],
["set loglevel <LEVEL>", "sets logging level to <LEVEL>. Levels: CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG"],
["get loglevel", "gets the logging level"],

View File

@ -25,10 +25,11 @@ import logging, os, subprocess, time, signal, tempfile
import threading, re
from abc import ABCMeta
from collections import MutableMapping
#from subprocess import call
from ..helpers import getLogger
# Gets the instance of the logger.
logSys = logging.getLogger(__name__)
logSys = getLogger(__name__)
# Create a lock for running system commands
_cmd_lock = threading.Lock()
@ -68,6 +69,9 @@ class CallingMap(MutableMapping):
def __init__(self, *args, **kwargs):
self.data = dict(*args, **kwargs)
def __repr__(self):
return "%s(%r)" % (self.__class__.__name__, self.data)
def __getitem__(self, key):
value = self.data[key]
if callable(value):
@ -87,6 +91,9 @@ class CallingMap(MutableMapping):
def __len__(self):
return len(self.data)
def copy(self):
return self.__class__(self.data.copy())
class ActionBase(object):
"""An abstract base class for actions in Fail2Ban.
@ -135,8 +142,7 @@ class ActionBase(object):
def __init__(self, jail, name):
self._jail = jail
self._name = name
self._logSys = logging.getLogger(
'%s.%s' % (__name__, self.__class__.__name__))
self._logSys = getLogger("fail2ban.%s" % self.__class__.__name__)
def start(self):
"""Executed when the jail/action is started.
@ -194,6 +200,8 @@ class CommandAction(ActionBase):
timeout
"""
_escapedTags = set(('matches', 'ipmatches', 'ipjailmatches'))
def __init__(self, jail, name):
super(CommandAction, self).__init__(jail, name)
self.timeout = 60
@ -351,8 +359,8 @@ class CommandAction(ActionBase):
if not self.executeCmd(stopCmd, self.timeout):
raise RuntimeError("Error stopping action")
@staticmethod
def substituteRecursiveTags(tags):
@classmethod
def substituteRecursiveTags(cls, tags):
"""Sort out tag definitions within other tags.
so: becomes:
@ -371,8 +379,11 @@ class CommandAction(ActionBase):
within the values recursively replaced.
"""
t = re.compile(r'<([^ >]+)>')
for tag, value in tags.iteritems():
value = str(value)
for tag in tags.iterkeys():
if tag in cls._escapedTags:
# Escaped so won't match
continue
value = str(tags[tag])
m = t.search(value)
done = []
#logSys.log(5, 'TAG: %s, value: %s' % (tag, value))
@ -383,6 +394,9 @@ class CommandAction(ActionBase):
# recursive definitions are bad
#logSys.log(5, 'recursion fail tag: %s value: %s' % (tag, value) )
return False
elif found_tag in cls._escapedTags:
# Escaped so won't match
continue
else:
if tags.has_key(found_tag):
value = value.replace('<%s>' % found_tag , tags[found_tag])
@ -441,10 +455,11 @@ class CommandAction(ActionBase):
`query` string with tags replaced.
"""
string = query
aInfo = cls.substituteRecursiveTags(aInfo)
for tag in aInfo:
if "<%s>" % tag in query:
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
# out of our control
value = cls.escapeTag(value)

View File

@ -41,9 +41,10 @@ from .banmanager import BanManager
from .jailthread import JailThread
from .action import ActionBase, CommandAction, CallingMap
from .mytime import MyTime
from ..helpers import getLogger
# Gets the instance of the logger.
logSys = logging.getLogger(__name__)
logSys = getLogger(__name__)
class Actions(JailThread, Mapping):
"""Handles jail actions.
@ -196,6 +197,8 @@ class Actions(JailThread, Mapping):
if ticket is not None:
# Unban the IP.
self.__unBan(ticket)
if self._jail.database is not None:
self._jail.database.delBan(self._jail, ticket)
else:
raise ValueError("IP %s is not banned" % ip)
@ -215,7 +218,8 @@ class Actions(JailThread, Mapping):
action.start()
except Exception as e:
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:
if not self.idle:
#logSys.debug(self._jail.name + ": action")
@ -234,7 +238,8 @@ class Actions(JailThread, Mapping):
action.stop()
except Exception as e:
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")
return True
@ -253,32 +258,31 @@ class Actions(JailThread, Mapping):
if ticket != False:
aInfo = CallingMap()
bTicket = BanManager.createBanTicket(ticket)
aInfo["ip"] = bTicket.getIP()
ip = bTicket.getIP()
aInfo["ip"] = ip
aInfo["failures"] = bTicket.getAttempt()
aInfo["time"] = bTicket.getTime()
aInfo["matches"] = "\n".join(bTicket.getMatches())
if self._jail.database is not None:
aInfo["ipmatches"] = lambda: "\n".join(
self._jail.database.getBansMerged(
ip=bTicket.getIP()).getMatches())
aInfo["ipjailmatches"] = lambda: "\n".join(
self._jail.database.getBansMerged(
ip=bTicket.getIP(), jail=self._jail).getMatches())
aInfo["ipfailures"] = lambda: "\n".join(
self._jail.database.getBansMerged(
ip=bTicket.getIP()).getAttempt())
aInfo["ipjailfailures"] = lambda: "\n".join(
self._jail.database.getBansMerged(
ip=bTicket.getIP(), jail=self._jail).getAttempt())
aInfo["ipmatches"] = lambda jail=self._jail: "\n".join(
jail.database.getBansMerged(ip=ip).getMatches())
aInfo["ipjailmatches"] = lambda jail=self._jail: "\n".join(
jail.database.getBansMerged(ip=ip, jail=jail).getMatches())
aInfo["ipfailures"] = lambda jail=self._jail: \
jail.database.getBansMerged(ip=ip).getAttempt()
aInfo["ipjailfailures"] = lambda jail=self._jail: \
jail.database.getBansMerged(ip=ip, jail=jail).getAttempt()
if self.__banManager.addBanTicket(bTicket):
logSys.notice("[%s] Ban %s" % (self._jail.name, aInfo["ip"]))
for name, action in self._actions.iteritems():
try:
action.ban(aInfo)
action.ban(aInfo.copy())
except Exception as e:
logSys.error(
"Failed to execute ban jail '%s' action '%s': %s",
self._jail.name, name, e)
"Failed to execute ban jail '%s' action '%s' "
"info '%r': %s",
self._jail.name, name, aInfo, e,
exc_info=logSys.getEffectiveLevel()<=logging.DEBUG)
return True
else:
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"]))
for name, action in self._actions.iteritems():
try:
action.unban(aInfo)
action.unban(aInfo.copy())
except Exception as e:
logSys.error(
"Failed to execute unban jail '%s' action '%s': %s",
self._jail.name, name, e)
"Failed to execute unban jail '%s' action '%s' "
"info '%r': %s",
self._jail.name, name, aInfo, e,
exc_info=logSys.getEffectiveLevel()<=logging.DEBUG)
@property
def status(self):

View File

@ -25,12 +25,12 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
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.
logSys = logging.getLogger(__name__)
logSys = getLogger(__name__)
if sys.version_info >= (3,):
# b"" causes SyntaxError in python <= 2.5, so below implements equivalent
@ -81,7 +81,7 @@ class RequestHandler(asynchat.async_chat):
self.close_when_done()
def handle_error(self):
e1, e2 = helpers.formatExceptionInfo()
e1, e2 = formatExceptionInfo()
logSys.error("Unexpected communication error: %s" % str(e2))
logSys.error(traceback.format_exc().splitlines())
self.close()

View File

@ -24,14 +24,14 @@ __author__ = "Cyril Jaquier"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import logging
from threading import Lock
from .ticket import BanTicket
from .mytime import MyTime
from ..helpers import getLogger
# Gets the instance of the logger.
logSys = logging.getLogger(__name__)
logSys = getLogger(__name__)
##
# Banning Manager.

View File

@ -21,7 +21,6 @@ __author__ = "Steven Hiscocks"
__copyright__ = "Copyright (c) 2013 Steven Hiscocks"
__license__ = "GPL"
import logging
import sys
import shutil, time
import sqlite3
@ -32,9 +31,10 @@ from threading import Lock
from .mytime import MyTime
from .ticket import FailTicket
from ..helpers import getLogger
# Gets the instance of the logger.
logSys = logging.getLogger(__name__)
logSys = getLogger(__name__)
if sys.version_info >= (3,):
sqlite3.register_adapter(
@ -368,10 +368,25 @@ class Fail2BanDb(object):
#TODO: Implement data parts once arbitrary match keys completed
cur.execute(
"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(),
"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
def _getBans(self, cur, jail=None, bantime=None, ip=None):
query = "SELECT ip, timeofban, data FROM bans WHERE 1"
@ -380,7 +395,7 @@ class Fail2BanDb(object):
if jail is not None:
query += " AND jail=?"
queryArgs.append(jail.name)
if bantime is not None:
if bantime is not None and bantime >= 0:
query += " AND timeofban > ?"
queryArgs.append(MyTime.time() - bantime)
if ip is not None:
@ -399,7 +414,8 @@ class Fail2BanDb(object):
Jail that the ban belongs to. Default `None`; all jails.
bantime : int
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 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.
bantime : int
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 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
returned.
"""
if bantime is None:
cacheKey = None
if bantime is None or bantime < 0:
cacheKey = (ip, jail)
if cacheKey in self._bansMergedCache:
return self._bansMergedCache[cacheKey]
@ -468,7 +486,7 @@ class Fail2BanDb(object):
ticket.setAttempt(failures)
tickets.append(ticket)
if bantime is None:
if cacheKey:
self._bansMergedCache[cacheKey] = tickets if ip is None else ticket
return tickets if ip is None else ticket

View File

@ -21,13 +21,13 @@ __author__ = "Cyril Jaquier and Fail2Ban Contributors"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import logging
from threading import Lock
from .datetemplate import DatePatternRegex, DateTai64n, DateEpoch
from ..helpers import getLogger
# Gets the instance of the logger.
logSys = logging.getLogger(__name__)
logSys = getLogger(__name__)
class DateDetector(object):
"""Manages one or more date templates to find a date within a log line.

View File

@ -25,12 +25,12 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import re
import logging
from abc import abstractmethod
from .strptime import reGroupDictStrptime, timeRE
from ..helpers import getLogger
logSys = logging.getLogger(__name__)
logSys = getLogger(__name__)
class DateTemplate(object):

View File

@ -24,10 +24,10 @@ __author__ = "Cyril Jaquier"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import logging
from ..helpers import getLogger
# Gets the instance of the logger.
logSys = logging.getLogger(__name__)
logSys = getLogger(__name__)
class FailData:

View File

@ -29,9 +29,10 @@ import logging
from .faildata import FailData
from .ticket import FailTicket
from ..helpers import getLogger
# Gets the instance of the logger.
logSys = logging.getLogger(__name__)
logSys = getLogger(__name__)
class FailManager:

View File

@ -21,7 +21,7 @@ __author__ = "Cyril Jaquier and Fail2Ban Contributors"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2013 Yaroslav Halchenko"
__license__ = "GPL"
import logging, re, os, fcntl, sys, locale, codecs
import re, os, fcntl, sys, locale, codecs
from .failmanager import FailManagerEmpty, FailManager
from .ticket import FailTicket
@ -31,9 +31,10 @@ from .datetemplate import DatePatternRegex, DateEpoch, DateTai64n
from .mytime import MyTime
from .failregex import FailRegex, Regex, RegexException
from .action import CommandAction
from ..helpers import getLogger
# Gets the instance of the logger.
logSys = logging.getLogger(__name__)
logSys = getLogger(__name__)
##
# Log reader class.
@ -790,8 +791,10 @@ class FileContainer:
try:
line = line.decode(self.getEncoding(), 'strict')
except UnicodeDecodeError:
logSys.warning("Error decoding line from '%s' with '%s': %s" %
(self.getFileName(), self.getEncoding(), `line`))
logSys.warning(
"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
line = line.decode(self.getEncoding(), 'ignore')
return line

View File

@ -23,16 +23,17 @@ __author__ = "Cyril Jaquier, Yaroslav Halchenko"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2012 Yaroslav Halchenko"
__license__ = "GPL"
import time, logging, fcntl
import time, fcntl
import gamin
from .failmanager import FailManagerEmpty
from .filter import FileFilter
from .mytime import MyTime
from ..helpers import getLogger
# Gets the instance of the logger.
logSys = logging.getLogger(__name__)
logSys = getLogger(__name__)
##
# Log reader class.

View File

@ -24,14 +24,15 @@ __author__ = "Cyril Jaquier, Yaroslav Halchenko"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier; 2012 Yaroslav Halchenko"
__license__ = "GPL"
import time, logging, os
import time, os
from .failmanager import FailManagerEmpty
from .filter import FileFilter
from .mytime import MyTime
from ..helpers import getLogger
# Gets the instance of the logger.
logSys = logging.getLogger(__name__)
logSys = getLogger(__name__)
##
# Log reader class.

View File

@ -32,6 +32,7 @@ import pyinotify
from .failmanager import FailManagerEmpty
from .filter import FileFilter
from .mytime import MyTime
from ..helpers import getLogger
if not hasattr(pyinotify, '__version__') \
@ -48,7 +49,7 @@ except Exception, e:
% str(e))
# Gets the instance of the logger.
logSys = logging.getLogger(__name__)
logSys = getLogger(__name__)
##
# Log reader class.

View File

@ -22,7 +22,7 @@ __author__ = "Steven Hiscocks"
__copyright__ = "Copyright (c) 2013 Steven Hiscocks"
__license__ = "GPL"
import logging, datetime, time
import datetime, time
from distutils.version import LooseVersion
from systemd import journal
@ -32,10 +32,10 @@ if LooseVersion(getattr(journal, '__version__', "0")) < '204':
from .failmanager import FailManagerEmpty
from .filter import JournalFilter
from .mytime import MyTime
from ..helpers import getLogger
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.filter")
logSys = getLogger(__name__)
##
# Journal reader class.
@ -167,8 +167,9 @@ class FilterSystemd(JournalFilter): # pragma: systemd no cover
logelements.append(logentry['_HOSTNAME'])
if logentry.get('SYSLOG_IDENTIFIER'):
logelements.append(logentry['SYSLOG_IDENTIFIER'])
if logentry.get('_PID'):
logelements[-1] += ("[%i]" % logentry['_PID'])
if logentry.get('SYSLOG_PID') or logentry.get('_PID'):
logelements[-1] += ("[%i]" % logentry.get(
'SYSLOG_PID', logentry['_PID']))
logelements[-1] += ":"
elif logentry.get('_COMM'):
logelements.append(logentry['_COMM'])
@ -212,6 +213,12 @@ class FilterSystemd(JournalFilter): # pragma: systemd no cover
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
start_time = datetime.datetime.now() - \
datetime.timedelta(seconds=int(self.getFindTime()))

View File

@ -26,9 +26,10 @@ __license__ = "GPL"
import Queue, logging
from .actions import Actions
from ..helpers import getLogger
# Gets the instance of the logger.
logSys = logging.getLogger(__name__)
logSys = getLogger(__name__)
class Jail:
"""Fail2Ban jail, which manages a filter and associated actions.

View File

@ -24,9 +24,12 @@ __author__ = "Cyril Jaquier"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import sys
from threading import Thread
from abc import abstractproperty, abstractmethod
from ..helpers import excepthook
class JailThread(Thread):
"""Abstract class for threading elements in Fail2Ban.
@ -53,6 +56,16 @@ class JailThread(Thread):
## The time the thread sleeps in the loop.
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
def status(self): # pragma: no cover - abstract
"""Abstract - Should provide status information.

View File

@ -32,9 +32,10 @@ from .filter import FileFilter, JournalFilter
from .transmitter import Transmitter
from .asyncserver import AsyncServer, AsyncServerException
from .. import version
from ..helpers import getLogger, excepthook
# Gets the instance of the logger.
logSys = logging.getLogger(__name__)
logSys = getLogger(__name__)
try:
from .database import Fail2BanDb
@ -69,6 +70,9 @@ class Server:
signal.signal(signal.SIGTERM, 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
os.umask(0077)
if self.__daemon: # pragma: no cover
@ -335,7 +339,7 @@ class Server:
def setLogLevel(self, value):
try:
self.__loggingLock.acquire()
logging.getLogger(__name__).parent.parent.setLevel(
getLogger("fail2ban").setLevel(
getattr(logging, value.upper()))
except AttributeError:
raise ValueError("Invalid log level")
@ -367,7 +371,7 @@ class Server:
try:
self.__loggingLock.acquire()
# 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":
# Syslog daemons already add date to the message.
formatter = logging.Formatter("%(name)s[%(process)d]: %(levelname)s %(message)s")
@ -388,7 +392,7 @@ class Server:
return False
# Removes previous handlers -- in reverse order since removeHandler
# 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]:
# Remove the handler.
logger.removeHandler(handler)
@ -425,7 +429,7 @@ class Server:
def flushLogs(self):
if self.__logTarget not in ['STDERR', 'STDOUT', 'SYSLOG']:
for handler in logging.getLogger(__name__).parent.parent.handlers:
for handler in getLogger("fail2ban").handlers:
try:
handler.doRollover()
logSys.info("rollover performed on %s" % self.__logTarget)
@ -434,7 +438,7 @@ class Server:
logSys.info("flush performed on %s" % self.__logTarget)
return "rolled over"
else:
for handler in logging.getLogger(__name__).parent.parent.handlers:
for handler in getLogger("fail2ban").handlers:
handler.flush()
logSys.info("flush performed on %s" % self.__logTarget)
return "flushed"
@ -523,11 +527,19 @@ class Server:
except (AttributeError, ValueError):
maxfd = 256 # default maximum
for fd in range(0, maxfd):
try:
os.close(fd)
except OSError: # ERROR (ignore)
pass
# 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):
try:
if not os.path.sameopenfile(urandom_fd, fd):
os.close(fd)
except OSError: # ERROR (ignore)
pass
os.close(urandom_fd)
else:
os.closerange(0, maxfd)
# Redirect the standard file descriptors to /dev/null.
os.open("/dev/null", os.O_RDONLY) # standard input (0)

View File

@ -190,5 +190,5 @@ def reGroupDictStrptime(found_dict):
if gmtoff is not None:
return calendar.timegm(date_result.utctimetuple())
else:
return time.mktime(date_result.utctimetuple())
return time.mktime(date_result.timetuple())

View File

@ -24,10 +24,10 @@ __author__ = "Cyril Jaquier"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import logging
from ..helpers import getLogger
# Gets the instance of the logger.
logSys = logging.getLogger(__name__)
logSys = getLogger(__name__)
class Ticket:

View File

@ -24,11 +24,14 @@ __author__ = "Cyril Jaquier"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import logging, time
import time
import json
from ..helpers import getLogger
from .. import version
# Gets the instance of the logger.
logSys = logging.getLogger(__name__)
logSys = getLogger(__name__)
class Transmitter:
@ -100,7 +103,9 @@ class Transmitter:
elif command[0] == "get":
return self.__commandGet(command[1:])
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")
def __commandSet(self, command):

View File

@ -29,6 +29,7 @@ import os
import tempfile
from ..server.actions import Actions
from ..server.ticket import FailTicket
from .dummyjail import DummyJail
from .utils import LogCaptureTestCase
@ -140,3 +141,28 @@ class ExecuteActions(LogCaptureTestCase):
self.__actions.stop()
self.__actions.join()
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"))

View File

@ -100,17 +100,24 @@ class CommandActionTest(LogCaptureTestCase):
{'ipjailmatches': "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
self.assertEqual(
self.__action.replaceTag("09 <callme> 11",
CallingMap(callme=lambda: str(10))),
self.__action.replaceTag("09 <matches> 11",
CallingMap(matches=lambda: str(10))),
"09 10 11")
# As tag not present, therefore callable should not be called
# Will raise ValueError if it is
self.assertEqual(
self.__action.replaceTag("abc",
CallingMap(callme=lambda: int("a"))), "abc")
CallingMap(matches=lambda: int("a"))), "abc")
def testExecuteActionBan(self):
self.__action.actionstart = "touch /tmp/fail2ban.test"

View File

@ -173,14 +173,25 @@ class DatabaseTest(unittest.TestCase):
self.assertTrue(
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):
if Fail2BanDb is None: # pragma: no cover
return
self.testAddJail()
ticket = FailTicket("127.0.0.1", MyTime.time() - 40, ["abc\n"])
self.db.addBan(self.jail, ticket)
self.db.addBan(
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=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):
if Fail2BanDb is None: # pragma: no cover
@ -251,6 +262,10 @@ class DatabaseTest(unittest.TestCase):
self.assertEqual(len(tickets), 1)
tickets = self.db.getBansMerged(bantime=5)
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):
if Fail2BanDb is None: # pragma: no cover

View File

@ -131,7 +131,7 @@ class DateDetectorTest(unittest.TestCase):
# see https://github.com/fail2ban/fail2ban/pull/130
# yoh: unfortunately this test is not really effective to reproduce the
# 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')
self.assertNotEqual(logdate, None)
( logTime, logMatch ) = logdate

View File

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

View File

@ -1,5 +1,9 @@
# 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.
# 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" }
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" }

View File

@ -1,5 +1,7 @@
# 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
# 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" }
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" }
@ -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]
# 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]
# 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]

View File

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

View File

@ -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
# 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
# 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
# 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
# failJSON: { "time": "2004-12-12T11:19:11", "match": true , "host": "190.210.136.21" }

View File

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

View File

@ -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)."/>

View File

@ -1,12 +1,14 @@
# 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]" }
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 }
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 }
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
# 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

View File

@ -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'
# 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'
# 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'
# 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'
# 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'
# 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'
# 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'
# 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'
# failJSON: { "time": "2013-10-30T07:57:43", "match": true , "host": "192.168.3.100" }

View File

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

View File

@ -24,6 +24,7 @@ __license__ = "GPL"
from __builtin__ import open as fopen
import unittest
import getpass
import os
import sys
import time
@ -349,10 +350,20 @@ class LogFileMonitor(LogCaptureTestCase):
# shorter wait time for not modified status
return not self.isModified(0.4)
def testNoLogFile(self):
def testUnaccessibleLogFile(self):
os.chmod(self.name, 0)
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):
self.filter.delFailRegex(0)
@ -794,7 +805,7 @@ class GetFailures(unittest.TestCase):
FILENAME_MULTILINE = os.path.join(TEST_FILES_DIR, "testcase-multiline.log")
# 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)
def setUp(self):
@ -844,7 +855,7 @@ class GetFailures(unittest.TestCase):
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'
% m for m in 53, 54, 57, 58])
@ -854,7 +865,7 @@ class GetFailures(unittest.TestCase):
_assert_correct_last_attempt(self, self.filter, output)
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.addFailRegex("error,relay=<HOST>,.*550 User unknown")
@ -862,7 +873,7 @@ class GetFailures(unittest.TestCase):
_assert_correct_last_attempt(self, self.filter, output)
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)]
self.filter.addLogPath(GetFailures.FILENAME_04)
@ -877,11 +888,11 @@ class GetFailures(unittest.TestCase):
def testGetFailuresUseDNS(self):
# 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: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'])
# Actually no exception would be raised -- it will be just set to 'no'
@ -904,7 +915,7 @@ class GetFailures(unittest.TestCase):
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.addFailRegex("Failed .* from <HOST>")
@ -923,8 +934,8 @@ class GetFailures(unittest.TestCase):
self.assertRaises(FailManagerEmpty, self.filter.failManager.toBan)
def testGetFailuresMultiLine(self):
output = [("192.0.43.10", 2, 1124017199.0),
("192.0.43.11", 1, 1124017198.0)]
output = [("192.0.43.10", 2, 1124013599.0),
("192.0.43.11", 1, 1124013598.0)]
self.filter.addLogPath(GetFailures.FILENAME_MULTILINE)
self.filter.addFailRegex("^.*rsyncd\[(?P<pid>\d+)\]: connect from .+ \(<HOST>\)$<SKIPLINES>^.+ rsyncd\[(?P=pid)\]: rsync error: .*$")
self.filter.setMaxLines(100)
@ -942,7 +953,7 @@ class GetFailures(unittest.TestCase):
self.assertEqual(sorted(foundList), sorted(output))
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.addFailRegex("^.*rsyncd\[(?P<pid>\d+)\]: connect from .+ \(<HOST>\)$<SKIPLINES>^.+ rsyncd\[(?P=pid)\]: rsync error: .*$")
self.filter.addIgnoreRegex("rsync error: Received SIGINT")
@ -956,9 +967,9 @@ class GetFailures(unittest.TestCase):
self.assertRaises(FailManagerEmpty, self.filter.failManager.toBan)
def testGetFailuresMultiLineMultiRegex(self):
output = [("192.0.43.10", 2, 1124017199.0),
("192.0.43.11", 1, 1124017198.0),
("192.0.43.15", 1, 1124017198.0)]
output = [("192.0.43.10", 2, 1124013599.0),
("192.0.43.11", 1, 1124013598.0),
("192.0.43.15", 1, 1124013598.0)]
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("^.* sendmail\[.*, msgid=<(?P<msgid>[^>]+).*relay=\[<HOST>\].*$<SKIPLINES>^.+ spamd: result: Y \d+ .*,mid=<(?P=msgid)>(,bayes=[.\d]+)?(,autolearn=\S+)?\s*$")

View File

@ -32,8 +32,7 @@ import datetime
from glob import glob
from StringIO import StringIO
from .utils import mbasename, TraceBack, FormatterWithTraceBack
from ..helpers import formatExceptionInfo
from ..helpers import formatExceptionInfo, mbasename, TraceBack, FormatterWithTraceBack, getLogger
from ..server.datetemplate import DatePatternRegex
@ -160,7 +159,7 @@ class TestsUtilsTest(unittest.TestCase):
# and both types of traceback at once
fmt = ' %(tb)s | %(tbc)s : %(message)s'
logSys = logging.getLogger("fail2ban_tests")
logSys = getLogger("fail2ban_tests")
out = logging.StreamHandler(strout)
out.setFormatter(Formatter(fmt))
logSys.addHandler(out)

View File

@ -129,7 +129,7 @@ def testSampleRegexsFactory(name):
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

View File

@ -30,11 +30,14 @@ import tempfile
import os
import locale
import sys
import logging
from ..server.failregex import Regex, FailRegex, RegexException
from ..server.server import Server
from ..server.jail import Jail
from ..server.jailthread import JailThread
from .utils import LogCaptureTestCase
from ..helpers import getLogger
from .. import version
try:
from ..server import filtersystemd
@ -146,15 +149,18 @@ class Transmitter(TransmitterBase):
def testPing(self):
self.assertEqual(self.transm.proceed(["ping"]), (0, "pong"))
def testVersion(self):
self.assertEqual(self.transm.proceed(["version"]), (0, version.version))
def testSleep(self):
t0 = time.time()
self.assertEqual(self.transm.proceed(["sleep", "1"]), (0, None))
t1 = time.time()
# Approx 1 second delay
self.assertAlmostEqual(t1 - t0, 1, places=2)
self.assertAlmostEqual(t1 - t0, 1, places=1)
def testDatabase(self):
_, tmpFilename = tempfile.mkstemp(".db", "Fail2Ban_")
tmp, tmpFilename = tempfile.mkstemp(".db", "fail2ban_")
# Jails present, can't change database
self.setGetTestNOK("dbfile", tmpFilename)
self.server.delJail(self.jailName)
@ -175,6 +181,8 @@ class Transmitter(TransmitterBase):
self.assertEqual(self.transm.proceed(
["get", "dbpurgeage"]),
(0, None))
os.close(tmp)
os.unlink(tmpFilename)
def testAddJail(self):
jail2 = "TestJail2"
@ -528,11 +536,27 @@ class Transmitter(TransmitterBase):
def testPythonActionMethodsAndProperties(self):
action = "TestCaseAction"
self.assertEqual(
self.transm.proceed(["set", self.jailName, "addaction", action,
os.path.join(TEST_FILES_DIR, "action.d", "action.py"),
'{"opt1": "value"}']),
(0, action))
try:
out = self.transm.proceed(
["set", self.jailName, "addaction", action,
os.path.join(TEST_FILES_DIR, "action.d", "action.py"),
'{"opt1": "value"}'])
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(
sorted(self.transm.proceed(["get", self.jailName,
"actionproperties", action])[1]),
@ -704,7 +728,7 @@ class TransmitterLogging(TransmitterBase):
os.close(f)
self.server.setLogLevel("WARNING")
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")
try:
f2, fn2 = tempfile.mkstemp("fail2ban.log")
@ -778,5 +802,19 @@ class RegexTests(unittest.TestCase):
self.assertTrue(fr.hasMatched())
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"))

View File

@ -22,89 +22,17 @@ __author__ = "Yaroslav Halchenko"
__copyright__ = "Copyright (c) 2013 Yaroslav Halchenko"
__license__ = "GPL"
import logging, os, re, traceback, time, unittest
from os.path import basename, dirname
import logging
import os
import re
import time
import unittest
from StringIO import StringIO
from ..server.mytime import MyTime
from ..helpers import getLogger
logSys = logging.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)
logSys = getLogger(__name__)
def mtimesleep():
# 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
tests = unittest.TestSuite()
else: # pragma: no cover
import re
class FilteredTestSuite(unittest.TestSuite):
_regexps = [re.compile(r) for r in regexps]
def addTest(self, suite):
@ -163,6 +90,7 @@ def gatherTests(regexps=None, no_network=False):
tests.addTest(unittest.makeSuite(servertestcase.Transmitter))
tests.addTest(unittest.makeSuite(servertestcase.JailTests))
tests.addTest(unittest.makeSuite(servertestcase.RegexTests))
tests.addTest(unittest.makeSuite(servertestcase.LoggingTests))
tests.addTest(unittest.makeSuite(actiontestcase.CommandActionTest))
tests.addTest(unittest.makeSuite(actionstestcase.ExecuteActions))
# FailManager
@ -259,7 +187,7 @@ class LogCaptureTestCase(unittest.TestCase):
# For extended testing of what gets output into logging
# system, we will redirect it to a string
logSys = logging.getLogger("fail2ban")
logSys = getLogger("fail2ban")
# Keep old settings
self._old_level = logSys.level
@ -272,7 +200,7 @@ class LogCaptureTestCase(unittest.TestCase):
def tearDown(self):
"""Call after every test case."""
# print "O: >>%s<<" % self._log.getvalue()
logSys = logging.getLogger("fail2ban")
logSys = getLogger("fail2ban")
logSys.handlers = self._old_handlers
logSys.level = self._old_level

View File

@ -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"
__license__ = "GPL-v2+"
version = "0.9.0"
version = "0.9.0.dev"

View File

@ -154,7 +154,7 @@ _fail2ban () {
fi
return 0
;;
delfailregex|delignoregex)
delfailregex|delignoreregex)
COMPREPLY=( $( compgen -W \
"$( "$1" get "$jail" "${prev/del/}" 2>/dev/null | awk -F"[][]" '{print $2}')" \
-- "$cur" ) )

Some files were not shown because too many files have changed in this diff Show More