Merged with 0.5.0 upstream release

debian-releases/etch
Yaroslav Halchenko 2005-07-13 10:01:01 +00:00
parent 5930368ed9
commit 61e23f45f7
22 changed files with 884 additions and 674 deletions

View File

@ -4,10 +4,25 @@
|_| \__,_|_|_/___|_.__/\__,_|_||_| |_| \__,_|_|_/___|_.__/\__,_|_||_|
============================================================= =============================================================
Fail2Ban (version 0.4.1) 06/30/2005 Fail2Ban (version 0.5.0) 2005/07/12
============================================================= =============================================================
ver. 0.4.1 (06/30/2005) - stable ver. 0.5.0 (2005/07/12) - beta
----------
- Added support for CIDR mask in ignoreip
- Added mail notification support
- Fixed bug #1234699
- Added tags replacement in rules definition. Should allow a
clean solution for Feature Request #1229479
- Removed "interface" and "firewall" options
- Added start and end commands in the configuration file.
Thanks to Yaroslav Halchenko
- Added firewall rules definition in the configuration file
- Cleaned fail2ban.py
- Added an initd script for RedHat/Fedora. Thanks to Andrey
G. Grozin
ver. 0.4.1 (2005/06/30) - stable
---------- ----------
- Fixed textToDNS method which generated wrong matches for - Fixed textToDNS method which generated wrong matches for
"rhost=12-xyz...". Thanks to Tom Pike "rhost=12-xyz...". Thanks to Tom Pike
@ -16,19 +31,19 @@ ver. 0.4.1 (06/30/2005) - stable
- Changed default PID lock file location from /tmp to - Changed default PID lock file location from /tmp to
/var/run /var/run
ver. 0.4.0 (04/24/2005) - stable ver. 0.4.0 (2005/04/24) - stable
---------- ----------
- Fixed textToDNS which did not recognize strings like - Fixed textToDNS which did not recognize strings like
"12-345-67-890.abcd.mnopqr.xyz" "12-345-67-890.abcd.mnopqr.xyz"
ver. 0.3.1 (03/31/2005) - beta ver. 0.3.1 (2005/03/31) - beta
---------- ----------
- Corrected level of messages - Corrected level of messages
- Added DNS lookup support - Added DNS lookup support
- Improved parsing speed. Only parse the new log messages - Improved parsing speed. Only parse the new log messages
- Added a second verbose level (-vv) - Added a second verbose level (-vv)
ver. 0.3.0 (02/24/2005) - beta ver. 0.3.0 (2005/02/24) - beta
---------- ----------
- Re-writting of parts of the code in order to handle several - Re-writting of parts of the code in order to handle several
log files with different rules log files with different rules
@ -39,7 +54,7 @@ ver. 0.3.0 (02/24/2005) - beta
- Added ipfw-start-rule option (thanks to Robert Edeker) - Added ipfw-start-rule option (thanks to Robert Edeker)
- Added -k option which kills a currently running Fail2Ban - Added -k option which kills a currently running Fail2Ban
ver. 0.1.2 (11/21/2004) - beta ver. 0.1.2 (2004/11/21) - beta
---------- ----------
- Add ipfw and ipfwadm support. The rules are taken from - Add ipfw and ipfwadm support. The rules are taken from
BlockIt. Thanks to Robert Edeker BlockIt. Thanks to Robert Edeker
@ -47,7 +62,7 @@ ver. 0.1.2 (11/21/2004) - beta
Robert Edeker who reminded me this Robert Edeker who reminded me this
- Small code cleaning - Small code cleaning
ver. 0.1.1 (10/23/2004) - beta ver. 0.1.1 (2004/10/23) - beta
---------- ----------
- Add SIGTERM handler in order to exit nicely when in daemon - Add SIGTERM handler in order to exit nicely when in daemon
mode mode
@ -61,6 +76,6 @@ ver. 0.1.1 (10/23/2004) - beta
- Code documentation - Code documentation
ver. 0.1.0 (10/12/2004) - alpha ver. 0.1.0 (2004/10/12) - alpha
---------- ----------
- Initial release - Initial release

View File

@ -2,15 +2,11 @@
# #
DESTDIR=debian/fail2ban DESTDIR=debian/fail2ban
all:: fail2ban fail2ban.1x all:: fail2ban.1x
fail2ban.1x: fail2ban fail2ban.h2m fail2ban.1x: fail2ban fail2ban.h2m
help2man --include fail2ban.h2m --section=1x --no-info --output $@ ./fail2ban help2man --include fail2ban.h2m --section=1x --no-info --output $@ ./fail2ban
fail2ban: fail2ban.py
cp fail2ban.py fail2ban
install:: all install:: all
mkdir -p $(DESTDIR)/etc/default mkdir -p $(DESTDIR)/etc/default
@ -18,7 +14,7 @@ install:: all
cp config/fail2ban.conf.default $(DESTDIR)/etc/fail2ban.conf cp config/fail2ban.conf.default $(DESTDIR)/etc/fail2ban.conf
cp config/gentoo-confd $(DESTDIR)/etc/default/fail2ban cp config/gentoo-confd $(DESTDIR)/etc/default/fail2ban
mkdir -p $(DESTDIR)/usr/lib/fail2ban/ mkdir -p $(DESTDIR)/usr/lib/fail2ban/
cp log4py.py $(DESTDIR)/usr/lib/fail2ban/ # cp log4py.py $(DESTDIR)/usr/lib/fail2ban/
clean:: clean::
rm -rf changelog.gz fail2ban{,.1x} build* `find -iname '*.pyc' ` rm -rf changelog.gz fail2ban{,.1x} build* `find -iname '*.pyc' `

View File

@ -1,8 +1,8 @@
Metadata-Version: 1.0 Metadata-Version: 1.0
Name: fail2ban Name: fail2ban
Version: 0.4.1 Version: 0.5.0
Summary: Ban IPs that make too many password failure Summary: Ban IPs that make too many password failure
Home-page: http://www.sourceforge.net/projects/fail2ban Home-page: http://fail2ban.sourceforge.net
Author: Cyril Jaquier Author: Cyril Jaquier
Author-email: lostcontrol@users.sourceforge.net Author-email: lostcontrol@users.sourceforge.net
License: UNKNOWN License: UNKNOWN

39
README
View File

