Compare commits

...
Author SHA1 Message Date
Yaroslav Halchenko 180a98db85 disabling entirely named-refused-udp jail with a big fat warning
git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@762 a942ae1a-1317-0410-a47c-b1dcaea8d605
2010-06-29 01:38:05 +00:00
Yaroslav Halchenko 24d8e29ace ENH: adding gen_badbots script to generate apache-badbots.conf
git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@760 a942ae1a-1317-0410-a47c-b1dcaea8d605
2010-06-22 14:33:05 +00:00
Arturo 'Buanzo' Busleiman 4f4f0399ef added psad filter and jail, needs some reviewing. works OK.
git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@759 a942ae1a-1317-0410-a47c-b1dcaea8d605
2010-05-01 13:26:02 +00:00
Arturo 'Buanzo' Busleiman 5aec43ae35 Added a "ban ip" command to fail2ban-client (manual IP banning for a certain jail).
Added two new jails: lighttpd-fastcgi and php-url-fopen.
Fixed release date for 0.8.3 (was ??/??).
Added "beta" version 0.8.4 which new items (see above).


git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@734 a942ae1a-1317-0410-a47c-b1dcaea8d605
2009-08-28 22:26:17 +00:00
Arturo 'Buanzo' Busleiman cdcc0cebfd Updated Changelog for 0.9.0 with manual IP ban command. Also buanzo's "test-commit".
git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@733 a942ae1a-1317-0410-a47c-b1dcaea8d605
2009-08-28 16:05:17 +00:00
Cyril Jaquier c7151895d8 - Added missing ignoreregex to filters. Thanks to Klaus Lehmann.
git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@699 a942ae1a-1317-0410-a47c-b1dcaea8d605
2008-05-21 22:17:00 +00:00
Cyril Jaquier ba1006bd7d - Removed deprecated mail*.conf actions.
git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@698 a942ae1a-1317-0410-a47c-b1dcaea8d605
2008-05-19 21:09:39 +00:00
Cyril Jaquier 2f629525d9 - Added and changed some logging level and messages.
git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@697 a942ae1a-1317-0410-a47c-b1dcaea8d605
2008-05-19 21:08:36 +00:00
Cyril Jaquier 9aa7c96b69 - Modified failregex to support new prefix remover.
git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@695 a942ae1a-1317-0410-a47c-b1dcaea8d605
2008-05-18 20:08:34 +00:00
Cyril Jaquier f9a9d249ef - Added new prefix remover.
- Added ISO 8601 date/time format.