@ -4,14 +4,14 @@
|_| \__,_|_|_/___|_.__/\__,_|_||_| |_| \__,_|_|_/___|_.__/\__,_|_||_|
============================================================= =============================================================
Fail2Ban (version 0.4.1) 06/30/2005 Fail2Ban (version 0.5.0) 2005/07/12
============================================================= =============================================================
Fail2Ban scans log files like /var/log/pwdfail and bans IP Fail2Ban scans log files like /var/log/pwdfail and bans IP
that makes too many password failures. It updates firewall that makes too many password failures. It updates firewall
rules to reject the IP address. Currently iptables, ipfw and rules to reject the IP address. These rules can be defined by
ipfwadm are supported. Fail2Ban can read multiple log files the user. Fail2Ban can read multiple log files such as sshd
such as sshd or Apache web server ones. It needs log4py. or Apache web server ones. It needs log4py.
This is my first Python program. Moreover, English is not my This is my first Python program. Moreover, English is not my
mother tongue... mother tongue...
@ -36,18 +36,19 @@ tries to find lines which match the failregex. Then it
retrieves the message time using timeregex and timepattern. retrieves the message time using timeregex and timepattern.
It finally gets the ip and if it has already done 3 or more It finally gets the ip and if it has already done 3 or more
password failures in the last banTime, the ip is banned for password failures in the last banTime, the ip is banned for
banTime using a firewall rule. After banTime, the rule is banTime using a firewall rule. This rule is set by the user
deleted. Notice that if no "plain" ip is available, Fail2Ban in the configuration file. Thus, Fail2Ban can be adapted for
try to do DNS lookup in order to found one or several ip's to lots of firewall. After banTime, the rule is deleted. Notice
ban. that if no "plain" ip is available, Fail2Ban try to do DNS
lookup in order to found one or several ip's to ban.
Sections can be freely added so it is possible to monitor Sections can be freely added so it is possible to monitor
several daemons at the same time. several daemons at the same time.
Runs on my server and does its job rather well :-) The idea Runs on my server and does its job rather well :-) The idea
is to make fail2ban usable with daemons and services that is to make fail2ban usable with daemons and services that
require a login (sshd, telnetd, ...). It should also support require a login (sshd, telnetd, ...) and with different
others firewalls than iptables. firewalls.
Installation: Installation:
@ -58,14 +59,15 @@ Require: python-2.3 (http://www.python.org)
To install, just do: To install, just do:
> tar xvfj fail2ban-0.4.1.tar.bz2 > tar xvfj fail2ban-0.5.0.tar.bz2
> cd fail2ban-0.4.1 > cd fail2ban-0.5.0
> python setup.py install > python setup.py install
This will install Fail2Ban into /usr/lib/fail2ban. The This will install Fail2Ban into /usr/lib/fail2ban. The
fail2ban.py executable is placed into /usr/bin. fail2ban.py executable is placed into /usr/bin.
For Gentoo users, an ebuild is available on the website. Gentoo: an ebuild is available on the website.
Debian: a package is available on the website.
Fail2Ban should now be correctly installed. Just type: Fail2Ban should now be correctly installed. Just type:
@ -93,18 +95,16 @@ options:
-b start fail2ban in background -b start fail2ban in background
-d start fail2ban in debug mode -d start fail2ban in debug mode
-e <INTF> ban IP on the INTF interface
-c <FILE> read configuration file FILE -c <FILE> read configuration file FILE
-p <FILE> create PID lock in FILE -p <FILE> create PID lock in FILE
-h display this help message -h display this help message
-i <IP(s)> IP(s) to ignore -i <IP(s)> IP(s) to ignore
-k kill a currently running Fail2Ban instance -k kill a currently running Fail2Ban instance
-l <FILE> log message in FILE -l <FILE> log messages in FILE
-r <VALUE> allow a max of VALUE password failure -r <VALUE> allow a max of VALUE password failure
-t <TIME> ban IP for TIME seconds -t <TIME> ban IP for TIME seconds
-v verbose. Use twice for greater effect -v verbose. Use twice for greater effect
-w <FIWA> select the firewall to use. Can be iptables, -V print software version
ipfwadm or ipfw
Contact: Contact:
-------- --------
@ -112,7 +112,7 @@ Contact:
You need some new features, you found bugs or you just You need some new features, you found bugs or you just
appreciate this program, you can contact me at : appreciate this program, you can contact me at :
Website: http://www.sourceforge.net/projects/fail2ban Website: http://fail2ban.sourceforge.net
Cyril Jaquier: <lostcontrol@users.sourceforge.net> Cyril Jaquier: <lostcontrol@users.sourceforge.net>
@ -121,8 +121,7 @@ Thanks:
------- -------
Kévin Drapel, Marvin Rouge, Sireyessire, Robert Edeker, Kévin Drapel, Marvin Rouge, Sireyessire, Robert Edeker,
Tom Pike, Iain Lea Tom Pike, Iain Lea, Andrey G. Grozin, Yaroslav Halchenko
License: License:
-------- --------

View File

@ -1,22 +1,10 @@
# Fail2Ban configuration file # Fail2Ban configuration file
# #
# $Revision: 1.8 $ # $Revision: 1.8.2.5 $
# #
# 2005.06.21 modified for readability Iain Lea iain@bricbrac.de # 2005.06.21 modified for readability Iain Lea iain@bricbrac.de
[DEFAULT] [DEFAULT]
# Option: firewall
# Notes.: select the firewall system to use.
# Values: [iptables | ipfwadm | ipfw] Default: iptables
#
firewall = iptables
# Option: ipfw-start-rule
# Notes.: set first firewall rule number used (only used if firewall = ipfw).
# Values: NUM Default: 100
#
ipfw-start-rule = 100
# Option: background # Option: background
# Notes.: start fail2ban as a daemon. Output is redirect to logfile. # Notes.: start fail2ban as a daemon. Output is redirect to logfile.
# Values: [true | false] Default: false # Values: [true | false] Default: false
@ -54,17 +42,24 @@ maxretry = 5
bantime = 600 bantime = 600
# Option: ignoreip # Option: ignoreip
# Notes.: space separated list of IP's to be ignored by fail2ban # Notes.: space separated list of IP's to be ignored by fail2ban.
# Example: ignoreip = 192.168.0.1 123.45.235.65 # You can use CIDR mask in order to specify a range.
# Values: IP Default: # Example: ignoreip = 192.168.0.1/24 123.45.235.65
# Values: IP Default: 192.168.0.0/24
# #
ignoreip = ignoreip = 192.168.0.0/24
# Option: interface # Option: cmdstart
# Notes.: interface name on which the IP will be banned. # Notes.: command executed once at the start of Fail2Ban
# Values: INT Default: eth0 # Values: CMD Default:
# #
interface = eth0 cmdstart =
# Option: cmdend
# Notes.: command executed once at the end of Fail2Ban
# Values: CMD Default:
#
cmdend =
# Option: polltime # Option: polltime
# Notes.: number of seconds fail2ban sleeps between iterations. # Notes.: number of seconds fail2ban sleeps between iterations.
@ -72,9 +67,64 @@ interface = eth0
# #
polltime = 1 polltime = 1
[MAIL]
# Option: enabled
# Notes.: enable mail notification when banning an IP address.
# Values: [true | false] Default: false
#
enabled = false
# Option: host
# Notes.: host running the mail server.
# Values: STR Default: localhost
#
host = localhost
# Option: port
# Notes.: port of the mail server.
# Values: INT Default: 25
#
port = 25
# Option: from
# Notes.: e-mail address of the sender.
# Values: MAIL Default: fail2ban
#
from = fail2ban
# Option: to
# Notes.: e-mail address of the receiver.
# Values: MAIL Default: root
#
to = root
# Option: subject
# Notes.: subject of the e-mail.
# Tags: <ip> IP address
# <failures> number of failures
# <failtime> unix timestamp of the last failure
# Values: TEXT Default: [Fail2Ban] Banned <ip>
#
subject = [Fail2Ban] Banned <ip>
# Option: message
# Notes.: message of the e-mail.
# Tags: <ip> IP address
# <failures> number of failures
# <failtime> unix timestamp of the last failure
# <br> new line
# Values: TEXT Default:
#
message = Hi,<br>
The IP <ip> has just been banned by Fail2Ban after
<failures> attempts.<br>
Regards,<br>
Fail2Ban
# You can define a new section for each log file to check for # You can define a new section for each log file to check for
# password failure. Each section has to define the following # password failure. Each section has to define the following
# options: logfile, timeregex, timepattern, failregex. # options: logfile, fwban, fwunban, timeregex, timepattern,
# failregex.
[Apache] [Apache]
# Option: enabled # Option: enabled
@ -89,10 +139,45 @@ enabled = false
# #
logfile = /var/log/apache/access.log logfile = /var/log/apache/access.log
# Option: fwstart
# Notes.: command executed once at the start of Fail2Ban
# Values: CMD Default:
#
fwstart =
# Option: fwend
# Notes.: command executed once at the end of Fail2Ban
# Values: CMD Default:
#
fwend =
# 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
# Default: iptables -I INPUT 1 -i eth0 -s <ip> -j DROP
#
fwban = iptables -I INPUT 1 -i eth0 -s <ip> -j DROP
# 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
# Default: iptables -D INPUT -i eth0 -s <ip> -j DROP
#
fwunban = iptables -D INPUT -i eth0 -s <ip> -j DROP
# Option: timeregex # Option: timeregex
# Notes.: regex to match timestamp in Apache logfile. # Notes.: regex to match timestamp in Apache logfile.
# Values: [Wed Jan 05 15:08:01 2005] # Values: [Wed Jan 05 15:08:01 2005]
# Default \S{3} \S{3} \d{2} \d{2}:\d{2}:\d{2} \d{4} # Default: \S{3} \S{3} \d{2} \d{2}:\d{2}:\d{2} \d{4}
# #
timeregex = \S{3} \S{3} \d{2} \d{2}:\d{2}:\d{2} \d{4} timeregex = \S{3} \S{3} \d{2} \d{2}:\d{2}:\d{2} \d{4}
@ -122,10 +207,45 @@ enabled = true
# #
logfile = /var/log/auth.log logfile = /var/log/auth.log
# Option: fwstart
# Notes.: command executed once at the start of Fail2Ban
# Values: CMD Default:
#
fwstart =
# Option: fwend
# Notes.: command executed once at the end of Fail2Ban
# Values: CMD Default:
#
fwend =
# Option: fwbanrule
# 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
# Default: iptables -I INPUT 1 -i eth0 -s <ip> -j DROP
#
fwban = iptables -I INPUT 1 -i eth0 -s <ip> -j DROP
# Option: fwunbanrule
# 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
# Default: iptables -D INPUT -i eth0 -s <ip> -j DROP
#
fwunban = iptables -D INPUT -i eth0 -s <ip> -j DROP
# Option: timeregex # Option: timeregex
# Notes.: regex to match timestamp in SSH logfile. # Notes.: regex to match timestamp in SSH logfile.
# Values: [Mar 7 17:53:28] # Values: [Mar 7 17:53:28]
# Default \S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2} # Default: \S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}
# #
timeregex = \S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2} timeregex = \S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}

View File

@ -17,11 +17,11 @@
# #
# Author: Sireyessire, Cyril Jaquier # Author: Sireyessire, Cyril Jaquier
# #
# $Revision: 1.1 $ # $Revision: 1.1.2.1 $
opts="start stop restart showlog" opts="start stop restart showlog"
FAIL2BAN="/usr/bin/fail2ban.py" FAIL2BAN="/usr/bin/fail2ban"
depend() { depend() {
need net need net

78
config/redhat-initd Normal file
View File

@ -0,0 +1,78 @@
#!/bin/bash
#
# fail2ban
#
# chkconfig: 345 91 9
# description: if many unsuccessfull login attempts from some ip address \
# during a short period happen, this address is banned \
# by the firewall
#
# Author: Andrey G. Grozin
#
# $Revision: 1.1.2.1 $
# Source function library.
. /etc/init.d/functions
# Get config.
. /etc/sysconfig/network
# Check that networking is up.
[ "${NETWORKING}" = "no" ] && exit 0
[ -f /etc/fail2ban.conf ] || exit 0
FAIL2BAN="/usr/bin/fail2ban"
PIDFILE="/var/run/fail2ban.pid"
RETVAL=0
start() {
echo -n $"Starting fail2ban: "
"${FAIL2BAN}" -b
RETVAL=$?
echo
}
stop() {
if [ -f "${PIDFILE}" ]; then
echo -n $"Stopping fail2ban: "
"${FAIL2BAN}" -k
echo
fi
}
restart() {
stop
start
}
# See how we were called.
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status fail2ban
RETVAL=$?
;;
reload)
restart
;;
restart)
restart
;;
condrestart)
if [ -f "${PIDFILE}" ]; then
restart
fi
;;
*)
echo $"Usage: $0 {start|stop|status|restart|condrestart}"
exit 1
;;
esac
exit $RETVAL

View File

@ -16,36 +16,30 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 1.5 $ # $Revision: 1.5.2.3 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 1.5 $" __version__ = "$Revision: 1.5.2.3 $"
__date__ = "$Date: 2005/03/06 17:45:55 $" __date__ = "$Date: 2005/07/12 13:07:35 $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
import log4py
from ConfigParser import * from ConfigParser import *
# Gets the instance of log4py.
logSys = log4py.Logger().get_instance()
class ConfigReader: class ConfigReader:
""" This class allow the handling of the configuration options. """ This class allow the handling of the configuration options.
The DEFAULT section contains the global information about The DEFAULT section contains the global information about
Fail2Ban. Each other section is for a different log file. Fail2Ban. Each other section is for a different log file.
""" """
# Each optionValues entry is composed of an array with: def __init__(self, confPath):
# 0 -> the type of the option
# 1 -> the name of the option
# 2 -> the default value for the option
optionValues = (["bool", "enabled", True],
["str", "logfile", "/dev/null"],
["str", "timeregex", ""],
["str", "timepattern", ""],
["str", "failregex", ""])
def __init__(self, logSys, confPath):
self.confPath = confPath self.confPath = confPath
self.configParser = SafeConfigParser() self.configParser = SafeConfigParser()
self.logSys = logSys
def openConf(self): def openConf(self):
""" Opens the configuration file. """ Opens the configuration file.
@ -54,16 +48,23 @@ class ConfigReader:
def getSections(self): def getSections(self):
""" Returns all the sections present in the configuration """ Returns all the sections present in the configuration
file except the DEFAULT section. file except the DEFAULT and MAIL sections.
""" """
return self.configParser.sections() sections = self.configParser.sections()
sections.remove("MAIL")
def getLogOptions(self, sec): logSys.debug("Found sections: " + `sections`)
return sections
# Each optionValues entry is composed of an array with:
# 0 -> the type of the option
# 1 -> the name of the option
# 2 -> the default value for the option
def getLogOptions(self, sec, options):
""" Gets all the options of a given section. The options """ Gets all the options of a given section. The options
are defined in the optionValues list. are defined in the optionValues list.
""" """
values = dict() values = dict()
for option in self.optionValues: for option in options:
try: try:
if option[0] == "bool": if option[0] == "bool":
v = self.configParser.getboolean(sec, option[1]) v = self.configParser.getboolean(sec, option[1])
@ -74,7 +75,7 @@ class ConfigReader:
values[option[1]] = v values[option[1]] = v
except NoOptionError: except NoOptionError:
self.logSys.warn("No '"+option[1]+"' defined in '"+sec+"'") logSys.warn("No '" + option[1] + "' defined in '" + sec + "'")
values[option[1]] = option[2] values[option[1]] = option[2]
return values return values

11
debian/README.Debian vendored
View File

@ -1,12 +1,13 @@
fail2ban for Debian fail2ban for Debian
------------------- -------------------
This package is nearly 100% identical to the upstream version. It was merely This package is nearly 100% identical to the upstream version. It was
packaged to be installed on a Debian system. merely packaged to be installed on a Debian system.
Module log4py installed into lib/fail2ban directory because there is no package for not-developed-in-a-long-time fail2ban Module log4py installed into lib/fail2ban directory because there is no
package for not-developed-in-a-long-time fail2ban
See the file TODO.Debian for more details, as well as the Debian Bug Tracking See the file TODO.Debian for more details, as well as the Debian Bug
system. Tracking system.
-- Yaroslav Halchenko <debian@onerussian.com>, Tue, 4 Jul 2005 00:00:00 -1000 -- Yaroslav Halchenko <debian@onerussian.com>, Tue, 4 Jul 2005 00:00:00 -1000

5
debian/TODO vendored
View File

@ -1,3 +1,4 @@
Because this is just a quick hack to package fail2ban it might be missing some crucial parts... Because this is just a quick hack to package fail2ban it might be missing
some crucial parts...
-- yoh@onerussian.com -- debian@onerussian.com

7
debian/changelog vendored
View File

@ -1,3 +1,10 @@
fail2ban (0.5.0-1) unstable; urgency=low
* New upstream release
* Corrections to the description of the package
-- Yaroslav Halchenko <debian@onerussian.com> Tue, 12 Jul 2005 23:33:20 -1000
fail2ban (0.4.1-1) unstable; urgency=low fail2ban (0.4.1-1) unstable; urgency=low
* First upstream release of a Debian package * First upstream release of a Debian package

48
firewall/iptables.py → fail2ban Normal file → Executable file
View File

@ -1,3 +1,5 @@
#!/usr/bin/env python
# This file is part of Fail2Ban. # This file is part of Fail2Ban.
# #
# Fail2Ban is free software; you can redistribute it and/or modify # Fail2Ban is free software; you can redistribute it and/or modify
@ -16,33 +18,31 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 1.5 $ # $Revision: 1.4.2.2 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 1.5 $" __version__ = "$Revision: 1.4.2.2 $"
__date__ = "$Date: 2004/11/06 14:02:07 $" __date__ = "$Date: 2005/07/08 10:21:52 $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
from firewall import Firewall from sys import exit, path
class Iptables(Firewall): #yoh: We do need to load this path first if we ship log4py with fail2ban
""" This class contains specific methods and variables for the # Appends our own modules path
iptables firewall. Must implements the 'abstracts' methods path.append('/usr/lib/fail2ban')
banIP(ip) and unBanIP(ip).
Must adds abstract methods definition: # Checks for required libs
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/266468 # Checks if log4py is present.
""" try:
import log4py
def banIP(self, ip): except:
""" Returns query to ban IP. print "log4py is needed (see README)"
""" exit(-1)
query = "iptables -I INPUT 1 -i "+self.interface+" -s "+ip+" -j DROP"
return query # Now we can import our module
import fail2ban
def unBanIP(self, ip):
""" Returns query to unban IP. # Start the application
""" fail2ban.main()
query = "iptables -D INPUT -i "+self.interface+" -s "+ip+" -j DROP"
return query

View File