git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@694 a942ae1a-1317-0410-a47c-b1dcaea8d605
2008-05-18 20:06:38 +00:00
Cyril Jaquier 419005fd1f - Fixed ignoreregex processing in fail2ban-client. Thanks to René Berber.
git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@691 a942ae1a-1317-0410-a47c-b1dcaea8d605
2008-05-12 09:03:04 +00:00
Cyril Jaquier 1b3828421e - Initial commit of the new prefix remover.
git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@688 a942ae1a-1317-0410-a47c-b1dcaea8d605
2008-05-05 21:22:27 +00:00
Cyril Jaquier 543741f6e8 - Updated a few more files.
git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@687 a942ae1a-1317-0410-a47c-b1dcaea8d605
2008-04-13 17:57:44 +00:00
Cyril Jaquier b070dc89aa - Overwrote head with some of 0.8.
git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@686 a942ae1a-1317-0410-a47c-b1dcaea8d605
2008-04-13 17:48:52 +00:00
Cyril Jaquier cc57497f5b - Oupss... Forget to commit this one. Thanks to Robert Vogelgesang
git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@625 a942ae1a-1317-0410-a47c-b1dcaea8d605
2007-11-12 22:46:32 +00:00
Cyril Jaquier 155f246354 - Fixed ipfw action script. Thanks to Nick Munger
git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@624 a942ae1a-1317-0410-a47c-b1dcaea8d605
2007-10-23 22:07:35 +00:00
Cyril Jaquier 714aa1f46b - Merged changes from FAIL2BAN-0_8
git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@622 a942ae1a-1317-0410-a47c-b1dcaea8d605
2007-10-06 17:57:50 +00:00
Cyril Jaquier 4c2cdb67c7 - Replaced ssocket.py with asyncore/asynchat implementation. Correct fix for bug #1769616
git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@620 a942ae1a-1317-0410-a47c-b1dcaea8d605
2007-09-09 22:00:45 +00:00
Cyril Jaquier d0259bb790 - Removed textToDns() which is not required anymore. Thanks to Yaroslav Halchenko
git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@619 a942ae1a-1317-0410-a47c-b1dcaea8d605
2007-09-09 21:51:11 +00:00
Cyril Jaquier 9e2430a80c - Improved regular expressions. Thanks to Yaroslav Halchenko
git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@593 a942ae1a-1317-0410-a47c-b1dcaea8d605
2007-06-25 21:59:38 +00:00
Cyril Jaquier bde90df32c - Expand <HOST> in ignoreregex. Thanks to Yaroslav Halchenko
git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@591 a942ae1a-1317-0410-a47c-b1dcaea8d605
2007-06-25 21:52:58 +00:00
Cyril Jaquier 475aa68c11 - Fixed vulnerability in sshd.conf. Thanks to Daniel B. Cid
git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@588 a942ae1a-1317-0410-a47c-b1dcaea8d605
2007-06-07 21:29:25 +00:00
Cyril Jaquier 2f0dedf206 - Fixed pure-ftpd regex when the LogPID option is on. Thanks to Roderick Schertler
git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@586 a942ae1a-1317-0410-a47c-b1dcaea8d605
2007-05-29 20:08:37 +00:00
Cyril Jaquier 9ace6dfa2e - Made interactive mode optional in fail2ban-client
git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@585 a942ae1a-1317-0410-a47c-b1dcaea8d605
2007-05-22 19:51:53 +00:00
Cyril Jaquier b71c6335f4 - Updated
git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@584 a942ae1a-1317-0410-a47c-b1dcaea8d605
2007-05-21 21:18:07 +00:00
Cyril Jaquier c1f5c4d189 - Removed Python 2.4 code. Need more testing
git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@583 a942ae1a-1317-0410-a47c-b1dcaea8d605
2007-05-20 21:46:23 +00:00
Cyril Jaquier e39d79b013 - Updated
git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@582 a942ae1a-1317-0410-a47c-b1dcaea8d605
2007-05-20 21:33:27 +00:00
Cyril Jaquier 2eaff70956 - Changed label
git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@581 a942ae1a-1317-0410-a47c-b1dcaea8d605
2007-05-20 21:21:09 +00:00
85 changed files with 2448 additions and 1392 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>fail2ban-unstable</name>
<name>fail2ban-head</name>
<comment></comment>
<projects>
</projects>
+5 -5
View File
@@ -2,11 +2,11 @@
<?eclipse-pydev version="1.0"?>
<pydev_project>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.4</pydev_property>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.3</pydev_property>
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
<path>/fail2ban-unstable/client</path>
<path>/fail2ban-unstable/server</path>
<path>/fail2ban-unstable/testcases</path>
<path>/fail2ban-unstable</path>
<path>/fail2ban-head/client</path>
<path>/fail2ban-head/server</path>
<path>/fail2ban-head/testcases</path>
<path>/fail2ban-head</path>
</pydev_pathproperty>
</pydev_project>
+29
View File
@@ -1,3 +1,7 @@
The following copyright applies to all files present in the Fail2ban package,
except if a different copyright is explicitly defined in this file.
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
@@ -337,3 +341,28 @@ proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
---------------------------------
The file server/iso8601.py is licensed under the following terms.
Copyright (c) 2007 Michael Twomey
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+89 -1
View File
@@ -4,9 +4,97 @@
|_| \__,_|_|_/___|_.__/\__,_|_||_|
=============================================================
Fail2Ban (version 0.8.0) 2007/05/03
Fail2Ban (version 0.9.0) 2009/??/??
=============================================================
ver. 0.9.0 (2009/??/??) - alpha
----------
- Added new prefix remover.
- Added ISO 8601 date/time format.
- Removed deprecated mail*.conf actions.
ver. 0.8.4 (2009/??/??) - beta
----------
- Added "banip" command to fail2ban-client. Manually ban a given IP for a given jail.
- Added jails lighttpd-fastci and php-url-fopen
ver. 0.8.3 (2008/07/18) - stable
----------
- Process failtickets as long as failmanager is not empty.
- Added "pam-generic" filter and more configuration fixes.
Thanks to Yaroslav Halchenko.
- Fixed socket path in redhat and suse init script. Thanks to
Jim Wight.
- Fixed PID file while started in daemon mode. Thanks to
Christian Jobic who submitted a similar patch.
- Fixed "fail2ban-client get <jail> logpath". Bug #1916986.
- Changed some log levels.
- Added gssftpd filter. Thanks to Kevin Zembower.
- Added "Day/Month/Year Hour:Minute:Second" date template.
Thanks to Dennis Winter.
ver. 0.8.2 (2008/03/06) - stable
----------
- Fixed named filter. Thanks to Yaroslav Halchenko
- Fixed wrong path for apache-auth in jail.conf. Thanks to
Vincent Deffontaines
- Fixed timezone bug with epoch date template. Thanks to
Michael Hanselmann
- Added "full line failregex" patch. Thanks to Yaroslav
Halchenko. It will be possible to create stronger failregex
against log injection
- Fixed ipfw action script. Thanks to Nick Munger
- Removed date from logging message when using SYSLOG. Thanks
to Iain Lea
- Fixed "ignore IPs". Only the first value was taken into
account. Thanks to Adrien Clerc
- Moved socket to /var/run/fail2ban.
- Rewrote the communication server.
- Refactoring. Reduced number of files.
- Removed Python 2.4. Minimum required version is now Python
2.3.
- New log rotation detection algorithm.
- Print monitored files in status.
- Create a PID file in /var/run/fail2ban/. Thanks to Julien
Perez.
- Fixed "Feb 29" bug. Thanks to James Andrewartha who pointed
this out. Thanks to Yaroslav Halchenko for the fix.
- "reload <jail>" reloads a single jail and the parameters in
fail2ban.conf.
- Added Mac OS/X startup script. Thanks to Bill Heaton.
- Absorbed some Debian patches. Thanks to Yaroslav Halchenko.
- Replaced "echo" with "printf" in actions. Fix #1839673
- Replaced "reject" with "drop" in shorwall action. Fix
#1854875
- Fixed Debian bug #456567, #468477, #462060, #461426
- readline is now optional in fail2ban-client (not needed in
fail2ban-server).
ver. 0.8.1 (2007/08/14) - stable
----------
- Fixed vulnerability in sshd.conf. Thanks to Daniel B. Cid
- Expand <HOST> in ignoreregex. Thanks to Yaroslav Halchenko
- Improved regular expressions. Thanks to Yaroslav Halchenko
and others
- Added sendmail actions. The action started with "mail" are
now deprecated. Thanks to Raphaël Marichez
- Added "ignoreregex" support to fail2ban-regex
- Updated suse-initd and added it to MANIFEST. Thanks to
Christian Rauch
- Tightening up the pid check in redhat-initd. Thanks to
David Nutter
- Added webmin authentication filter. Thanks to Guillaume
Delvit
- Removed textToDns() which is not required anymore. Thanks
to Yaroslav Halchenko
- Added new action iptables-allports. Thanks to Yaroslav
Halchenko
- Added "named" date format to date detector. Thanks to
Yaroslav Halchenko
- Added filter file for named (bind9). Thanks to Yaroslav
Halchenko
- Fixed vsftpd filter. Thanks to Yaroslav Halchenko
ver. 0.8.0 (2007/05/03) - stable
----------
- Fixed RedHat init script. Thanks to Jonathan Underwood
+23 -15
View File
@@ -1,5 +1,5 @@
README
CHANGELOG
ChangeLog
TODO
COPYING
fail2ban-client
@@ -7,6 +7,7 @@ fail2ban-server
fail2ban-testcases
fail2ban-regex
client/configreader.py
client/configparserinc.py
client/jailreader.py
client/fail2banreader.py
client/jailsreader.py
@@ -16,38 +17,34 @@ client/actionreader.py
client/__init__.py
client/configurator.py
client/csocket.py
server/ssocket.py
server/banticket.py
server/asyncserver.py
server/filter.py
server/filtergamin.py
server/filterpoll.py
server/server.py
server/datestrptime.py
server/failticket.py
server/iso8601.py
server/actions.py
server/datetai64n.py
server/faildata.py
server/failmanager.py
server/datedetector.py
server/jailthread.py
server/transmitter.py
server/template.py
server/hosttemplate.py
server/action.py
server/ticket.py
server/jail.py
server/jails.py
server/__init__.py
server/dateepoch.py
server/banmanager.py
server/datetemplate.py
server/timetemplate.py
server/prefixtemplate.py
server/mytime.py
server/regex.py
server/failregex.py
testcases/banmanagertestcase.py
testcases/failmanagertestcase.py
testcases/clientreadertestcase.py
testcases/filtertestcase.py
testcases/__init__.py
testcases/datedetectortestcase.py
testcases/actiontestcase.py
testcases/servertestcase.py
testcases/files/testcase01.log
@@ -60,30 +57,39 @@ common/__init__.py
common/version.py
common/protocol.py
config/jail.conf
config/filter.d/common.conf
config/filter.d/apache-auth.conf
config/filter.d/apache-badbots.conf
config/filter.d/apache-noscript.conf
config/filter.d/apache-overflows.conf
config/filter.d/courierlogin.conf
config/filter.d/couriersmtp.conf
config/filter.d/exim.conf
config/filter.d/gssftpd.conf
config/filter.d/named-refused.conf
config/filter.d/postfix.conf
config/filter.d/proftpd.conf
config/filter.d/pure-ftpd.conf
config/filter.d/qmail.conf
config/filter.d/pam-generic.conf
config/filter.d/sasl.conf
config/filter.d/sshd.conf
config/filter.d/sshd-ddos.conf
config/filter.d/vsftpd.conf
config/filter.d/webmin-auth.conf
config/filter.d/wuftpd.conf
config/filter.d/xinetd-fail.conf
config/action.d/hostsdeny.conf
config/action.d/ipfw.conf
config/action.d/iptables.conf
config/action.d/iptables-allports.conf
config/action.d/iptables-multiport.conf
config/action.d/iptables-multiport-log.conf
config/action.d/iptables-new.conf
config/action.d/mail.conf
config/action.d/mail-buffered.conf
config/action.d/mail-whois.conf
config/action.d/mail-whois-lines.conf
config/action.d/sendmail.conf
config/action.d/sendmail-buffered.conf
config/action.d/sendmail-whois.conf
config/action.d/sendmail-whois-lines.conf
config/action.d/shorewall.conf
config/fail2ban.conf
man/fail2ban-client.1
@@ -96,8 +102,10 @@ man/generate-man
files/gentoo-initd
files/gentoo-confd
files/redhat-initd
files/macosx-initd
files/solaris-fail2ban.xml
files/solaris-svc-fail2ban
files/suse-initd
files/cacti/fail2ban_stats.sh
files/cacti/cacti_host_template_fail2ban.xml
files/cacti/README
+9 -6
View File
@@ -4,7 +4,7 @@
|_| \__,_|_|_/___|_.__/\__,_|_||_|
=============================================================
Fail2Ban (version 0.8.0) 2007/05/03
Fail2Ban (version 0.8.3) 2008/??/??
=============================================================
Fail2Ban scans log files like /var/log/pwdfail and bans IP
@@ -21,15 +21,15 @@ Installation:
-------------
Required:
>=python-2.4 (http://www.python.org)
>=python-2.3 (http://www.python.org)
Optional:
>=gamin-0.0.21 (http://www.gnome.org/~veillard/gamin)
To install, just do:
> tar xvfj fail2ban-0.8.0.tar.bz2
> cd fail2ban-0.8.0
> tar xvfj fail2ban-0.8.3.tar.bz2
> cd fail2ban-0.8.3
> python setup.py install
This will install Fail2Ban into /usr/share/fail2ban. The
@@ -62,7 +62,7 @@ appreciate this program, you can contact me at:
Website: http://www.fail2ban.org
Cyril Jaquier: <lostcontrol@users.sourceforge.net>
Cyril Jaquier: <cyril.jaquier@fail2ban.org>
Thanks:
-------
@@ -73,7 +73,10 @@ Jonathan Kamens, Stephen Gildea, Markus Hoffmann, Mark
Edgington, Patrick Börjesson, kojiro, zugeschmiert, Tyler,
Nick Munger, Christoph Haas, Justin Shore, Joël Bertrand,
René Berber, mEDI, Axel Thimm, Eric Gerbier, Christian Rauch,
Michael C. Haller, Jonathan Underwood, Hanno 'Rince' Wagner
Michael C. Haller, Jonathan Underwood, Hanno 'Rince' Wagner,
Daniel B. Cid, David Nutter, Raphaël Marichez, Guillaume
Delvit, Vaclav Misek, Adrien Clerc, Michael Hanselmann,
Vincent Deffontaines, Bill Heaton and many others.
License:
--------
-7
View File
@@ -15,9 +15,6 @@ Legend:
- Removed relative imports
- Discuss where Fail2ban should be installed (/usr/share,
/usr/lib/python/site-packages/, etc)
- Cleanup fail2ban-client and fail2ban-server. Move code to
server/ and client/
@@ -45,12 +42,8 @@ Legend:
- Add gettext support (I18N)
- Fix the cPickle issue with Python 2.5
- Multiline log reading
- Improve communication. (asyncore, asynchat??)
- Improve execution of action. Why does subprocess.call
deadlock with multi-jails?
+5
View File
@@ -72,9 +72,14 @@ class Beautifier:
ipList = ""
for ip in response[1][1][2][1]:
ipList += ip + " "
# Creates file list.
fileList = ""
for f in response[0][1][2][1]:
fileList += f + " "
# Display information
msg = "Status for the jail: " + inC[1] + "\n"
msg = msg + "|- " + response[0][0] + "\n"
msg = msg + "| |- " + response[0][1][2][0] + ":\t" + fileList + "\n"
msg = msg + "| |- " + response[0][1][0][0] + ":\t" + `response[0][1][0][1]` + "\n"
msg = msg + "| `- " + response[0][1][1][0] + ":\t" + `response[0][1][1][1]` + "\n"
msg = msg + "`- " + response[1][0] + "\n"
+108
View File
@@ -0,0 +1,108 @@
# This file is part of Fail2Ban.
#
# Fail2Ban is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# Fail2Ban is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Fail2Ban; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# Author: Yaroslav Halchenko
# Modified: Cyril Jaquier
# $Revision: 656 $
__author__ = 'Yaroslav Halhenko'
__revision__ = '$Revision: $'
__date__ = '$Date: $'
__copyright__ = 'Copyright (c) 2007 Yaroslav Halchenko'
__license__ = 'GPL'
import logging, os
from ConfigParser import SafeConfigParser
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.client.config")
class SafeConfigParserWithIncludes(SafeConfigParser):
"""
Class adds functionality to SafeConfigParser to handle included
other configuration files (or may be urls, whatever in the future)
File should have section [includes] and only 2 options implemented
are 'files_before' and 'files_after' where files are listed 1 per
line.
Example:
[INCLUDES]
before = 1.conf
3.conf
after = 1.conf
It is a simple implementation, so just basic care is taken about
recursion. Includes preserve right order, ie new files are
inserted to the list of read configs before original, and their
includes correspondingly so the list should follow the leaves of
the tree.
I wasn't sure what would be the right way to implement generic (aka c++
template) so we could base at any *configparser class... so I will
leave it for the future
"""
SECTION_NAME = "INCLUDES"
#@staticmethod
def getIncludes(resource, seen = []):
"""
Given 1 config resource returns list of included files
(recursively) with the original one as well
Simple loops are taken care about
"""
# Use a short class name ;)
SCPWI = SafeConfigParserWithIncludes
parser = SafeConfigParser()
parser.read(resource)
resourceDir = os.path.dirname(resource)
newFiles = [ ('before', []), ('after', []) ]
if SCPWI.SECTION_NAME in parser.sections():
for option_name, option_list in newFiles:
if option_name in parser.options(SCPWI.SECTION_NAME):
newResources = parser.get(SCPWI.SECTION_NAME, option_name)
for newResource in newResources.split('\n'):
if os.path.isabs(newResource):
r = newResource
else:
r = "%s/%s" % (resourceDir, newResource)
if r in seen:
continue
s = seen + [resource]
option_list += SCPWI.getIncludes(r, s)
# combine lists
return newFiles[0][1] + [resource] + newFiles[1][1]
#print "Includes list for " + resource + " is " + `resources`
getIncludes = staticmethod(getIncludes)
def read(self, filenames):
fileNamesFull = []
if not isinstance(filenames, list):
filenames = [ filenames ]
for filename in filenames:
fileNamesFull += SafeConfigParserWithIncludes.getIncludes(filename)
logSys.debug("Reading files: %s" % fileNamesFull)
return SafeConfigParser.read(self, fileNamesFull)
+9 -7
View File
@@ -15,7 +15,7 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# Author: Cyril Jaquier
#
# Modified by: Yaroslav Halchenko (SafeConfigParserWithIncludes)
# $Revision$
__author__ = "Cyril Jaquier"
@@ -25,28 +25,30 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import logging, os
from ConfigParser import SafeConfigParser
from configparserinc import SafeConfigParserWithIncludes
from ConfigParser import NoOptionError, NoSectionError
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.client.config")
class ConfigReader(SafeConfigParser):
class ConfigReader(SafeConfigParserWithIncludes):
BASE_DIRECTORY = "/etc/fail2ban/"
def __init__(self):
SafeConfigParser.__init__(self)
SafeConfigParserWithIncludes.__init__(self)
self.__opts = None
@staticmethod
#@staticmethod
def setBaseDir(folderName):
path = folderName.rstrip('/')
ConfigReader.BASE_DIRECTORY = path + '/'
setBaseDir = staticmethod(setBaseDir)
@staticmethod
#@staticmethod
def getBaseDir():
return ConfigReader.BASE_DIRECTORY
getBaseDir = staticmethod(getBaseDir)
def read(self, filename):
basename = ConfigReader.BASE_DIRECTORY + filename
@@ -54,7 +56,7 @@ class ConfigReader(SafeConfigParser):
bConf = basename + ".conf"
bLocal = basename + ".local"
if os.path.exists(bConf) or os.path.exists(bLocal):
SafeConfigParser.read(self, [bConf, bLocal])
SafeConfigParserWithIncludes.read(self, [bConf, bLocal])
return True
else:
logSys.error(bConf + " and " + bLocal + " do not exist")
+8 -6
View File
@@ -40,13 +40,15 @@ class Configurator:
self.__fail2ban = Fail2banReader()
self.__jails = JailsReader()
@staticmethod
#@staticmethod
def setBaseDir(folderName):
ConfigReader.setBaseDir(folderName)
setBaseDir = staticmethod(setBaseDir)
@staticmethod
#@staticmethod
def getBaseDir():
return ConfigReader.getBaseDir()
getBaseDir = staticmethod(getBaseDir)
def readEarly(self):
self.__fail2ban.read()
@@ -54,13 +56,13 @@ class Configurator:
def readAll(self):
self.readEarly()
self.__jails.read()
def getEarlyOptions(self):
return self.__fail2ban.getEarlyOptions()
def getAllOptions(self):
def getOptions(self, jail = None):
self.__fail2ban.getOptions()
return self.__jails.getOptions()
return self.__jails.getOptions(jail)
def convertToProtocol(self):
self.__streams["general"] = self.__fail2ban.convert()
+3 -2
View File
@@ -32,7 +32,7 @@ class CSocket:
END_STRING = "<F2B_END_COMMAND>"
def __init__(self, sock = "/tmp/fail2ban.sock"):
def __init__(self, sock = "/var/run/fail2ban/fail2ban.sock"):
# Create an INET, STREAMing socket
#self.csock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.__csock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
@@ -47,7 +47,7 @@ class CSocket:
self.__csock.close()
return ret
@staticmethod
#@staticmethod
def receive(sock):
msg = ''
while msg.rfind(CSocket.END_STRING) == -1:
@@ -56,3 +56,4 @@ class CSocket:
raise RuntimeError, "socket connection broken"
msg = msg + chunk
return loads(msg)
receive = staticmethod(receive)
+7 -3
View File
@@ -90,7 +90,7 @@ class JailReader(ConfigReader):
self.__actions.append(action)
else:
raise AttributeError("Unable to read action")
except AttributeError, e:
except Exception, e:
logSys.error("Error in action definition " + act)
logSys.debug(e)
return False
@@ -122,14 +122,17 @@ class JailReader(ConfigReader):
elif opt == "failregex":
stream.append(["set", self.__name, "failregex", self.__opts[opt]])
elif opt == "ignoreregex":
stream.append(["set", self.__name, "ignoreregex", self.__opts[opt]])
for regex in self.__opts[opt].split('\n'):
# Do not send a command if the rule is empty.
if regex != '':
stream.append(["set", self.__name, "addignoreregex", regex])
stream.extend(self.__filter.convert())
for action in self.__actions:
stream.extend(action.convert())
stream.insert(0, ["add", self.__name, backend])
return stream
@staticmethod
#@staticmethod
def splitAction(action):
m = JailReader.actionCRE.match(action)
d = dict()
@@ -165,3 +168,4 @@ class JailReader(ConfigReader):
except IndexError:
logSys.error("Invalid argument %s in '%s'" % (p, m.group(2)))
return [m.group(1), d]
splitAction = staticmethod(splitAction)
+18 -4
View File
@@ -40,12 +40,13 @@ class JailsReader(ConfigReader):
def read(self):
ConfigReader.read(self, "jail")
def getOptions(self):
def getOptions(self, section = None):
opts = []
self.__opts = ConfigReader.getOptions(self, "Definition", opts)
for sec in self.sections():
jail = JailReader(sec)
if section:
# Get the options of a specific jail.
jail = JailReader(section)
jail.read()
ret = jail.getOptions()
if ret:
@@ -53,8 +54,21 @@ class JailsReader(ConfigReader):
# We only add enabled jails
self.__jails.append(jail)
else:
logSys.error("Errors in jail '" + sec + "'. Skipping...")
logSys.error("Errors in jail '%s'. Skipping..." % section)
return False
else:
# Get the options of all jails.
for sec in self.sections():
jail = JailReader(sec)
jail.read()
ret = jail.getOptions()
if ret:
if jail.isEnabled():
# We only add enabled jails
self.__jails.append(jail)
else:
logSys.error("Errors in jail '" + sec + "'. Skipping...")
return False
return True
def convert(self):
+3 -3
View File
@@ -33,6 +33,7 @@ protocol = [
['', "BASIC", ""],
["start", "starts the server and the jails"],
["reload", "reloads the configuration"],
["reload <JAIL>", "reloads the jail <JAIL>"],
["stop", "stops all jails and terminate the server"],
["status", "gets the current status of the server"],
["ping", "tests if the server is alive"],
@@ -51,15 +52,14 @@ protocol = [
["set <JAIL> addignoreip <IP>", "adds <IP> to the ignore list of <JAIL>"],
["set <JAIL> delignoreip <IP>", "removes <IP> from the ignore list of <JAIL>"],
["set <JAIL> addlogpath <FILE>", "adds <FILE> to the monitoring list of <JAIL>"],
["set <JAIL> dellogpath <FILE>", "removes <FILE> to the monitoring list of <JAIL>"],
["set <JAIL> timeregex <REGEX>", "sets the regular expression <REGEX> to match the date format for <JAIL>. This will disable the autodetection feature."],
["set <JAIL> timepattern <PATTERN>", "sets the pattern <PATTERN> to match the date format for <JAIL>. This will disable the autodetection feature."],
["set <JAIL> dellogpath <FILE>", "removes <FILE> to the monitoring list of <JAIL>"],
["set <JAIL> addfailregex <REGEX>", "adds the regular expression <REGEX> which must match failures for <JAIL>"],
["set <JAIL> delfailregex <INDEX>", "removes the regular expression at <INDEX> for failregex"],
["set <JAIL> addignoreregex <REGEX>", "adds the regular expression <REGEX> which should match pattern to exclude for <JAIL>"],
["set <JAIL> delignoreregex <INDEX>", "removes the regular expression at <INDEX> for ignoreregex"],
["set <JAIL> findtime <TIME>", "sets the number of seconds <TIME> for which the filter will look back for <JAIL>"],
["set <JAIL> bantime <TIME>", "sets the number of seconds <TIME> a host will be banned for <JAIL>"],
["set <JAIL> banip <IP>", "manually Ban <IP> for <JAIL>"],
["set <JAIL> maxretry <RETRY>", "sets the number of failures <RETRY> before banning the host for <JAIL>"],
["set <JAIL> addaction <ACT>", "adds a new action named <NAME> for <JAIL>"],
["set <JAIL> delaction <ACT>", "removes the action <NAME> from <JAIL>"],
+1 -1
View File
@@ -24,4 +24,4 @@ __date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
version = "0.8.0"
version = "0.9-SVN"
+4 -4
View File
@@ -12,9 +12,9 @@
# Values: CMD
#
actionstart = touch /tmp/fail2ban.dummy
echo "<init>" >> /tmp/fail2ban.dummy
printf %%b "<init>\n" >> /tmp/fail2ban.dummy
# Option: actionend
# Option: actionstop
# Notes.: command executed once at the end of Fail2Ban
# Values: CMD
#
@@ -34,7 +34,7 @@ actioncheck =
# <time> unix timestamp of the ban time
# Values: CMD
#
actionban = echo "+<ip>" >> /tmp/fail2ban.dummy
actionban = printf %%b "+<ip>\n" >> /tmp/fail2ban.dummy
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
@@ -44,7 +44,7 @@ actionban = echo "+<ip>" >> /tmp/fail2ban.dummy
# <time> unix timestamp of the ban time
# Values: CMD
#
actionunban = echo "-<ip>" >> /tmp/fail2ban.dummy
actionunban = printf %%b "-<ip>\n" >> /tmp/fail2ban.dummy
[Init]
+2 -2
View File
@@ -13,7 +13,7 @@
#
actionstart =
# Option: actionend
# Option: actionstop
# Notes.: command executed once at the end of Fail2Ban
# Values: CMD
#
@@ -34,7 +34,7 @@ actioncheck =
# Values: CMD
#
actionban = IP=<ip> &&
echo "ALL: $IP" >> <file>
printf %%b "ALL: $IP\n" >> <file>
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
+3 -3
View File
@@ -15,7 +15,7 @@
actionstart =
# Option: actionend
# Option: actionstop
# Notes.: command executed once at the end of Fail2Ban
# Values: CMD
#
@@ -37,7 +37,7 @@ actioncheck =
# <time> unix timestamp of the ban time
# Values: CMD
#
actionban = ipaction add deny tcp from <ip> to <localhost> <port>
actionban = ipfw add deny tcp from <ip> to <localhost> <port>
# Option: actionunban
@@ -48,7 +48,7 @@ actionban = ipaction add deny tcp from <ip> to <localhost> <port>
# <time> unix timestamp of the ban time
# Values: CMD
#
actionunban = ipaction delete `ipfw list | grep -i <ip> | awk '{print $1;}'`
actionunban = ipfw delete `ipfw list | grep -i <ip> | awk '{print $1;}'`
[Init]
+65
View File
@@ -0,0 +1,65 @@
# Fail2Ban configuration file
#
# Author: Cyril Jaquier
# Modified: Yaroslav O. Halchenko <debian@onerussian.com>
# made active on all ports from original iptables.conf
#
# $Revision: 658 $
#
[Definition]
# Option: actionstart
# Notes.: command executed once at the start of Fail2Ban.
# Values: CMD
#
actionstart = iptables -N fail2ban-<name>
iptables -A fail2ban-<name> -j RETURN
iptables -I INPUT -p <protocol> -j fail2ban-<name>
# Option: actionstop
# Notes.: command executed once at the end of Fail2Ban
# Values: CMD
#
actionstop = iptables -D INPUT -p <protocol> -j fail2ban-<name>
iptables -F fail2ban-<name>
iptables -X fail2ban-<name>
# Option: actioncheck
# Notes.: command executed once before each actionban command
# Values: CMD
#
actioncheck = iptables -n -L INPUT | grep -q fail2ban-<name>
# 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 = iptables -I fail2ban-<name> 1 -s <ip> -j DROP
# 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 = iptables -D fail2ban-<name> -s <ip> -j DROP
[Init]
# Defaut 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
@@ -0,0 +1,78 @@
# Fail2Ban configuration file
#
# Author: Guido Bozzetto
# Modified: Cyril Jaquier
#
# make "fail2ban-<name>" chain to match drop IP
# make "fail2ban-<name>-log" chain to log and drop
# insert a jump to fail2ban-<name> from -I INPUT if proto/port match
#
# $Revision: 668 $
#
[Definition]
# Option: actionstart
# Notes.: command executed once at the start of Fail2Ban.
# Values: CMD
#
actionstart = iptables -N fail2ban-<name>
iptables -A fail2ban-<name> -j RETURN
iptables -I INPUT 1 -p <protocol> -m multiport --dports <port> -j fail2ban-<name>
iptables -N fail2ban-<name>-log
iptables -I fail2ban-<name>-log -j LOG --log-prefix "$(expr fail2ban-<name> : '\(.\{1,23\}\)'):DROP " --log-level warning -m limit --limit 6/m --limit-burst 2
iptables -A fail2ban-<name>-log -j DROP
# Option: actionstop
# Notes.: command executed once at the end of Fail2Ban
# Values: CMD
#
actionstop = iptables -D INPUT -p <protocol> -m multiport --dports <port> -j fail2ban-<name>
iptables -F fail2ban-<name>
iptables -F fail2ban-<name>-log
iptables -X fail2ban-<name>
iptables -X fail2ban-<name>-log
# Option: actioncheck
# Notes.: command executed once before each actionban command
# Values: CMD
#
actioncheck = iptables -n -L fail2ban-<name>-log >/dev/null
# 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 = iptables -I fail2ban-<name> 1 -s <ip> -j fail2ban-<name>-log
# 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 = iptables -D fail2ban-<name> -s <ip> -j fail2ban-<name>-log
[Init]
# Defaut 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
+1 -1
View File
@@ -17,7 +17,7 @@ actionstart = iptables -N fail2ban-<name>
iptables -A fail2ban-<name> -j RETURN
iptables -I INPUT -m state --state NEW -p <protocol> --dport <port> -j fail2ban-<name>
# Option: actionend
# Option: actionstop
# Notes.: command executed once at the end of Fail2Ban
# Values: CMD
#
-75
View File
@@ -1,75 +0,0 @@
# Fail2Ban configuration file
#
# Author: Cyril Jaquier
# Modified-By: Yaroslav Halchenko to include grepping on IP over log files
# $Revision$
#
[Definition]
# Option: fwstart
# Notes.: command executed once at the start of Fail2Ban.
# Values: CMD
#
actionstart = echo -en "Hi,\n
The jail <name> has been started successfuly.\n
Regards,\n
Fail2Ban"|mail -s "[Fail2Ban] <name>: started" <dest>
# Option: fwend
# Notes.: command executed once at the end of Fail2Ban
# Values: CMD
#
actionstop = echo -en "Hi,\n
The jail <name> has been stopped.\n
Regards,\n
Fail2Ban"|mail -s "[Fail2Ban] <name>: stopped" <dest>
# Option: fwcheck
# Notes.: command executed once before each fwban command
# Values: CMD
#
actioncheck =
# Option: fwban
# 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
# <failtime> unix timestamp of the last failure
# <bantime> unix timestamp of the ban time
# Values: CMD
#
actionban = echo -en "Hi,\n
The IP <ip> has just been banned by Fail2Ban after
<failures> attempts against <name>.\n\n
Here are more information about <ip>:\n
`whois <ip>`\n\n
Lines containing IP:<ip> in <logpath>\n
`grep '\<<ip>\>' <logpath>`\n\n
Regards,\n
Fail2Ban"|mail -s "[Fail2Ban] <name>: banned <ip>" <dest>
# Option: fwunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <bantime> unix timestamp of the ban time
# <unbantime> unix timestamp of the unban time
# Values: CMD
#
actionunban =
[Init]
# Defaut name of the chain
#
name = default
# Destinataire of the mail
#
dest = root
# Path to the log files which contain relevant lines for the abuser IP
#
logpath = /dev/null
@@ -2,7 +2,7 @@
#
# Author: Cyril Jaquier
#
# $Revision: 510 $
# $Revision: 660 $
#
[Definition]
@@ -11,28 +11,37 @@
# Notes.: command executed once at the start of Fail2Ban.
# Values: CMD
#
actionstart = echo -en "Hi,\n
The jail <name> has been started successfuly.\n
actionstart = printf %%b "Subject: [Fail2Ban] <name>: started
From: Fail2Ban <<sender>>
To: <dest>\n
Hi,\n
The jail <name> has been started successfully.\n
Output will be buffered until <lines> lines are available.\n
Regards,\n
Fail2Ban"|mail -s "[Fail2Ban] <name>: started" <dest>
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
# Option: actionend
# Option: actionstop
# Notes.: command executed once at the end of Fail2Ban
# Values: CMD
#
actionstop = if [ -f <tmpfile> ]; then
echo -en "Hi,\n
printf %%b "Subject: [Fail2Ban] <name>: summary
From: Fail2Ban <<sender>>
To: <dest>\n
Hi,\n
These hosts have been banned by Fail2Ban.\n
`cat <tmpfile>`
Regards,\n
Fail2Ban"|mail -s "[Fail2Ban] <name>: Summary" <dest>
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
rm <tmpfile>
fi
echo -en "Hi,\n
printf %%b "Subject: [Fail2Ban] <name>: stopped
From: Fail2Ban <<sender>>
To: <dest>\n
Hi,\n
The jail <name> has been stopped.\n
Regards,\n
Fail2Ban"|mail -s "[Fail2Ban] <name>: stopped" <dest>
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
# Option: actioncheck
# Notes.: command executed once before each actionban command
@@ -48,14 +57,17 @@ actioncheck =
# <time> unix timestamp of the ban time
# Values: CMD
#
actionban = echo `date`": <ip> (<failures> failures)" >> <tmpfile>
actionban = printf %%b "`date`: <ip> (<failures> failures)\n" >> <tmpfile>
LINE=$( wc -l <tmpfile> | awk '{ print $1 }' )
if [ $LINE -eq <lines> ]; then
echo -en "Hi,\n
printf %%b "Subject: [Fail2Ban] <name>: summary
From: Fail2Ban <<sender>>
To: <dest>\n
Hi,\n
These hosts have been banned by Fail2Ban.\n
`cat <tmpfile>`
\nRegards,\n
Fail2Ban"|mail -s "[Fail2Ban] <name>: Summary" <dest>
Regards,\n
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
rm <tmpfile>
fi
@@ -71,10 +83,18 @@ actionunban =
[Init]
# Default name of the chain
# Defaut name of the chain
#
name = default
# Destination/Addressee of the mail
#
dest = root
# Sender of the mail
#
sender = fail2ban
# Default number of lines that are buffered
#
lines = 5
@@ -83,6 +103,3 @@ lines = 5
#
tmpfile = /tmp/fail2ban-mail.txt
# Destination/Addressee of the mail
#
dest = root
+88
View File
@@ -0,0 +1,88 @@
# Fail2Ban configuration file
#
# Author: Cyril Jaquier
#
# $Revision: 660 $
#
[Definition]
# Option: actionstart
# Notes.: command executed once at the start of Fail2Ban.
# Values: CMD
#
actionstart = printf %%b "Subject: [Fail2Ban] <name>: started
From: Fail2Ban <<sender>>
To: <dest>\n
Hi,\n
The jail <name> has been started successfully.\n
Regards,\n
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
# Option: actionstop
# Notes.: command executed once at the end of Fail2Ban
# Values: CMD
#
actionstop = printf %%b "Subject: [Fail2Ban] <name>: stopped
From: Fail2Ban <<sender>>
To: <dest>\n
Hi,\n
The jail <name> has been stopped.\n
Regards,\n
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
# Option: actioncheck
# Notes.: command executed once before each actionban command
# Values: CMD
#
actioncheck =
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Values: CMD
#
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip>
From: Fail2Ban <<sender>>
To: <dest>\n
Hi,\n
The IP <ip> has just been banned by Fail2Ban after
<failures> attempts against <name>.\n\n
Here are more information about <ip>:\n
`/usr/bin/whois <ip>`\n\n
Lines containing IP:<ip> in <logpath>\n
`/bin/grep '\<<ip>\>' <logpath>`\n\n
Regards,\n
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Values: CMD
#
actionunban =
[Init]
# Defaut name of the chain
#
name = default
# Destination/Addressee of the mail
#
dest = root
# Sender of the mail
#
sender = fail2ban
# Path to the log files which contain relevant lines for the abuser IP
#
logpath = /dev/null
@@ -2,7 +2,7 @@
#
# Author: Cyril Jaquier
#
# $Revision$
# $Revision: 660 $
#
[Definition]
@@ -11,19 +11,25 @@
# Notes.: command executed once at the start of Fail2Ban.
# Values: CMD
#
actionstart = echo -en "Hi,\n
The jail <name> has been started successfuly.\n
actionstart = printf %%b "Subject: [Fail2Ban] <name>: started
From: Fail2Ban <<sender>>
To: <dest>\n
Hi,\n
The jail <name> has been started successfully.\n
Regards,\n
Fail2Ban"|mail -s "[Fail2Ban] <name>: started" <dest>
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
# Option: actionend
# Option: actionstop
# Notes.: command executed once at the end of Fail2Ban
# Values: CMD
#
actionstop = echo -en "Hi,\n
actionstop = printf %%b "Subject: [Fail2Ban] <name>: stopped
From: Fail2Ban <<sender>>
To: <dest>\n
Hi,\n
The jail <name> has been stopped.\n
Regards,\n
Fail2Ban"|mail -s "[Fail2Ban] <name>: stopped" <dest>
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
# Option: actioncheck
# Notes.: command executed once before each actionban command
@@ -39,13 +45,16 @@ actioncheck =
# <time> unix timestamp of the ban time
# Values: CMD
#
actionban = echo -en "Hi,\n
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip>
From: Fail2Ban <<sender>>
To: <dest>\n
Hi,\n
The IP <ip> has just been banned by Fail2Ban after
<failures> attempts against <name>.\n\n
Here are more information about <ip>:\n
`whois <ip>`\n
`/usr/bin/whois <ip>`\n
Regards,\n
Fail2Ban"|mail -s "[Fail2Ban] <name>: banned <ip>" <dest>
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
@@ -67,3 +76,7 @@ name = default
#
dest = root
# Sender of the mail
#
sender = fail2ban
@@ -2,7 +2,7 @@
#
# Author: Cyril Jaquier
#
# $Revision$
# $Revision: 660 $
#
[Definition]
@@ -11,19 +11,25 @@
# Notes.: command executed once at the start of Fail2Ban.
# Values: CMD
#
actionstart = echo -en "Hi,\n
The jail <name> has been started successfuly.\n
actionstart = printf %%b "Subject: [Fail2Ban] <name>: started
From: Fail2Ban <<sender>>
To: <dest>\n
Hi,\n
The jail <name> has been started successfully.\n
Regards,\n
Fail2Ban"|mail -s "[Fail2Ban] <name>: started" <dest>
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
# Option: actionend
# Option: actionstop
# Notes.: command executed once at the end of Fail2Ban
# Values: CMD
#
actionstop = echo -en "Hi,\n
actionstop = printf %%b "Subject: [Fail2Ban] <name>: stopped
From: Fail2Ban <<sender>>
To: <dest>\n
Hi,\n
The jail <name> has been stopped.\n
Regards,\n
Fail2Ban"|mail -s "[Fail2Ban] <name>: stopped" <dest>
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
# Option: actioncheck
# Notes.: command executed once before each actionban command
@@ -39,11 +45,14 @@ actioncheck =
# <time> unix timestamp of the ban time
# Values: CMD
#
actionban = echo -en "Hi,\n
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip>
From: Fail2Ban <<sender>>
To: <dest>\n
Hi,\n
The IP <ip> has just been banned by Fail2Ban after
<failures> attempts against <name>.\n
Regards,\n
Fail2Ban"|mail -s "[Fail2Ban] <name>: banned <ip>" <dest>
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
@@ -65,3 +74,7 @@ name = default
#
dest = root
# Sender of the mail
#
sender = fail2ban
+2 -2
View File
@@ -28,7 +28,7 @@ logtarget = /var/log/fail2ban.log
# Notes.: Set the socket file. This is used to communicate with the daemon. Do
# not remove this file when Fail2ban runs. It will not be possible to
# communicate with the server afterwards.
# Values: FILE Default: /tmp/fail2ban.sock
# Values: FILE Default: /var/run/fail2ban/fail2ban.sock
#
socket = /tmp/fail2ban.sock
socket = /var/run/fail2ban/fail2ban.sock
+4 -2
View File
@@ -1,14 +1,16 @@
# Fail2Ban configuration file
#
# List of bad bots fetched from http://www.user-agents.org
# Generated on Sun Feb 11 01:09:15 EST 2007 by ./badbots.sh
# Generated on Sun Feb 11 01:09:15 EST 2007 by gen_badbots
#
# Author: Yaroslav Halchenko
#
# $Revision: 668 $
#
[Definition]
badbotscustom = EmailCollector|WebEMailExtrac|TrackBack/1\.02
badbotscustom = EmailCollector|WebEMailExtrac|TrackBack/1\.02|sogou music spider
badbots = atSpider/1\.0|autoemailspider|China Local Browse 2\.6|ContentSmartz|DataCha0s/2\.0|DataCha0s/2\.0|DBrowse 1\.4b|DBrowse 1\.4d|Demo Bot DOT 16b|Demo Bot Z 16b|DSurf15a 01|DSurf15a 71|DSurf15a 81|DSurf15a VA|EBrowse 1\.4b|Educate Search VxB|EmailSiphon|EmailWolf 1\.00|ESurf15a 15|ExtractorPro|Franklin Locator 1\.8|FSurf15a 01|Full Web Bot 0416B|Full Web Bot 0516B|Full Web Bot 2816B|Industry Program 1\.0\.x|ISC Systems iRc Search 2\.1|IUPUI Research Bot v 1\.9a|LARBIN-EXPERIMENTAL \(efp@gmx\.net\)|LetsCrawl\.com/1\.0 +http\://letscrawl\.com/|Lincoln State Web Browser|LWP\:\:Simple/5\.803|Mac Finder 1\.0\.xx|MFC Foundation Class Library 4\.0|Microsoft URL Control - 6\.00\.8xxx|Missauga Locate 1\.0\.0|Missigua Locator 1\.9|Missouri College Browse|Mizzu Labs 2\.2|Mo College 1\.9|Mozilla/2\.0 \(compatible; NEWT ActiveX; Win32\)|Mozilla/3\.0 \(compatible; Indy Library\)|Mozilla/4\.0 \(compatible; Advanced Email Extractor v2\.xx\)|Mozilla/4\.0 \(compatible; Iplexx Spider/1\.0 http\://www\.iplexx\.at\)|Mozilla/4\.0 \(compatible; MSIE 5\.0; Windows NT; DigExt; DTS Agent|Mozilla/4\.0 efp@gmx\.net|Mozilla/5\.0 \(Version\: xxxx Type\:xx\)|MVAClient|NASA Search 1\.0|Nsauditor/1\.x|PBrowse 1\.4b|PEval 1\.4b|Poirot|Port Huron Labs|Production Bot 0116B|Production Bot 2016B|Production Bot DOT 3016B|Program Shareware 1\.0\.2|PSurf15a 11|PSurf15a 51|PSurf15a VA|psycheclone|RSurf15a 41|RSurf15a 51|RSurf15a 81|searchbot admin@google\.com|sogou spider|sohu agent|SSurf15a 11 |TSurf15a 11|Under the Rainbow 2\.2|User-Agent\: Mozilla/4\.0 \(compatible; MSIE 6\.0; Windows NT 5\.1\)|WebVulnCrawl\.blogspot\.com/1\.0 libwww-perl/5\.803|Wells Search II|WEP Search 00
# Option: failregex
+1 -1
View File
@@ -14,7 +14,7 @@
# (?:::f{4,6}:)?(?P<host>\S+)
# Values: TEXT
#
failregex = [[]client <HOST>[]] File does not exist: .*(\.php|\.asp)
failregex = [[]client <HOST>[]] (File does not exist|script not found or unable to stat): .*(\.php|\.asp|\.exe|\.pl)
# Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored.
+20
View File
@@ -0,0 +1,20 @@
# Fail2Ban configuration file
#
# Author: Tim Connors
#
# $Revision: 668 $
#
[Definition]
# Option: failregex
# Notes.: Regexp to catch Apache overflow attempts.
# Values: TEXT
#
failregex = [[]client <HOST>[]] (Invalid method in request|request failed: URI too long|erroneous characters after protocol string)
# Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored.
# Values: TEXT
#
ignoreregex =
+41
View File
@@ -0,0 +1,41 @@
# Generic configuration items (to be used as interpolations) in other
# filters or actions configurations
#
# Author: Yaroslav Halchenko
#
# $Revision: $
#
[INCLUDES]
# Load customizations if any available
after = common.local
[DEFAULT]
# Daemon definition is to be specialized (if needed) in .conf file
_daemon = \S*
#
# Shortcuts for easier comprehension of the failregex
#
# PID.
# EXAMPLES: [123]
__pid_re = (?:\[\d+\])
# Daemon name (with optional source_file:line or whatever)
# EXAMPLES: pam_rhosts_auth, [sshd], pop(pam_unix)
__daemon_re = [\[\(]?%(_daemon)s(?:\(\S+\))?[\]\)]?:?
# Combinations of daemon name and PID
# EXAMPLES: sshd[31607], pop(pam_unix)[4920]
__daemon_combs_re = (?:%(__pid_re)s?:\s+%(__daemon_re)s|%(__daemon_re)s%(__pid_re)s?:)
#
# Common line prefixes (beginnings) which could be used in filters
#
# [hostname] [vserver tag] daemon_id spaces
# this can be optional (for instance if we match named native log files)
__prefix_line = \s*(?:\S+ )?(?:@vserver_\S+ )?%(__daemon_combs_re)s?\s*
+20
View File
@@ -0,0 +1,20 @@
# Fail2Ban configuration file for wuftpd
#
# Author: Kevin Zembower (copied from wsftpd.conf)
#
# $Revision: 684 $
#
[Definition]
# Option: failregex
# Notes.: regex to match the password failures messages in the logfile.
# Values: TEXT
#
failregex = ftpd(?:\[\d+\])?:\s+repeated login failures from <HOST> \(\S+\)$
# Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored.
# Values: TEXT
#
ignoreregex =
+18
View File
@@ -0,0 +1,18 @@
# Fail2Ban configuration file
#
# Author: Arturo 'Buanzo' Busleiman <buanzo@buanzo.com.ar>
#
[Definition]
# Option: failregex
# Notes.: regex to match ALERTS as notified by lighttpd's FastCGI Module
# Values: TEXT
#
failregex = .*ALERT\ -\ .*attacker\ \'<HOST>\'
# Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored.
# Values: TEXT
#
ignoreregex =
+35
View File
@@ -0,0 +1,35 @@
# Fail2Ban configuration file for named (bind9). Trying to generalize the
# structure which is general to capture general patterns in log
# lines to cover different configurations/distributions
#
# Author: Yaroslav Halchenko
#
# $Revision: 616 $
#
[Definition]
#
# Daemon name
_daemon=named
#
# Shortcuts for easier comprehension of the failregex
__pid_re=(?:\[\d+\])
__daemon_re=\(?%(_daemon)s(?:\(\S+\))?\)?:?
__daemon_combs_re=(?:%(__pid_re)s?:\s+%(__daemon_re)s|%(__daemon_re)s%(__pid_re)s?:)
# hostname daemon_id spaces
# this can be optional (for instance if we match named native log files)
__line_prefix=(?:\s\S+ %(__daemon_combs_re)s\s+)?
# Option: failregex
# Notes.: regex to match the password failures messages in the logfile.
# Values: TEXT
#
failregex = %(__line_prefix)sclient <HOST>#\S+: query(?: \(cache\))? '.*' denied\s*$
# Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored.
# Values: TEXT
#
ignoreregex =
+31
View File
@@ -0,0 +1,31 @@
# Fail2Ban configuration file for generic PAM authentication errors
#
# Author: Yaroslav Halchenko
#
# $Revision: $
#
[Definition]
# if you want to catch only login erros from specific daemons, use smth like
#_ttys_re=(?:ssh|pure-ftpd|ftp)
# To catch all failed logins
_ttys_re=\S*
#
# Shortcuts for easier comprehension of the failregex
__pid_re=(?:\[\d+\])
__pam_re=\(?pam_unix(?:\(\S+\))?\)?:?
__pam_combs_re=(?:%(__pid_re)s?:\s+%(__pam_re)s|%(__pam_re)s%(__pid_re)s?:)
# Option: failregex
# Notes.: regex to match the password failures messages in the logfile.
# Values: TEXT
#
failregex = \s\S+ \S+%(__pam_combs_re)s\s+authentication failure; logname=\S* uid=\S* euid=\S* tty=%(_ttys_re)s ruser=\S* rhost=<HOST>(?:\s+user=.*)?\s*$
# Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored.
# Values: TEXT
#
ignoreregex =
+23
View File
@@ -0,0 +1,23 @@
# Fail2Ban configuration file
#
# Author: Arturo 'Buanzo' Busleiman <buanzo@buanzo.com.ar>
# Version 2
# fixes the failregex so REFERERS that contain =http:// don't get blocked
# (mentioned by "fasuto" (no real email provided... blog comment) in this entry:
# http://blogs.buanzo.com.ar/2009/04/fail2ban-filter-for-php-injection-attacks.html#comment-1489
#
[Definition]
# Option: failregex
# Notes.: regex to match this kind of request:
#
# 66.185.212.172 - - [26/Mar/2009:08:44:20 -0500] "GET /index.php?n=http://eatmyfood.hostinginfive.com/pizza.htm? HTTP/1.1" 200 114 "-" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)"
#
failregex = ^<HOST> -.*"(GET|POST).*\?.*\=http\:\/\/.* HTTP\/.*$
# Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored.
# Values: TEXT
#
ignoreregex =
+4 -1
View File
@@ -14,7 +14,10 @@
# (?:::f{4,6}:)?(?P<host>\S+)
# Values: TEXT
#
failregex = USER \S+: no such user found from \S* ?\[<HOST>\] to \S+\s*$
failregex = \(\S+\[<HOST>\]\)[: -]+ USER \S+: no such user found from \S+ \[\S+\] to \S+:\S+$
\(\S+\[<HOST>\]\)[: -]+ USER \S+ \(Login failed\): Incorrect password\.$
\(\S+\[<HOST>\]\)[: -]+ SECURITY VIOLATION: \S+ login attempted\.$
\(\S+\[<HOST>\]\)[: -]+ Maximum login attempts \(\d+\) exceeded$
# Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored.
+19
View File
@@ -0,0 +1,19 @@
# Fail2Ban configuration file
#
# Author: Arturo 'Buanzo' Busleiman <buanzo@buanzo.com.ar>
#
[Definition]
# Option: failregex
# Notes.: regex to match psad "scan detected" line, example:
#
# Apr 30 23:35:06 mx4 psad: scan detected: 221.192.199.49 -> 85.13.200.90 tcp: [8000] flags: SYN tcp pkts: 1 DL: 2
#
failregex = ^.*\ psad\:\ scan\ detected\:\ <HOST>\ \-\>\ .*$
# Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored.
# Values: TEXT
#
ignoreregex =
+1 -1
View File
@@ -19,7 +19,7 @@ __errmsg = (?:Authentication failed for user|Erreur d'authentification pour l'ut
# (?:::f{4,6}:)?(?P<host>\S+)
# Values: TEXT
#
failregex = pure-ftpd: (.+?@<HOST>) \[WARNING\] %(__errmsg)s \[.+\]$
failregex = pure-ftpd(?:\[\d+\])?: (.+?@<HOST>) \[WARNING\] %(__errmsg)s \[.+\]$
# Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored.
+1 -1
View File
@@ -14,7 +14,7 @@
# (?:::f{4,6}:)?(?P<host>\S+)
# Values: TEXT
#
failregex = sshd\[\S*\]: Did not receive identification string from <HOST>
failregex = <TIME> <PREFIX> Did not receive identification string from <HOST>$
# Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored.
+8 -4
View File
@@ -14,10 +14,14 @@
# (?:::f{4,6}:)?(?P<host>\S+)
# Values: TEXT
#
failregex = Authentication failure for .* from <HOST>
Failed [-/\w]+ for .* from <HOST>
ROOT LOGIN REFUSED .* FROM <HOST>
[iI](?:llegal|nvalid) user .* from <HOST>
failregex = <TIME> <PREFIX> (?:error: PAM: )?Authentication failure for .* from <HOST>\s*$
<TIME> <PREFIX> Failed [-/\w]+ for .* from <HOST>(?: port \d*)?(?: ssh\d*)?$
<TIME> <PREFIX> ROOT LOGIN REFUSED.* FROM <HOST>\s*$
<TIME> <PREFIX> [iI](?:llegal|nvalid) user .* from <HOST>\s*$
<TIME> <PREFIX> User \S+ from <HOST> not allowed because not listed in AllowUsers$
<TIME> <PREFIX> authentication failure; logname=\S* uid=\S* euid=\S* tty=\S* ruser=\S* rhost=<HOST>(?:\s+user=.*)?\s*$
<TIME> <PREFIX> refused connect from \S+ \(<HOST>\)\s*$
<TIME> <PREFIX> Address <HOST> .* POSSIBLE BREAK-IN ATTEMPT\s*$
# Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored.
+2 -2
View File
@@ -14,8 +14,8 @@
# (?:::f{4,6}:)?(?P<host>\S+)
# Values: TEXT
#
failregex = vsftpd: .* authentication failure; .* rhost=<HOST>$
\[.+\] FAIL LOGIN: Client "<HOST>"$
failregex = vsftpd(?:\(pam_unix\))?(?:\[\d+\])?:.* authentication failure; .* rhost=<HOST>(?:\s+user=\S*)?\s*$
\[.+\] FAIL LOGIN: Client "<HOST>"\s*$
# Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored.
+28
View File
@@ -0,0 +1,28 @@
# Fail2Ban configuration file
#
# Author: Cyril Jaquier
# Rule by : Delvit Guillaume
#
# $Revision: 601 $
#
[Definition]
# patern : webmin[15673]: Non-existent login as toto from 86.0.6.217
# webmin[29544]: Invalid login as root from 86.0.6.217
#
# Option: failregex
# Notes.: regex to match the password failure 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>\S+)
# Values: TEXT
#
failregex = webmin.* Non-existent login as .+ from <HOST>$
webmin.* Invalid login as .+ from <HOST>$
# Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored.
# Values: TEXT
#
ignoreregex =
+8 -2
View File
@@ -2,7 +2,7 @@
#
# Author: Yaroslav Halchenko
#
# $Revision: $
# $Revision$
#
[Definition]
@@ -11,4 +11,10 @@
# Notes.: regex to match the password failures messages in the logfile.
# Values: TEXT
#
failregex = wu-ftpd\[\d+\]:\s+\(pam_unix\)\s+authentication failure.* rhost=<HOST>
failregex = wu-ftpd(?:\[\d+\])?:\s+\(pam_unix\)\s+authentication failure.* rhost=<HOST>$
# Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored.
# Values: TEXT
#
ignoreregex =
+30
View File
@@ -0,0 +1,30 @@
# Fail2Ban configuration file
#
# Author: Guido Bozzetto
#
# $Revision: 668 $
#
[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>\S+)
# Values: TEXT
#
# Cfr.: /var/log/(daemon\.|sys)log
# libwrap => tcp wrappers: hosts.(allow|deny)
# address => xinetd: deny_from|only_from
# load => xinetd: max_load (temporary problem)
#
failregex = xinetd(?:\[\d{1,5}\])?: FAIL: \S+ address from=<HOST>$
xinetd(?:\[\d{1,5}\])?: FAIL: \S+ libwrap from=<HOST>$
# Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored.
# Values: TEXT
#
ignoreregex =
+100 -12
View File
@@ -45,7 +45,7 @@ backend = auto
enabled = false
filter = sshd
action = iptables[name=SSH, port=ssh, protocol=tcp]
mail-whois[name=SSH, dest=yourmail@mail.com]
sendmail-whois[name=SSH, dest=you@mail.com, sender=fail2ban@mail.com]
logpath = /var/log/sshd.log
maxretry = 5
@@ -54,7 +54,7 @@ maxretry = 5
enabled = false
filter = proftpd
action = iptables[name=ProFTPD, port=ftp, protocol=tcp]
mail-whois[name=ProFTPD, dest=yourmail@mail.com]
sendmail-whois[name=ProFTPD, dest=you@mail.com]
logpath = /var/log/proftpd/proftpd.log
maxretry = 6
@@ -66,7 +66,7 @@ enabled = false
filter = sasl
backend = polling
action = iptables[name=sasl, port=smtp, protocol=tcp]
mail-whois[name=sasl, dest=yourmail@mail.com]
sendmail-whois[name=sasl, dest=you@mail.com]
logpath = /var/log/mail.log
# Here we use TCP-Wrappers instead of Netfilter/Iptables. "ignoreregex" is
@@ -77,7 +77,7 @@ logpath = /var/log/mail.log
enabled = false
filter = sshd
action = hostsdeny
mail-whois[name=SSH, dest=yourmail@mail.com]
sendmail-whois[name=SSH, dest=you@mail.com]
ignoreregex = for myuser from
logpath = /var/log/sshd.log
@@ -89,8 +89,8 @@ logpath = /var/log/sshd.log
enabled = false
filter = apache-auth
action = hostsdeny
logpath = /var/log/apache*/*access.log
/home/www/myhomepage/access.log
logpath = /var/log/apache*/*error.log
/home/www/myhomepage/error.log
maxretry = 6
# The hosts.deny path can be defined with the "file" argument if it is
@@ -101,7 +101,7 @@ maxretry = 6
enabled = false
filter = postfix
action = hostsdeny[file=/not/a/standard/path/hosts.deny]
mail[name=Postfix, dest=yourmail@mail.com]
sendmail[name=Postfix, dest=you@mail.com]
logpath = /var/log/postfix.log
bantime = 300
@@ -112,7 +112,7 @@ bantime = 300
enabled = false
filter = vsftpd
action = mail-whois[name=VSFTPD, dest=yourmail@mail.com]
action = sendmail-whois[name=VSFTPD, dest=you@mail.com]
logpath = /var/log/vsftpd.log
maxretry = 5
bantime = 1800
@@ -124,7 +124,7 @@ bantime = 1800
enabled = false
filter = vsftpd
action = iptables[name=VSFTPD, port=ftp, protocol=tcp]
mail-whois[name=VSFTPD, dest=yourmail@mail.com]
sendmail-whois[name=VSFTPD, dest=you@mail.com]
logpath = /var/log/vsftpd.log
maxretry = 5
bantime = 1800
@@ -137,7 +137,7 @@ bantime = 1800
enabled = false
filter = apache-badbots
action = iptables-multiport[name=BadBots, port="http,https"]
mail-buffered[name=BadBots, lines=5, dest=yourmail@mail.com]
sendmail-buffered[name=BadBots, lines=5, dest=you@mail.com]
logpath = /var/www/*/logs/access_log
bantime = 172800
maxretry = 1
@@ -149,9 +149,38 @@ maxretry = 1
enabled = false
filter = apache-noscript
action = shorewall
mail[name=Postfix, dest=yourmail@mail.com]
sendmail[name=Postfix, dest=you@mail.com]
logpath = /var/log/apache2/error_log
# Ban attackers that try to use PHP's URL-fopen() functionality
# through GET/POST variables.
# Make sure you edit the filter if you need to allow certain requests.
# Adapt logpath as required!
[php-url-fopen]
enabled = false
port = http,https
filter = php-url-fopen
logpath = /var/www/*/logs/access_log
maxretry = 1
# A simple PHP-fastcgi jail which works with lighttpd.
# If you run a lighttpd server, then you probably will
# find these kinds of messages in your error_log:
# ALERT – tried to register forbidden variable ‘GLOBALS’
# through GET variables (attacker '1.2.3.4', file '/var/www/default/htdocs/index.php')
# This jail would block the IP 1.2.3.4.
[lighttpd-fastcgi]
enabled = true
port = http,https
filter = lighttpd-fastcgi
# adapt the following two items as needed
logpath = /var/log/lighttpd/error.log
maxretry = 2
# This jail uses ipfw, the standard firewall on FreeBSD. The "ignoreip"
# option is overridden in this jail. Moreover, the action "mail-whois" defines
# the variable "name" which contains a comma using "". The characters '' are
@@ -162,6 +191,65 @@ logpath = /var/log/apache2/error_log
enabled = false
filter = sshd
action = ipfw[localhost=192.168.0.1]
mail-whois[name="SSH,IPFW", dest=yourmail@mail.com]
sendmail-whois[name="SSH,IPFW", dest=you@mail.com]
logpath = /var/log/auth.log
ignoreip = 168.192.0.1
# These jails block attacks against named (bind9). By default, logging is off
# with bind9 installation. You will need something like this:
#
# logging {
# channel security_file {
# file "/var/log/named/security.log" versions 3 size 30m;
# severity dynamic;
# print-time yes;
# };
# category security {
# security_file;
# };
# }
#
# in your named.conf to provide proper logging.
# This jail blocks UDP traffic for DNS requests.
# !!! WARNING !!!
# Since UDP is connectionless protocol, spoofing of IP and immitation
# of illegal actions is way too simple. Thus enabling of this filter
# might provide an easy way for implementing a DoS against a chosen
# victim. See
# http://nion.modprobe.de/blog/archives/690-fail2ban-+-dns-fail.html
# Please DO NOT USE this jail unless you know what you are doing.
#
# [named-refused-udp]
#
# enabled = false
# filter = named-refused
# action = iptables-multiport[name=Named, port="domain,953", protocol=udp]
# sendmail-whois[name=Named, dest=you@mail.com]
# logpath = /var/log/named/security.log
# ignoreip = 168.192.0.1
# This jail blocks TCP traffic for DNS requests.
[named-refused-tcp]
enabled = false
filter = named-refused
action = iptables-multiport[name=Named, port="domain,953", protocol=tcp]
sendmail-whois[name=Named, dest=you@mail.com]
logpath = /var/log/named/security.log
ignoreip = 168.192.0.1
# PSAD support for fail2ban by Buanzo.
# psad detects portscans, and logs the details to /var/log/messages
# With this jail, you can ban attackers that portscan you (for example, with the excellent Nmap)
# TODO: It needs a proper action, and decide on default port range/list
[psad]
enabled = false
port = ssh,http,https,smtp,ftp
filter = psad
logpath = /var/log/messages
maxretry = 1
+42 -14
View File
@@ -26,7 +26,7 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import sys, string, os, pickle, re, logging, signal
import getopt, time, readline, shlex, socket
import getopt, time, shlex, socket
# Inserts our own modules path first in the list
# fix for bug #343821
@@ -48,7 +48,8 @@ logSys = logging.getLogger("fail2ban.client")
class Fail2banClient:
prompt = "fail2ban> "
SERVER = "fail2ban-server"
PROMPT = "fail2ban> "
def __init__(self):
self.__argv = None
@@ -65,11 +66,11 @@ class Fail2banClient:
def dispVersion(self):
print "Fail2Ban v" + version
print
print "Copyright (c) 2004-2006 Cyril Jaquier"
print "Copyright (c) 2004-2008 Cyril Jaquier"
print "Copyright of modifications held by their respective authors."
print "Licensed under the GNU General Public License v2 (GPL)."
print
print "Written by Cyril Jaquier <lostcontrol@users.sourceforge.net>."
print "Written by Cyril Jaquier <cyril.jaquier@fail2ban.org>."
print "Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>."
def dispUsage(self):
@@ -97,7 +98,7 @@ class Fail2banClient:
printFormatted()
print
print "Report bugs to <lostcontrol@users.sourceforge.net>"
print "Report bugs to <cyril.jaquier@fail2ban.org>"
def dispInteractive(self):
print "Fail2Ban v" + version + " reads log file that contains password failure report"
@@ -208,6 +209,19 @@ class Fail2banClient:
else:
logSys.error("Could not find server")
return False
elif len(cmd) == 2 and cmd[0] == "reload":
if self.__ping():
jail = cmd[1]
ret = self.__readJailConfig(jail)
# Do not continue if configuration is not 100% valid
if not ret:
return False
self.__processCmd([['stop', jail]], False)
# Configure the server
return self.__processCmd(self.__stream, False)
else:
logSys.error("Could not find server")
return False
else:
return self.__processCmd([cmd])
@@ -222,7 +236,7 @@ class Fail2banClient:
pid = os.fork()
if pid == 0:
args = list()
args.append("fail2ban-server")
args.append(self.SERVER)
# Start in background mode.
args.append("-b")
# Set the socket path.
@@ -232,14 +246,15 @@ class Fail2banClient:
if force:
args.append("-x")
try:
# Use the PATH env
os.execvp("fail2ban-server", args)
# Use the current directory.
exe = os.path.abspath(os.path.join(sys.path[0], self.SERVER))
os.execv(exe, args)
except OSError:
try:
# Use the current directory
os.execv("fail2ban-server", args)
# Use the PATH env.
os.execvp(self.SERVER, args)
except OSError:
print "Could not find fail2ban-server"
print "Could not find %s" % self.SERVER
os.exit(-1)
@@ -325,6 +340,11 @@ class Fail2banClient:
# Interactive mode
if self.__conf["interactive"]:
try:
import readline
except ImportError:
logSys.error("Readline not available")
return False
try:
ret = True
if len(args) > 0:
@@ -333,7 +353,7 @@ class Fail2banClient:
readline.parse_and_bind("tab: complete")
self.dispInteractive()
while True:
cmd = raw_input(self.prompt)
cmd = raw_input(self.PROMPT)
if cmd == "exit" or cmd == "quit":
# Exit
return True
@@ -352,16 +372,24 @@ class Fail2banClient:
def __readConfig(self):
# Read the configuration
self.__configurator.readAll()
ret = self.__configurator.getAllOptions()
ret = self.__configurator.getOptions()
self.__configurator.convertToProtocol()
self.__stream = self.__configurator.getConfigStream()
return ret
@staticmethod
def __readJailConfig(self, jail):
self.__configurator.readAll()
ret = self.__configurator.getOptions(jail)
self.__configurator.convertToProtocol()
self.__stream = self.__configurator.getConfigStream()
return ret
#@staticmethod
def dumpConfig(cmd):
for c in cmd:
print c
return True
dumpConfig = staticmethod(dumpConfig)
class ServerExecutionException(Exception):
+122 -25
View File
@@ -31,11 +31,11 @@ import getopt, sys, time, logging, os
# fix for bug #343821
sys.path.insert(1, "/usr/share/fail2ban")
from ConfigParser import SafeConfigParser
from client.configparserinc import SafeConfigParserWithIncludes
from ConfigParser import NoOptionError, NoSectionError, MissingSectionHeaderError
from common.version import version
from server.filter import Filter
from server.regex import RegexException
from server.failregex import RegexException
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.regex")
@@ -65,9 +65,12 @@ class RegexStat:
class Fail2banRegex:
test = None
CONFIG_DEFAULTS = {'configpath' : "/etc/fail2ban/"}
def __init__(self):
self.__filter = Filter(None)
self.__ignoreregex = list()
self.__failregex = list()
# Setup logging
logging.getLogger("fail2ban").handlers = []
@@ -79,20 +82,21 @@ class Fail2banRegex:
logging.getLogger("fail2ban").addHandler(self.__hdlr)
logging.getLogger("fail2ban").setLevel(logging.ERROR)
@staticmethod
#@staticmethod
def dispVersion():
print "Fail2Ban v" + version
print
print "Copyright (c) 2004-2006 Cyril Jaquier"
print "Copyright (c) 2004-2008 Cyril Jaquier"
print "Copyright of modifications held by their respective authors."
print "Licensed under the GNU General Public License v2 (GPL)."
print
print "Written by Cyril Jaquier <lostcontrol@users.sourceforge.net>."
print "Written by Cyril Jaquier <cyril.jaquier@fail2ban.org>."
print "Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>."
dispVersion = staticmethod(dispVersion)
@staticmethod
#@staticmethod
def dispUsage():
print "Usage: "+sys.argv[0]+" [OPTIONS] <LOG> <REGEX>"
print "Usage: "+sys.argv[0]+" [OPTIONS] <LOG> <REGEX> [IGNOREREGEX]"
print
print "Fail2Ban v" + version + " reads log file that contains password failure report"
print "and bans the corresponding IP addresses using firewall rules."
@@ -111,7 +115,12 @@ class Fail2banRegex:
print " string a string representing a 'failregex'"
print " filename path to a filter file (filter.d/sshd.conf)"
print
print "Report bugs to <lostcontrol@users.sourceforge.net>"
print "IgnoreRegex:"
print " string a string representing an 'ignoreregex'"
print " filename path to a filter file (filter.d/sshd.conf)"
print
print "Report bugs to <cyril.jaquier@fail2ban.org>"
dispUsage = staticmethod(dispUsage)
def getCmdLineOptions(self, optList):
""" Gets the command line options
@@ -124,13 +133,43 @@ class Fail2banRegex:
self.dispVersion()
sys.exit(0)
@staticmethod
#@staticmethod
def logIsFile(value):
return os.path.isfile(value)
logIsFile = staticmethod(logIsFile)
def readIgnoreRegex(self, value):
if os.path.isfile(value):
reader = SafeConfigParserWithIncludes(defaults=self.CONFIG_DEFAULTS)
try:
reader.read(value)
print "Use ignoreregex file : " + value
self.__ignoreregex = [RegexStat(m)
for m in reader.get("Definition", "ignoreregex").split('\n')]
except NoSectionError:
print "No [Definition] section in " + value
print
return False
except NoOptionError:
print "No failregex option in " + value
print
return False
except MissingSectionHeaderError:
print "No section headers in " + value
print
return False
else:
if len(value) > 53:
stripReg = value[0:50] + "..."
else:
stripReg = value
print "Use ignoreregex line : " + stripReg
self.__ignoreregex = [RegexStat(value)]
return True
def readRegex(self, value):
if os.path.isfile(value):
reader = SafeConfigParser()
reader = SafeConfigParserWithIncludes(defaults=self.CONFIG_DEFAULTS)
try:
reader.read(value)
print "Use regex file : " + value
@@ -157,14 +196,38 @@ class Fail2banRegex:
self.__failregex = [RegexStat(value)]
return True
def testIgnoreRegex(self, line):
found = False
for regex in self.__ignoreregex:
logging.getLogger("fail2ban").setLevel(logging.DEBUG)
try:
self.__filter.addIgnoreRegex(regex.getFailRegex())
try:
ret = self.__filter.ignoreLine(line)
if ret:
regex.inc()
except RegexException, e:
print e
return False
finally:
self.__filter.delIgnoreRegex(0)
logging.getLogger("fail2ban").setLevel(logging.CRITICAL)
def testRegex(self, line):
found = False
for regex in self.__ignoreregex:
self.__filter.addIgnoreRegex(regex.getFailRegex())
for regex in self.__failregex:
logging.getLogger("fail2ban").setLevel(logging.DEBUG)
try:
self.__filter.addFailRegex(regex.getFailRegex())
try:
ret = self.__filter.findFailure(line)
try:
# Decode line to UTF-8
l = line.decode('utf-8')
except UnicodeDecodeError:
l = line
ret = self.__filter.findFailure(l)
if not len(ret) == 0:
if found == True:
ret[0].append(True)
@@ -182,6 +245,8 @@ class Fail2banRegex:
finally:
self.__filter.delFailRegex(0)
logging.getLogger("fail2ban").setLevel(logging.CRITICAL)
for regex in self.__ignoreregex:
self.__filter.delIgnoreRegex(0)
def printStats(self):
print
@@ -191,25 +256,51 @@ class Fail2banRegex:
# Print title
cnt = 1
print "Failregex:"
print "Failregex"
print "|- Regular expressions:"
for failregex in self.__failregex:
print "[" + str(cnt) + "] " + failregex.getFailRegex()
print "| [" + str(cnt) + "] " + failregex.getFailRegex()
cnt += 1
cnt = 1
print
print "|"
# Print stats
cnt = 1
total = 0
print "Number of matches:"
print "`- Number of matches:"
for failregex in self.__failregex:
match = failregex.getStats()
total += match
print "[" + str(cnt) + "] " + str(match) + " match(es)"
print " [" + str(cnt) + "] " + str(match) + " match(es)"
cnt += 1
print
# Print title
cnt = 1
print "Ignoreregex"
print "|- Regular expressions:"
for failregex in self.__ignoreregex:
print "| [" + str(cnt) + "] " + failregex.getFailRegex()
cnt += 1
cnt = 1
print "|"
# Print stats
cnt = 1
print "`- Number of matches:"
for failregex in self.__ignoreregex:
match = failregex.getStats()
print " [" + str(cnt) + "] " + str(match) + " match(es)"
cnt += 1
print
print "Summary"
print "======="
print
if total == 0:
print "Sorry, no match"
print
@@ -234,12 +325,6 @@ class Fail2banRegex:
print
print "Date template hits:"
for template in self.__filter.dateDetector.getTemplates():
print `template.getHits()` + " hit: " + template.getName()
print
print "Success, the total number of match is " + str(total)
print
print "However, look at the above section 'Running tests' which could contain important"
@@ -247,7 +332,7 @@ class Fail2banRegex:
return True
if __name__ == "__main__":
def main():
fail2banRegex = Fail2banRegex()
# Reads the command line options.
try:
@@ -260,7 +345,7 @@ if __name__ == "__main__":
# Process command line
fail2banRegex.getCmdLineOptions(optList)
# We need exactly 3 parameters
if not len(sys.argv) == 3:
if not len(sys.argv) in (3, 4):
fail2banRegex.dispUsage()
sys.exit(-1)
else:
@@ -269,6 +354,10 @@ if __name__ == "__main__":
print "============="
print
if len(sys.argv) == 4:
if fail2banRegex.readIgnoreRegex(sys.argv[3]) == False:
sys.exit(-1)
if fail2banRegex.readRegex(sys.argv[2]) == False:
sys.exit(-1)
@@ -278,6 +367,7 @@ if __name__ == "__main__":
print "Use log file : " + sys.argv[1]
print
for line in hdlr:
fail2banRegex.testIgnoreRegex(line)
fail2banRegex.testRegex(line)
except IOError, e:
print e
@@ -290,9 +380,16 @@ if __name__ == "__main__":
stripLog = sys.argv[1]
print "Use single line: " + stripLog
print
fail2banRegex.testIgnoreRegex(sys.argv[1])
fail2banRegex.testRegex(sys.argv[1])
if fail2banRegex.printStats():
sys.exit(0)
else:
sys.exit(-1)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print "Interrupted by the user"
+9 -6
View File
@@ -25,7 +25,7 @@ __date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import getopt, sys
import getopt, sys, logging
# Inserts our own modules path first in the list
# fix for bug #343821
@@ -34,6 +34,9 @@ sys.path.insert(1, "/usr/share/fail2ban")
from common.version import version
from server.server import Server
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban")
##
# \mainpage Fail2Ban
#
@@ -50,16 +53,16 @@ class Fail2banServer:
self.__conf = dict()
self.__conf["background"] = True
self.__conf["force"] = False
self.__conf["socket"] = "/tmp/fail2ban.sock"
self.__conf["socket"] = "/var/run/fail2ban/fail2ban.sock"
def dispVersion(self):
print "Fail2Ban v" + version
print
print "Copyright (c) 2004-2006 Cyril Jaquier"
print "Copyright (c) 2004-2008 Cyril Jaquier"
print "Copyright of modifications held by their respective authors."
print "Licensed under the GNU General Public License v2 (GPL)."
print
print "Written by Cyril Jaquier <lostcontrol@users.sourceforge.net>."
print "Written by Cyril Jaquier <cyril.jaquier@fail2ban.org>."
print "Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>."
def dispUsage(self):
@@ -82,7 +85,7 @@ class Fail2banServer:
print " -h, --help display this help message"
print " -V, --version print the version"
print
print "Report bugs to <lostcontrol@users.sourceforge.net>"
print "Report bugs to <cyril.jaquier@fail2ban.org>"
def __getCmdLineOptions(self, optList):
""" Gets the command line options
@@ -123,7 +126,7 @@ class Fail2banServer:
self.__server.start(self.__conf["socket"], self.__conf["force"])
return True
except Exception, e:
print e
logSys.exception(e)
self.__server.quit()
return False
-3
View File
@@ -34,7 +34,6 @@ from testcases import clientreadertestcase
from testcases import failmanagertestcase
from testcases import filtertestcase
from testcases import servertestcase
from testcases import datedetectortestcase
from testcases import actiontestcase
from server.mytime import MyTime
@@ -67,8 +66,6 @@ tests.addTest(unittest.makeSuite(failmanagertestcase.AddFailure))
tests.addTest(unittest.makeSuite(banmanagertestcase.AddFailure))
# ClientReader
tests.addTest(unittest.makeSuite(clientreadertestcase.JailReaderTest))
# DateDetector
tests.addTest(unittest.makeSuite(datedetectortestcase.DateDetectorTest))
# Tests runner
testRunner = unittest.TextTestRunner()
+3 -3
View File
@@ -4,7 +4,7 @@
|_| \__,_|_|_/___|_.__/\__,_|_||_|
=============================================================
Fail2Ban (version 0.7.7) 2007/??/??
Fail2Ban (version 0.8.2) 2008/03/06
=============================================================
Cacti is a graphing solution using RRDTool. It is possible to
@@ -13,7 +13,7 @@ use Cacti to display statistics about Fail2ban.
Installation:
-------------
1/ Install Fail2ban version 0.7 or higher and ensure that it
1/ Install Fail2ban version 0.8 or higher and ensure that it
works properly.
2/ The user running poller.php must have read and write
access to the socket used by Fail2ban.
@@ -30,7 +30,7 @@ appreciate this program, you can contact me at:
Website: http://www.fail2ban.org
Cyril Jaquier: <lostcontrol@users.sourceforge.net>
Cyril Jaquier: <cyril.jaquier@fail2ban.org>
License:
--------
+83
View File
@@ -0,0 +1,83 @@
#!/bin/bash
#-------------------------- =+- Shell script -+= --------------------------
#
# @file badbots.sh
# @date Sun Feb 11 00:49:53 2007
# @brief
#
#
# Yaroslav Halchenko CS@UNM, CS@NJIT
# web: http://www.onerussian.com & PSYCH@RUTGERS
# e-mail: yoh@onerussian.com ICQ#: 60653192
#
# DESCRIPTION (NOTES):
#
# Script to fetch list of agent strings from http://www.user-agents.org
# which are known to be from mailicious bots, and create apache-badbots.conf
# filter for fail2ban
#
# COPYRIGHT: Yaroslav Halchenko 2007-2010
#
# LICENSE:
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the
# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
# MA 02110-1301, USA.
#
# On Debian system see /usr/share/common-licenses/GPL for the full license.
#
#-----------------\____________________________________/------------------
url=http://www.user-agents.org/index.shtml
badbots=$(
for f in "" "?g_l" "?m" "?n_s" "?t_z"; do
wget -q -O- $url$f;
done \
| grep -h -B4 '<td class="smallcell" nowrap>S&nbsp;</td>'\
| sed -e 's/&nbsp;//g' \
| awk '/^--/{getline; gsub(" ",""); print $0}' \
| sed -e 's/\([.\:|()]\)/\\\1/g' \
| tr '\n' '|' \
| sed -e 's/|$//g'
)
echo $badbots >| /tmp/badbots.tmp
cat >| apache-badbots.conf <<EOF
# Fail2Ban configuration file
#
# List of bad bots fetched from http://www.user-agents.org
# Generated on `date` by $0
#
# Author: Yaroslav Halchenko
#
#
[Definition]
badbotscustom = EmailCollector|WebEMailExtrac
badbots = $badbots
# Option: failregex
# Notes.: Regexp to catch known spambots and software alike. Please verify that
# it is your intent to block IPs which were driven by abovementioned bots
# Values: TEXT
#
failregex = ^(?P<host>\S*) -.*"GET.*HTTP.*"(?:%(badbots)s|%(badbotscustom)s)"\$
# Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored.
# Values: TEXT
#
ignoreregex =
EOF
+19
View File
@@ -0,0 +1,19 @@
/Library/LaunchDaemonsm/org.fail2ban.plist
===================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Disabled</key>
<false/>
<key>Label</key>
<string>fail2ban</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/fail2ban-client</string>
<string>start</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
+2 -2
View File
@@ -20,14 +20,14 @@ FAIL2BAN="/usr/bin/fail2ban-client"
RETVAL=0
getpid() {
pid=`ps -ef | grep fail2ban-|grep -v grep|awk '{print $2}'`
pid=`ps -eo pid,comm | grep fail2ban- | awk '{ print $1 }'`
}
start() {
echo -n $"Starting fail2ban: "
getpid
if [ -z "$pid" ]; then
rm -rf /tmp/fail2ban.sock # in case of unclean shutdown
rm -rf /var/run/fail2ban/fail2ban.sock # in case of unclean shutdown
$FAIL2BAN start > /dev/null
RETVAL=$?
fi
+60 -51
View File
@@ -6,26 +6,26 @@
#
### BEGIN INIT INFO
# Provides: fail2ban
# Required-Start: $syslog $remote_fs postfix
# Required-Start: $syslog $remote_fs sendmail
# Required-Stop: $syslog $remote_fs
# Should-Stop: $time ypbind sendmail
# Default-Start: 3 5
# Default-Stop: 0 1 2 6
# Description: startup Fail2Ban
### END INIT INFO
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/sbin:/usr/bin
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/sbin:/usr/sbin:/usr/bin:/bin
FAIL2BAN_BIN=/usr/local/bin/fail2ban-client
FAIL2BAN_SERVER=/usr/local/bin/fail2ban-server
FAIL2BAN_SOCKET=/tmp/fail2ban.sock
test -x $FAIL2BAN_BIN || { echo "$FAIL2BAN_BIN not installed";
if [ "$1" = "stop" ]; then exit 0;
else exit 5; fi; }
FAIL2BAN_SOCKET=/var/run/fail2ban/fail2ban.sock
test -x $FAIL2BAN_BIN || { echo "$FAIL2BAN_BIN not installed";
if [ "$1" = "stop" ]; then exit 0;
else exit 5; fi; }
# Check for existence of needed config file and read it
FAIL2BAN_CONFIG=/etc/fail2ban/fail2ban.conf
test -r $FAIL2BAN_CONFIG || { echo "$FAIL2BAN_CONFIG not existing";
if [ "$1" = "stop" ]; then exit 0;
else exit 6; fi; }
if [ "$1" = "stop" ]; then exit 0;
else exit 6; fi; }
. /etc/rc.status
@@ -34,54 +34,63 @@ rc_reset
case "$1" in
start)
echo -n "Starting Fail2Ban "
/sbin/startproc $FAIL2BAN_BIN -q start 2>1 > /dev/null
rc_status -v
;;
echo -n "Starting Fail2Ban "
/sbin/startproc $FAIL2BAN_BIN start &>/dev/null
rc_status -v
;;
stop)
echo -n "Shutting down Fail2ban "
/sbin/startproc $FAIL2BAN_BIN -q stop
rc_status -v
;;
echo -n "Shutting down Fail2ban "
/sbin/startproc $FAIL2BAN_BIN -q stop
rc_status -v
;;
try-restart|condrestart)
if test "$1" = "condrestart"; then
echo "${attn} Use try-restart ${done}(LSB)${attn} rather than condrestart ${warn}(RH)${norm}"
fi
$0 status
if test $? = 0; then
$0 restart
else
rc_reset # Not running is not a failure.
fi
rc_status
;;
if test "$1" = "condrestart"; then
echo "${attn} Use try-restart ${done}(LSB)${attn} rather than condrestart ${warn}(RH)${norm}"
fi
$0 status
if test $? = 0; then
$0 restart
else
rc_reset # Not running is not a failure.
fi
rc_status
;;
restart)
$0 stop
i=60
while [ -e $FAIL2BAN_SOCKET ] && [ $i -gt 60 ]
sleep 1
i=$[i-1]
echo -n "."
done
echo "."
$0 start
$0 stop
echo -n "-wait a minute "
i=60
while [ -e $FAIL2BAN_SOCKET ] && [ $i -gt 0 ]; do
sleep 1
i=$[$i-1]
echo -n "."
done
echo "."
$0 start
# Remember status and be quiet
rc_status
;;
# Remember status and be quiet
rc_status
;;
force-reload)
echo -n "Reload service Fail2ban "
/sbin/startproc $FAIL2BAN_BIN -q reload
rc_status -v
;;
echo -n "Reload service Fail2ban "
/sbin/startproc $FAIL2BAN_BIN -q reload
rc_status -v
;;
reload)
echo -n "Reload service Fail2ban "
/sbin/startproc $FAIL2BAN_BIN -q reload
rc_status -v
;;
echo -n "Reload service Fail2ban "
/sbin/startproc $FAIL2BAN_BIN -q reload
rc_status -v
;;
status)
echo -n "Checking for service Fail2ban "
/sbin/checkproc $FAIL2BAN_SERVER
rc_status -v
;;
echo -n "Checking for service Fail2ban "
/sbin/checkproc $FAIL2BAN_SERVER
rc_status -v
;;
probe)
test /etc/fail2ban/fail2ban.conf -nt /var/run/fail2ban.pid && echo reload
;;
*)
echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload|probe}"
exit 1
;;
esac
rc_exit
+10 -7
View File
@@ -106,7 +106,7 @@ class Action:
def setActionStart(self, value):
self.__actionStart = value
logSys.info("Set actionStart = %s" % value)
logSys.debug("Set actionStart = %s" % value)
##
# Get the "start" command.
@@ -135,7 +135,7 @@ class Action:
def setActionBan(self, value):
self.__actionBan = value
logSys.info("Set actionBan = %s" % value)
logSys.debug("Set actionBan = %s" % value)
##
# Get the "ban" command.
@@ -160,7 +160,7 @@ class Action:
def setActionUnban(self, value):
self.__actionUnban = value
logSys.info("Set actionUnban = %s" % value)
logSys.debug("Set actionUnban = %s" % value)
##
# Get the "unban" command.
@@ -185,7 +185,7 @@ class Action:
def setActionCheck(self, value):
self.__actionCheck = value
logSys.info("Set actionCheck = %s" % value)
logSys.debug("Set actionCheck = %s" % value)
##
# Get the "check" command.
@@ -202,7 +202,7 @@ class Action:
def setActionStop(self, value):
self.__actionStop = value
logSys.info("Set actionStop = %s" % value)
logSys.debug("Set actionStop = %s" % value)
##
# Get the "stop" command.
@@ -231,7 +231,7 @@ class Action:
# @param aInfo the properties
# @return a string
@staticmethod
#@staticmethod
def replaceTag(query, aInfo):
""" Replace tags in query
"""
@@ -241,6 +241,7 @@ class Action:
# New line
string = string.replace("<br>", '\n')
return string
replaceTag = staticmethod(replaceTag)
##
# Executes a command with preliminary checks and substitutions.
@@ -297,7 +298,7 @@ class Action:
# @param realCmd the command to execute
# @return True if the command succeeded
@staticmethod
#@staticmethod
def executeCmd(realCmd):
logSys.debug(realCmd)
try:
@@ -312,3 +313,5 @@ class Action:
except OSError, e:
logSys.error("%s failed with %s" % (realCmd, e))
return False
executeCmd = staticmethod(executeCmd)
+155
View File
@@ -0,0 +1,155 @@
# This file is part of Fail2Ban.
#
# Fail2Ban is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# Fail2Ban is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Fail2Ban; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# Author: Cyril Jaquier
#
# $Revision: 567 $
__author__ = "Cyril Jaquier"
__version__ = "$Revision: 567 $"
__date__ = "$Date: 2007-03-26 23:17:31 +0200 (Mon, 26 Mar 2007) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
from pickle import dumps, loads, HIGHEST_PROTOCOL
import asyncore, asynchat, socket, os, logging
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.server")
##
# Request handler class.
#
# This class extends asynchat in order to provide a request handler for
# incoming query.
class RequestHandler(asynchat.async_chat):
END_STRING = "<F2B_END_COMMAND>"
def __init__(self, conn, transmitter):
asynchat.async_chat.__init__(self, conn)
self.__transmitter = transmitter
self.__buffer = []
# Sets the terminator.
self.set_terminator(RequestHandler.END_STRING)
def collect_incoming_data(self, data):
#logSys.debug("Received raw data: " + str(data))
self.__buffer.append(data)
##
# Handles a new request.
#
# This method is called once we have a complete request.
def found_terminator(self):
# Joins the buffer items.
message = loads("".join(self.__buffer))
# Gives the message to the transmitter.
message = self.__transmitter.proceed(message)
# Serializes the response.
message = dumps(message, HIGHEST_PROTOCOL)
# Sends the response to the client.
self.send(message + RequestHandler.END_STRING)
# Closes the channel.
self.close_when_done()
def handle_error(self):
logSys.error("Unexpected communication error")
self.close()
##
# Asynchronous server class.
#
# This class extends asyncore and dispatches connection requests to
# RequestHandler.
class AsyncServer(asyncore.dispatcher):
def __init__(self, transmitter):
asyncore.dispatcher.__init__(self)
self.__transmitter = transmitter
self.__sock = "/var/run/fail2ban/fail2ban.sock"
self.__init = False
##
# Returns False as we only read the socket first.
def writable(self):
return False
def handle_accept(self):
try:
conn, addr = self.accept()
except socket.error:
logSys.warning("Socket error")
return
except TypeError:
logSys.warning("Type error")
return
# Creates an instance of the handler class to handle the
# request/response on the incoming connection.
RequestHandler(conn, self.__transmitter)
##
# Starts the communication server.
#
# @param sock: socket file.
# @param force: remove the socket file if exists.
def start(self, sock, force):
self.__sock = sock
# Remove socket
if os.path.exists(sock):
logSys.error("Fail2ban seems to be already running")
if force:
logSys.warn("Forcing execution of the server")
os.remove(sock)
else:
raise AsyncServerException("Server already running")
# Creates the socket.
self.create_socket(socket.AF_UNIX, socket.SOCK_STREAM)
self.set_reuse_addr()
try:
self.bind(sock)
except Exception:
raise AsyncServerException("Unable to bind socket %s" % self.__sock)
self.listen(1)
# Sets the init flag.
self.__init = True
# TODO Add try..catch
asyncore.loop()
##
# Stops the communication server.
def stop(self):
if self.__init:
# Only closes the socket if it was initialized first.
self.close()
# Remove socket
if os.path.exists(self.__sock):
logSys.debug("Removed socket file " + self.__sock)
os.remove(self.__sock)
logSys.debug("Socket shutdown")
##
# AsyncServerException is used to wrap communication exceptions.
class AsyncServerException(Exception):
pass
+3 -2
View File
@@ -24,7 +24,7 @@ __date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
from banticket import BanTicket
from ticket import BanTicket
from threading import Lock
from mytime import MyTime
import logging
@@ -125,7 +125,7 @@ class BanManager:
# @param ticket the FailTicket
# @return a BanTicket
@staticmethod
#@staticmethod
def createBanTicket(ticket):
ip = ticket.getIP()
#lastTime = ticket.getTime()
@@ -133,6 +133,7 @@ class BanManager:
banTicket = BanTicket(ip, lastTime)
banTicket.setAttempt(ticket.getAttempt())
return banTicket
createBanTicket = staticmethod(createBanTicket)
##
# Add a ban ticket.
-50
View File
@@ -1,50 +0,0 @@
# This file is part of Fail2Ban.
#
# Fail2Ban is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# Fail2Ban is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Fail2Ban; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# Author: Cyril Jaquier
#
# $Revision$
__author__ = "Cyril Jaquier"
__version__ = "$Revision$"
__date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import logging
from ticket import Ticket
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban")
##
# Ban Ticket.
#
# This class extends the Ticket class. It is mainly used by the BanManager.
class BanTicket(Ticket):
##
# Constructor.
#
# Call the Ticket (parent) constructor and initialize default
# values.
# @param ip the IP address
# @param time the ban time
def __init__(self, ip, time):
Ticket.__init__(self, ip, time)
-165
View File
@@ -1,165 +0,0 @@
# This file is part of Fail2Ban.
#
# Fail2Ban is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# Fail2Ban is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Fail2Ban; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# Author: Cyril Jaquier
#
# $Revision$
__author__ = "Cyril Jaquier"
__version__ = "$Revision$"
__date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import time, logging
from datestrptime import DateStrptime
from datetai64n import DateTai64n
from dateepoch import DateEpoch
from threading import Lock
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.filter.datedetector")
class DateDetector:
def __init__(self):
self.__lock = Lock()
self.__templates = list()
self.__defTemplate = DateStrptime()
def addDefaultTemplate(self):
try:
self.__lock.acquire()
# standard
template = DateStrptime()
template.setName("Month Day Hour:Minute:Second")
template.setRegex("^\S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}")
template.setPattern("%b %d %H:%M:%S")
self.__templates.append(template)
# asctime
template = DateStrptime()
template.setName("Weekday Month Day Hour:Minute:Second Year")
template.setRegex("\S{3} \S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2} \d{4}")
template.setPattern("%a %b %d %H:%M:%S %Y")
self.__templates.append(template)
# asctime without year
template = DateStrptime()
template.setName("Weekday Month Day Hour:Minute:Second")
template.setRegex("\S{3} \S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}")
template.setPattern("%a %b %d %H:%M:%S")
self.__templates.append(template)
# simple date
template = DateStrptime()
template.setName("Year/Month/Day Hour:Minute:Second")
template.setRegex("\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}")
template.setPattern("%Y/%m/%d %H:%M:%S")
self.__templates.append(template)
# Apache format [31/Oct/2006:09:22:55 -0000]
template = DateStrptime()
template.setName("Day/Month/Year:Hour:Minute:Second")
template.setRegex("\d{2}/\S{3}/\d{4}:\d{2}:\d{2}:\d{2}")
template.setPattern("%d/%b/%Y:%H:%M:%S")
self.__templates.append(template)
# Exim 2006-12-21 06:43:20
template = DateStrptime()
template.setName("Year-Month-Day Hour:Minute:Second")
template.setRegex("\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}")
template.setPattern("%Y-%m-%d %H:%M:%S")
self.__templates.append(template)
# TAI64N
template = DateTai64n()
template.setName("TAI64N")
self.__templates.append(template)
# Epoch
template = DateEpoch()
template.setName("Epoch")
self.__templates.append(template)
finally:
self.__lock.release()
def getTemplates(self):
return self.__templates
def setDefaultRegex(self, value):
self.__defTemplate.setRegex(value)
def getDefaultRegex(self):
return self.__defTemplate.getRegex()
def setDefaultPattern(self, value):
self.__defTemplate.setPattern(value)
def getDefaultPattern(self):
return self.__defTemplate.getPattern()
def matchTime(self, line):
if self.__defTemplate.isValid():
return self.__defTemplate.matchDate(line)
else:
try:
self.__lock.acquire()
for template in self.__templates:
match = template.matchDate(line)
if not match == None:
return match
return None
finally:
self.__lock.release()
def getTime(self, line):
if self.__defTemplate.isValid():
try:
date = self.__defTemplate.getDate(line)
return date
except ValueError:
return None
else:
try:
self.__lock.acquire()
for template in self.__templates:
try:
date = template.getDate(line)
if date == None:
continue
template.incHits()
return date
except ValueError:
pass
return None
finally:
self.__lock.release()
def getUnixTime(self, line):
date = self.getTime(line)
if date == None:
return None
else:
return time.mktime(date)
##
# Sort the template lists using the hits score. This method is not called
# in this object and thus should be called from time to time.
def sortTemplate(self):
try:
self.__lock.acquire()
logSys.debug("Sorting the template list")
self.__templates.sort(cmp = lambda x, y:
cmp(x.getHits(), y.getHits()),
reverse = True)
finally:
self.__lock.release()
-84
View File
@@ -1,84 +0,0 @@
# -*- coding: utf8 -*-
# This file is part of Fail2Ban.
#
# Fail2Ban is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# Fail2Ban is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Fail2Ban; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# Author: Cyril Jaquier
#
# $Revision$
__author__ = "Cyril Jaquier"
__version__ = "$Revision$"
__date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
from mytime import MyTime
import time
from datetemplate import DateTemplate
##
# Use strptime() to parse a date. Our current locale is the 'C'
# one because we do not set the locale explicitly. This is POSIX
# standard.
class DateStrptime(DateTemplate):
TABLE = dict()
TABLE["Jan"] = []
TABLE["Feb"] = [u"Fév"]
TABLE["Mar"] = [u"Mär"]
TABLE["Apr"] = ["Avr"]
TABLE["May"] = ["Mai"]
TABLE["Jun"] = []
TABLE["Jul"] = []
TABLE["Aug"] = ["Aou"]
TABLE["Sep"] = []
TABLE["Oct"] = ["Okt"]
TABLE["Nov"] = []
TABLE["Dec"] = [u"Déc", "Dez"]
def __init__(self):
DateTemplate.__init__(self)
@staticmethod
def convertLocale(date):
for t in DateStrptime.TABLE:
for m in DateStrptime.TABLE[t]:
if date.find(m) >= 0:
return date.replace(m, t)
return date
def getDate(self, line):
date = None
dateMatch = self.matchDate(line)
if dateMatch:
try:
# Try first with 'C' locale
date = list(time.strptime(dateMatch.group(), self.getPattern()))
except ValueError:
# Try to convert date string to 'C' locale
conv = self.convertLocale(dateMatch.group())
date = list(time.strptime(conv, self.getPattern()))
if date[0] < 2000:
# There is probably no year field in the logs
date[0] = MyTime.gmtime()[0]
# Bug fix for #1241756
# If the date is greater than the current time, we suppose
# that the log is not from this year but from the year before
if time.mktime(date) > MyTime.time():
date[0] -= 1
return date
+1 -1
View File
@@ -25,7 +25,7 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
from faildata import FailData
from failticket import FailTicket
from ticket import FailTicket
from threading import Lock
import logging
+111 -27
View File
@@ -24,39 +24,123 @@ __date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
from regex import Regex, RegexException
import re, sre_constants, logging
##
# Regular expression class.
#
# This class represents a regular expression with its compiled version.
from template import Template
from timetemplate import TimeTemplates
from prefixtemplate import PrefixTemplates
from hosttemplate import HostTemplates
class FailRegex(Regex):
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.filter.failregex")
##
# Constructor.
#
# Creates a new object. This method can throw RegexException in order to
# avoid construction of invalid object.
# @param value the regular expression
class Regex:
def __init__(self, value):
# Replace "<HOST>" with default regular expression for host.
regex = value.replace("<HOST>", "(?:::f{4,6}:)?(?P<host>\S+)")
# Initializes the parent.
Regex.__init__(self, regex)
# Check for group "host"
if "host" not in self._regexObj.groupindex:
raise RegexException("No 'host' group in '%s'" % self._regex)
def __init__(self, regex):
self.__originalRegex = regex
self.__convertedRegex = None
self.__compiledRegex = None
self.__templates = dict()
self.__hostRegex = None
self.__dateRegex = None
self.__prefixRegex = None
def process(self):
regex = self.__originalRegex
for item in self.__templates.values():
regex = regex.replace(item.getTag(), item.getRegex(), 1)
try:
self.__compiledRegex = re.compile(regex)
self.__convertedRegex = regex
except sre_constants.error:
raise RegexException("Unable to compile regular expression '%s'" %
regex)
def register(self, template):
self.__templates[template.getName()] = template
##
# Returns the matched host.
#
# This corresponds to the pattern matched by the named group "host".
# @return the matched host
def getTemplate(self, tag):
return self.__templates[tag]
def match(self, line):
return self.__compiledRegex.match(line)
def getOriginalRegex(self):
return self.__originalRegex
def getConvertedRegex(self):
return self.__convertedRegex
class FailRegex:
HOST_TEMPLATES = HostTemplates()
PREFIX_TEMPLATES = PrefixTemplates()
TIME_TEMPLATES = TimeTemplates()
def __init__(self, regex):
self.__regex = Regex(regex)
self.__match = None
self.__found = False
def __autoDetection(self, line):
for host in self.HOST_TEMPLATES.getTemplates():
self.__regex.register(host)
for date in self.TIME_TEMPLATES.getTemplates():
self.__regex.register(date)
for prefix in self.PREFIX_TEMPLATES.getTemplates():
self.__regex.register(prefix)
self.__regex.process()
match = self.__regex.match(line)
if match:
self.__found = True
#logSys.debug("Auto-detection succeeded")
#logSys.debug("failregex is %s" %
# self.__regex.getConvertedRegex())
return match
return None
def search(self, line):
if self.__found:
self.__match = self.__regex.match(line)
else:
self.__match = self.__autoDetection(line)
def hasMatched(self):
if self.__match:
return True
else:
return False
def getOriginalRegex(self):
return self.__regex.getOriginalRegex()
def getHost(self):
host = self._matchCache.group("host")
template = self.__regex.getTemplate(Template.TEMPLATE_HOST)
host = self.__match.group(template.getName())
if host == None:
raise RegexException("Unexpected error. Please check your regex")
# Gets a few information.
s = self.__match.string
r = self.__match.re
raise RegexException("No 'host' found in '%s' using '%s'" % (s, r))
return host
def getTime(self):
template = self.__regex.getTemplate(Template.TEMPLATE_TIME)
time = self.__match.group(template.getName())
if time == None:
# Gets a few information.
s = self.__match.string
r = self.__match.re
raise RegexException("No 'time' found in '%s' using '%s'" % (s, r))
try:
return template.getTime(time)
except Exception:
return None
##
# Exception dedicated to the class Regex.
class RegexException(Exception):
pass
-37
View File
@@ -1,37 +0,0 @@
# This file is part of Fail2Ban.
#
# Fail2Ban is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# Fail2Ban is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Fail2Ban; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# Author: Cyril Jaquier
#
# $Revision$
__author__ = "Cyril Jaquier"
__version__ = "$Revision$"
__date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import logging
from ticket import Ticket
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban")
class FailTicket(Ticket):
def __init__(self, ip, time):
Ticket.__init__(self, ip, time)
+235 -237
View File
@@ -25,14 +25,12 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
from failmanager import FailManager
from failticket import FailTicket
from ticket import FailTicket
from jailthread import JailThread
from datedetector import DateDetector
from mytime import MyTime
from regex import Regex, RegexException
from failregex import FailRegex
from failregex import FailRegex, Regex, RegexException
import logging, re
import logging, re, time
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.filter")
@@ -58,11 +56,6 @@ class Filter(JailThread):
self.jail = jail
## The failures manager.
self.failManager = FailManager()
## The log file handler.
self.__crtHandler = None
self.__crtFilename = None
## The log file path.
self.__logPath = []
## The regular expression list matching the failures.
self.__failRegex = list()
## The regular expression list with expressions to ignore.
@@ -71,92 +64,9 @@ class Filter(JailThread):
self.__findTime = 6000
## The ignore IP list.
self.__ignoreIpList = []
## The last position of the file.
self.__lastPos = dict()
## The last date in tht log file.
self.__lastDate = dict()
self.dateDetector = DateDetector()
self.dateDetector.addDefaultTemplate()
logSys.info("Created Filter")
logSys.debug("Created Filter")
##
# Add a log file path
#
# @param path log file path
def addLogPath(self, path):
self.getLogPath().append(path)
# Initialize default values
self.__lastDate[path] = 0
self.__lastPos[path] = 0
##
# Delete a log path
#
# @param path the log file to delete
def delLogPath(self, path):
self.getLogPath().remove(path)
del self.__lastDate[path]
del self.__lastPos[path]
##
# Get the log file path
#
# @return log file path
def getLogPath(self):
return self.__logPath
##
# Check whether path is already monitored.
#
# @param path The path
# @return True if the path is already monitored else False
def containsLogPath(self, path):
try:
self.getLogPath().index(path)
return True
except ValueError:
return False
##
# Set the regular expression which matches the time.
#
# @param value the regular expression
def setTimeRegex(self, value):
self.dateDetector.setDefaultRegex(value)
logSys.info("Set default regex = %s" % value)
##
# Get the regular expression which matches the time.
#
# @return the regular expression
def getTimeRegex(self):
return self.dateDetector.getDefaultRegex()
##
# Set the time pattern.
#
# @param value the time pattern
def setTimePattern(self, value):
self.dateDetector.setDefaultPattern(value)
logSys.info("Set default pattern = %s" % value)
##
# Get the time pattern.
#
# @return the time pattern
def getTimePattern(self):
return self.dateDetector.getDefaultPattern()
##
# Add a regular expression which matches the failure.
#
@@ -187,7 +97,7 @@ class Filter(JailThread):
def getFailRegex(self):
failRegex = list()
for regex in self.__failRegex:
failRegex.append(regex.getRegex())
failRegex.append(regex.getOriginalRegex())
return failRegex
##
@@ -269,6 +179,17 @@ class Filter(JailThread):
def run(self):
raise Exception("run() is abstract")
##
# Ban an IP - http://blogs.buanzo.com.ar/2009/04/fail2ban-patch-ban-ip-address-manually.html
# Arturo 'Buanzo' Busleiman <buanzo@buanzo.com.ar>
#
# to enable banip fail2ban-client BAN command
def addBannedIP(self, ip):
unixTime = time.time()
self.failManager.addFailure(FailTicket(ip, unixTime))
return ip
##
# Add an IP/DNS to the ignore list.
#
@@ -299,7 +220,7 @@ class Filter(JailThread):
for i in self.__ignoreIpList:
# An empty string is always false
if i == "":
return False
continue
s = i.split('/', 1)
# IP address without CIDR mask
if len(s) == 1:
@@ -314,107 +235,44 @@ class Filter(JailThread):
if ip in ips:
return True
else:
return False
continue
if a == b:
return True
return False
##
# Open the log file.
def __openLogFile(self, filename):
""" Opens the log file specified on init.
"""
def processLineAndAdd(self, line):
try:
self.__crtFilename = filename
self.__crtHandler = open(filename)
logSys.debug("Opened " + filename)
return True
except OSError:
logSys.error("Unable to open " + filename)
except IOError:
logSys.error("Unable to read " + filename +
". Please check permissions")
return False
##
# Close the log file.
def __closeLogFile(self):
self.__crtFilename = None
self.__crtHandler.close()
##
# Set the file position.
#
# Sets the file position. We must take care of log file rotation
# and reset the position to 0 in that case. Use the log message
# timestamp in order to detect this.
def __setFilePos(self):
line = self.__crtHandler.readline()
lastDate = self.__lastDate[self.__crtFilename]
lineDate = self.dateDetector.getUnixTime(line)
if lastDate < lineDate:
logSys.debug("Date " + `lastDate` + " is smaller than " + `lineDate`)
logSys.debug("Log rotation detected for " + self.__crtFilename)
self.__lastPos[self.__crtFilename] = 0
lastPos = self.__lastPos[self.__crtFilename]
logSys.debug("Setting file position to " + `lastPos` + " for " +
self.__crtFilename)
self.__crtHandler.seek(lastPos)
##
# Get the file position.
def __getFilePos(self):
return self.__crtHandler.tell()
##
# Gets all the failure in the log file.
#
# Gets all the failure in the log file which are newer than
# MyTime.time()-self.findTime. When a failure is detected, a FailTicket
# is created and is added to the FailManager.
def getFailures(self, filename):
# Try to open log file.
if not self.__openLogFile(filename):
logSys.error("Unable to get failures in " + filename)
return False
self.__setFilePos()
lastLine = None
for line in self.__crtHandler:
if not self._isActive():
# The jail has been stopped
# Decode line to UTF-8
l = line.decode('utf-8')
except UnicodeDecodeError:
l = line
for element in self.findFailure(l):
ip = element[0]
unixTime = element[1]
if unixTime < MyTime.time() - self.getFindTime():
break
try:
# Decode line to UTF-8
line = line.decode('utf-8')
except UnicodeDecodeError:
pass
if not self.dateDetector.matchTime(line):
# There is no valid time in this line
if self.inIgnoreIPList(ip):
logSys.debug("Ignore %s" % ip)
continue
lastLine = line
for element in self.findFailure(line):
ip = element[0]
unixTime = element[1]
if unixTime < MyTime.time()-self.__findTime:
break
if self.inIgnoreIPList(ip):
logSys.debug("Ignore "+ip)
continue
logSys.debug("Found "+ip)
self.failManager.addFailure(FailTicket(ip, unixTime))
self.__lastPos[filename] = self.__getFilePos()
if lastLine:
self.__lastDate[filename] = self.dateDetector.getUnixTime(lastLine)
self.__closeLogFile()
return True
logSys.debug("Found %s" % ip)
self.failManager.addFailure(FailTicket(ip, unixTime))
##
# Finds the failure in a line.
# Returns true if the line should be ignored.
#
# Uses ignoreregex.
# @param line: the line
# @return: a boolean
def ignoreLine(self, line):
for ignoreRegex in self.__ignoreRegex:
ignoreRegex.process()
if ignoreRegex.match(line):
return True
return False
##
# Finds the failure in a line given split into time and log parts.
#
# Uses the failregex pattern to find it and timeregex in order
# to find the logging time.
@@ -423,34 +281,30 @@ class Filter(JailThread):
def findFailure(self, line):
failList = list()
# Checks if we must ignore this line.
for ignoreRegex in self.__ignoreRegex:
ignoreRegex.search(line)
if ignoreRegex.hasMatched():
# The ignoreregex matched. Return.
logSys.debug("Ignoring this line")
return failList
if self.ignoreLine(line):
# The ignoreregex matched. Return.
return failList
# Iterates over all the regular expressions.
for failRegex in self.__failRegex:
failRegex.search(line)
if failRegex.hasMatched():
# The failregex matched.
date = self.dateDetector.getUnixTime(line)
if date == None:
logSys.debug("Found a match but no valid date/time found "
+ "for " + line + ". Please contact the "
+ "author in order to get support for this "
+ "format")
else:
try:
host = failRegex.getHost()
ipMatch = DNSUtils.textToIp(host)
if ipMatch:
for ip in ipMatch:
failList.append([ip, date])
# We matched a regex, it is enough to stop.
break
except RegexException, e:
logSys.error(e)
try:
host = failRegex.getHost()
date = failRegex.getTime()
if not date:
logSys.warning("Unable to get a valid date: %s" % line)
break
# Use Unix timestamp.
dateUnix = time.mktime(date)
ipMatch = DNSUtils.textToIp(host)
if ipMatch:
for ip in ipMatch:
failList.append([ip, dateUnix])
# We matched a regex, it is enough to stop.
break
except RegexException, e:
logSys.error(e)
return failList
@@ -462,11 +316,162 @@ class Filter(JailThread):
# @return a list with tuple
def status(self):
ret = [("Currently failed", self.failManager.size()),
ret = [("Currently failed", self.failManager.size()),
("Total failed", self.failManager.getFailTotal())]
return ret
class FileFilter(Filter):
def __init__(self, jail):
Filter.__init__(self, jail)
## The log file path.
self.__logPath = []
##
# Add a log file path
#
# @param path log file path
def addLogPath(self, path, tail = True):
container = FileContainer(path, tail)
self.__logPath.append(container)
##
# Delete a log path
#
# @param path the log file to delete
def delLogPath(self, path):
for log in self.__logPath:
if log.getFileName() == path:
self.__logPath.remove(log)
return
##
# Get the log file path
#
# @return log file path
def getLogPath(self):
return self.__logPath
##
# Check whether path is already monitored.
#
# @param path The path
# @return True if the path is already monitored else False
def containsLogPath(self, path):
for log in self.__logPath:
if log.getFileName() == path:
return True
return False
def getFileContainer(self, path):
for log in self.__logPath:
if log.getFileName() == path:
return log
return None
##
# Gets all the failure in the log file.
#
# Gets all the failure in the log file which are newer than
# MyTime.time()-self.findTime. When a failure is detected, a FailTicket
# is created and is added to the FailManager.
def getFailures(self, filename):
container = self.getFileContainer(filename)
if container == None:
logSys.error("Unable to get failures in " + filename)
return False
# Try to open log file.
try:
container.open()
except Exception, e:
logSys.error("Unable to open %s" % filename)
logSys.exception(e)
return False
line = container.readline()
while not line == "":
if not self._isActive():
# The jail has been stopped
break
self.processLineAndAdd(line)
# Read a new line.
line = container.readline()
container.close()
return True
def status(self):
ret = Filter.status(self)
path = [m.getFileName() for m in self.getLogPath()]
ret.append(("File list", path))
return ret
##
# FileContainer class.
#
# This class manages a file handler and takes care of log rotation detection.
# In order to detect log rotation, the hash (MD5) of the first line of the file
# is computed and compared to the previous hash of this line.
import md5
class FileContainer:
def __init__(self, filename, tail = True):
self.__filename = filename
self.__tail = tail
self.__handler = None
# Try to open the file. Raises an exception if an error occured.
handler = open(filename)
try:
firstLine = handler.readline()
# Computes the MD5 of the first line.
self.__hash = md5.new(firstLine).digest()
# Start at the beginning of file if tail mode is off.
if tail:
handler.seek(0, 2)
self.__pos = handler.tell()
else:
self.__pos = 0
finally:
handler.close()
def getFileName(self):
return self.__filename
def open(self):
self.__handler = open(self.__filename)
firstLine = self.__handler.readline()
# Computes the MD5 of the first line.
myHash = md5.new(firstLine).digest()
# Compare hash.
if not self.__hash == myHash:
logSys.info("Log rotation detected for %s" % self.__filename)
self.__hash = myHash
self.__pos = 0
# Sets the file pointer to the last position.
self.__handler.seek(self.__pos)
def readline(self):
if self.__handler == None:
return ""
return self.__handler.readline()
def close(self):
if not self.__handler == None:
# Saves the last position.
self.__pos = self.__handler.tell()
# Closes the file.
self.__handler.close()
self.__handler = None
##
# Utils class for DNS and IP handling.
#
@@ -477,10 +482,9 @@ import socket, struct
class DNSUtils:
DNS_CRE = re.compile("(?:(?:\w|-)+\.){2,}\w+")
IP_CRE = re.compile("(?:\d{1,3}\.){3}\d{1,3}")
@staticmethod
#@staticmethod
def dnsToIp(dns):
""" Convert a DNS into an IP address using the Python socket module.
Thanks to Kevin Drapel.
@@ -491,19 +495,9 @@ class DNSUtils:
logSys.warn("Unable to find a corresponding IP address for %s"
% dns)
return list()
dnsToIp = staticmethod(dnsToIp)
@staticmethod
def textToDns(text):
""" Search for possible DNS in an arbitrary text.
Thanks to Tom Pike.
"""
match = DNSUtils.DNS_CRE.match(text)
if match:
return match
else:
return None
@staticmethod
#@staticmethod
def searchIP(text):
""" Search if an IP address if directly available and return
it.
@@ -513,8 +507,9 @@ class DNSUtils:
return match
else:
return None
searchIP = staticmethod(searchIP)
@staticmethod
#@staticmethod
def isValidIP(string):
""" Return true if str is a valid IP
"""
@@ -524,8 +519,9 @@ class DNSUtils:
return True
except socket.error:
return False
isValidIP = staticmethod(isValidIP)
@staticmethod
#@staticmethod
def textToIp(text):
""" Return the IP of DNS found in a given text.
"""
@@ -538,14 +534,13 @@ class DNSUtils:
ipList.append(plainIPStr)
if not ipList:
# Try to get IP from possible DNS
dns = DNSUtils.textToDns(text)
if not dns == None:
ip = DNSUtils.dnsToIp(dns.group(0))
for e in ip:
ipList.append(e)
ip = DNSUtils.dnsToIp(text)
for e in ip:
ipList.append(e)
return ipList
textToIp = staticmethod(textToIp)
@staticmethod
#@staticmethod
def cidr(i, n):
""" Convert an IP address string with a CIDR mask into a 32-bit
integer.
@@ -553,15 +548,18 @@ class DNSUtils:
# 32-bit IPv4 address mask
MASK = 0xFFFFFFFFL
return ~(MASK >> n) & MASK & DNSUtils.addr2bin(i)
cidr = staticmethod(cidr)
@staticmethod
#@staticmethod
def addr2bin(string):
""" Convert a string IPv4 address into an unsigned integer.
"""
return struct.unpack("!L", socket.inet_aton(string))[0]
addr2bin = staticmethod(addr2bin)
@staticmethod
#@staticmethod
def bin2addr(addr):
""" Convert a numeric IPv4 address into string n.n.n.n form.
"""
return socket.inet_ntoa(struct.pack("!L", addr))
bin2addr = staticmethod(bin2addr)
+12 -12
View File
@@ -25,7 +25,7 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
from failmanager import FailManagerEmpty
from filter import Filter
from filter import FileFilter
from mytime import MyTime
import time, logging, gamin
@@ -40,7 +40,7 @@ logSys = logging.getLogger("fail2ban.filter")
# that matches a given regular expression. This class is instanciated by
# a Jail object.
class FilterGamin(Filter):
class FilterGamin(FileFilter):
##
# Constructor.
@@ -49,11 +49,11 @@ class FilterGamin(Filter):
# @param jail the jail object
def __init__(self, jail):
Filter.__init__(self, jail)
FileFilter.__init__(self, jail)
self.__modified = False
# Gamin monitor
self.monitor = gamin.WatchMonitor()
logSys.info("Created FilterGamin")
logSys.debug("Created FilterGamin")
def callback(self, path, event):
@@ -69,12 +69,12 @@ class FilterGamin(Filter):
#
# @param path log file path
def addLogPath(self, path):
def addLogPath(self, path, tail = True):
if self.containsLogPath(path):
logSys.error(path + " already exists")
else:
self.monitor.watch_file(path, self.callback)
Filter.addLogPath(self, path)
FileFilter.addLogPath(self, path, tail)
logSys.info("Added logfile = %s" % path)
##
@@ -87,7 +87,7 @@ class FilterGamin(Filter):
logSys.error(path + " is not monitored")
else:
self.monitor.stop_watch(path)
Filter.delLogPath(self, path)
FileFilter.delLogPath(self, path)
logSys.info("Removed logfile = %s" % path)
##
@@ -108,11 +108,11 @@ class FilterGamin(Filter):
if self.__modified:
try:
ticket = self.failManager.toBan()
self.jail.putFailTicket(ticket)
while True:
ticket = self.failManager.toBan()
self.jail.putFailTicket(ticket)
except FailManagerEmpty:
self.failManager.cleanup(MyTime.time())
self.dateDetector.sortTemplate()
self.__modified = False
time.sleep(self.getSleepTime())
else:
@@ -126,6 +126,6 @@ class FilterGamin(Filter):
# Desallocates the resources used by Gamin.
def __cleanup(self):
for path in Filter.getLogPath(self):
self.monitor.stop_watch(path)
for path in self.getLogPath():
self.monitor.stop_watch(path.getFileName())
del self.monitor
+13 -13
View File
@@ -25,7 +25,7 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
from failmanager import FailManagerEmpty
from filter import Filter
from filter import FileFilter
from mytime import MyTime
import time, logging, os
@@ -40,7 +40,7 @@ logSys = logging.getLogger("fail2ban.filter")
# that matches a given regular expression. This class is instanciated by
# a Jail object.
class FilterPoll(Filter):
class FilterPoll(FileFilter):
##
# Constructor.
@@ -49,25 +49,25 @@ class FilterPoll(Filter):
# @param jail the jail object
def __init__(self, jail):
Filter.__init__(self, jail)
FileFilter.__init__(self, jail)
self.__modified = False
## The time of the last modification of the file.
self.__lastModTime = dict()
self.__file404Cnt = dict()
logSys.info("Created FilterPoll")
logSys.debug("Created FilterPoll")
##
# Add a log file path
#
# @param path log file path
def addLogPath(self, path):
def addLogPath(self, path, tail = True):
if self.containsLogPath(path):
logSys.error(path + " already exists")
else:
self.__lastModTime[path] = 0
self.__file404Cnt[path] = 0
Filter.addLogPath(self, path)
FileFilter.addLogPath(self, path, tail)
logSys.info("Added logfile = %s" % path)
##
@@ -81,7 +81,7 @@ class FilterPoll(Filter):
else:
del self.__lastModTime[path]
del self.__file404Cnt[path]
Filter.delLogPath(self, path)
FileFilter.delLogPath(self, path)
logSys.info("Removed logfile = %s" % path)
##
@@ -96,18 +96,18 @@ class FilterPoll(Filter):
while self._isActive():
if not self.getIdle():
# Get file modification
for f in self.getLogPath():
if self.isModified(f):
self.getFailures(f)
for container in self.getLogPath():
if self.isModified(container.getFileName()):
self.getFailures(container.getFileName())
self.__modified = True
if self.__modified:
try:
ticket = self.failManager.toBan()
self.jail.putFailTicket(ticket)
while True:
ticket = self.failManager.toBan()
self.jail.putFailTicket(ticket)
except FailManagerEmpty:
self.failManager.cleanup(MyTime.time())
self.dateDetector.sortTemplate()
self.__modified = False
time.sleep(self.getSleepTime())
else:
+16 -17
View File
@@ -1,3 +1,4 @@
# -*- coding: utf8 -*-
# This file is part of Fail2Ban.
#
# Fail2Ban is free software; you can redistribute it and/or modify
@@ -16,29 +17,27 @@
# Author: Cyril Jaquier
#
# $Revision$
# $Revision: 645 $
__author__ = "Cyril Jaquier"
__version__ = "$Revision$"
__date__ = "$Date$"
__version__ = "$Revision: 645 $"
__date__ = "$Date: 2008-01-16 23:55:04 +0100 (Wed, 16 Jan 2008) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import time
from template import Template, Templates
from datetemplate import DateTemplate
class HostTemplate(Template):
class DateEpoch(DateTemplate):
def __init__(self):
Template.__init__(self, Template.TEMPLATE_HOST, "<HOST>")
class HostTemplates(Templates):
def __init__(self):
DateTemplate.__init__(self)
# We already know the format for TAI64N
self.setRegex("^\d{10}(\.\d{6})?")
def getDate(self, line):
date = None
dateMatch = self.matchDate(line)
if dateMatch:
# extract part of format which represents seconds since epoch
date = list(time.gmtime(float(dateMatch.group())))
return date
Templates.__init__(self)
# ::ffff:141.3.81.106
template = HostTemplate()
template.setRegex("(?:::f{4,6}:)?(?P<%s>\S+)" % template.getName())
self.templates.append(template)
+123
View File
@@ -0,0 +1,123 @@
# Copyright (c) 2007 Michael Twomey
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""ISO 8601 date time string parsing
Basic usage:
>>> import iso8601
>>> iso8601.parse_date("2007-01-25T12:00:00Z")
datetime.datetime(2007, 1, 25, 12, 0, tzinfo=<iso8601.iso8601.Utc ...>)
>>>
"""
from datetime import datetime, timedelta, tzinfo
import re
__all__ = ["parse_date", "ParseError"]
# Adapted from http://delete.me.uk/2005/03/iso8601.html
ISO8601_REGEX = re.compile(r"(?P<year>[0-9]{4})(-(?P<month>[0-9]{1,2})(-(?P<day>[0-9]{1,2})"
r"((?P<separator>.)(?P<hour>[0-9]{2}):(?P<minute>[0-9]{2})(:(?P<second>[0-9]{2})(\.(?P<fraction>[0-9]+))?)?"
r"(?P<timezone>Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?"
)
TIMEZONE_REGEX = re.compile("(?P<prefix>[+-])(?P<hours>[0-9]{2}).(?P<minutes>[0-9]{2})")
class ParseError(Exception):
"""Raised when there is a problem parsing a date string"""
# Yoinked from python docs
ZERO = timedelta(0)
class Utc(tzinfo):
"""UTC
"""
def utcoffset(self, dt):
return ZERO
def tzname(self, dt):
return "UTC"
def dst(self, dt):
return ZERO
UTC = Utc()
class FixedOffset(tzinfo):
"""Fixed offset in hours and minutes from UTC
"""
def __init__(self, offset_hours, offset_minutes, name):
self.__offset = timedelta(hours=offset_hours, minutes=offset_minutes)
self.__name = name
def utcoffset(self, dt):
return self.__offset
def tzname(self, dt):
return self.__name
def dst(self, dt):
return ZERO
def __repr__(self):
return "<FixedOffset %r>" % self.__name
def parse_timezone(tzstring, default_timezone=UTC):
"""Parses ISO 8601 time zone specs into tzinfo offsets
"""
if tzstring == "Z":
return default_timezone
# This isn't strictly correct, but it's common to encounter dates without
# timezones so I'll assume the default (which defaults to UTC).
# Addresses issue 4.
if tzstring is None:
return default_timezone
m = TIMEZONE_REGEX.match(tzstring)
prefix, hours, minutes = m.groups()
hours, minutes = int(hours), int(minutes)
if prefix == "-":
hours = -hours
minutes = -minutes
return FixedOffset(hours, minutes, tzstring)
def parse_date(datestring, default_timezone=UTC):
"""Parses ISO 8601 dates into datetime objects
The timezone is parsed from the date string. However it is quite common to
have dates without a timezone (not strictly correct). In this case the
default timezone specified in default_timezone is used. This is UTC by
default.
"""
if not isinstance(datestring, basestring):
raise ParseError("Expecting a string %r" % datestring)
m = ISO8601_REGEX.match(datestring)
if not m:
raise ParseError("Unable to parse date string %r" % datestring)
groups = m.groupdict()
tz = parse_timezone(groups["timezone"], default_timezone=default_timezone)
if groups["fraction"] is None:
groups["fraction"] = 0
else:
groups["fraction"] = int(float("0.%s" % groups["fraction"]) * 1e6)
return datetime(int(groups["year"]), int(groups["month"]), int(groups["day"]),
int(groups["hour"]), int(groups["minute"]), int(groups["second"]),
int(groups["fraction"]), tz)
+5 -2
View File
@@ -37,6 +37,7 @@ class Jail:
self.__name = name
self.__queue = Queue.Queue()
self.__filter = None
logSys.info("Creating new jail '%s'" % self.__name)
if backend == "polling":
self.__initPoller()
else:
@@ -47,14 +48,14 @@ class Jail:
self.__action = Actions(self)
def __initPoller(self):
logSys.info("Using poller")
logSys.info("Jail '%s' uses poller" % self.__name)
from filterpoll import FilterPoll
self.__filter = FilterPoll(self)
def __initGamin(self):
# Try to import gamin
import gamin
logSys.info("Using Gamin")
logSys.info("Jail '%s' uses Gamin" % self.__name)
from filtergamin import FilterGamin
self.__filter = FilterGamin(self)
@@ -82,12 +83,14 @@ class Jail:
def start(self):
self.__filter.start()
self.__action.start()
logSys.info("Jail '%s' started" % self.__name)
def stop(self):
self.__filter.stop()
self.__action.stop()
self.__filter.join()
self.__action.join()
logSys.info("Jail '%s' stopped" % self.__name)
def isAlive(self):
isAlive0 = self.__filter.isAlive()
+6 -3
View File
@@ -46,31 +46,34 @@ class MyTime:
#
# @param t the time to set or None
@staticmethod
#@staticmethod
def setTime(t):
MyTime.myTime = t
setTime = staticmethod(setTime)
##
# Equivalent to time.time()
#
# @return time.time() if setTime was called with None
@staticmethod
#@staticmethod
def time():
if MyTime.myTime == None:
return time.time()
else:
return MyTime.myTime
time = staticmethod(time)
##
# Equivalent to time.gmtime()
#
# @return time.gmtime() if setTime was called with None
@staticmethod
#@staticmethod
def gmtime():
if MyTime.myTime == None:
return time.gmtime()
else:
return time.gmtime(MyTime.myTime)
gmtime = staticmethod(gmtime)
@@ -1,3 +1,4 @@
# -*- coding: utf8 -*-
# This file is part of Fail2Ban.
#
# Fail2Ban is free software; you can redistribute it and/or modify
@@ -16,31 +17,35 @@
# Author: Cyril Jaquier
#
# $Revision$
# $Revision: 645 $
__author__ = "Cyril Jaquier"
__version__ = "$Revision$"
__date__ = "$Date$"
__version__ = "$Revision: 645 $"
__date__ = "$Date: 2008-01-16 23:55:04 +0100 (Wed, 16 Jan 2008) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import time
from template import Template, Templates
from datetemplate import DateTemplate
class PrefixTemplate(Template):
class DateTai64n(DateTemplate):
def __init__(self):
Template.__init__(self, Template.TEMPLATE_PREFIX, "<PREFIX>")
class PrefixTemplates(Templates):
def __init__(self):
DateTemplate.__init__(self)
# We already know the format for TAI64N
self.setRegex("@[0-9a-f]{24}")
def getDate(self, line):
date = None
dateMatch = self.matchDate(line)
if dateMatch:
# extract part of format which represents seconds since epoch
value = dateMatch.group()
seconds_since_epoch = value[2:17]
date = list(time.gmtime(int(seconds_since_epoch, 16)))
return date
Templates.__init__(self)
# i60p295 sshd[12365]:
template = PrefixTemplate()
template.setRegex("\S+ \S+\[\d+\]:")
self.templates.append(template)
# [sshd] error: PAM:
template = PrefixTemplate()
template.setRegex("\[\S+\] \S+: \S+:")
self.templates.append(template)
# HOSTNAME courieresmtpd
template = PrefixTemplate()
template.setRegex("\S+ \S+:")
self.templates.append(template)
-90
View File
@@ -1,90 +0,0 @@
# This file is part of Fail2Ban.
#
# Fail2Ban is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# Fail2Ban is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Fail2Ban; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# Author: Cyril Jaquier
#
# $Revision$
__author__ = "Cyril Jaquier"
__version__ = "$Revision$"
__date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import re, sre_constants
##
# Regular expression class.
#
# This class represents a regular expression with its compiled version.
class Regex:
##
# Constructor.
#
# Creates a new object. This method can throw RegexException in order to
# avoid construction of invalid object.
# @param value the regular expression
def __init__(self, regex):
self._matchCache = None
if regex.lstrip() == '':
raise RegexException("Cannot add empty regex")
try:
self._regexObj = re.compile(regex)
self._regex = regex
except sre_constants.error:
raise RegexException("Unable to compile regular expression '%s'" %
regex)
##
# Gets the regular expression.
#
# The effective regular expression used is returned.
# @return the regular expression
def getRegex(self):
return self._regex
##
# Searches the regular expression.
#
# Sets an internal cache (match object) in order to avoid searching for
# the pattern again. This method must be called before calling any other
# method of this object.
# @param value the line
def search(self, value):
self._matchCache = self._regexObj.search(value)
##
# Checks if the previous call to search() matched.
#
# @return True if a match was found, False otherwise
def hasMatched(self):
if self._matchCache:
return True
else:
return False
##
# Exception dedicated to the class Regex.
class RegexException(Exception):
pass
+58 -37
View File
@@ -27,14 +27,17 @@ __license__ = "GPL"
from threading import Lock, RLock
from jails import Jails
from transmitter import Transmitter
from ssocket import SSocket
from ssocket import SSocketErrorException
from asyncserver import AsyncServer
from asyncserver import AsyncServerException
from common import version
import logging, logging.handlers, sys, os, signal
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.server")
class Server:
PID_FILE = "/var/run/fail2ban/fail2ban.pid"
def __init__(self, daemon = False):
self.__loggingLock = Lock()
@@ -42,19 +45,19 @@ class Server:
self.__jails = Jails()
self.__daemon = daemon
self.__transm = Transmitter(self)
self.__socket = SSocket(self.__transm)
self.__logLevel = 3
self.__logTarget = "STDOUT"
self.__asyncServer = AsyncServer(self.__transm)
self.__logLevel = None
self.__logTarget = None
# Set logging level
self.setLogLevel(self.__logLevel)
self.setLogTarget(self.__logTarget)
self.setLogLevel(3)
self.setLogTarget("STDOUT")
def __sigTERMhandler(self, signum, frame):
logSys.debug("Caught signal %d. Exiting" % signum)
self.quit()
def start(self, sock, force = False):
logSys.info("Starting Fail2ban")
logSys.info("Starting Fail2ban v" + version.version)
# Install signal handlers
signal.signal(signal.SIGTERM, self.__sigTERMhandler)
@@ -63,29 +66,47 @@ class Server:
# First set the mask to only allow access to owner
os.umask(0077)
if self.__daemon:
logSys.info("Starting in daemon mode")
ret = self.__createDaemon()
if ret:
logSys.info("Daemon started")
else:
logSys.error("Could not create daemon")
raise ServerInitializationError("Could not create daemon")
# Creates a PID file.
try:
logSys.debug("Creating PID file %s" % Server.PID_FILE)
pidFile = open(Server.PID_FILE, 'w')
pidFile.write("%s\n" % os.getpid())
pidFile.close()
except IOError, e:
logSys.error("Unable to create PID file: %s" % e)
# Start the communication
logSys.debug("Starting communication")
try:
self.__socket.initialize(sock, force)
self.__socket.start()
# Workaround (???) for join() bug.
# https://sourceforge.net/tracker/?func=detail&atid=105470&aid=1167930&group_id=5470
while self.__socket.isAlive():
self.__socket.join(1)
except SSocketErrorException:
logSys.error("Could not start server")
self.__asyncServer.start(sock, force)
except AsyncServerException, e:
logSys.error("Could not start server: %s", e)
# Removes the PID file.
try:
logSys.debug("Remove PID file %s" % Server.PID_FILE)
os.remove(Server.PID_FILE)
except OSError, e:
logSys.error("Unable to remove PID file: %s" % e)
logSys.info("Exiting Fail2ban")
def quit(self):
self.stopAllJail()
# Stop communication
self.__socket.stop()
self.__asyncServer.stop()
# Shutdowns the logging.
try:
self.__loggingLock.acquire()
logging.shutdown()
finally:
self.__loggingLock.release()
def addJail(self, name, backend):
self.__jails.add(name, backend)
@@ -139,25 +160,15 @@ class Server:
return self.__jails.getFilter(name).getIgnoreIP()
def addLogPath(self, name, fileName):
self.__jails.getFilter(name).addLogPath(fileName)
# Enable tail mode.
self.__jails.getFilter(name).addLogPath(fileName, True)
def delLogPath(self, name, fileName):
self.__jails.getFilter(name).delLogPath(fileName)
def getLogPath(self, name):
return self.__jails.getFilter(name).getLogPath()
def setTimeRegex(self, name, value):
self.__jails.getFilter(name).setTimeRegex(value)
def getTimeRegex(self, name):
return self.__jails.getFilter(name).getTimeRegex()
def setTimePattern(self, name, value):
self.__jails.getFilter(name).setTimePattern(value)
def getTimePattern(self, name):
return self.__jails.getFilter(name).getTimePattern()
return [m.getFileName()
for m in self.__jails.getFilter(name).getLogPath()]
def setFindTime(self, name, value):
self.__jails.getFilter(name).setFindTime(value)
@@ -211,6 +222,9 @@ class Server:
def setBanTime(self, name, value):
self.__jails.getAction(name).setBanTime(value)
def setBanIP(self, name, value):
return self.__jails.getFilter(name).addBannedIP(value)
def getBanTime(self, name):
return self.__jails.getAction(name).getBanTime()
@@ -254,7 +268,7 @@ class Server:
length = len(jailList)
if not length == 0:
jailList = jailList[:length-2]
ret = [("Number of jail", self.__jails.size()),
ret = [("Number of jail", self.__jails.size()),
("Jail list", jailList)]
return ret
finally:
@@ -315,9 +329,13 @@ class Server:
def setLogTarget(self, target):
try:
self.__loggingLock.acquire()
# set a format which is simpler for console use
formatter = logging.Formatter("%(asctime)s %(name)-16s: %(levelname)-6s %(message)s")
if target == "SYSLOG":
# Syslog daemons already add date to the message.
formatter = logging.Formatter("%(name)-16s: %(levelname)-6s %(message)s")
facility = logging.handlers.SysLogHandler.LOG_DAEMON
hdlr = logging.handlers.SysLogHandler("/dev/log",
hdlr = logging.handlers.SysLogHandler("/dev/log",
facility = facility)
elif target == "STDOUT":
hdlr = logging.StreamHandler(sys.stdout)
@@ -332,17 +350,20 @@ class Server:
logSys.error("Unable to log to " + target)
logSys.info("Logging to previous target " + self.__logTarget)
return False
self.__logTarget = target
# Removes previous handlers
for handler in logging.getLogger("fail2ban").handlers:
# Closes the handler.
handler.close()
logging.getLogger("fail2ban").removeHandler(handler)
# set a format which is simpler for console use
formatter = logging.Formatter("%(asctime)s %(name)-16s: %(levelname)-6s %(message)s")
handler.close()
# tell the handler to use this format
hdlr.setFormatter(formatter)
logging.getLogger("fail2ban").addHandler(hdlr)
# Does not display this message at startup.
if not self.__logTarget == None:
logSys.info("Changed logging target to %s for Fail2ban v%s" %
(target, version.version))
# Sets the logging target.
self.__logTarget = target
return True
finally:
self.__loggingLock.release()
-136
View File
@@ -1,136 +0,0 @@
# This file is part of Fail2Ban.
#
# Fail2Ban is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# Fail2Ban is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Fail2Ban; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# Author: Cyril Jaquier
#
# $Revision$
__author__ = "Cyril Jaquier"
__version__ = "$Revision$"
__date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
from threading import Thread
# cPickle generates an exception with Python 2.5
#from cPickle import dumps, loads, HIGHEST_PROTOCOL
from pickle import dumps, loads, HIGHEST_PROTOCOL
import socket, logging, os, os.path
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.comm")
class SSocket(Thread):
END_STRING = "<F2B_END_COMMAND>"
def __init__(self, transmitter):
Thread.__init__(self)
self.__transmit = transmitter
self.__isRunning = False
self.__socket = "/tmp/fail2ban.sock"
self.__ssock = None
logSys.debug("Created SSocket")
def initialize(self, sock = "/tmp/fail2ban.sock", force = False):
self.__socket = sock
# Remove socket
if os.path.exists(sock):
logSys.error("Fail2ban seems to be already running")
if force:
logSys.warn("Forcing execution of the server")
os.remove(sock)
else:
raise SSocketErrorException("Server already running")
# Create an INET, STREAMing socket
#self.__ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.__ssock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
#self.__ssock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
#self.__ssock.setblocking(False)
# Do not use a blocking socket as there is problem at shutdown.
# Use a timeout instead. Daemon exits at most 'timeout' seconds
# after the command.
self.__ssock.settimeout(1)
# Bind the socket to a public host and a well-known port
#self.__ssock.bind(("localhost", 2222))
self.__ssock.bind(sock)
# Become a server socket
self.__ssock.listen(1)
def run(self):
self.__isRunning = True
while self.__isRunning:
try:
(csock, address) = self.__ssock.accept()
thread = SocketWorker(csock, self.__transmit)
thread.start()
except socket.timeout:
# Do nothing here
pass
except socket.error:
# Do nothing here
pass
self.__ssock.close()
# Remove socket
if os.path.exists(self.__socket):
logSys.debug("Removed socket file " + self.__socket)
os.remove(self.__socket)
logSys.debug("Socket shutdown")
return True
##
# Stop the thread.
#
# Set the isRunning flag to False.
# @bug It seems to be some concurrency problem with this flag
def stop(self):
self.__isRunning = False
class SocketWorker(Thread):
def __init__(self, csock, transmitter):
Thread.__init__(self)
self.__csock = csock
self.__transmit = transmitter
def run(self):
logSys.debug("Starting new thread to handle the request")
msg = self.__receive(self.__csock)
msg = self.__transmit.proceed(msg)
self.__send(self.__csock, msg)
self.__csock.close()
logSys.debug("Connection closed")
@staticmethod
def __send(sock, msg):
obj = dumps(msg, HIGHEST_PROTOCOL)
sock.send(obj + SSocket.END_STRING)
@staticmethod
def __receive(sock):
msg = ''
while msg.rfind(SSocket.END_STRING) == -1:
chunk = sock.recv(128)
if chunk == '':
raise RuntimeError, "socket connection broken"
msg = msg + chunk
return loads(msg)
class SSocketErrorException(Exception):
pass
+28 -34
View File
@@ -16,56 +16,50 @@
# Author: Cyril Jaquier
#
# $Revision$
# $Revision: 642 $
__author__ = "Cyril Jaquier"
__version__ = "$Revision$"
__date__ = "$Date$"
__version__ = "$Revision: 642 $"
__date__ = "$Date: 2008-01-05 23:33:44 +0100 (Sat, 05 Jan 2008) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import re
class DateTemplate:
class Template:
def __init__(self):
self.__name = ""
self.__regex = ""
self.__cRegex = None
self.__pattern = ""
self.__hits = 0
TEMPLATE_HOST = "host"
TEMPLATE_TIME = "time"
TEMPLATE_PREFIX = "prefix"
def setName(self, name):
def __init__(self, name, tag):
self.__name = name
self.__tag = tag
self.__regex = ""
self.__description = ""
def getName(self):
return self.__name
def getTag(self):
return self.__tag
def setDescription(self, description):
self.__description = description
def getDescription(self):
return self.__description
def setRegex(self, regex):
self.__regex = regex.strip()
self.__cRegex = re.compile(regex)
self.__regex = regex
def getRegex(self):
return self.__regex
class Templates:
def setPattern(self, pattern):
self.__pattern = pattern.strip()
def __init__(self):
self.templates = list()
def getPattern(self):
return self.__pattern
def isValid(self):
return self.__regex != "" and self.__pattern != ""
def incHits(self):
self.__hits = self.__hits + 1
def getHits(self):
return self.__hits
def matchDate(self, line):
dateMatch = self.__cRegex.search(line)
return dateMatch
def getDate(self, line):
raise Exception("matchDate() is abstract")
def getTemplates(self):
return self.templates
+25 -1
View File
@@ -53,4 +53,28 @@ class Ticket:
def getAttempt(self):
return self.__attempt
class FailTicket(Ticket):
def __init__(self, ip, time):
Ticket.__init__(self, ip, time)
##
# Ban Ticket.
#
# This class extends the Ticket class. It is mainly used by the BanManager.
class BanTicket(Ticket):
##
# Constructor.
#
# Call the Ticket (parent) constructor and initialize default
# values.
# @param ip the IP address
# @param time the ban time
def __init__(self, ip, time):
Ticket.__init__(self, ip, time)
+219
View File
@@ -0,0 +1,219 @@
# -*- coding: utf8 -*-
# This file is part of Fail2Ban.
#
# Fail2Ban is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# Fail2Ban is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Fail2Ban; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# Author: Cyril Jaquier
#
# $Revision: 645 $
__author__ = "Cyril Jaquier"
__version__ = "$Revision: 645 $"
__date__ = "$Date: 2008-01-16 23:55:04 +0100 (Wed, 16 Jan 2008) $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import time, logging
from template import Template, Templates
from mytime import MyTime
# Import ISO 8601 support.
import iso8601
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.timetemplate")
class TimeTemplate(Template):
def __init__(self):
Template.__init__(self, Template.TEMPLATE_TIME, "<TIME>")
def setRegex(self, regex):
Template.setRegex(self, "(?P<%s>%s)" % (self.getName(), regex))
def getTime(self, line):
raise Exception("getTime() is abstract")
class TimeISO8601(TimeTemplate):
def __init__(self):
TimeTemplate.__init__(self)
date_re = "[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}" \
".[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?" \
"(Z|(([-+])([0-9]{2}):([0-9]{2})))?"
self.setRegex(date_re)
def getTime(self, line):
# Parses the date.
return list(iso8601.parse_date(line).utctimetuple())
class TimeEpoch(TimeTemplate):
def __init__(self):
TimeTemplate.__init__(self)
# We already know the format for TAI64N
self.setRegex("\d{10}(\.\d{6})?")
def getTime(self, line):
# extract part of format which represents seconds since epoch
return list(time.localtime(float(line)))
##
# Use strptime() to parse a date. Our current locale is the 'C'
# one because we do not set the locale explicitly. This is POSIX
# standard.
class TimeStrptime(TimeTemplate):
TABLE = dict()
TABLE["Jan"] = []
TABLE["Feb"] = [u"Fév"]
TABLE["Mar"] = [u"Mär"]
TABLE["Apr"] = ["Avr"]
TABLE["May"] = ["Mai"]
TABLE["Jun"] = []
TABLE["Jul"] = []
TABLE["Aug"] = ["Aou"]
TABLE["Sep"] = []
TABLE["Oct"] = ["Okt"]
TABLE["Nov"] = []
TABLE["Dec"] = [u"Déc", "Dez"]
def __init__(self):
TimeTemplate.__init__(self)
self.__pattern = ""
def setPattern(self, pattern):
self.__pattern = pattern.strip()
def getPattern(self):
return self.__pattern
#@staticmethod
def convertLocale(date):
for t in TimeStrptime.TABLE:
for m in TimeStrptime.TABLE[t]:
if date.find(m) >= 0:
return date.replace(m, t)
return date
convertLocale = staticmethod(convertLocale)
def getTime(self, line):
try:
# Try first with 'C' locale
date = list(time.strptime(line, self.getPattern()))
except ValueError:
# Try to convert date string to 'C' locale
conv = self.convertLocale(line)
try:
date = list(time.strptime(conv, self.getPattern()))
except ValueError:
# Try to add the current year to the pattern. Should fix
# the "Feb 29" issue.
conv += " %s" % MyTime.gmtime()[0]
pattern = "%s %%Y" % self.getPattern()
date = list(time.strptime(conv, pattern))
if date[0] < 2000:
# There is probably no year field in the logs
date[0] = MyTime.gmtime()[0]
# Bug fix for #1241756
# If the date is greater than the current time, we suppose
# that the log is not from this year but from the year before
if time.mktime(date) > MyTime.time():
date[0] -= 1
return date
class TimeTai64n(TimeTemplate):
def __init__(self):
TimeTemplate.__init__(self)
# We already know the format for TAI64N
self.setRegex("@[0-9a-f]{24}")
def getTime(self, line):
# extract part of format which represents seconds since epoch
seconds_since_epoch = line[2:17]
return list(time.gmtime(int(seconds_since_epoch, 16)))
class TimeTemplates(Templates):
def __init__(self):
Templates.__init__(self)
# standard
template = TimeStrptime()
template.setDescription("Month Day Hour:Minute:Second")
template.setRegex("\S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}")
template.setPattern("%b %d %H:%M:%S")
self.templates.append(template)
# asctime
template = TimeStrptime()
template.setDescription("Weekday Month Day Hour:Minute:Second Year")
template.setRegex("\S{3} \S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2} \d{4}")
template.setPattern("%a %b %d %H:%M:%S %Y")
self.templates.append(template)
# asctime without year
template = TimeStrptime()
template.setDescription("Weekday Month Day Hour:Minute:Second")
template.setRegex("\S{3} \S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}")
template.setPattern("%a %b %d %H:%M:%S")
self.templates.append(template)
# simple date
template = TimeStrptime()
template.setDescription("Year/Month/Day Hour:Minute:Second")
template.setRegex("\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}")
template.setPattern("%Y/%m/%d %H:%M:%S")
self.templates.append(template)
# simple date too (from x11vnc)
template = TimeStrptime()
template.setDescription("Day/Month/Year Hour:Minute:Second")
template.setRegex("\d{2}/\d{2}/\d{4} \d{2}:\d{2}:\d{2}")
template.setPattern("%d/%m/%Y %H:%M:%S")
self.templates.append(template)
# Apache format [31/Oct/2006:09:22:55 -0000]
template = TimeStrptime()
template.setDescription("Day/Month/Year:Hour:Minute:Second")
template.setRegex("\d{2}/\S{3}/\d{4}:\d{2}:\d{2}:\d{2}")
template.setPattern("%d/%b/%Y:%H:%M:%S")
self.templates.append(template)
# Exim 2006-12-21 06:43:20
template = TimeStrptime()
template.setDescription("Year-Month-Day Hour:Minute:Second")
template.setRegex("\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}")
template.setPattern("%Y-%m-%d %H:%M:%S")
self.templates.append(template)
# named 26-Jul-2007 15:20:52.252
template = TimeStrptime()
template.setDescription("Day-Month-Year Hour:Minute:Second[.Millisecond]")
template.setRegex("\d{2}-\S{3}-\d{4} \d{2}:\d{2}:\d{2}")
template.setPattern("%d-%b-%Y %H:%M:%S")
self.templates.append(template)
# TAI64N
template = TimeTai64n()
template.setDescription("TAI64N")
self.templates.append(template)
# Epoch
template = TimeEpoch()
template.setDescription("Epoch")
self.templates.append(template)
# ISO 8601
template = TimeISO8601()
template.setDescription("ISO 8601")
self.templates.append(template)
+3 -12
View File
@@ -135,14 +135,6 @@ class Transmitter:
value = command[2]
self.__server.delLogPath(name, value)
return self.__server.getLogPath(name)
elif command[1] == "timeregex":
value = command[2]
self.__server.setTimeRegex(name, value)
return self.__server.getTimeRegex(name)
elif command[1] == "timepattern":
value = command[2]
self.__server.setTimePattern(name, value)
return self.__server.getTimePattern(name)
elif command[1] == "addfailregex":
value = command[2]
self.__server.addFailRegex(name, value)
@@ -172,6 +164,9 @@ class Transmitter:
value = command[2]
self.__server.setBanTime(name, int(value))
return self.__server.getBanTime(name)
elif command[1] == "banip":
value = command[2]
return self.__server.setBanIP(name,value)
elif command[1] == "addaction":
value = command[2]
self.__server.addAction(name, value)
@@ -229,10 +224,6 @@ class Transmitter:
return self.__server.getLogPath(name)
elif command[1] == "ignoreip":
return self.__server.getIgnoreIP(name)
elif command[1] == "timeregex":
return self.__server.getTimeRegex(name)
elif command[1] == "timepattern":
return self.__server.getTimePattern(name)
elif command[1] == "failregex":
return self.__server.getFailRegex(name)
elif command[1] == "ignoreregex":
+5 -2
View File
@@ -45,8 +45,8 @@ setup(
description = "Ban IPs that make too many password failure",
long_description = longdesc,
author = "Cyril Jaquier",
author_email = "lostcontrol@users.sourceforge.net",
url = "http://fail2ban.sourceforge.net",
author_email = "cyril.jaquier@fail2ban.org",
url = "http://www.fail2ban.org",
license = "GPL",
platforms = "Posix",
scripts = [
@@ -68,6 +68,9 @@ setup(
),
('/etc/fail2ban/action.d',
glob("config/action.d/*.conf")
),
('/var/run/fail2ban',
''
)
]
)
+2 -2
View File
@@ -24,9 +24,9 @@ __date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import unittest, socket, time, pickle
import unittest
from server.banmanager import BanManager
from server.banticket import BanTicket
from server.ticket import BanTicket
class AddFailure(unittest.TestCase):
-67
View File
@@ -1,67 +0,0 @@
# This file is part of Fail2Ban.
#
# Fail2Ban is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# Fail2Ban is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Fail2Ban; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# Author: Cyril Jaquier
#
# $Revision$
__author__ = "Cyril Jaquier"
__version__ = "$Revision$"
__date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import unittest
from server.datedetector import DateDetector
from server.datetemplate import DateTemplate
class DateDetectorTest(unittest.TestCase):
def setUp(self):
"""Call before every test case."""
self.__datedetector = DateDetector()
self.__datedetector.addDefaultTemplate()
def tearDown(self):
"""Call after every test case."""
def testGetEpochTime(self):
log = "1138049999 [sshd] error: PAM: Authentication failure"
date = [2006, 1, 23, 20, 59, 59, 0, 23, 0]
dateUnix = 1138046399.0
self.assertEqual(self.__datedetector.getTime(log), date)
self.assertEqual(self.__datedetector.getUnixTime(log), dateUnix)
def testGetTime(self):
log = "Jan 23 21:59:59 [sshd] error: PAM: Authentication failure"
date = [2005, 1, 23, 21, 59, 59, 1, 23, -1]
dateUnix = 1106513999.0
self.assertEqual(self.__datedetector.getTime(log), date)
self.assertEqual(self.__datedetector.getUnixTime(log), dateUnix)
def testDefaultTempate(self):
self.__datedetector.setDefaultRegex("^\S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}")
self.__datedetector.setDefaultPattern("%b %d %H:%M:%S")
log = "Jan 23 21:59:59 [sshd] error: PAM: Authentication failure"
date = [2005, 1, 23, 21, 59, 59, 1, 23, -1]
dateUnix = 1106513999.0
self.assertEqual(self.__datedetector.getTime(log), date)
self.assertEqual(self.__datedetector.getUnixTime(log), dateUnix)
+2 -3
View File
@@ -25,9 +25,8 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import unittest, socket, time, pickle
from server.failmanager import FailManager
from server.failmanager import FailManagerEmpty
from server.failticket import FailTicket
from server.failmanager import FailManager, FailManagerEmpty
from server.ticket import FailTicket
class AddFailure(unittest.TestCase):
+18 -18
View File
@@ -26,7 +26,7 @@ __license__ = "GPL"
import unittest
from server.filterpoll import FilterPoll
from server.filter import Filter
from server.filter import FileFilter
from server.failmanager import FailManager
from server.failmanager import FailManagerEmpty
@@ -34,7 +34,7 @@ class IgnoreIP(unittest.TestCase):
def setUp(self):
"""Call before every test case."""
self.__filter = Filter(None)
self.__filter = FileFilter(None)
def tearDown(self):
"""Call after every test case."""
@@ -86,7 +86,7 @@ class GetFailures(unittest.TestCase):
def setUp(self):
"""Call before every test case."""
self.__filter = Filter(None)
self.__filter = FileFilter(None)
self.__filter.setActive(True)
# TODO Test this
#self.__filter.setTimeRegex("\S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}")
@@ -98,8 +98,8 @@ class GetFailures(unittest.TestCase):
def testGetFailures01(self):
output = ('193.168.0.128', 3, 1124013599.0)
self.__filter.addLogPath(GetFailures.FILENAME_01)
self.__filter.addFailRegex("(?:(?:Authentication failure|Failed [-/\w+]+) for(?: [iI](?:llegal|nvalid) user)?|[Ii](?:llegal|nvalid) user|ROOT LOGIN REFUSED) .*(?: from|FROM) (?:::f{4,6}:)?(?P<host>\S*)")
self.__filter.addLogPath(GetFailures.FILENAME_01, False)
self.__filter.addFailRegex("<TIME> <PREFIX> (?:(?:Authentication failure|Failed [-/\w+]+) for(?: [iI](?:llegal|nvalid) user)?|[Ii](?:llegal|nvalid) user|ROOT LOGIN REFUSED) .*(?: from|FROM) <HOST>")
self.__filter.getFailures(GetFailures.FILENAME_01)
@@ -115,8 +115,8 @@ class GetFailures(unittest.TestCase):
def testGetFailures02(self):
output = ('141.3.81.106', 4, 1124013539.0)
self.__filter.addLogPath(GetFailures.FILENAME_02)
self.__filter.addFailRegex("Failed .* (?:::f{4,6}:)(?P<host>\S*)")
self.__filter.addLogPath(GetFailures.FILENAME_02, False)
self.__filter.addFailRegex("<TIME> <PREFIX> Failed .* (?:::f{4,6}:)(?P<host>\S*)")
self.__filter.getFailures(GetFailures.FILENAME_02)
@@ -132,8 +132,8 @@ class GetFailures(unittest.TestCase):
def testGetFailures03(self):
output = ('203.162.223.135', 6, 1124013544.0)
self.__filter.addLogPath(GetFailures.FILENAME_03)
self.__filter.addFailRegex("error,relay=(?:::f{4,6}:)?(?P<host>\S*),.*550 User unknown")
self.__filter.addLogPath(GetFailures.FILENAME_03, False)
self.__filter.addFailRegex("<TIME> <PREFIX> error,relay=(?:::f{4,6}:)?(?P<host>\S*),.*550 User unknown")
self.__filter.getFailures(GetFailures.FILENAME_03)
@@ -150,8 +150,8 @@ class GetFailures(unittest.TestCase):
output = [('212.41.96.186', 4, 1124013600.0),
('212.41.96.185', 4, 1124013598.0)]
self.__filter.addLogPath(GetFailures.FILENAME_04)
self.__filter.addFailRegex("Invalid user .* (?P<host>\S*)")
self.__filter.addLogPath(GetFailures.FILENAME_04, False)
self.__filter.addFailRegex("<TIME> <PREFIX> Invalid user .* (?P<host>\S*)")
self.__filter.getFailures(GetFailures.FILENAME_04)
@@ -169,9 +169,9 @@ class GetFailures(unittest.TestCase):
def testGetFailuresMultiRegex(self):
output = ('141.3.81.106', 8, 1124013541.0)
self.__filter.addLogPath(GetFailures.FILENAME_02)
self.__filter.addFailRegex("Failed .* from <HOST>")
self.__filter.addFailRegex("Accepted .* from <HOST>")
self.__filter.addLogPath(GetFailures.FILENAME_02, False)
self.__filter.addFailRegex("<TIME> <PREFIX> Failed .* from <HOST>")
self.__filter.addFailRegex("<TIME> <PREFIX> Accepted .* from <HOST>")
self.__filter.getFailures(GetFailures.FILENAME_02)
@@ -187,10 +187,10 @@ class GetFailures(unittest.TestCase):
def testGetFailuresIgnoreRegex(self):
output = ('141.3.81.106', 8, 1124013541.0)
self.__filter.addLogPath(GetFailures.FILENAME_02)
self.__filter.addFailRegex("Failed .* from <HOST>")
self.__filter.addFailRegex("Accepted .* from <HOST>")
self.__filter.addIgnoreRegex("for roehl")
self.__filter.addLogPath(GetFailures.FILENAME_02, False)
self.__filter.addFailRegex("<TIME> <PREFIX> Failed .* from <HOST>")
self.__filter.addFailRegex("<TIME> <PREFIX> Accepted .* from <HOST>")
self.__filter.addIgnoreRegex(".* for roehl")
self.__filter.getFailures(GetFailures.FILENAME_02)