@ -1,5 +1,3 @@
#!/usr/bin/env python
# This file is part of Fail2Ban. # This file is part of Fail2Ban.
# #
# Fail2Ban is free software; you can redistribute it and/or modify # Fail2Ban is free software; you can redistribute it and/or modify
@ -18,37 +16,34 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 1.20 $ # $Revision: 1.20.2.5 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 1.20 $" __version__ = "$Revision: 1.20.2.5 $"
__date__ = "$Date: 2005/06/30 09:26:38 $" __date__ = "$Date: 2005/07/12 13:11:58 $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
import time, sys, getopt, os, signal, string import time, sys, getopt, os, string, signal, log4py
from ConfigParser import * from ConfigParser import *
# Appends our own modules path from firewall.firewall import Firewall
# you: moved before loading log4py so we add path to it
sys.path.append('/usr/lib/fail2ban')
# Checks if log4py is present.
try:
import log4py
except:
print "log4py is needed (see README)"
sys.exit(-1)
from firewall.iptables import Iptables
from firewall.ipfw import Ipfw
from firewall.ipfwadm import Ipfwadm
from logreader.logreader import LogReader from logreader.logreader import LogReader
from confreader.configreader import ConfigReader from confreader.configreader import ConfigReader
from utils.process import *
from utils.mail import Mail
from version import version from version import version
def usage(): # Gets the instance of log4py.
logSys = log4py.Logger().get_instance()
# Global variables
logFwList = list()
conf = dict()
def dispUsage():
""" Prints Fail2Ban command line options and exits
"""
print "Usage: "+sys.argv[0]+" [OPTIONS]" print "Usage: "+sys.argv[0]+" [OPTIONS]"
print print
print "Fail2Ban v"+version+" reads log file that contains password failure report" print "Fail2Ban v"+version+" reads log file that contains password failure report"
@ -56,22 +51,26 @@ def usage():
print print
print " -b start fail2ban in background" print " -b start fail2ban in background"
print " -d start fail2ban in debug mode" print " -d start fail2ban in debug mode"
print " -e <INTF> ban IP on the INTF interface"
print " -c <FILE> read configuration file FILE" print " -c <FILE> read configuration file FILE"
print " -p <FILE> create PID lock in FILE" print " -p <FILE> create PID lock in FILE"
print " -h display this help message" print " -h display this help message"
print " -i <IP(s)> IP(s) to ignore" print " -i <IP(s)> IP(s) to ignore"
print " -k kill a currently running Fail2Ban instance" print " -k kill a currently running Fail2Ban instance"
print " -l <FILE> log message in FILE" print " -l <FILE> log messages in FILE"
print " -r <VALUE> allow a max of VALUE password failure" print " -r <VALUE> allow a max of VALUE password failure"
print " -t <TIME> ban IP for TIME seconds" print " -t <TIME> ban IP for TIME seconds"
print " -v verbose. Use twice for greater effect" print " -v verbose. Use twice for greater effect"
print " -w <FIWA> select the firewall to use. Can be iptables," print " -V print software version"
print " ipfwadm or ipfw"
print print
print "Report bugs to <lostcontrol@users.sourceforge.net>" print "Report bugs to <lostcontrol@users.sourceforge.net>"
sys.exit(0) sys.exit(0)
def dispVersion():
""" Prints Fail2Ban version and exits
"""
print sys.argv[0]+" "+version
sys.exit(0)
def checkForRoot(): def checkForRoot():
""" Check for root user. """ Check for root user.
""" """
@ -81,304 +80,46 @@ def checkForRoot():
else: else:
return False return False
def createDaemon():
"""Detach a process from the controlling terminal and run it in the
background as a daemon.
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278731
"""
try:
# Fork a child process so the parent can exit. This will return control
# to the command line or shell. This is required so that the new process
# is guaranteed not to be a process group leader. We have this guarantee
# because the process GID of the parent is inherited by the child, but
# the child gets a new PID, making it impossible for its PID to equal its
# PGID.
pid = os.fork()
except OSError, e:
return((e.errno, e.strerror)) # ERROR (return a tuple)
if (pid == 0): # The first child.
# Next we call os.setsid() to become the session leader of this new
# session. The process also becomes the process group leader of the
# new process group. Since a controlling terminal is associated with a
# session, and this new session has not yet acquired a controlling
# terminal our process now has no controlling terminal. This shouldn't
# fail, since we're guaranteed that the child is not a process group
# leader.
os.setsid()
# When the first child terminates, all processes in the second child
# are sent a SIGHUP, so it's ignored.
signal.signal(signal.SIGHUP, signal.SIG_IGN)
try:
# Fork a second child to prevent zombies. Since the first child is
# a session leader without a controlling terminal, it's possible for
# it to acquire one by opening a terminal in the future. This second
# fork guarantees that the child is no longer a session leader, thus
# preventing the daemon from ever acquiring a controlling terminal.
pid = os.fork() # Fork a second child.
except OSError, e:
return((e.errno, e.strerror)) # ERROR (return a tuple)
if (pid == 0): # The second child.
# Ensure that the daemon doesn't keep any directory in use. Failure
# to do this could make a filesystem unmountable.
os.chdir("/")
# Give the child complete control over permissions.
# yoh: BAD BAD BAD IDEA - generated files are writable by everybody
# changing to restrictive umask
os.umask(0022)
else:
os._exit(0) # Exit parent (the first child) of the second child.
else:
os._exit(0) # Exit parent of the first child.
# Close all open files. Try the system configuration variable, SC_OPEN_MAX,
# for the maximum number of open files to close. If it doesn't exist, use
# the default value (configurable).
try:
maxfd = os.sysconf("SC_OPEN_MAX")
except (AttributeError, ValueError):
maxfd = 256 # default maximum
for fd in range(0, maxfd):
try:
os.close(fd)
except OSError: # ERROR (ignore)
pass
# Redirect the standard file descriptors to /dev/null.
os.open("/dev/null", os.O_RDONLY) # standard input (0)
os.open("/dev/null", os.O_RDWR) # standard output (1)
os.open("/dev/null", os.O_RDWR) # standard error (2)
return True
def sigTERMhandler(signum, frame): def sigTERMhandler(signum, frame):
""" Handles the TERM signal when in daemon mode in order to """ Handles the TERM signal when in daemon mode in order to
exit properly. exit properly.
""" """
logSys.debug("Signal handler called with sig "+`signum`) logSys.debug("Signal handler called with sig "+`signum`)
killApp() killApp()
def killApp(): def killApp():
""" Flush the ban list, remove the PID lock file and exit """ Flush the ban list, remove the PID lock file and exit
nicely. nicely.
""" """
logSys.warn("Restoring firewall rules...") logSys.warn("Restoring firewall rules...")
fireWall.flushBanList(conf["debug"]) for element in logFwList:
element[2].flushBanList(conf["debug"])
# Execute end command of each section
for element in logFwList:
l = element[4]
executeCmd(l["fwend"], conf["debug"])
# Execute global start command
executeCmd(conf["cmdend"], conf["debug"])
# Remove the PID lock
removePID(conf["pidlock"]) removePID(conf["pidlock"])
logSys.info("Exiting...") logSys.info("Exiting...")
sys.exit(0) sys.exit(0)
def checkForPID(lockfile):
""" Checks for running Fail2Ban.
Returns the current PID if Fail2Ban is running or False
if no instance found.
"""
try:
fileHandler = open(lockfile)
pid = fileHandler.readline()
return pid
except IOError:
return False
def createPID(lockfile):
""" Creates a PID lock file with the current PID.
"""
fileHandler = open(lockfile, mode='w')
pid = os.getpid()
fileHandler.write(`pid`+'\n')
fileHandler.close()
logSys.debug("Created PID lock ("+`pid`+") in "+lockfile)
def removePID(lockfile):
""" Remove PID lock.
"""
os.remove(lockfile)
logSys.debug("Removed PID lock "+lockfile)
def killPID(pid): def getCmdLineOptions(optList):
""" Kills the process with the given PID using the """ Gets the command line options
INT signal (same effect as <ctrl>+<c>).
""" """
try:
return os.kill(pid, 2)
except OSError:
logSys.error("Can not kill process " + `pid` + ". Please check that " +
"Fail2Ban is not running and remove the file " +
"'/tmp/fail2ban.pid'")
if __name__ == "__main__":
# Gets an instance of log4py.
logSys = log4py.Logger().get_instance()
logSys.set_formatstring("%T %L %M")
conf = dict()
conf["verbose"] = 0
conf["background"] = False
conf["debug"] = False
conf["conffile"] = "/etc/fail2ban.conf"
conf["pidlock"] = "/var/run/fail2ban.pid"
conf["logging"] = False
conf["logfile"] = "/var/log/fail2ban.log"
conf["maxretry"] = 3
conf["bantime"] = 600
conf["ignoreip"] = ''
conf["interface"] = "eth0"
conf["firewall"] = "iptables"
conf["ipfw-start-rule"] = 0
conf["polltime"] = 1
# Reads the command line options.
try:
optList, args = getopt.getopt(sys.argv[1:], 'hvbdkc:l:t:i:r:e:w:p:', ['help','version'])
except getopt.GetoptError:
sys.stderr.write("Error during parsing of command line parameters\n");
usage()
# Pre-parsing of command line options for the -c option
for opt in optList: for opt in optList:
if opt[0] == "-c": if opt[0] in ["-h", "--help"]:
conf["conffile"] = opt[1] dispUsage()
if opt[0] in ["-V", "--version"]:
# Config file dispVersion()
configParser = SafeConfigParser()
configParser.read(conf["conffile"])
# background
try:
conf["background"] = configParser.getboolean("DEFAULT", "background")
except ValueError:
logSys.warn("background option should be a boolean")
logSys.warn("Using default value")
except NoOptionError:
logSys.warn("background option not in config file")
logSys.warn("Using default value")
# debug
try:
conf["debug"] = configParser.getboolean("DEFAULT", "debug")
except ValueError:
logSys.warn("debug option should be a boolean")
logSys.warn("Using default value")
except NoOptionError:
logSys.warn("debug option not in config file")
logSys.warn("Using default value")
# logfile
try:
conf["logfile"] = configParser.get("DEFAULT", "logfile")
except ValueError:
logSys.warn("logfile option should be a string")
logSys.warn("Using default value")
except NoOptionError:
logSys.warn("logfile option not in config file")
logSys.warn("Using default value")
# pidlock
try:
conf["pidlock"] = configParser.get("DEFAULT", "pidlock")
except ValueError:
logSys.warn("pidlock option should be a string")
logSys.warn("Using default value")
except NoOptionError:
logSys.warn("pidlock option not in config file")
logSys.warn("Using default value")
# maxretry
try:
conf["maxretry"] = configParser.getint("DEFAULT", "maxretry")
except ValueError:
logSys.warn("maxretry option should be an integer")
logSys.warn("Using default value")
except NoOptionError:
logSys.warn("maxretry option not in config file")
logSys.warn("Using default value")
# bantime
try:
conf["bantime"] = configParser.getint("DEFAULT", "bantime")
except ValueError:
logSys.warn("bantime option should be an integer")
logSys.warn("Using default value")
except NoOptionError:
logSys.warn("bantime option not in config file")
logSys.warn("Using default value")
# ignoreip
try:
conf["ignoreip"] = configParser.get("DEFAULT", "ignoreip")
except ValueError:
logSys.warn("ignoreip option should be a string")
logSys.warn("Using default value")
except NoOptionError:
logSys.warn("ignoreip option not in config file")
logSys.warn("Using default value")
# interface
try:
conf["interface"] = configParser.get("DEFAULT", "interface")
except ValueError:
logSys.warn("interface option should be a string")
logSys.warn("Using default value")
except NoOptionError:
logSys.warn("interface option not in config file")
logSys.warn("Using default value")
# firewall
try:
conf["firewall"] = configParser.get("DEFAULT", "firewall")
except ValueError:
logSys.warn("firewall option should be a string")
logSys.warn("Using default value")
except NoOptionError:
logSys.warn("firewall option not in config file")
logSys.warn("Using default value")
# ipfw-start-rule
try:
conf["ipfw-start-rule"] = configParser.getint("DEFAULT",
"ipfw-start-rule")
except ValueError:
logSys.warn("ipfw-start-rule option should be an integer")
logSys.warn("Using default value")
except NoOptionError:
logSys.warn("ipfw-start-rule option not in config file")
logSys.warn("Using default value")
# polltime
try:
conf["polltime"] = configParser.getint("DEFAULT", "polltime")
except ValueError:
logSys.warn("polltime option should be an integer")
logSys.warn("Using default value")
except NoOptionError:
logSys.warn("polltime option not in config file")
logSys.warn("Using default value")
for opt in optList:
if opt[0] in ["-h","--help"]:
usage()
if opt[0] in ["--version"]:
print version
sys.exit(0)
if opt[0] == "-v": if opt[0] == "-v":
conf["verbose"] = conf["verbose"] + 1 conf["verbose"] = conf["verbose"] + 1
if opt[0] == "-b": if opt[0] == "-b":
conf["background"] = True conf["background"] = True
if opt[0] == "-d": if opt[0] == "-d":
conf["debug"] = True conf["debug"] = True
if opt[0] == "-e":
conf["interface"] = opt[1]
if opt[0] == "-l": if opt[0] == "-l":
conf["logging"] = True
conf["logfile"] = opt[1] conf["logfile"] = opt[1]
if opt[0] == "-t": if opt[0] == "-t":
try: try:
@ -390,8 +131,6 @@ if __name__ == "__main__":
conf["ignoreip"] = opt[1] conf["ignoreip"] = opt[1]
if opt[0] == "-r": if opt[0] == "-r":
conf["retrymax"] = int(opt[1]) conf["retrymax"] = int(opt[1])
if opt[0] == "-w":
conf["firewall"] = opt[1]
if opt[0] == "-p": if opt[0] == "-p":
conf["pidlock"] = opt[1] conf["pidlock"] = opt[1]
if opt[0] == "-k": if opt[0] == "-k":
@ -404,41 +143,84 @@ if __name__ == "__main__":
logSys.error("No running Fail2Ban found") logSys.error("No running Fail2Ban found")
sys.exit(-1) sys.exit(-1)
def main():
""" Fail2Ban main function
"""
logSys.set_formatstring("%T %L %M")
conf["verbose"] = 0
conf["conffile"] = "/etc/fail2ban.conf"
# Reads the command line options.
try:
cmdOpts = 'hvVbdkc:l:t:i:r:p:'
cmdLongOpts = ['help','version']
optList, args = getopt.getopt(sys.argv[1:], cmdOpts, cmdLongOpts)
except getopt.GetoptError:
dispUsage()
# Pre-parsing of command line options for the -c option
for opt in optList:
if opt[0] == "-c":
conf["conffile"] = opt[1]
# Reads the config file and create a LogReader instance for
# each log file to check.
confReader = ConfigReader(conf["conffile"]);
confReader.openConf()
# Options
optionValues = (["bool", "background", False],
["bool", "debug", False],
["str", "logfile", "/var/log/fail2ban.log"],
["str", "pidlock", "/var/run/fail2ban.pid"],
["int", "maxretry", 3],
["int", "bantime", 600],
["str", "ignoreip", ""],
["int", "polltime", 1],
["str", "cmdstart", ""],
["str", "cmdend", ""])
# Gets global configuration options
conf.update(confReader.getLogOptions("DEFAULT", optionValues))
# Gets command line options
getCmdLineOptions(optList)
# Process some options # Process some options
for c in conf: # Verbose level
if c == "verbose": if conf["verbose"]:
logSys.warn("Verbose level is "+`conf[c]`) logSys.warn("Verbose level is "+`conf["verbose"]`)
if conf[c] == 1: if conf["verbose"] == 1:
logSys.set_loglevel(log4py.LOGLEVEL_VERBOSE) logSys.set_loglevel(log4py.LOGLEVEL_VERBOSE)
elif conf[c] > 1: elif conf["verbose"] > 1:
logSys.set_loglevel(log4py.LOGLEVEL_DEBUG)
elif c == "debug" and conf[c]:
logSys.set_loglevel(log4py.LOGLEVEL_DEBUG) logSys.set_loglevel(log4py.LOGLEVEL_DEBUG)
logSys.set_formatstring(log4py.FMT_DEBUG)
elif c == "background" and conf[c]: # Set debug log level
retCode = createDaemon() if conf["debug"]:
signal.signal(signal.SIGTERM, sigTERMhandler) logSys.set_loglevel(log4py.LOGLEVEL_DEBUG)
logSys.set_formatstring(log4py.FMT_DEBUG)
logSys.warn("DEBUG MODE: FIREWALL COMMANDS ARE _NOT_ EXECUTED BUT " +
"ONLY DISPLAYED IN THE LOG MESSAGES")
# Start Fail2Ban in daemon mode
if conf["background"]:
retCode = createDaemon()
signal.signal(signal.SIGTERM, sigTERMhandler)
if not retCode:
logSys.error("Unable to start daemon")
sys.exit(-1)
# Bug fix for #1234699
os.umask(0077)
try:
open(conf["logfile"], "a")
logSys.set_target(conf["logfile"]) logSys.set_target(conf["logfile"])
if not retCode: except IOError:
logSys.error("Unable to start daemon") logSys.error("Unable to log to " + conf["logfile"])
sys.exit(-1) logSys.warn("Using default output for logging")
elif c == "logging" and conf[c]:
try: # Ignores IP list
open(conf["logfile"], "a") ignoreIPList = conf["ignoreip"].split(' ')
logSys.set_target(conf["logfile"])
except IOError:
logSys.warn("Unable to log to "+conf["logfile"])
logSys.warn("Using default output for logging")
elif c == "ignoreip":
ignoreIPList = conf[c].split(' ')
elif c == "firewall":
conf[c] = string.lower(conf[c])
if conf[c] == "ipfw":
fireWallName = "Ipfw"
elif conf[c] == "ipfwadm":
fireWallName = "Ipfwadm"
else:
fireWallName = "Iptables"
# Checks for root user. This is necessary because log files # Checks for root user. This is necessary because log files
# are owned by root and firewall needs root access. # are owned by root and firewall needs root access.
@ -459,40 +241,66 @@ if __name__ == "__main__":
logSys.debug("BanTime is "+`conf["bantime"]`) logSys.debug("BanTime is "+`conf["bantime"]`)
logSys.debug("retryAllowed is "+`conf["maxretry"]`) logSys.debug("retryAllowed is "+`conf["maxretry"]`)
# Reads the config file and create a LogReader instance for # Options
# each log file to check. optionValues = (["bool", "enabled", False],
confReader = ConfigReader(logSys, conf["conffile"]); ["str", "host", "localhost"],
confReader.openConf() ["int", "port", "25"],
logList = list() ["str", "from", "root"],
["str", "to", "root"],
["str", "subject", "[Fail2Ban] Banned <ip>"],
["str", "message", "Fail2Ban notification"])
# Gets global configuration options
mailConf = confReader.getLogOptions("MAIL", optionValues)
# Create mailer if enabled
if mailConf["enabled"]:
logSys.debug("Mail enabled")
mail = Mail(mailConf["host"], mailConf["port"])
mail.setFromAddr(mailConf["from"])
mail.setToAddr(mailConf["to"])
logSys.debug("to: " + mailConf["to"] + " from: " + mailConf["from"])
# Options
optionValues = (["bool", "enabled", True],
["str", "logfile", "/dev/null"],
["str", "timeregex", ""],
["str", "timepattern", ""],
["str", "failregex", ""],
["str", "fwstart", ""],
["str", "fwend", ""],
["str", "fwban", ""],
["str", "fwunban", ""])
# Gets the options of each sections
for t in confReader.getSections(): for t in confReader.getSections():
l = confReader.getLogOptions(t) l = confReader.getLogOptions(t, optionValues)
if l["enabled"]: if l["enabled"]:
lObj = LogReader(logSys, l["logfile"], l["timeregex"], # Creates a logreader object
l["timepattern"], l["failregex"], conf["bantime"]) lObj = LogReader(l["logfile"], l["timeregex"], l["timepattern"],
lObj.setName(t) l["failregex"], conf["bantime"])
logList.append(lObj) # Creates a firewall object
fObj = Firewall(l["fwban"], l["fwunban"], conf["bantime"])
# Creates one instance of Iptables (thanks to Pyhton dynamic # Links them into a list. I'm not really happy
# features). # with this :/
fireWallObj = eval(fireWallName) logFwList.append([t, lObj, fObj, dict(), l])
fireWall = fireWallObj(conf["bantime"], logSys, conf["interface"])
# IPFW needs rules number. The configuration option "ipfw-start-rule"
# defines the first rule number used by Fail2Ban.
if fireWallName == "Ipfw":
fireWall.setCrtRuleNbr(conf["ipfw-start-rule"])
# We add 127.0.0.1 to the ignore list has we do not want # We add 127.0.0.1 to the ignore list has we do not want
# to be ban ourself. # to be ban ourself.
for element in logList: for element in logFwList:
element.addIgnoreIP("127.0.0.1") element[1].addIgnoreIP("127.0.0.1")
while len(ignoreIPList) > 0: while len(ignoreIPList) > 0:
ip = ignoreIPList.pop() ip = ignoreIPList.pop()
for element in logList: for element in logFwList:
element.addIgnoreIP(ip) element[1].addIgnoreIP(ip)
logSys.info("Fail2Ban v"+version+" is running") logSys.info("Fail2Ban v"+version+" is running")
failListFull = dict() # Execute global start command
executeCmd(conf["cmdstart"], conf["debug"])
# Execute start command of each section
for element in logFwList:
l = element[4]
executeCmd(l["fwstart"], conf["debug"])
# Main loop # Main loop
while True: while True:
try: try:
@ -501,14 +309,15 @@ if __name__ == "__main__":
# Checks if some IP have to be remove from ban # Checks if some IP have to be remove from ban
# list. # list.
fireWall.checkForUnBan(conf["debug"]) for element in logFwList:
element[2].checkForUnBan(conf["debug"])
# If the log file has not been modified since the # If the log file has not been modified since the
# last time, we sleep for 1 second. This is active # last time, we sleep for 1 second. This is active
# polling so not very effective. # polling so not very effective.
modList = list() modList = list()
for element in logList: for element in logFwList:
if element.isModified(): if element[1].isModified():
modList.append(element) modList.append(element)
if len(modList) == 0: if len(modList) == 0:
@ -517,42 +326,39 @@ if __name__ == "__main__":
# Gets the failure list from the log file. For a given IP, # Gets the failure list from the log file. For a given IP,
# takes only the service which has the most password failures. # takes only the service which has the most password failures.
failList = dict()
for element in modList: for element in modList:
e = element.getFailures() e = element[1].getFailures()
for key in e.iterkeys(): for key in e.iterkeys():
if failList.has_key(key): if element[3].has_key(key):
if failList[key][0] < e[key][0]: element[3][key] = (element[3][key][0] + e[key][0],
failList[key] = (e[key][0], e[key][1], element) e[key][1])
else: else:
failList[key] = (e[key][0], e[key][1], element) element[3][key] = (e[key][0], e[key][1])
# Add the last log failures to the global failure list.
for key in failList.iterkeys():
if failListFull.has_key(key):
failListFull[key] = (failListFull[key][0] + 1,
failList[key][1], failList[key][2])
else:
failListFull[key] = failList[key]
# Remove the oldest failure attempts from the global list. # Remove the oldest failure attempts from the global list.
unixTime = time.time()
failListFullTemp = failListFull.copy()
for key in failListFullTemp.iterkeys():
failTime = failListFullTemp[key][2].getFindTime()
if failListFullTemp[key][1] < unixTime - failTime:
del failListFull[key]
# We iterate the failure list and ban IP that make # We iterate the failure list and ban IP that make
# *retryAllowed* login failures. # *retryAllowed* login failures.
failListFullTemp = failListFull.copy() unixTime = time.time()
for key in failListFullTemp.iterkeys(): for element in logFwList:
element = failListFullTemp[key] fails = element[3].copy()
if element[0] >= conf["maxretry"]: findTime = element[1].getFindTime()
logSys.info(element[2].getName()+": "+key+" has "+ for attempt in fails:
`element[0]`+" login failure(s). Banned.") failTime = fails[attempt][1]
fireWall.addBanIP(key, conf["debug"]) if failTime < unixTime - findTime:
del failListFull[key] del element[3][attempt]
elif fails[attempt][0] >= conf["maxretry"]:
aInfo = {"ip": attempt,
"failures": element[3][attempt][0],
"failtime": failTime}
logSys.info(element[0] + ": " + aInfo["ip"] +
" has " + `aInfo["failures"]` +
" login failure(s). Banned.")
element[2].addBanIP(aInfo, conf["debug"])
# Send a mail notification
if 'mail' in locals():
mail.sendmail(mailConf["subject"],
mailConf["message"], aInfo)
del element[3][attempt]
except KeyboardInterrupt: except KeyboardInterrupt:
# When the user press <ctrl>+<c> we exit nicely. # When the user press <ctrl>+<c> we exit nicely.

View File

@ -16,15 +16,21 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 1.8 $ # $Revision: 1.8.2.4 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 1.8 $" __version__ = "$Revision: 1.8.2.4 $"
__date__ = "$Date: 2005/03/06 17:46:56 $" __date__ = "$Date: 2005/07/12 13:08:24 $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
import time, os import time, os, log4py, re
from utils.process import executeCmd
from utils.strings import replaceTag
# Gets the instance of log4py.
logSys = log4py.Logger().get_instance()
class Firewall: class Firewall:
""" Manages the ban list and executes the command that ban """ Manages the ban list and executes the command that ban
@ -33,30 +39,34 @@ class Firewall:
banList = dict() banList = dict()
def __init__(self, banTime, logSys, interface): def __init__(self, banRule, unBanRule, banTime):
self.banRule = banRule
self.unBanRule = unBanRule
self.banTime = banTime self.banTime = banTime
self.logSys = logSys
self.interface = interface
def addBanIP(self, ip, debug): def addBanIP(self, aInfo, debug):
""" Bans an IP. """ Bans an IP.
""" """
ip = aInfo["ip"]
if not self.inBanList(ip): if not self.inBanList(ip):
self.logSys.warn("Ban "+ip) crtTime = time.time()
self.banList[ip] = time.time() logSys.warn("Ban " + ip)
self.__executeCmd(self.banIP(ip), debug) self.banList[ip] = crtTime
aInfo["bantime"] = crtTime
executeCmd(self.banIP(aInfo), debug)
else: else:
self.logSys.error(ip+" already in ban list") logSys.error(ip+" already in ban list")
def delBanIP(self, ip, debug): def delBanIP(self, aInfo, debug):
""" Unban an IP. """ Unban an IP.
""" """
ip = aInfo["ip"]
if self.inBanList(ip): if self.inBanList(ip):
self.logSys.warn("Unban "+ip) logSys.warn("Unban "+ip)
del self.banList[ip] del self.banList[ip]
self.__executeCmd(self.unBanIP(ip), debug) executeCmd(self.unBanIP(aInfo), debug)
else: else:
self.logSys.error(ip+" not in ban list") logSys.error(ip+" not in ban list")
def inBanList(self, ip): def inBanList(self, ip):
""" Checks if IP is in ban list. """ Checks if IP is in ban list.
@ -68,10 +78,12 @@ class Firewall:
""" """
banListTemp = self.banList.copy() banListTemp = self.banList.copy()
for element in banListTemp.iteritems(): for element in banListTemp.iteritems():
ip = element[0]
btime = element[1] btime = element[1]
if btime < time.time()-self.banTime: if btime < time.time()-self.banTime:
self.delBanIP(ip, debug) aInfo = {"ip": element[0],
"bantime": btime,
"unbantime": time.time()}
self.delBanIP(aInfo, debug)
def flushBanList(self, debug): def flushBanList(self, debug):
""" Flushes the ban list and of course the firewall rules. """ Flushes the ban list and of course the firewall rules.
@ -79,18 +91,23 @@ class Firewall:
""" """
banListTemp = self.banList.copy() banListTemp = self.banList.copy()
for element in banListTemp.iteritems(): for element in banListTemp.iteritems():
ip = element[0] aInfo = {"ip": element[0],
self.delBanIP(ip, debug) "bantime": element[1],
"unbantime": time.time()}
def __executeCmd(self, cmd, debug): self.delBanIP(aInfo, debug)
""" Executes an OS command.
def banIP(self, aInfo):
""" Returns query to ban IP.
""" """
self.logSys.debug(cmd) query = replaceTag(self.banRule, aInfo)
if not debug: return query
return os.system(cmd)
else: def unBanIP(self, aInfo):
return None """ Returns query to unban IP.
"""
query = replaceTag(self.unBanRule, aInfo)
return query
def viewBanList(self): def viewBanList(self):
""" Prints the ban list on screen. Usefull for debugging. """ Prints the ban list on screen. Usefull for debugging.
""" """

View File

@ -1,72 +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: 1.4 $
__author__ = "Cyril Jaquier"
__version__ = "$Revision: 1.4 $"
__date__ = "$Date: 2005/03/06 17:47:51 $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import os
from firewall import Firewall
class Ipfw(Firewall):
""" This class contains specific methods and variables for the
iptables firewall. Must implements the 'abstracts' methods
banIP(ip) and unBanIP(ip).
Must adds abstract methods definition:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/266468
"""
crtRuleNbr = 0
def getCrtRuleNbr(self):
""" Gets the current rule number.
"""
return self.crtRuleNbr
def setCrtRuleNbr(self, value):
""" Sets the current rule number.
"""
self.crtRuleNbr = value
def banIP(self, ip):
""" Returns query to ban IP.
"""
query = "ipfw -q add "+`self.crtRuleNbr`+" deny ip from "+ip+" to any"
self.crtRuleNbr = self.crtRuleNbr + 1
return query
def unBanIP(self, ip):
""" Returns query to unban IP.
"""
ruleNbr = str(self.__findRuleNumber(ip))
query = "ipfw -q delete "+ruleNbr
return query
def __findRuleNumber(self, ip):
""" Uses shell commands in order to find the rule
number we want to delete.
"""
output = os.popen("ipfw list|grep \"from "+ip+" to\"|awk '{print $1}'",
"r");
return output.read()

View File

@ -16,26 +16,29 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 1.13 $ # $Revision: 1.13.2.2 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 1.13 $" __version__ = "$Revision: 1.13.2.2 $"
__date__ = "$Date: 2005/03/31 15:45:24 $" __date__ = "$Date: 2005/07/12 13:09:09 $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
import os, sys, time, re import os, sys, time, re, log4py
from utils.dns import * from utils.dns import *
# Gets the instance of log4py.
logSys = log4py.Logger().get_instance()
class LogReader: class LogReader:
""" Reads a log file and reports information about IP that make password """ Reads a log file and reports information about IP that make password
failure, bad user or anything else that is considered as doubtful login failure, bad user or anything else that is considered as doubtful login
attempt. attempt.
""" """
def __init__(self, logSys, logPath, timeregex, timepattern, failregex, def __init__(self, logPath, timeregex, timepattern, failregex,
findTime = 3600): findTime = 3600):
self.logPath = logPath self.logPath = logPath
self.timeregex = timeregex self.timeregex = timeregex
self.timepattern = timepattern self.timepattern = timepattern
@ -43,20 +46,9 @@ class LogReader:
self.findTime = findTime self.findTime = findTime
self.ignoreIpList = [] self.ignoreIpList = []
self.lastModTime = 0 self.lastModTime = 0
self.logSys = logSys
self.lastPos = 0 self.lastPos = 0
self.lastDate = 0 self.lastDate = 0
self.logStats = None self.logStats = None
def setName(self, name):
""" Sets the name of the log reader.
"""
self.name = name
def getName(self):
""" Gets the name of the log reader.
"""
return self.name
def getFindTime(self): def getFindTime(self):
""" Gets the find time. """ Gets the find time.
@ -66,13 +58,23 @@ class LogReader:
def addIgnoreIP(self, ip): def addIgnoreIP(self, ip):
""" Adds an IP to the ignore list. """ Adds an IP to the ignore list.
""" """
self.logSys.debug("Add "+ip+" to ignore list") logSys.debug("Add "+ip+" to ignore list")
self.ignoreIpList.append(ip) self.ignoreIpList.append(ip)
def inIgnoreIPList(self, ip): def inIgnoreIPList(self, ip):
""" Checks if IP is in the ignore list. """ Checks if IP is in the ignore list.
""" """
return ip in self.ignoreIpList for i in self.ignoreIpList:
s = i.split('/', 1)
# IP address without CIDR mask
if len(s) == 1:
s.insert(1, '32')
s[1] = long(s[1])
a = cidr(s[0], s[1])
b = cidr(ip, s[1])
if a == b:
return True
return False
def openLogFile(self): def openLogFile(self):
""" Opens the log file specified on init. """ Opens the log file specified on init.
@ -80,7 +82,7 @@ class LogReader:
try: try:
fileHandler = open(self.logPath) fileHandler = open(self.logPath)
except OSError: except OSError:
self.logSys.error("Unable to open "+self.logPath) logSys.error("Unable to open "+self.logPath)
sys.exit(-1) sys.exit(-1)
return fileHandler return fileHandler
@ -90,13 +92,13 @@ class LogReader:
try: try:
self.logStats = os.stat(self.logPath) self.logStats = os.stat(self.logPath)
except OSError: except OSError:
self.logSys.error("Unable to get stat on "+self.logPath) logSys.error("Unable to get stat on "+self.logPath)
sys.exit(-1) sys.exit(-1)
if self.lastModTime == self.logStats.st_mtime: if self.lastModTime == self.logStats.st_mtime:
return False return False
else: else:
self.logSys.debug(self.logPath+" has been modified") logSys.debug(self.logPath+" has been modified")
self.lastModTime = self.logStats.st_mtime self.lastModTime = self.logStats.st_mtime
return True return True
@ -107,13 +109,13 @@ class LogReader:
""" """
line = file.readline() line = file.readline()
if self.lastDate < self.getTime(line): if self.lastDate < self.getTime(line):
self.logSys.debug("Date " + `self.lastDate` + " is " + logSys.debug("Date " + `self.lastDate` + " is " + "smaller than " +
"smaller than " + `self.getTime(line)`) `self.getTime(line)`)
self.logSys.debug("Log rotation detected for " + self.logPath) logSys.debug("Log rotation detected for " + self.logPath)
self.lastPos = 0 self.lastPos = 0
self.logSys.debug("Setting file position to " + `self.lastPos` + " for " logSys.debug("Setting file position to " + `self.lastPos` + " for " +
+ self.logPath) self.logPath)
file.seek(self.lastPos) file.seek(self.lastPos)
def getFailures(self): def getFailures(self):
@ -124,7 +126,7 @@ class LogReader:
and the latest failure time. and the latest failure time.
""" """
ipList = dict() ipList = dict()
self.logSys.debug(self.logPath) logSys.debug(self.logPath)
logFile = self.openLogFile() logFile = self.openLogFile()
self.setFilePos(logFile) self.setFilePos(logFile)
lastLine = '' lastLine = ''
@ -137,9 +139,9 @@ class LogReader:
if unixTime < time.time()-self.findTime: if unixTime < time.time()-self.findTime:
break break
if self.inIgnoreIPList(ip): if self.inIgnoreIPList(ip):
self.logSys.debug("Ignore "+ip) logSys.debug("Ignore "+ip)
continue continue
self.logSys.debug("Found "+ip) logSys.debug("Found "+ip)
if ipList.has_key(ip): if ipList.has_key(ip):
ipList[ip] = (ipList[ip][0]+1, unixTime) ipList[ip] = (ipList[ip][0]+1, unixTime)
else: else:

View File

@ -18,11 +18,11 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 1.4 $ # $Revision: 1.4.2.1 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 1.4 $" __version__ = "$Revision: 1.4.2.1 $"
__date__ = "$Date: 2005/03/31 15:52:02 $" __date__ = "$Date: 2005/07/07 16:57:05 $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
@ -35,8 +35,8 @@ setup(
description = "Ban IPs that make too many password failure", description = "Ban IPs that make too many password failure",
author = "Cyril Jaquier", author = "Cyril Jaquier",
author_email = "lostcontrol@users.sourceforge.net", author_email = "lostcontrol@users.sourceforge.net",
url = "http://www.sourceforge.net/projects/fail2ban", url = "http://fail2ban.sourceforge.net",
scripts = ['fail2ban'], scripts = ['fail2ban'],
py_modules = ['version'], py_modules = ['fail2ban', 'version','log4py'],
packages = ['firewall', 'logreader', 'confreader', 'utils'] packages = ['firewall', 'logreader', 'confreader', 'utils']
) )

View File

@ -16,15 +16,15 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 1.7 $ # $Revision: 1.7.2.1 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 1.7 $" __version__ = "$Revision: 1.7.2.1 $"
__date__ = "$Date: 2005/05/28 19:46:18 $" __date__ = "$Date: 2005/07/12 13:10:14 $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
import os, re, socket import os, re, socket, struct
def dnsToIp(dns): def dnsToIp(dns):
""" Convert a DNS into an IP address using the Python socket module. """ Convert a DNS into an IP address using the Python socket module.
@ -71,3 +71,21 @@ def textToIp(text):
for e in dns: for e in dns:
ipList.append(e) ipList.append(e)
return ipList return ipList
def cidr(i, n):
""" Convert an IP address string with a CIDR mask into a 32-bit
integer.
"""
# 32-bit IPv4 address mask
MASK = 0xFFFFFFFFL
return ~(MASK >> n) & MASK & addr2bin(i)
def addr2bin(str):
""" Convert a string IPv4 address into an unsigned integer.
"""
return struct.unpack("!L", socket.inet_aton(str))[0]
def bin2addr(addr):
""" Convert a numeric IPv4 address into string n.n.n.n form.
"""
return socket.inet_ntoa(struct.pack("!L", addr))

71
utils/mail.py Normal file
View File

@ -0,0 +1,71 @@
# 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: 1.1.2.1 $
__author__ = "Cyril Jaquier"
__version__ = "$Revision: 1.1.2.1 $"
__date__ = "$Date: 2005/07/12 13:09:47 $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import log4py, smtplib
from utils.strings import replaceTag
# Gets the instance of log4py.
logSys = log4py.Logger().get_instance()
class Mail:
""" Mailer class
"""
def __init__(self, host, port = 25):
self.host = host
self.port = port
def setFromAddr(self, fromAddr):
""" Set from: address
"""
self.fromAddr = fromAddr
def setToAddr(self, toAddr):
""" Set to: address
"""
self.toAddr = toAddr.split()
def sendmail(self, subject, message, aInfo):
""" Send an email using smtplib
"""
subj = replaceTag(subject, aInfo)
msg = replaceTag(message, aInfo)
mail = ("From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n" %
(self.fromAddr, ", ".join(self.toAddr), subj)) + msg
try:
server = smtplib.SMTP(self.host, self.port)
#server.set_debuglevel(1)
server.sendmail(self.fromAddr, self.toAddr, mail)
logSys.debug("Email sent to " + `self.toAddr`)
server.quit()
except Exception:
logSys.error("Unable to send mail to " + self.host + ":" +
`self.port` + " from " + self.fromAddr + " to " +
`self.toAddr`)

158
utils/process.py Normal file
View File

@ -0,0 +1,158 @@
# 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: 1.1.2.1 $
__author__ = "Cyril Jaquier"
__version__ = "$Revision: 1.1.2.1 $"
__date__ = "$Date: 2005/07/07 16:53:47 $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import os, log4py, signal
# Gets the instance of log4py.
logSys = log4py.Logger().get_instance()
def createDaemon():
"""Detach a process from the controlling terminal and run it in the
background as a daemon.
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278731
"""
try:
# Fork a child process so the parent can exit. This will return control
# to the command line or shell. This is required so that the new process
# is guaranteed not to be a process group leader. We have this guarantee
# because the process GID of the parent is inherited by the child, but
# the child gets a new PID, making it impossible for its PID to equal its
# PGID.
pid = os.fork()
except OSError, e:
return((e.errno, e.strerror)) # ERROR (return a tuple)
if (pid == 0): # The first child.
# Next we call os.setsid() to become the session leader of this new
# session. The process also becomes the process group leader of the
# new process group. Since a controlling terminal is associated with a
# session, and this new session has not yet acquired a controlling
# terminal our process now has no controlling terminal. This shouldn't
# fail, since we're guaranteed that the child is not a process group
# leader.
os.setsid()
# When the first child terminates, all processes in the second child
# are sent a SIGHUP, so it's ignored.
signal.signal(signal.SIGHUP, signal.SIG_IGN)
try:
# Fork a second child to prevent zombies. Since the first child is
# a session leader without a controlling terminal, it's possible for
# it to acquire one by opening a terminal in the future. This second
# fork guarantees that the child is no longer a session leader, thus
# preventing the daemon from ever acquiring a controlling terminal.
pid = os.fork() # Fork a second child.
except OSError, e:
return((e.errno, e.strerror)) # ERROR (return a tuple)
if (pid == 0): # The second child.
# Ensure that the daemon doesn't keep any directory in use. Failure
# to do this could make a filesystem unmountable.
os.chdir("/")
# Give the child complete control over permissions.
os.umask(0)
else:
os._exit(0) # Exit parent (the first child) of the second child.
else:
os._exit(0) # Exit parent of the first child.
# Close all open files. Try the system configuration variable, SC_OPEN_MAX,
# for the maximum number of open files to close. If it doesn't exist, use
# the default value (configurable).
try:
maxfd = os.sysconf("SC_OPEN_MAX")
except (AttributeError, ValueError):
maxfd = 256 # default maximum
for fd in range(0, maxfd):
try:
os.close(fd)
except OSError: # ERROR (ignore)
pass
# Redirect the standard file descriptors to /dev/null.
os.open("/dev/null", os.O_RDONLY) # standard input (0)
os.open("/dev/null", os.O_RDWR) # standard output (1)
os.open("/dev/null", os.O_RDWR) # standard error (2)
return True
def checkForPID(lockfile):
""" Checks for running Fail2Ban.
Returns the current PID if Fail2Ban is running or False
if no instance found.
"""
try:
fileHandler = open(lockfile)
pid = fileHandler.readline()
return pid
except IOError:
return False
def createPID(lockfile):
""" Creates a PID lock file with the current PID.
"""
fileHandler = open(lockfile, mode='w')
pid = os.getpid()
fileHandler.write(`pid`+'\n')
fileHandler.close()
logSys.debug("Created PID lock ("+`pid`+") in "+lockfile)
def removePID(lockfile):
""" Remove PID lock.
"""
os.remove(lockfile)
logSys.debug("Removed PID lock "+lockfile)
def killPID(pid):
""" Kills the process with the given PID using the
INT signal (same effect as <ctrl>+<c>).
"""
try:
return os.kill(pid, 2)
except OSError:
logSys.error("Can not kill process " + `pid` + ". Please check that " +
"Fail2Ban is not running and remove the file " +
"'/tmp/fail2ban.pid'")
return False
def executeCmd(cmd, debug):
""" Executes an OS command.
"""
if cmd == "":
logSys.debug("Nothing to do")
return None
logSys.debug(cmd)
if not debug:
return os.system(cmd)
else:
return None

View File

@ -16,33 +16,25 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 1.1 $ # $Revision: 1.1.2.1 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 1.1 $" __version__ = "$Revision: 1.1.2.1 $"
__date__ = "$Date: 2004/11/06 14:02:27 $" __date__ = "$Date: 2005/07/12 13:09:47 $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
from firewall import Firewall import log4py
class Ipfwadm(Firewall): # Gets the instance of log4py.
""" This class contains specific methods and variables for the logSys = log4py.Logger().get_instance()
iptables firewall. Must implements the 'abstracts' methods
banIP(ip) and unBanIP(ip). def replaceTag(query, aInfo):
""" Replace tags in query
Must adds abstract methods definition:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/266468
""" """
string = query
def banIP(self, ip): for tag in aInfo:
""" Returns query to ban IP. string = string.replace('<'+tag+'>', `aInfo[tag]`)
""" # New line
query = "ipfwadm -I -a deny -W "+self.interface+" -S "+ip string = string.replace('<br>', '\n')
return query return string
def unBanIP(self, ip):
""" Returns query to unban IP.
"""
query = "ipfwadm -I -d deny -W "+self.interface+" -S "+ip
return query

View File

@ -16,12 +16,12 @@
# Author: Cyril Jaquier # Author: Cyril Jaquier
# #
# $Revision: 1.12 $ # $Revision: 1.12.2.2 $
__author__ = "Cyril Jaquier" __author__ = "Cyril Jaquier"
__version__ = "$Revision: 1.12 $" __version__ = "$Revision: 1.12.2.2 $"
__date__ = "$Date: 2005/06/30 09:30:59 $" __date__ = "$Date: 2005/07/12 13:12:40 $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
version = "0.4.1" version = "0.5.0"