mirror of https://github.com/fail2ban/fail2ban
New upstream release, removed log4py, fresh man file for conf
parent
e965ab1504
commit
e46b5f6665
12
CHANGELOG
12
CHANGELOG
|
@ -4,9 +4,19 @@
|
||||||
|_| \__,_|_|_/___|_.__/\__,_|_||_|
|
|_| \__,_|_|_/___|_.__/\__,_|_||_|
|
||||||
|
|
||||||
=============================================================
|
=============================================================
|
||||||
Fail2Ban (version 0.5.1) 2005/07/23
|
Fail2Ban (version 0.5.2) 2005/08/06
|
||||||
=============================================================
|
=============================================================
|
||||||
|
|
||||||
|
ver. 0.5.2 (2005/08/06) - beta
|
||||||
|
----------
|
||||||
|
- Better PID lock file handling. Should close #1239562
|
||||||
|
- Added man pages
|
||||||
|
- Removed log4py dependency. Use logging module instead
|
||||||
|
- "maxretry" and "bantime" can be overridden in each section
|
||||||
|
- Fixed bug #1246278 (excessive memory usage)
|
||||||
|
- Fixed crash on wrong option value in configuration file
|
||||||
|
- Changed custom chains to lowercase
|
||||||
|
|
||||||
ver. 0.5.1 (2005/07/23) - beta
|
ver. 0.5.1 (2005/07/23) - beta
|
||||||
----------
|
----------
|
||||||
- Fixed bugs #1241756, #1239557
|
- Fixed bugs #1241756, #1239557
|
||||||
|
|
13
PKG-INFO
13
PKG-INFO
|
@ -1,10 +1,15 @@
|
||||||
Metadata-Version: 1.0
|
Metadata-Version: 1.0
|
||||||
Name: fail2ban
|
Name: fail2ban
|
||||||
Version: 0.5.1
|
Version: 0.5.2
|
||||||
Summary: Ban IPs that make too many password failure
|
Summary: Ban IPs that make too many password failure
|
||||||
Home-page: http://fail2ban.sourceforge.net
|
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: GPL
|
||||||
Description: UNKNOWN
|
Description:
|
||||||
Platform: UNKNOWN
|
Fail2Ban scans log files like /var/log/pwdfail or
|
||||||
|
/var/log/apache/error_log and bans IP that makes
|
||||||
|
too many password failures. It updates firewall rules
|
||||||
|
to reject the IP address or executes user defined
|
||||||
|
commands. It needs log4py.
|
||||||
|
Platform: Posix
|
||||||
|
|
15
README
15
README
|
@ -4,14 +4,14 @@
|
||||||
|_| \__,_|_|_/___|_.__/\__,_|_||_|
|
|_| \__,_|_|_/___|_.__/\__,_|_||_|
|
||||||
|
|
||||||
=============================================================
|
=============================================================
|
||||||
Fail2Ban (version 0.5.1) 2005/07/23
|
Fail2Ban (version 0.5.2) 2005/08/06
|
||||||
=============================================================
|
=============================================================
|
||||||
|
|
||||||
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. These rules can be defined by
|
rules to reject the IP address. These rules can be defined by
|
||||||
the user. Fail2Ban can read multiple log files such as sshd
|
the user. Fail2Ban can read multiple log files such as sshd
|
||||||
or Apache web server ones. It needs log4py.
|
or Apache web server ones.
|
||||||
|
|
||||||
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...
|
||||||
|
@ -55,12 +55,11 @@ Installation:
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
Require: python-2.3 (http://www.python.org)
|
Require: python-2.3 (http://www.python.org)
|
||||||
log4py-1.3 (http://sourceforge.net/projects/log4py)
|
|
||||||
|
|
||||||
To install, just do:
|
To install, just do:
|
||||||
|
|
||||||
> tar xvfj fail2ban-0.5.1.tar.bz2
|
> tar xvfj fail2ban-0.5.2.tar.bz2
|
||||||
> cd fail2ban-0.5.1
|
> cd fail2ban-0.5.2
|
||||||
> python setup.py install
|
> python setup.py install
|
||||||
|
|
||||||
This will install Fail2Ban into /usr/lib/fail2ban. The fail2ban
|
This will install Fail2Ban into /usr/lib/fail2ban. The fail2ban
|
||||||
|
@ -95,13 +94,13 @@ or using command line options. Command line options override
|
||||||
the value stored in fail2ban.conf. Here are the command line
|
the value stored in fail2ban.conf. Here are the command line
|
||||||
options:
|
options:
|
||||||
|
|
||||||
-b start fail2ban in background
|
-b start in background
|
||||||
-d start fail2ban in debug mode
|
-d start in debug mode
|
||||||
-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 instance
|
||||||
-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
|
||||||
|
|
1
TODO
1
TODO
|
@ -10,4 +10,3 @@ ToDo
|
||||||
See Feature Request Tracking System at SourceForge.net
|
See Feature Request Tracking System at SourceForge.net
|
||||||
|
|
||||||
- improve installation process (better prefix support)
|
- improve installation process (better prefix support)
|
||||||
- add better documentation (man page)
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Fail2Ban configuration file
|
# Fail2Ban configuration file
|
||||||
#
|
#
|
||||||
# $Revision: 1.8.2.7 $
|
# $Revision: 1.8.2.9 $
|
||||||
#
|
#
|
||||||
# 2005.06.21 modified for readability Iain Lea iain@bricbrac.de
|
# 2005.06.21 modified for readability Iain Lea iain@bricbrac.de
|
||||||
|
|
||||||
|
@ -19,9 +19,9 @@ debug = false
|
||||||
|
|
||||||
# Option: logtargets
|
# Option: logtargets
|
||||||
# Notes.: log targets. Space separated list of logging targets.
|
# Notes.: log targets. Space separated list of logging targets.
|
||||||
# Values: STDOUT STDERR SYSLOG file Default: STDOUT /var/log/fail2ban.log
|
# Values: STDERR SYSLOG file Default: /var/log/fail2ban.log
|
||||||
#
|
#
|
||||||
logtargets = STDOUT /var/log/fail2ban.log
|
logtargets = /var/log/fail2ban.log
|
||||||
|
|
||||||
# Option: pidlock
|
# Option: pidlock
|
||||||
# Notes.: path of the PID lock file (must be able to write to file).
|
# Notes.: path of the PID lock file (must be able to write to file).
|
||||||
|
@ -49,7 +49,7 @@ bantime = 600
|
||||||
# Examples
|
# Examples
|
||||||
# ignoreip = 192.168.0.0/24
|
# ignoreip = 192.168.0.0/24
|
||||||
#
|
#
|
||||||
ignoreip =
|
ignoreip = 192.168.0.0/16
|
||||||
|
|
||||||
|
|
||||||
# Option: cmdstart
|
# Option: cmdstart
|
||||||
|
@ -147,17 +147,17 @@ logfile = /var/log/apache/access.log
|
||||||
# Notes.: command executed once at the start of Fail2Ban
|
# Notes.: command executed once at the start of Fail2Ban
|
||||||
# Values: CMD Default:
|
# Values: CMD Default:
|
||||||
#
|
#
|
||||||
fwstart = iptables -N fail2ban-HTTP
|
fwstart = iptables -N fail2ban-http
|
||||||
iptables -I INPUT -i eth0 -p tcp --dport http -j fail2ban-HTTP
|
iptables -I INPUT -p tcp --dport http -j fail2ban-http
|
||||||
iptables -A fail2ban-HTTP -j RETURN
|
iptables -A fail2ban-http -j RETURN
|
||||||
|
|
||||||
# Option: fwend
|
# Option: fwend
|
||||||
# Notes.: command executed once at the end of Fail2Ban
|
# Notes.: command executed once at the end of Fail2Ban
|
||||||
# Values: CMD Default:
|
# Values: CMD Default:
|
||||||
#
|
#
|
||||||
fwend = iptables -D INPUT -i eth0 -p tcp --dport http -j fail2ban-HTTP
|
fwend = iptables -D INPUT -p tcp --dport http -j fail2ban-http
|
||||||
iptables -D fail2ban-HTTP -j RETURN
|
iptables -D fail2ban-http -j RETURN
|
||||||
iptables -X fail2ban-HTTP
|
iptables -X fail2ban-http
|
||||||
|
|
||||||
# Option: fwban
|
# Option: fwban
|
||||||
# Notes.: command executed when banning an IP. Take care that the
|
# Notes.: command executed when banning an IP. Take care that the
|
||||||
|
@ -167,9 +167,9 @@ fwend = iptables -D INPUT -i eth0 -p tcp --dport http -j fail2ban-HTTP
|
||||||
# <failtime> unix timestamp of the last failure
|
# <failtime> unix timestamp of the last failure
|
||||||
# <bantime> unix timestamp of the ban time
|
# <bantime> unix timestamp of the ban time
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
# Default: iptables -I INPUT 1 -i eth0 -s <ip> -j DROP
|
# Default: iptables -I INPUT 1 -s <ip> -j DROP
|
||||||
#
|
#
|
||||||
fwban = iptables -I fail2ban-HTTP 1 -i eth0 -s <ip> -j DROP
|
fwban = iptables -I fail2ban-http 1 -s <ip> -j DROP
|
||||||
|
|
||||||
# Option: fwunban
|
# Option: fwunban
|
||||||
# Notes.: command executed when unbanning an IP. Take care that the
|
# Notes.: command executed when unbanning an IP. Take care that the
|
||||||
|
@ -178,9 +178,9 @@ fwban = iptables -I fail2ban-HTTP 1 -i eth0 -s <ip> -j DROP
|
||||||
# <bantime> unix timestamp of the ban time
|
# <bantime> unix timestamp of the ban time
|
||||||
# <unbantime> unix timestamp of the unban time
|
# <unbantime> unix timestamp of the unban time
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
# Default: iptables -D INPUT -i eth0 -s <ip> -j DROP
|
# Default: iptables -D INPUT -s <ip> -j DROP
|
||||||
#
|
#
|
||||||
fwunban = iptables -D fail2ban-HTTP -i eth0 -s <ip> -j DROP
|
fwunban = iptables -D fail2ban-http -s <ip> -j DROP
|
||||||
|
|
||||||
# Option: timeregex
|
# Option: timeregex
|
||||||
# Notes.: regex to match timestamp in Apache logfile.
|
# Notes.: regex to match timestamp in Apache logfile.
|
||||||
|
@ -219,17 +219,17 @@ logfile = /var/log/auth.log
|
||||||
# Notes.: command executed once at the start of Fail2Ban
|
# Notes.: command executed once at the start of Fail2Ban
|
||||||
# Values: CMD Default:
|
# Values: CMD Default:
|
||||||
#
|
#
|
||||||
fwstart = iptables -N fail2ban-SSH
|
fwstart = iptables -N fail2ban-ssh
|
||||||
iptables -I INPUT -i eth0 -p tcp --dport ssh -j fail2ban-SSH
|
iptables -I INPUT -p tcp --dport ssh -j fail2ban-ssh
|
||||||
iptables -A fail2ban-SSH -j RETURN
|
iptables -A fail2ban-ssh -j RETURN
|
||||||
|
|
||||||
# Option: fwend
|
# Option: fwend
|
||||||
# Notes.: command executed once at the end of Fail2Ban
|
# Notes.: command executed once at the end of Fail2Ban
|
||||||
# Values: CMD Default:
|
# Values: CMD Default:
|
||||||
#
|
#
|
||||||
fwend = iptables -D INPUT -i eth0 -p tcp --dport ssh -j fail2ban-SSH
|
fwend = iptables -D INPUT -p tcp --dport ssh -j fail2ban-ssh
|
||||||
iptables -D fail2ban-SSH -j RETURN
|
iptables -D fail2ban-ssh -j RETURN
|
||||||
iptables -X fail2ban-SSH
|
iptables -X fail2ban-ssh
|
||||||
|
|
||||||
# Option: fwbanrule
|
# Option: fwbanrule
|
||||||
# Notes.: command executed when banning an IP. Take care that the
|
# Notes.: command executed when banning an IP. Take care that the
|
||||||
|
@ -239,9 +239,9 @@ fwend = iptables -D INPUT -i eth0 -p tcp --dport ssh -j fail2ban-SSH
|
||||||
# <failtime> unix timestamp of the last failure
|
# <failtime> unix timestamp of the last failure
|
||||||
# <bantime> unix timestamp of the ban time
|
# <bantime> unix timestamp of the ban time
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
# Default: iptables -I INPUT 1 -i eth0 -s <ip> -j DROP
|
# Default: iptables -I INPUT 1 -s <ip> -j DROP
|
||||||
#
|
#
|
||||||
fwban = iptables -I fail2ban-SSH 1 -i eth0 -s <ip> -j DROP
|
fwban = iptables -I fail2ban-ssh 1 -s <ip> -j DROP
|
||||||
|
|
||||||
# Option: fwunbanrule
|
# Option: fwunbanrule
|
||||||
# Notes.: command executed when unbanning an IP. Take care that the
|
# Notes.: command executed when unbanning an IP. Take care that the
|
||||||
|
@ -250,9 +250,9 @@ fwban = iptables -I fail2ban-SSH 1 -i eth0 -s <ip> -j DROP
|
||||||
# <bantime> unix timestamp of the ban time
|
# <bantime> unix timestamp of the ban time
|
||||||
# <unbantime> unix timestamp of the unban time
|
# <unbantime> unix timestamp of the unban time
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
# Default: iptables -D INPUT -i eth0 -s <ip> -j DROP
|
# Default: iptables -D INPUT -s <ip> -j DROP
|
||||||
#
|
#
|
||||||
fwunban = iptables -D fail2ban-SSH -i eth0 -s <ip> -j DROP
|
fwunban = iptables -D fail2ban-ssh -s <ip> -j DROP
|
||||||
|
|
||||||
# Option: timeregex
|
# Option: timeregex
|
||||||
# Notes.: regex to match timestamp in SSH logfile.
|
# Notes.: regex to match timestamp in SSH logfile.
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
#
|
#
|
||||||
# Author: Sireyessire, Cyril Jaquier
|
# Author: Sireyessire, Cyril Jaquier
|
||||||
#
|
#
|
||||||
# $Revision: 1.1.2.1 $
|
# $Revision: 1.1.2.2 $
|
||||||
|
|
||||||
opts="start stop restart showlog"
|
opts="start stop restart showlog"
|
||||||
|
|
||||||
|
@ -31,13 +31,13 @@ depend() {
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
ebegin "Starting fail2ban"
|
ebegin "Starting fail2ban"
|
||||||
${FAIL2BAN} -b ${FAIL2BAN_OPTS}
|
${FAIL2BAN} -b ${FAIL2BAN_OPTS} > /dev/null
|
||||||
eend $? "Failed to start fail2ban"
|
eend $? "Failed to start fail2ban"
|
||||||
}
|
}
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
ebegin "Stopping fail2ban"
|
ebegin "Stopping fail2ban"
|
||||||
${FAIL2BAN} -k
|
${FAIL2BAN} -k > /dev/null
|
||||||
eend $? "Failed to stop fail2ban"
|
eend $? "Failed to stop fail2ban"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#
|
#
|
||||||
# Author: Andrey G. Grozin
|
# Author: Andrey G. Grozin
|
||||||
#
|
#
|
||||||
# $Revision: 1.1.2.1 $
|
# $Revision: 1.1.2.2 $
|
||||||
|
|
||||||
# Source function library.
|
# Source function library.
|
||||||
. /etc/init.d/functions
|
. /etc/init.d/functions
|
||||||
|
@ -28,7 +28,7 @@ RETVAL=0
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
echo -n $"Starting fail2ban: "
|
echo -n $"Starting fail2ban: "
|
||||||
"${FAIL2BAN}" -b
|
"${FAIL2BAN}" -b > /dev/null
|
||||||
RETVAL=$?
|
RETVAL=$?
|
||||||
echo
|
echo
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ start() {
|
||||||
stop() {
|
stop() {
|
||||||
if [ -f "${PIDFILE}" ]; then
|
if [ -f "${PIDFILE}" ]; then
|
||||||
echo -n $"Stopping fail2ban: "
|
echo -n $"Stopping fail2ban: "
|
||||||
"${FAIL2BAN}" -k
|
"${FAIL2BAN}" -k > /dev/null
|
||||||
echo
|
echo
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,20 +16,20 @@
|
||||||
|
|
||||||
# Author: Cyril Jaquier
|
# Author: Cyril Jaquier
|
||||||
#
|
#
|
||||||
# $Revision: 1.5.2.3 $
|
# $Revision: 1.5.2.5 $
|
||||||
|
|
||||||
__author__ = "Cyril Jaquier"
|
__author__ = "Cyril Jaquier"
|
||||||
__version__ = "$Revision: 1.5.2.3 $"
|
__version__ = "$Revision: 1.5.2.5 $"
|
||||||
__date__ = "$Date: 2005/07/12 13:07:35 $"
|
__date__ = "$Date: 2005/08/01 16:31:13 $"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import log4py
|
import logging
|
||||||
|
|
||||||
from ConfigParser import *
|
from ConfigParser import *
|
||||||
|
|
||||||
# Gets the instance of log4py.
|
# Gets the instance of the logger.
|
||||||
logSys = log4py.Logger().get_instance()
|
logSys = logging.getLogger("fail2ban")
|
||||||
|
|
||||||
class ConfigReader:
|
class ConfigReader:
|
||||||
""" This class allow the handling of the configuration options.
|
""" This class allow the handling of the configuration options.
|
||||||
|
@ -77,5 +77,9 @@ class ConfigReader:
|
||||||
except NoOptionError:
|
except NoOptionError:
|
||||||
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]
|
||||||
|
except ValueError:
|
||||||
|
logSys.warn("Wrong value for '" + option[1] + "' in '" + sec +
|
||||||
|
"'. Using default one: '" + `option[2]` + "'")
|
||||||
|
values[option[1]] = option[2]
|
||||||
return values
|
return values
|
||||||
|
|
|
@ -10,9 +10,6 @@ Currently the main difference with upstream: python libraries are
|
||||||
placed under /usr/share/fail2ban insteadh of /usr/lib/fail2ban to
|
placed under /usr/share/fail2ban insteadh of /usr/lib/fail2ban to
|
||||||
comply with policy regarding architecture independent resources.
|
comply with policy regarding architecture independent resources.
|
||||||
|
|
||||||
Module log4py installed along into 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
|
See the file TODO.Debian for more details, as well as the Debian Bug
|
||||||
Tracking system.
|
Tracking system.
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,11 @@
|
||||||
|
fail2ban (0.5.2-1) unstable; urgency=low
|
||||||
|
|
||||||
|
* New upstream release
|
||||||
|
* No log4py any more
|
||||||
|
* removed -i eth0 from config
|
||||||
|
|
||||||
|
-- Yaroslav Halchenko <debian@onerussian.com> Sat, 6 Aug 2005 09:21:07 -1000
|
||||||
|
|
||||||
fail2ban (0.5.1-1) unstable; urgency=low
|
fail2ban (0.5.1-1) unstable; urgency=low
|
||||||
|
|
||||||
* New upstream release
|
* New upstream release
|
||||||
|
|
|
@ -25,26 +25,3 @@ Boston, MA 02111-1307, USA.
|
||||||
|
|
||||||
See /usr/share/common-licenses/GPL for the full license.
|
See /usr/share/common-licenses/GPL for the full license.
|
||||||
|
|
||||||
log4py.py module included in this package is distributed in complience
|
|
||||||
with MIT License:
|
|
||||||
|
|
||||||
Copyright (c) 2001 Martin Preishuber
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
|
@ -87,7 +87,7 @@ binary-arch: build install copy-inits
|
||||||
dh_installinit
|
dh_installinit
|
||||||
# dh_installcron
|
# dh_installcron
|
||||||
# dh_installinfo
|
# dh_installinfo
|
||||||
dh_installman fail2ban.1x
|
dh_installman fail2ban.1x man/fail2ban.conf.5
|
||||||
dh_link
|
dh_link
|
||||||
dh_strip
|
dh_strip
|
||||||
dh_compress
|
dh_compress
|
||||||
|
|
50
fail2ban
50
fail2ban
|
@ -18,31 +18,43 @@
|
||||||
|
|
||||||
# Author: Cyril Jaquier
|
# Author: Cyril Jaquier
|
||||||
#
|
#
|
||||||
# $Revision: 1.4.2.3 $
|
# $Revision: 1.4.2.5 $
|
||||||
|
|
||||||
__author__ = "Cyril Jaquier"
|
__author__ = "Cyril Jaquier"
|
||||||
__version__ = "$Revision: 1.4.2.3 $"
|
__version__ = "$Revision: 1.4.2.5 $"
|
||||||
__date__ = "$Date: 2005/07/15 14:11:21 $"
|
__date__ = "$Date: 2005/08/04 20:51:14 $"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
from sys import exit, path
|
import sys, traceback, logging
|
||||||
|
|
||||||
#yoh: We do need to load this path first if we ship log4py with fail2ban
|
# Appends our own modules path.
|
||||||
# Appends our own modules path. Added before log4py import
|
sys.path.append("/usr/lib/fail2ban")
|
||||||
# because log4py could be distributed with Fail2Ban.
|
|
||||||
path.append('/usr/share/fail2ban')
|
|
||||||
|
|
||||||
# Checks for required libs
|
# Now we can import our modules.
|
||||||
# Checks if log4py is present.
|
|
||||||
try:
|
|
||||||
import log4py
|
|
||||||
except:
|
|
||||||
print "log4py is needed (see README)"
|
|
||||||
exit(-1)
|
|
||||||
|
|
||||||
# Now we can import our module
|
|
||||||
import fail2ban
|
import fail2ban
|
||||||
|
from utils.pidlock import PIDLock
|
||||||
|
|
||||||
# Start the application
|
# Get the instance of the logger.
|
||||||
fail2ban.main()
|
logSys = logging.getLogger("fail2ban")
|
||||||
|
|
||||||
|
# Get PID lock file instance
|
||||||
|
pidLock = PIDLock()
|
||||||
|
|
||||||
|
# Start the application. Handle all the unhandled exceptions
|
||||||
|
try:
|
||||||
|
fail2ban.main()
|
||||||
|
except SystemExit:
|
||||||
|
# We called sys.exit(). Nothing wrong so just pass
|
||||||
|
pass
|
||||||
|
except Exception, e:
|
||||||
|
# Print the exception data
|
||||||
|
(type, value, tb) = sys.exc_info()
|
||||||
|
tbStack = traceback.extract_tb(tb)
|
||||||
|
logSys.error("Fail2Ban got an unhandled exception and died.")
|
||||||
|
logSys.error("Type: " + `type.__name__` + "\n" +
|
||||||
|
"Value: " + `e.args` + "\n" +
|
||||||
|
"TB: " + `tbStack`)
|
||||||
|
# Remove the PID lock file. Should close #1239562
|
||||||
|
pidLock.remove()
|
||||||
|
logging.shutdown()
|
||||||
|
|
176
fail2ban.py
176
fail2ban.py
|
@ -16,15 +16,15 @@
|
||||||
|
|
||||||
# Author: Cyril Jaquier
|
# Author: Cyril Jaquier
|
||||||
#
|
#
|
||||||
# $Revision: 1.20.2.8 $
|
# $Revision: 1.20.2.13 $
|
||||||
|
|
||||||
__author__ = "Cyril Jaquier"
|
__author__ = "Cyril Jaquier"
|
||||||
__version__ = "$Revision: 1.20.2.8 $"
|
__version__ = "$Revision: 1.20.2.13 $"
|
||||||
__date__ = "$Date: 2005/07/22 21:13:19 $"
|
__date__ = "$Date: 2005/08/06 18:44:06 $"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import time, sys, getopt, os, string, signal, log4py
|
import time, sys, getopt, os, string, signal, logging, logging.handlers
|
||||||
from ConfigParser import *
|
from ConfigParser import *
|
||||||
|
|
||||||
from version import version
|
from version import version
|
||||||
|
@ -32,11 +32,15 @@ from firewall.firewall import Firewall
|
||||||
from logreader.logreader import LogReader
|
from logreader.logreader import LogReader
|
||||||
from confreader.configreader import ConfigReader
|
from confreader.configreader import ConfigReader
|
||||||
from utils.mail import Mail
|
from utils.mail import Mail
|
||||||
|
from utils.pidlock import PIDLock
|
||||||
from utils.dns import *
|
from utils.dns import *
|
||||||
from utils.process import *
|
from utils.process import *
|
||||||
|
|
||||||
# Gets the instance of log4py.
|
# Get the instance of the logger.
|
||||||
logSys = log4py.Logger().get_instance()
|
logSys = logging.getLogger("fail2ban")
|
||||||
|
|
||||||
|
# Get PID lock file instance
|
||||||
|
pidLock = PIDLock()
|
||||||
|
|
||||||
# Global variables
|
# Global variables
|
||||||
logFwList = list()
|
logFwList = list()
|
||||||
|
@ -50,13 +54,13 @@ def dispUsage():
|
||||||
print "Fail2Ban v"+version+" reads log file that contains password failure report"
|
print "Fail2Ban v"+version+" reads log file that contains password failure report"
|
||||||
print "and bans the corresponding IP addresses using firewall rules."
|
print "and bans the corresponding IP addresses using firewall rules."
|
||||||
print
|
print
|
||||||
print " -b start fail2ban in background"
|
print " -b start in background"
|
||||||
print " -d start fail2ban in debug mode"
|
print " -d start in debug mode"
|
||||||
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 instance"
|
||||||
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"
|
||||||
|
@ -101,8 +105,9 @@ def killApp():
|
||||||
# Execute global start command
|
# Execute global start command
|
||||||
executeCmd(conf["cmdend"], conf["debug"])
|
executeCmd(conf["cmdend"], conf["debug"])
|
||||||
# Remove the PID lock
|
# Remove the PID lock
|
||||||
removePID(conf["pidlock"])
|
pidLock.remove()
|
||||||
logSys.info("Exiting...")
|
logSys.info("Exiting...")
|
||||||
|
logging.shutdown()
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
def getCmdLineOptions(optList):
|
def getCmdLineOptions(optList):
|
||||||
|
@ -128,23 +133,23 @@ def getCmdLineOptions(optList):
|
||||||
if opt[0] == "-i":
|
if opt[0] == "-i":
|
||||||
conf["ignoreip"] = opt[1]
|
conf["ignoreip"] = opt[1]
|
||||||
if opt[0] == "-r":
|
if opt[0] == "-r":
|
||||||
conf["retrymax"] = int(opt[1])
|
conf["maxretry"] = int(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":
|
||||||
pid = checkForPID(conf["pidlock"])
|
conf["kill"] = True
|
||||||
if pid:
|
|
||||||
killPID(int(pid))
|
|
||||||
logSys.warn("Killed Fail2Ban with PID "+pid)
|
|
||||||
sys.exit(0)
|
|
||||||
else:
|
|
||||||
logSys.error("No running Fail2Ban found")
|
|
||||||
sys.exit(-1)
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
""" Fail2Ban main function
|
""" Fail2Ban main function
|
||||||
"""
|
"""
|
||||||
logSys.set_formatstring("%T %L %M")
|
|
||||||
|
# Add the default logging handler
|
||||||
|
stdout = logging.StreamHandler(sys.stdout)
|
||||||
|
logSys.addHandler(stdout)
|
||||||
|
|
||||||
|
# Default formatter
|
||||||
|
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
|
||||||
|
stdout.setFormatter(formatter)
|
||||||
|
|
||||||
conf["verbose"] = 0
|
conf["verbose"] = 0
|
||||||
conf["conffile"] = "/etc/fail2ban.conf"
|
conf["conffile"] = "/etc/fail2ban.conf"
|
||||||
|
@ -169,7 +174,7 @@ def main():
|
||||||
|
|
||||||
# Options
|
# Options
|
||||||
optionValues = (["bool", "background", False],
|
optionValues = (["bool", "background", False],
|
||||||
["str", "logtargets", "STDOUT /var/log/fail2ban.log"],
|
["str", "logtargets", "/var/log/fail2ban.log"],
|
||||||
["bool", "debug", False],
|
["bool", "debug", False],
|
||||||
["str", "pidlock", "/var/run/fail2ban.pid"],
|
["str", "pidlock", "/var/run/fail2ban.pid"],
|
||||||
["int", "maxretry", 3],
|
["int", "maxretry", 3],
|
||||||
|
@ -185,46 +190,22 @@ def main():
|
||||||
# Gets command line options
|
# Gets command line options
|
||||||
getCmdLineOptions(optList)
|
getCmdLineOptions(optList)
|
||||||
|
|
||||||
# Process some options
|
# PID lock
|
||||||
# Log targets
|
pidLock.setPath(conf["pidlock"])
|
||||||
# Bug fix for #1234699
|
|
||||||
os.umask(0077)
|
# Now we can kill properly a running instance if needed
|
||||||
# Remove all the targets before setting our own
|
|
||||||
logSys.remove_all_targets()
|
|
||||||
for target in conf["logtargets"].split():
|
|
||||||
if target == "STDOUT":
|
|
||||||
logSys.add_target(log4py.TARGET_SYS_STDOUT)
|
|
||||||
elif target == "STDERR":
|
|
||||||
logSys.add_target(log4py.TARGET_SYS_STDERR)
|
|
||||||
elif target == "SYSLOG":
|
|
||||||
logSys.add_target(log4py.TARGET_SYSLOG)
|
|
||||||
else:
|
|
||||||
# Target should be a file
|
|
||||||
try:
|
try:
|
||||||
open(target, "a")
|
conf["kill"]
|
||||||
logSys.add_target(target)
|
pid = pidLock.exists()
|
||||||
except IOError:
|
if pid:
|
||||||
logSys.error("Unable to log to " + target)
|
killPID(int(pid))
|
||||||
|
logSys.warn("Killed Fail2Ban with PID "+pid)
|
||||||
# Check if at least one target exists
|
sys.exit(0)
|
||||||
if len(logSys.get_targets()) == 0:
|
else:
|
||||||
logSys.add_target(log4py.TARGET_SYS_STDOUT)
|
logSys.error("No running Fail2Ban found")
|
||||||
logSys.error("No valid logging target found. Logging to STDOUT")
|
sys.exit(-1)
|
||||||
|
except KeyError:
|
||||||
# Verbose level
|
pass
|
||||||
if conf["verbose"]:
|
|
||||||
logSys.warn("Verbose level is "+`conf["verbose"]`)
|
|
||||||
if conf["verbose"] == 1:
|
|
||||||
logSys.set_loglevel(log4py.LOGLEVEL_VERBOSE)
|
|
||||||
elif conf["verbose"] > 1:
|
|
||||||
logSys.set_loglevel(log4py.LOGLEVEL_DEBUG)
|
|
||||||
|
|
||||||
# Set debug log level
|
|
||||||
if conf["debug"]:
|
|
||||||
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
|
# Start Fail2Ban in daemon mode
|
||||||
if conf["background"]:
|
if conf["background"]:
|
||||||
|
@ -234,9 +215,54 @@ def main():
|
||||||
logSys.error("Unable to start daemon")
|
logSys.error("Unable to start daemon")
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
|
# Verbose level
|
||||||
|
if conf["verbose"]:
|
||||||
|
logSys.warn("Verbose level is "+`conf["verbose"]`)
|
||||||
|
if conf["verbose"] == 1:
|
||||||
|
logSys.setLevel(logging.INFO)
|
||||||
|
elif conf["verbose"] > 1:
|
||||||
|
logSys.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
# Set debug log level
|
||||||
|
if conf["debug"]:
|
||||||
|
logSys.setLevel(logging.DEBUG)
|
||||||
|
formatter = logging.Formatter("%(asctime)s %(levelname)s " +
|
||||||
|
"[%(filename)s (%(lineno)d)] " +
|
||||||
|
"%(message)s")
|
||||||
|
stdout.setFormatter(formatter)
|
||||||
|
logSys.warn("DEBUG MODE: FIREWALL COMMANDS ARE _NOT_ EXECUTED BUT " +
|
||||||
|
"ONLY DISPLAYED IN THE LOG MESSAGES")
|
||||||
|
|
||||||
|
# Process some options
|
||||||
|
# Log targets
|
||||||
|
# Bug fix for #1234699
|
||||||
|
os.umask(0077)
|
||||||
|
for target in conf["logtargets"].split():
|
||||||
|
if target == "STDERR":
|
||||||
|
hdlr = logging.StreamHandler(sys.stderr)
|
||||||
|
elif target == "SYSLOG":
|
||||||
|
hdlr = logging.handlers.SysLogHandler()
|
||||||
|
else:
|
||||||
|
# Target should be a file
|
||||||
|
try:
|
||||||
|
open(target, "a")
|
||||||
|
hdlr = logging.FileHandler(target)
|
||||||
|
except IOError:
|
||||||
|
logSys.error("Unable to log to " + target)
|
||||||
|
continue
|
||||||
|
# Set formatter and add handler to logger
|
||||||
|
hdlr.setFormatter(formatter)
|
||||||
|
logSys.addHandler(hdlr)
|
||||||
|
|
||||||
# Ignores IP list
|
# Ignores IP list
|
||||||
ignoreIPList = conf["ignoreip"].split(' ')
|
ignoreIPList = conf["ignoreip"].split(' ')
|
||||||
|
|
||||||
|
# maxretry option
|
||||||
|
maxRetry = conf["maxretry"]
|
||||||
|
|
||||||
|
# bantime option
|
||||||
|
banTime = conf["bantime"]
|
||||||
|
|
||||||
# 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.
|
||||||
if not checkForRoot():
|
if not checkForRoot():
|
||||||
|
@ -245,16 +271,16 @@ def main():
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
# Checks that no instance of Fail2Ban is currently running.
|
# Checks that no instance of Fail2Ban is currently running.
|
||||||
pid = checkForPID(conf["pidlock"])
|
pid = pidLock.exists()
|
||||||
if pid:
|
if pid:
|
||||||
logSys.error("Fail2Ban already running with PID "+pid)
|
logSys.error("Fail2Ban already running with PID "+pid)
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
else:
|
else:
|
||||||
createPID(conf["pidlock"])
|
pidLock.create()
|
||||||
|
|
||||||
logSys.debug("ConfFile is "+conf["conffile"])
|
logSys.debug("ConfFile is " + conf["conffile"])
|
||||||
logSys.debug("BanTime is "+`conf["bantime"]`)
|
logSys.debug("BanTime is " + `conf["bantime"]`)
|
||||||
logSys.debug("retryAllowed is "+`conf["maxretry"]`)
|
logSys.debug("retryAllowed is " + `conf["maxretry"]`)
|
||||||
|
|
||||||
# Options
|
# Options
|
||||||
optionValues = (["bool", "enabled", False],
|
optionValues = (["bool", "enabled", False],
|
||||||
|
@ -277,8 +303,10 @@ def main():
|
||||||
logSys.debug("to: " + mailConf["to"] + " from: " + mailConf["from"])
|
logSys.debug("to: " + mailConf["to"] + " from: " + mailConf["from"])
|
||||||
|
|
||||||
# Options
|
# Options
|
||||||
optionValues = (["bool", "enabled", True],
|
optionValues = (["bool", "enabled", False],
|
||||||
["str", "logfile", "/dev/null"],
|
["str", "logfile", "/dev/null"],
|
||||||
|
["int", "maxretry", None],
|
||||||
|
["int", "bantime", None],
|
||||||
["str", "timeregex", ""],
|
["str", "timeregex", ""],
|
||||||
["str", "timepattern", ""],
|
["str", "timepattern", ""],
|
||||||
["str", "failregex", ""],
|
["str", "failregex", ""],
|
||||||
|
@ -291,11 +319,19 @@ def main():
|
||||||
for t in confReader.getSections():
|
for t in confReader.getSections():
|
||||||
l = confReader.getLogOptions(t, optionValues)
|
l = confReader.getLogOptions(t, optionValues)
|
||||||
if l["enabled"]:
|
if l["enabled"]:
|
||||||
|
# Override maxretry option
|
||||||
|
if not l["maxretry"] == None:
|
||||||
|
maxRetry = l["maxretry"]
|
||||||
|
|
||||||
|
# Override bantime option
|
||||||
|
if not l["bantime"] == None:
|
||||||
|
banTime = l["bantime"]
|
||||||
|
|
||||||
# Creates a logreader object
|
# Creates a logreader object
|
||||||
lObj = LogReader(l["logfile"], l["timeregex"], l["timepattern"],
|
lObj = LogReader(l["logfile"], l["timeregex"], l["timepattern"],
|
||||||
l["failregex"], conf["bantime"])
|
l["failregex"], maxRetry, banTime)
|
||||||
# Creates a firewall object
|
# Creates a firewall object
|
||||||
fObj = Firewall(l["fwban"], l["fwunban"], conf["bantime"])
|
fObj = Firewall(l["fwban"], l["fwunban"], banTime)
|
||||||
# Links them into a list. I'm not really happy
|
# Links them into a list. I'm not really happy
|
||||||
# with this :/
|
# with this :/
|
||||||
logFwList.append([t, lObj, fObj, dict(), l])
|
logFwList.append([t, lObj, fObj, dict(), l])
|
||||||
|
@ -311,7 +347,7 @@ def main():
|
||||||
for element in logFwList:
|
for element in logFwList:
|
||||||
element[1].addIgnoreIP(ip)
|
element[1].addIgnoreIP(ip)
|
||||||
|
|
||||||
logSys.info("Fail2Ban v"+version+" is running")
|
logSys.info("Fail2Ban v" + version + " is running")
|
||||||
# Execute global start command
|
# Execute global start command
|
||||||
executeCmd(conf["cmdstart"], conf["debug"])
|
executeCmd(conf["cmdstart"], conf["debug"])
|
||||||
# Execute start command of each section
|
# Execute start command of each section
|
||||||
|
@ -363,7 +399,7 @@ def main():
|
||||||
failTime = fails[attempt][1]
|
failTime = fails[attempt][1]
|
||||||
if failTime < unixTime - findTime:
|
if failTime < unixTime - findTime:
|
||||||
del element[3][attempt]
|
del element[3][attempt]
|
||||||
elif fails[attempt][0] >= conf["maxretry"]:
|
elif fails[attempt][0] >= element[1].getMaxRetry():
|
||||||
aInfo = {"ip": attempt,
|
aInfo = {"ip": attempt,
|
||||||
"failures": element[3][attempt][0],
|
"failures": element[3][attempt][0],
|
||||||
"failtime": failTime}
|
"failtime": failTime}
|
||||||
|
|
|
@ -16,21 +16,21 @@
|
||||||
|
|
||||||
# Author: Cyril Jaquier
|
# Author: Cyril Jaquier
|
||||||
#
|
#
|
||||||
# $Revision: 1.8.2.5 $
|
# $Revision: 1.8.2.6 $
|
||||||
|
|
||||||
__author__ = "Cyril Jaquier"
|
__author__ = "Cyril Jaquier"
|
||||||
__version__ = "$Revision: 1.8.2.5 $"
|
__version__ = "$Revision: 1.8.2.6 $"
|
||||||
__date__ = "$Date: 2005/07/15 14:07:08 $"
|
__date__ = "$Date: 2005/08/01 16:31:42 $"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import time, os, log4py, re
|
import time, os, logging, re
|
||||||
|
|
||||||
from utils.process import executeCmd
|
from utils.process import executeCmd
|
||||||
from utils.strings import replaceTag
|
from utils.strings import replaceTag
|
||||||
|
|
||||||
# Gets the instance of log4py.
|
# Gets the instance of the logger.
|
||||||
logSys = log4py.Logger().get_instance()
|
logSys = logging.getLogger("fail2ban")
|
||||||
|
|
||||||
class Firewall:
|
class Firewall:
|
||||||
""" Manages the ban list and executes the command that ban
|
""" Manages the ban list and executes the command that ban
|
||||||
|
|
593
log4py.py
593
log4py.py
|
@ -1,593 +0,0 @@
|
||||||
"""
|
|
||||||
|
|
||||||
Python logging module - Version 1.3
|
|
||||||
|
|
||||||
Loglevels:
|
|
||||||
|
|
||||||
LOGLEVEL_NONE, LOGLEVEL_ERROR, LOGLEVEL_NORMAL, LOGLEVEL_VERBOSE, LOGLEVEL_DEBUG
|
|
||||||
|
|
||||||
Format-Parameters:
|
|
||||||
|
|
||||||
%C -- The name of the current class.
|
|
||||||
%D -- Program duration since program start.
|
|
||||||
%d -- Program duration for the last step (last output).
|
|
||||||
%F -- The name of the current function.
|
|
||||||
%f -- Current filename
|
|
||||||
%L -- Log type (Error, Warning, Debug or Info)
|
|
||||||
%M -- The actual message.
|
|
||||||
%N -- The current line number.
|
|
||||||
%T -- Current time (human readable).
|
|
||||||
%t -- Current time (machine readable)
|
|
||||||
%U -- Current fully qualified module/file.
|
|
||||||
%u -- Current module/file.
|
|
||||||
%x -- NDC (nested diagnostic contexts).
|
|
||||||
|
|
||||||
Pre-defined Formats:
|
|
||||||
|
|
||||||
FMT_SHORT -- %M
|
|
||||||
FMT_MEDIUM -- [ %C.%F ] %D: %M
|
|
||||||
FMT_LONG -- %T %L %C [%F] %x%M
|
|
||||||
FMT_DEBUG -- %T [%D (%d)] %L %C [%F (%N)] %x%M
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Logging levels
|
|
||||||
LOGLEVEL_NONE = 1 << 0
|
|
||||||
LOGLEVEL_ERROR = 1 << 1
|
|
||||||
LOGLEVEL_NORMAL = 1 << 2
|
|
||||||
LOGLEVEL_VERBOSE = 1 << 3
|
|
||||||
LOGLEVEL_DEBUG = 1 << 4
|
|
||||||
|
|
||||||
# Pre-defined format strings
|
|
||||||
FMT_SHORT = "%M"
|
|
||||||
FMT_MEDIUM = "[ %C.%F ] %D: %M"
|
|
||||||
FMT_LONG = "%T %L %C [%F] %x%M"
|
|
||||||
FMT_DEBUG = "%T [%D (%d)] %L %C [%F (%N)] %x%M"
|
|
||||||
|
|
||||||
# Special logging targets
|
|
||||||
TARGET_MYSQL = "MySQL"
|
|
||||||
TARGET_POSTGRES = "Postgres"
|
|
||||||
TARGET_SYSLOG = "Syslog"
|
|
||||||
TARGET_SYS_STDOUT = "sys.stdout"
|
|
||||||
TARGET_SYS_STDERR = "sys.stderr"
|
|
||||||
TARGET_SYS_STDOUT_ALIAS = "stdout"
|
|
||||||
TARGET_SYS_STDERR_ALIAS = "stderr"
|
|
||||||
|
|
||||||
SPECIAL_TARGETS = [ TARGET_MYSQL, TARGET_POSTGRES, TARGET_SYSLOG, TARGET_SYS_STDOUT, TARGET_SYS_STDERR, TARGET_SYS_STDOUT_ALIAS, TARGET_SYS_STDERR_ALIAS ]
|
|
||||||
|
|
||||||
# Configuration files
|
|
||||||
CONFIGURATION_FILES = {}
|
|
||||||
CONFIGURATION_FILES[1] = "log4py.conf" # local directory
|
|
||||||
CONFIGURATION_FILES[2] = "$HOME/.log4py.conf" # hidden file in the home directory
|
|
||||||
CONFIGURATION_FILES[3] = "/etc/log4py.conf" # system wide file
|
|
||||||
|
|
||||||
# Constants for the FileAppender
|
|
||||||
ROTATE_NONE = 0
|
|
||||||
ROTATE_DAILY = 1
|
|
||||||
ROTATE_WEEKLY = 2
|
|
||||||
ROTATE_MONTHLY = 3
|
|
||||||
|
|
||||||
# The following constants are of internal interest only
|
|
||||||
|
|
||||||
# Message constants (used for ansi colors and for logtype %L)
|
|
||||||
MSG_DEBUG = 1 << 0
|
|
||||||
MSG_WARN = 1 << 1
|
|
||||||
MSG_ERROR = 1 << 2
|
|
||||||
MSG_INFO = 1 << 3
|
|
||||||
|
|
||||||
# Boolean constants
|
|
||||||
TRUE = "TRUE"
|
|
||||||
FALSE = "FALSE"
|
|
||||||
|
|
||||||
# Color constants
|
|
||||||
BLACK = 30
|
|
||||||
RED = 31
|
|
||||||
GREEN = 32
|
|
||||||
YELLOW = 33
|
|
||||||
BLUE = 34
|
|
||||||
PURPLE = 35
|
|
||||||
AQUA = 36
|
|
||||||
WHITE = 37
|
|
||||||
|
|
||||||
LOG_MSG = { MSG_DEBUG: "DEBUG", MSG_WARN: "WARNING", MSG_ERROR: "ERROR", MSG_INFO: "INFO"}
|
|
||||||
LOG_COLORS = { MSG_DEBUG: [WHITE, BLACK, FALSE], MSG_WARN: [WHITE, BLACK, FALSE], MSG_ERROR: [WHITE, BLACK, TRUE], MSG_INFO: [WHITE, BLACK, FALSE]}
|
|
||||||
LOG_LEVELS = { "DEBUG": LOGLEVEL_DEBUG, "VERBOSE": LOGLEVEL_VERBOSE, "NORMAL": LOGLEVEL_NORMAL, "NONE": LOGLEVEL_NONE, "ERROR": LOGLEVEL_ERROR }
|
|
||||||
|
|
||||||
SECTION_DEFAULT = "Default"
|
|
||||||
|
|
||||||
from time import time, strftime, localtime
|
|
||||||
from types import StringType, ClassType, InstanceType, FileType, TupleType
|
|
||||||
from string import zfill, atoi, lower, upper, join, replace, split, strip
|
|
||||||
from re import sub
|
|
||||||
from ConfigParser import ConfigParser, NoOptionError
|
|
||||||
from os import stat, rename
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import traceback
|
|
||||||
import os
|
|
||||||
import copy
|
|
||||||
import socket
|
|
||||||
import locale
|
|
||||||
if (os.name == "posix"):
|
|
||||||
import syslog
|
|
||||||
|
|
||||||
try:
|
|
||||||
import MySQLdb
|
|
||||||
mysql_available = TRUE
|
|
||||||
except:
|
|
||||||
mysql_available = FALSE
|
|
||||||
|
|
||||||
def get_homedirectory():
|
|
||||||
if (sys.platform == "win32"):
|
|
||||||
if (os.environ.has_key("USERPROFILE")):
|
|
||||||
return os.environ["USERPROFILE"]
|
|
||||||
else:
|
|
||||||
return "C:\\"
|
|
||||||
else:
|
|
||||||
if (os.environ.has_key("HOME")):
|
|
||||||
return os.environ["HOME"]
|
|
||||||
else:
|
|
||||||
# No home directory set
|
|
||||||
return ""
|
|
||||||
|
|
||||||
# This is the main class for the logging module
|
|
||||||
|
|
||||||
class Logger:
|
|
||||||
|
|
||||||
cache = {}
|
|
||||||
instance = None
|
|
||||||
configfiles = []
|
|
||||||
hostname = socket.gethostname()
|
|
||||||
|
|
||||||
def __init__(self, useconfigfiles = TRUE, customconfigfiles = None):
|
|
||||||
""" **(private)** Class initalization & customization. """
|
|
||||||
if (customconfigfiles):
|
|
||||||
if (type(customconfigfiles) == StringType):
|
|
||||||
customconfigfiles = [customconfigfiles]
|
|
||||||
Logger.configfiles = customconfigfiles
|
|
||||||
|
|
||||||
if (not Logger.instance):
|
|
||||||
self.__Logger_setdefaults()
|
|
||||||
if (useconfigfiles == TRUE):
|
|
||||||
self.__Logger_appendconfigfiles(Logger.configfiles)
|
|
||||||
# read the default options
|
|
||||||
self.__Logger_parse_options()
|
|
||||||
|
|
||||||
self.__Logger_timeinit = time()
|
|
||||||
self.__Logger_timelaststep = self.__Logger_timeinit
|
|
||||||
|
|
||||||
Logger.instance = self
|
|
||||||
|
|
||||||
if (useconfigfiles == TRUE):
|
|
||||||
# read and pre-cache settings for named classids
|
|
||||||
self.__Logger_cache_options()
|
|
||||||
|
|
||||||
def get_root(self):
|
|
||||||
""" Provides a way to change the base logger object's properties. """
|
|
||||||
return Logger.instance
|
|
||||||
|
|
||||||
def get_instance(self, classid = "Main", use_cache = TRUE):
|
|
||||||
""" Either get the cached logger instance or create a new one
|
|
||||||
|
|
||||||
Note that this is safe, even if you have your target set to sys.stdout
|
|
||||||
or sys.stderr
|
|
||||||
"""
|
|
||||||
|
|
||||||
cache = Logger.cache
|
|
||||||
|
|
||||||
if (type(classid) == ClassType):
|
|
||||||
classid = classid.__name__
|
|
||||||
elif (type(classid) == InstanceType):
|
|
||||||
classid = classid.__class__.__name__
|
|
||||||
|
|
||||||
# classid has to be lowercase, because the ConfigParser returns sections lowercase
|
|
||||||
classid = lower(classid)
|
|
||||||
|
|
||||||
if ((cache.has_key(classid)) and (use_cache == TRUE)):
|
|
||||||
cat = Logger.cache[classid]
|
|
||||||
else:
|
|
||||||
instance = Logger.instance
|
|
||||||
|
|
||||||
# test for targets which won't deep copy
|
|
||||||
targets = instance.__Logger_targets
|
|
||||||
deepcopyable = TRUE
|
|
||||||
for i in range(len(targets)):
|
|
||||||
if (type(targets[i]) == FileType):
|
|
||||||
deepcopyable = FALSE
|
|
||||||
if (deepcopyable == FALSE):
|
|
||||||
# swap the non-copyable target out for a moment
|
|
||||||
del instance.__Logger_targets
|
|
||||||
cat = copy.deepcopy(instance)
|
|
||||||
instance.__Logger_targets = targets
|
|
||||||
cat.__Logger_targets = targets
|
|
||||||
else:
|
|
||||||
cat = copy.deepcopy(instance)
|
|
||||||
|
|
||||||
cat.__Logger_classname = classid
|
|
||||||
# new categories have their own private Nested Diagnostic Contexts
|
|
||||||
self.__Logger_ndc = []
|
|
||||||
self.__Logger_classid = classid
|
|
||||||
|
|
||||||
cat.debug("Class %s instantiated" % classid)
|
|
||||||
if (use_cache == TRUE):
|
|
||||||
cache[classid] = cat
|
|
||||||
|
|
||||||
return cat
|
|
||||||
|
|
||||||
# Log-target handling (add, remove, set, remove_all)
|
|
||||||
|
|
||||||
def add_target(self, target, *args):
|
|
||||||
""" Add a target to the logger targets. """
|
|
||||||
if (not target in self.__Logger_targets):
|
|
||||||
if (target == TARGET_MYSQL):
|
|
||||||
if (mysql_available == TRUE):
|
|
||||||
# Required parameters: dbhost, dbname, dbuser, dbpass, dbtable
|
|
||||||
try:
|
|
||||||
self.__Logger_mysql_connection = MySQLdb.connect(host=args[0], db=args[1], user=args[2], passwd=args[3])
|
|
||||||
self.__Logger_mysql_cursor = self.__Logger_mysql_connection.cursor()
|
|
||||||
self.__Logger_mysql_tablename = args[4]
|
|
||||||
self.__Logger_targets.append(target)
|
|
||||||
except MySQLdb.OperationalError, detail:
|
|
||||||
self.error("MySQL connection failed: %s" % detail)
|
|
||||||
else:
|
|
||||||
self.error("MySQL target not added - Python-mysql not available")
|
|
||||||
else:
|
|
||||||
if (type(target) == StringType):
|
|
||||||
if (target not in SPECIAL_TARGETS):
|
|
||||||
# This is a filename
|
|
||||||
target = FileAppender(target, self.__Logger_rotation)
|
|
||||||
if ((target == TARGET_SYSLOG) and (os.name != "posix")):
|
|
||||||
self.warn("TARGET_SYSLOG is not available on non-posix platforms!")
|
|
||||||
else:
|
|
||||||
self.__Logger_targets.append(target)
|
|
||||||
|
|
||||||
def remove_target(self, target):
|
|
||||||
""" Remove a target from the logger targets. """
|
|
||||||
if (target in self.__Logger_targets):
|
|
||||||
if (target == TARGET_MYSQL):
|
|
||||||
self.__Logger_mysql_connection.close()
|
|
||||||
self.__Logger_targets.remove(target)
|
|
||||||
|
|
||||||
def set_target(self, target):
|
|
||||||
""" Set a single target. """
|
|
||||||
if (type(target) == StringType):
|
|
||||||
if (target not in SPECIAL_TARGETS):
|
|
||||||
# File target
|
|
||||||
target = FileAppender(target, self.__Logger_rotation)
|
|
||||||
self.__Logger_targets = [ target ]
|
|
||||||
|
|
||||||
def remove_all_targets(self):
|
|
||||||
""" Remove all targets from the logger targets. """
|
|
||||||
self.__Logger_targets=[]
|
|
||||||
|
|
||||||
def get_targets(self):
|
|
||||||
""" Returns all defined targets. """
|
|
||||||
return self.__Logger_targets
|
|
||||||
|
|
||||||
# Methods to set properties
|
|
||||||
|
|
||||||
def set_loglevel(self, loglevel):
|
|
||||||
""" Set the loglevel for the current instance. """
|
|
||||||
self.__Logger_loglevel = loglevel
|
|
||||||
|
|
||||||
def set_formatstring(self, formatstring):
|
|
||||||
""" Set a format string. """
|
|
||||||
self.__Logger_formatstring = formatstring
|
|
||||||
|
|
||||||
def set_use_ansi_codes(self, useansicodes):
|
|
||||||
""" Use ansi codes for output to the console (TRUE or FALSE). """
|
|
||||||
self.__Logger_useansicodes = useansicodes
|
|
||||||
|
|
||||||
def set_time_format(self, timeformat):
|
|
||||||
""" Set the time format (default: loaded from the system locale). """
|
|
||||||
self.__Logger_timeformat = timeformat
|
|
||||||
|
|
||||||
def set_rotation(self, rotation):
|
|
||||||
""" Set the file rotation mode to one of ROTATE_NONE, ROTATE_DAILY, ROTATE_WEEKLY, ROTATE_MONTHLY """
|
|
||||||
self.__Logger_rotation = rotation
|
|
||||||
for i in range(len(self.__Logger_targets)):
|
|
||||||
target = self.__Logger_targets[i]
|
|
||||||
if (isinstance(target, FileAppender)):
|
|
||||||
target.set_rotation(rotation)
|
|
||||||
|
|
||||||
# Method to get properties
|
|
||||||
|
|
||||||
def get_loglevel(self):
|
|
||||||
""" Returns the current loglevel. """
|
|
||||||
return self.__Logger_loglevel
|
|
||||||
|
|
||||||
def get_formatstring(self):
|
|
||||||
""" Returns the current format string. """
|
|
||||||
return self.__Logger_formatstring
|
|
||||||
|
|
||||||
def get_use_ansi_codes(self):
|
|
||||||
""" Returns, wether ansi codes are being used or not. """
|
|
||||||
return self.__Logger_useansicodes
|
|
||||||
|
|
||||||
def get_time_format(self):
|
|
||||||
""" Returns the current time format. """
|
|
||||||
return self.__Logger_timeformat
|
|
||||||
|
|
||||||
def get_rotation(self):
|
|
||||||
""" Returns the current rotation setting. """
|
|
||||||
return self.__Logger_rotation
|
|
||||||
|
|
||||||
# Methods to push and pop trace messages for nested contexts
|
|
||||||
|
|
||||||
def push(self, message):
|
|
||||||
""" Add a trace message. """
|
|
||||||
self.__Logger_ndc.append(message)
|
|
||||||
|
|
||||||
def pop(self):
|
|
||||||
""" Remove the topmost trace message. """
|
|
||||||
ct = len(self.__Logger_ndc)
|
|
||||||
if (ct):
|
|
||||||
del(self.__Logger_ndc[ct-1])
|
|
||||||
|
|
||||||
def clear_ndc(self):
|
|
||||||
""" Clears all NDC messages. """
|
|
||||||
self.__Logger_ndc = []
|
|
||||||
|
|
||||||
# Methods to actually print messages
|
|
||||||
|
|
||||||
def debug(self, *messages):
|
|
||||||
""" Write a debug message. """
|
|
||||||
if (self.__Logger_loglevel >= LOGLEVEL_DEBUG):
|
|
||||||
message = self.__Logger_collate_messages(messages)
|
|
||||||
self.__Logger_showmessage(message, MSG_DEBUG)
|
|
||||||
|
|
||||||
def warn(self, *messages):
|
|
||||||
""" Write a warning message. """
|
|
||||||
if (self.__Logger_loglevel >= LOGLEVEL_VERBOSE):
|
|
||||||
message = self.__Logger_collate_messages(messages)
|
|
||||||
self.__Logger_showmessage(message, MSG_WARN)
|
|
||||||
|
|
||||||
def error(self, *messages):
|
|
||||||
""" Write a error message. """
|
|
||||||
if (self.__Logger_loglevel >= LOGLEVEL_ERROR):
|
|
||||||
message = self.__Logger_collate_messages(messages)
|
|
||||||
self.__Logger_showmessage(message, MSG_ERROR)
|
|
||||||
|
|
||||||
def info(self, *messages):
|
|
||||||
""" Write a info message. """
|
|
||||||
if (self.__Logger_loglevel >= LOGLEVEL_NORMAL):
|
|
||||||
message = self.__Logger_collate_messages(messages)
|
|
||||||
self.__Logger_showmessage(message, MSG_INFO)
|
|
||||||
|
|
||||||
# Private methods of the Logger class - you never have to use those directly
|
|
||||||
|
|
||||||
def __Logger_collate_messages(self, messages):
|
|
||||||
""" **(private)** Create a single string from a number of messages. """
|
|
||||||
return strip(reduce(lambda x, y: "%s%s" % (x, y), messages))
|
|
||||||
|
|
||||||
def __Logger_tracestack(self):
|
|
||||||
""" **(private)** Analyze traceback stack and set linenumber and functionname. """
|
|
||||||
stack = traceback.extract_stack()
|
|
||||||
self.__Logger_module = stack[-4][0]
|
|
||||||
self.__Logger_linenumber = stack[-4][1]
|
|
||||||
self.__Logger_functionname = stack[-4][2]
|
|
||||||
self.__Logger_filename = stack[-4][0]
|
|
||||||
if (self.__Logger_functionname == "?"):
|
|
||||||
self.__Logger_functionname = "Main"
|
|
||||||
|
|
||||||
def __Logger_setdefaults(self):
|
|
||||||
""" **(private)** Set default values for internal variables. """
|
|
||||||
locale.setlocale(locale.LC_ALL)
|
|
||||||
self.__Logger_classid = None
|
|
||||||
self.__Logger_targets = [ TARGET_SYS_STDOUT ] # default target = sys.stdout
|
|
||||||
self.__Logger_formatstring = FMT_LONG
|
|
||||||
self.__Logger_loglevel = LOGLEVEL_NORMAL
|
|
||||||
self.__Logger_rotation = ROTATE_NONE
|
|
||||||
self.__Logger_useansicodes = FALSE
|
|
||||||
self.__Logger_functionname = ""
|
|
||||||
self.__Logger_filename = ""
|
|
||||||
self.__Logger_linenumber = -1
|
|
||||||
try:
|
|
||||||
self.__Logger_timeformat = "%s %s" % (locale.nl_langinfo(locale.D_FMT), locale.nl_langinfo(locale.T_FMT))
|
|
||||||
except (AttributeError):
|
|
||||||
self.__Logger_timeformat = "%d.%m.%Y %H:%M:%S"
|
|
||||||
self.__Logger_classname = None
|
|
||||||
self.__Logger_configfilename = ""
|
|
||||||
self.__Logger_module = ""
|
|
||||||
self.__Logger_ndc = [] # ndc = Nested Diagnostic Context
|
|
||||||
|
|
||||||
def __Logger_find_config(self):
|
|
||||||
""" **(private)** Search for configuration files. """
|
|
||||||
if (not self.__Logger_configfilename):
|
|
||||||
priorities = CONFIGURATION_FILES.keys()
|
|
||||||
priorities.sort()
|
|
||||||
configfilename = ""
|
|
||||||
for i in range(len(priorities)):
|
|
||||||
filename = CONFIGURATION_FILES[priorities[i]]
|
|
||||||
home_directory = get_homedirectory()
|
|
||||||
if (os.sep == "\\"):
|
|
||||||
home_directory = replace(home_directory, "\\", "\\\\")
|
|
||||||
filename = sub("\$HOME", home_directory, filename)
|
|
||||||
if (os.path.exists(filename)):
|
|
||||||
configfilename = filename
|
|
||||||
break
|
|
||||||
self.__Logger_configfilename = configfilename
|
|
||||||
return self.__Logger_configfilename
|
|
||||||
|
|
||||||
def __Logger_parse_options(self, section = SECTION_DEFAULT):
|
|
||||||
""" **(private)** Parse main options from config file. """
|
|
||||||
configfilename = self.__Logger_find_config()
|
|
||||||
|
|
||||||
if (configfilename != ""):
|
|
||||||
parser = ConfigParser()
|
|
||||||
parser.read(configfilename)
|
|
||||||
self.__Logger_set_instance_options(parser, section, self)
|
|
||||||
return TRUE
|
|
||||||
|
|
||||||
def __Logger_set_instance_options(self, parser, section, instance):
|
|
||||||
""" **(private)** Set the options for a given instance from the parser section """
|
|
||||||
|
|
||||||
for i in range(len(parser.options(section))):
|
|
||||||
option = lower(parser.options(section)[i])
|
|
||||||
value = parser.get(section, option)
|
|
||||||
if (option == "format"):
|
|
||||||
instance.set_formatstring(value)
|
|
||||||
elif (option == "timeformat"):
|
|
||||||
instance.set_time_format(value)
|
|
||||||
elif (option == "ansi"):
|
|
||||||
instance.set_use_ansi_codes(upper(value))
|
|
||||||
elif (option == "loglevel"):
|
|
||||||
instance.set_loglevel(LOG_LEVELS[upper(value)])
|
|
||||||
elif (option == "target"):
|
|
||||||
splitted = split(value, ",")
|
|
||||||
instance.remove_all_targets()
|
|
||||||
for i in range(len(splitted)):
|
|
||||||
instance.add_target(strip(splitted[i]))
|
|
||||||
|
|
||||||
def __Logger_cache_options(self):
|
|
||||||
""" **(private)** Read and cache debug levels for categories from config file. """
|
|
||||||
configfilename = self.__Logger_find_config()
|
|
||||||
|
|
||||||
if (configfilename != ""):
|
|
||||||
parser = ConfigParser()
|
|
||||||
parser.read(configfilename)
|
|
||||||
|
|
||||||
for i in range(len(parser.sections())):
|
|
||||||
section = parser.sections()[i]
|
|
||||||
if (section != SECTION_DEFAULT):
|
|
||||||
instance = self.get_instance(section)
|
|
||||||
self.__Logger_set_instance_options(parser, section, instance)
|
|
||||||
return TRUE
|
|
||||||
|
|
||||||
def __Logger_appendconfigfiles(self, filenames):
|
|
||||||
""" **(private)** Append a filename to the list of configuration files. """
|
|
||||||
filenames.reverse()
|
|
||||||
for i in range(len(filenames)):
|
|
||||||
keys = CONFIGURATION_FILES.keys()
|
|
||||||
CONFIGURATION_FILES[min(keys) - 1] = filenames[i]
|
|
||||||
|
|
||||||
def __Logger_get_ndc(self):
|
|
||||||
""" **(private)** Returns the NDC (nested diagnostic context) joined with single-spaces. """
|
|
||||||
if (len(self.__Logger_ndc)):
|
|
||||||
return join(self.__Logger_ndc)
|
|
||||||
else:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
def __Logger_showmessage(self, message, messagesource):
|
|
||||||
""" **(private)** Writes a message to all targets set. """
|
|
||||||
|
|
||||||
if (isinstance(message, Exception)):
|
|
||||||
(exc_type, exc_value, tb) = sys.exc_info()
|
|
||||||
exception_summary = traceback.format_exception(exc_type, exc_value, tb)
|
|
||||||
message = 'Exception caught:\n'
|
|
||||||
for line in exception_summary:
|
|
||||||
message = "%s%s" % (message, line)
|
|
||||||
|
|
||||||
currenttime = time()
|
|
||||||
self.__Logger_tracestack()
|
|
||||||
timedifference = "%.3f" % (currenttime - self.__Logger_timeinit)
|
|
||||||
timedifflaststep = "%.3f" % (currenttime - self.__Logger_timelaststep)
|
|
||||||
self.__Logger_timelaststep = currenttime
|
|
||||||
milliseconds = int(round((currenttime - long(currenttime)) * 1000))
|
|
||||||
timeformat = sub("%S", "%S." + (zfill(milliseconds, 3)), self.__Logger_timeformat)
|
|
||||||
currentformattedtime = strftime(timeformat, localtime(currenttime))
|
|
||||||
|
|
||||||
line = self.__Logger_formatstring
|
|
||||||
line = sub("%C", str(self.__Logger_classname), line)
|
|
||||||
line = sub("%D", timedifference, line)
|
|
||||||
line = sub("%d", timedifflaststep, line)
|
|
||||||
line = sub("%F", self.__Logger_functionname, line)
|
|
||||||
line = sub("%f", self.__Logger_filename, line)
|
|
||||||
line = sub("%U", self.__Logger_module, line)
|
|
||||||
line = sub("%u", os.path.split(self.__Logger_module)[-1], line)
|
|
||||||
ndc = self.__Logger_get_ndc()
|
|
||||||
if (ndc != ""):
|
|
||||||
line = sub("%x", "%s - " % ndc, line)
|
|
||||||
else:
|
|
||||||
line = sub("%x", "", line)
|
|
||||||
message = replace(message, "\\", "\\\\")
|
|
||||||
if (self.__Logger_useansicodes == TRUE):
|
|
||||||
line = sub("%L", self.__Logger_ansi(LOG_MSG[messagesource], messagesource), line)
|
|
||||||
line = sub("%M", self.__Logger_ansi(message, messagesource), line)
|
|
||||||
else:
|
|
||||||
line = sub("%L", LOG_MSG[messagesource], line)
|
|
||||||
line = sub("%M", message, line)
|
|
||||||
line = sub("%N", str(self.__Logger_linenumber), line)
|
|
||||||
line = sub("%T", currentformattedtime, line)
|
|
||||||
line = sub("%t", `currenttime`, line)
|
|
||||||
|
|
||||||
for i in range(len(self.__Logger_targets)):
|
|
||||||
target = self.__Logger_targets[i]
|
|
||||||
if (target == TARGET_MYSQL):
|
|
||||||
sqltime = strftime("'%Y-%m-%d', '%H:%M:%S'", localtime(currenttime))
|
|
||||||
sqlStatement = "INSERT INTO %s (host, facility, level, date, time, program, msg) VALUES ('%s', '%s', '%s', %s, '%s', '%s')" % (self.__Logger_mysql_tablename, self.hostname, self.__Logger_functionname, LOG_MSG[messagesource], sqltime, str(self.__Logger_classname), sub("'", "`", message + " " + ndc))
|
|
||||||
self.__Logger_mysql_cursor.execute(sqlStatement)
|
|
||||||
elif (target == TARGET_SYSLOG):
|
|
||||||
# We don't need time and stuff here
|
|
||||||
syslog.syslog(message)
|
|
||||||
elif (isinstance(target, FileAppender)):
|
|
||||||
target.writeline(line)
|
|
||||||
elif (target == sys.stdout) or (lower(target) == TARGET_SYS_STDOUT) or (lower(target) == TARGET_SYS_STDOUT_ALIAS):
|
|
||||||
sys.stdout.write("%s\n" % line)
|
|
||||||
elif (target == sys.stderr) or (lower(target) == TARGET_SYS_STDERR) or (lower(target) == TARGET_SYS_STDERR_ALIAS):
|
|
||||||
sys.stderr.write("%s\n" % line)
|
|
||||||
else:
|
|
||||||
target.write("%s\n" % line)
|
|
||||||
|
|
||||||
def __Logger_ansi(self, text, messagesource):
|
|
||||||
""" **(private)** Converts plain text to ansi text. """
|
|
||||||
bold = LOG_COLORS[messagesource][2]
|
|
||||||
fg = str(LOG_COLORS[messagesource][0])
|
|
||||||
bg = LOG_COLORS[messagesource][1]
|
|
||||||
if (bold == TRUE):
|
|
||||||
fg = "%s;1" % fg
|
|
||||||
bg = bg + 10
|
|
||||||
text = "\033[%d;%sm%s\033[0m" % (bg, fg, text)
|
|
||||||
return text
|
|
||||||
|
|
||||||
class FileAppender:
|
|
||||||
|
|
||||||
def __init__(self, filename, rotation = ROTATE_NONE):
|
|
||||||
""" **(private)** Class initalization & customization. """
|
|
||||||
self.__FileAppender_filename = sub("\$HOME", get_homedirectory(), filename)
|
|
||||||
self.__FileAppender_filename = os.path.expanduser(self.__FileAppender_filename)
|
|
||||||
self.__FileAppender_filename = os.path.expandvars(self.__FileAppender_filename)
|
|
||||||
self.__FileAppender_rotation = rotation
|
|
||||||
|
|
||||||
def __FileAppender_rotate(self, modification_time):
|
|
||||||
""" **(private)** Check, wether the file has to be rotated yet or not. """
|
|
||||||
if (self.__FileAppender_rotation == ROTATE_DAILY):
|
|
||||||
strftime_mask = "%Y%j"
|
|
||||||
elif (self.__FileAppender_rotation == ROTATE_WEEKLY):
|
|
||||||
strftime_mask = "%Y%W"
|
|
||||||
elif (self.__FileAppender_rotation == ROTATE_MONTHLY):
|
|
||||||
strftime_mask = "%Y%m"
|
|
||||||
return (strftime(strftime_mask, localtime(time())) != strftime(strftime_mask, localtime(modification_time)))
|
|
||||||
|
|
||||||
def __FileAppender_date_string(self, modification_time):
|
|
||||||
""" **(private)** Returns a new filename for the rotated file with the appropriate time included. """
|
|
||||||
if (self.__FileAppender_rotation == ROTATE_DAILY):
|
|
||||||
return strftime("%Y-%m-%d", localtime(modification_time))
|
|
||||||
elif (self.__FileAppender_rotation == ROTATE_WEEKLY):
|
|
||||||
return strftime("%Y-Week %W", localtime(modification_time))
|
|
||||||
elif (self.__FileAppender_rotation == ROTATE_MONTHLY):
|
|
||||||
return strftime("%Y-Month %m", localtime(modification_time))
|
|
||||||
|
|
||||||
def get_rotation(self):
|
|
||||||
""" Returns the current rotation setting. """
|
|
||||||
return self.__FileAppender_rotation
|
|
||||||
|
|
||||||
def set_rotation(self, rotation):
|
|
||||||
""" Set the file rotation mode to one of ROTATE_NONE, ROTATE_DAILY, ROTATE_WEEKLY, ROTATE_MONTHLY """
|
|
||||||
self.__FileAppender_rotation = rotation
|
|
||||||
|
|
||||||
def write(self, text):
|
|
||||||
""" Write some text to the file appender. """
|
|
||||||
if ((os.path.exists(self.__FileAppender_filename)) and (self.__FileAppender_rotation != ROTATE_NONE)):
|
|
||||||
statinfo = stat(self.__FileAppender_filename)
|
|
||||||
if (self.__FileAppender_rotate(statinfo[8])):
|
|
||||||
splitted = os.path.splitext(self.__FileAppender_filename)
|
|
||||||
target_file = "%s-%s%s" % (splitted[0], self.__FileAppender_date_string(statinfo[8]), splitted[1])
|
|
||||||
rename(self.__FileAppender_filename, target_file)
|
|
||||||
file = open(self.__FileAppender_filename, "a")
|
|
||||||
file.write(text)
|
|
||||||
file.close()
|
|
||||||
|
|
||||||
def writeline(self, text):
|
|
||||||
""" Write some text including newline to the file appender. """
|
|
||||||
self.write("%s\n" % text)
|
|
|
@ -16,20 +16,20 @@
|
||||||
|
|
||||||
# Author: Cyril Jaquier
|
# Author: Cyril Jaquier
|
||||||
#
|
#
|
||||||
# $Revision: 1.13.2.4 $
|
# $Revision: 1.13.2.7 $
|
||||||
|
|
||||||
__author__ = "Cyril Jaquier"
|
__author__ = "Cyril Jaquier"
|
||||||
__version__ = "$Revision: 1.13.2.4 $"
|
__version__ = "$Revision: 1.13.2.7 $"
|
||||||
__date__ = "$Date: 2005/07/23 09:07:53 $"
|
__date__ = "$Date: 2005/08/06 18:43:11 $"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import os, sys, time, re, log4py
|
import os, sys, time, re, logging
|
||||||
|
|
||||||
from utils.dns import *
|
from utils.dns import *
|
||||||
|
|
||||||
# Gets the instance of log4py.
|
# Gets the instance of the logger.
|
||||||
logSys = log4py.Logger().get_instance()
|
logSys = logging.getLogger("fail2ban")
|
||||||
|
|
||||||
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
|
||||||
|
@ -38,8 +38,9 @@ class LogReader:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, logPath, timeregex, timepattern, failregex,
|
def __init__(self, logPath, timeregex, timepattern, failregex,
|
||||||
findTime = 3600):
|
maxRetry, findTime):
|
||||||
self.logPath = logPath
|
self.logPath = logPath
|
||||||
|
self.maxRetry = maxRetry
|
||||||
self.timeregex = timeregex
|
self.timeregex = timeregex
|
||||||
self.timepattern = timepattern
|
self.timepattern = timepattern
|
||||||
self.failregex = failregex
|
self.failregex = failregex
|
||||||
|
@ -50,6 +51,11 @@ class LogReader:
|
||||||
self.lastDate = 0
|
self.lastDate = 0
|
||||||
self.logStats = None
|
self.logStats = None
|
||||||
|
|
||||||
|
def getMaxRetry(self):
|
||||||
|
""" Gets the maximum number of failures
|
||||||
|
"""
|
||||||
|
return self.maxRetry
|
||||||
|
|
||||||
def getFindTime(self):
|
def getFindTime(self):
|
||||||
""" Gets the find time.
|
""" Gets the find time.
|
||||||
"""
|
"""
|
||||||
|
@ -83,7 +89,7 @@ class LogReader:
|
||||||
fileHandler = open(self.logPath)
|
fileHandler = open(self.logPath)
|
||||||
except OSError:
|
except OSError:
|
||||||
logSys.error("Unable to open "+self.logPath)
|
logSys.error("Unable to open "+self.logPath)
|
||||||
sys.exit(-1)
|
|
||||||
return fileHandler
|
return fileHandler
|
||||||
|
|
||||||
def isModified(self):
|
def isModified(self):
|
||||||
|
@ -93,7 +99,6 @@ class LogReader:
|
||||||
self.logStats = os.stat(self.logPath)
|
self.logStats = os.stat(self.logPath)
|
||||||
except OSError:
|
except OSError:
|
||||||
logSys.error("Unable to get stat on "+self.logPath)
|
logSys.error("Unable to get stat on "+self.logPath)
|
||||||
sys.exit(-1)
|
|
||||||
|
|
||||||
if self.lastModTime == self.logStats.st_mtime:
|
if self.lastModTime == self.logStats.st_mtime:
|
||||||
return False
|
return False
|
||||||
|
@ -130,7 +135,7 @@ class LogReader:
|
||||||
logFile = self.openLogFile()
|
logFile = self.openLogFile()
|
||||||
self.setFilePos(logFile)
|
self.setFilePos(logFile)
|
||||||
lastLine = ''
|
lastLine = ''
|
||||||
for line in logFile.readlines():
|
for line in logFile:
|
||||||
lastLine = line
|
lastLine = line
|
||||||
failList = self.findFailure(line)
|
failList = self.findFailure(line)
|
||||||
for element in failList:
|
for element in failList:
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
.\"
|
||||||
|
.TH "FAIL2BAN" "8" "July 2005" "Cyril Jaquier" "System administration tools"
|
||||||
|
.SH "NAME"
|
||||||
|
fail2ban \- bans IP that makes too many password failures
|
||||||
|
.SH "SYNOPSIS"
|
||||||
|
.B fail2ban
|
||||||
|
[\fIOPTIONS\fR]
|
||||||
|
.SH "DESCRIPTION"
|
||||||
|
\fBFail2Ban\fR reads log file that contains password failure report
|
||||||
|
and bans the corresponding IP addresses using firewall rules. It updates
|
||||||
|
firewall rules to reject the IP address.
|
||||||
|
.SH "OPTIONS"
|
||||||
|
.TP
|
||||||
|
\fB\-b\fR
|
||||||
|
start in background
|
||||||
|
.TP
|
||||||
|
\fB\-d\fR
|
||||||
|
start in debug mode. Commands are NOT executed but only displayed
|
||||||
|
.TP
|
||||||
|
\fB\-c\fR \fIFILE\fR
|
||||||
|
read configuration file \fIFILE\fR
|
||||||
|
.TP
|
||||||
|
\fB\-p\fR \fIFILE\fR
|
||||||
|
create PID lock in \fIFILE\fR
|
||||||
|
.TP
|
||||||
|
\fB\-h, \-\-help\fR
|
||||||
|
display this help message
|
||||||
|
.TP
|
||||||
|
\fB\-i\fR \fIIP\fR
|
||||||
|
\fIIP\fR(s) to ignore
|
||||||
|
.TP
|
||||||
|
\fB\-k\fR
|
||||||
|
kill a currently running Fail2Ban instance
|
||||||
|
.TP
|
||||||
|
\fB\-r\fR \fIVALUE\fR
|
||||||
|
allow a max of \fIVALUE\fR password failure
|
||||||
|
.TP
|
||||||
|
\fB\-t\fR \fITIME\fR
|
||||||
|
ban IP for \fITIME\fR seconds
|
||||||
|
.TP
|
||||||
|
\fB\-v\fR
|
||||||
|
verbose. Use twice for greater effect
|
||||||
|
.TP
|
||||||
|
\fB\-V, \-\-version\fR
|
||||||
|
print software version
|
||||||
|
.SH "FILES"
|
||||||
|
.I /etc/fail2ban.conf
|
||||||
|
.RS
|
||||||
|
The configuration file. See \fBfail2ban.conf\fR(5) for further details.
|
||||||
|
.SH "REPORTING BUGS"
|
||||||
|
Please report bugs at http://sourceforge.net/projects/fail2ban/
|
||||||
|
via bug tracker
|
||||||
|
.SH "AUTHOR"
|
||||||
|
Cyril Jaquier <lostcontrol@users.sourceforge.net>
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
.TP
|
||||||
|
See
|
||||||
|
.BR "http://fail2ban.sourceforge.net/".
|
|
@ -0,0 +1,20 @@
|
||||||
|
.\"
|
||||||
|
.TH "FAIL2BAN.CONF" "5" "July 2005" "Cyril Jaquier" "System administration tools"
|
||||||
|
.SH "NAME"
|
||||||
|
fail2ban.conf \- configuration data for fail2ban
|
||||||
|
.SH "DESCRIPTION"
|
||||||
|
\fB/etc/fail2ban.conf\fR contains data about the general configuration of fail2ban, the mail notification and services to monitor.
|
||||||
|
.SH "VARIABLES"
|
||||||
|
Please look at the file itself
|
||||||
|
.SH "FILES"
|
||||||
|
.I /etc/fail2ban.conf
|
||||||
|
.SH "REPORTING BUGS"
|
||||||
|
Please report bugs at http://sourceforge.net/projects/fail2ban/
|
||||||
|
via bug tracker
|
||||||
|
.SH "AUTHOR"
|
||||||
|
Cyril Jaquier <lostcontrol@users.sourceforge.net>
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
.BR fail2ban (8)
|
||||||
|
.TP
|
||||||
|
See
|
||||||
|
.BR "http://fail2ban.sourceforge.net/".
|
18
setup.py
18
setup.py
|
@ -18,11 +18,11 @@
|
||||||
|
|
||||||
# Author: Cyril Jaquier
|
# Author: Cyril Jaquier
|
||||||
#
|
#
|
||||||
# $Revision: 1.4.2.2 $
|
# $Revision: 1.4.2.3 $
|
||||||
|
|
||||||
__author__ = "Cyril Jaquier"
|
__author__ = "Cyril Jaquier"
|
||||||
__version__ = "$Revision: 1.4.2.2 $"
|
__version__ = "$Revision: 1.4.2.3 $"
|
||||||
__date__ = "$Date: 2005/07/15 14:14:12 $"
|
__date__ = "$Date: 2005/07/28 20:30:34 $"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
@ -31,15 +31,25 @@ from version import version
|
||||||
from os.path import isfile, join
|
from os.path import isfile, join
|
||||||
from sys import exit, argv
|
from sys import exit, argv
|
||||||
|
|
||||||
|
longdesc = '''
|
||||||
|
Fail2Ban scans log files like /var/log/pwdfail or
|
||||||
|
/var/log/apache/error_log and bans IP that makes
|
||||||
|
too many password failures. It updates firewall rules
|
||||||
|
to reject the IP address or executes user defined
|
||||||
|
commands.'''
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name = "fail2ban",
|
name = "fail2ban",
|
||||||
version = version,
|
version = version,
|
||||||
description = "Ban IPs that make too many password failure",
|
description = "Ban IPs that make too many password failure",
|
||||||
|
long_description = longdesc,
|
||||||
author = "Cyril Jaquier",
|
author = "Cyril Jaquier",
|
||||||
author_email = "lostcontrol@users.sourceforge.net",
|
author_email = "lostcontrol@users.sourceforge.net",
|
||||||
url = "http://fail2ban.sourceforge.net",
|
url = "http://fail2ban.sourceforge.net",
|
||||||
|
license = "GPL",
|
||||||
|
platforms = "Posix",
|
||||||
scripts = ['fail2ban'],
|
scripts = ['fail2ban'],
|
||||||
py_modules = ['fail2ban', 'version', 'log4py'],
|
py_modules = ['fail2ban', 'version'],
|
||||||
packages = ['firewall', 'logreader', 'confreader', 'utils']
|
packages = ['firewall', 'logreader', 'confreader', 'utils']
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -16,20 +16,20 @@
|
||||||
|
|
||||||
# Author: Cyril Jaquier
|
# Author: Cyril Jaquier
|
||||||
#
|
#
|
||||||
# $Revision: 1.1.2.1 $
|
# $Revision: 1.1.2.2 $
|
||||||
|
|
||||||
__author__ = "Cyril Jaquier"
|
__author__ = "Cyril Jaquier"
|
||||||
__version__ = "$Revision: 1.1.2.1 $"
|
__version__ = "$Revision: 1.1.2.2 $"
|
||||||
__date__ = "$Date: 2005/07/12 13:09:47 $"
|
__date__ = "$Date: 2005/08/01 16:35:18 $"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import log4py, smtplib
|
import logging, smtplib
|
||||||
|
|
||||||
from utils.strings import replaceTag
|
from utils.strings import replaceTag
|
||||||
|
|
||||||
# Gets the instance of log4py.
|
# Gets the instance of the logger.
|
||||||
logSys = log4py.Logger().get_instance()
|
logSys = logging.getLogger("fail2ban")
|
||||||
|
|
||||||
class Mail:
|
class Mail:
|
||||||
""" Mailer class
|
""" Mailer class
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
# 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/08/04 20:48:30 $"
|
||||||
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
import os, logging
|
||||||
|
|
||||||
|
# Gets the instance of the logger.
|
||||||
|
logSys = logging.getLogger("fail2ban")
|
||||||
|
|
||||||
|
class PIDLock:
|
||||||
|
""" Manages the PID lock file.
|
||||||
|
|
||||||
|
The following class shows how to implement the singleton pattern[1] in
|
||||||
|
Python. A singleton is a class that makes sure only one instance of it
|
||||||
|
is ever created. Typically such classes are used to manage resources
|
||||||
|
that by their very nature can only exist once.
|
||||||
|
|
||||||
|
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52558
|
||||||
|
"""
|
||||||
|
|
||||||
|
class __impl:
|
||||||
|
""" Implementation of the singleton interface """
|
||||||
|
|
||||||
|
def setPath(self, path):
|
||||||
|
""" Set PID lock file path.
|
||||||
|
"""
|
||||||
|
self.path = path
|
||||||
|
|
||||||
|
def create(self):
|
||||||
|
""" Create PID lock.
|
||||||
|
"""
|
||||||
|
fileHandler = open(self.path, mode='w')
|
||||||
|
pid = os.getpid()
|
||||||
|
fileHandler.write(`pid` + '\n')
|
||||||
|
fileHandler.close()
|
||||||
|
logSys.debug("Created PID lock (" + `pid` + ") in " + self.path)
|
||||||
|
|
||||||
|
def remove(self):
|
||||||
|
""" Remove PID lock.
|
||||||
|
"""
|
||||||
|
os.remove(self.path)
|
||||||
|
logSys.debug("Removed PID lock " + self.path)
|
||||||
|
|
||||||
|
def exists(self):
|
||||||
|
""" Returns the current PID if Fail2Ban is running or False
|
||||||
|
if no instance found.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
fileHandler = open(self.path)
|
||||||
|
pid = fileHandler.readline()
|
||||||
|
fileHandler.close()
|
||||||
|
return pid
|
||||||
|
except IOError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# storage for the instance reference
|
||||||
|
__instance = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
""" Create singleton instance """
|
||||||
|
# Check whether we already have an instance
|
||||||
|
if PIDLock.__instance is None:
|
||||||
|
# Create and remember instance
|
||||||
|
PIDLock.__instance = PIDLock.__impl()
|
||||||
|
|
||||||
|
# Store instance reference as the only member in the handle
|
||||||
|
self.__dict__['_PIDLock__instance'] = PIDLock.__instance
|
||||||
|
|
||||||
|
def __getattr__(self, attr):
|
||||||
|
""" Delegate access to implementation """
|
||||||
|
return getattr(self.__instance, attr)
|
||||||
|
|
||||||
|
def __setattr__(self, attr, value):
|
||||||
|
""" Delegate access to implementation """
|
||||||
|
return setattr(self.__instance, attr, value)
|
||||||
|
|
|
@ -16,21 +16,21 @@
|
||||||
|
|
||||||
# Author: Cyril Jaquier
|
# Author: Cyril Jaquier
|
||||||
#
|
#
|
||||||
# $Revision: 1.1.2.2 $
|
# $Revision: 1.1.2.4 $
|
||||||
|
|
||||||
__author__ = "Cyril Jaquier"
|
__author__ = "Cyril Jaquier"
|
||||||
__version__ = "$Revision: 1.1.2.2 $"
|
__version__ = "$Revision: 1.1.2.4 $"
|
||||||
__date__ = "$Date: 2005/07/15 14:08:17 $"
|
__date__ = "$Date: 2005/08/04 20:48:30 $"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import os, log4py, signal
|
import os, logging, signal
|
||||||
|
|
||||||
# Gets the instance of log4py.
|
# Gets the instance of the logger.
|
||||||
logSys = log4py.Logger().get_instance()
|
logSys = logging.getLogger("fail2ban")
|
||||||
|
|
||||||
def createDaemon():
|
def createDaemon():
|
||||||
"""Detach a process from the controlling terminal and run it in the
|
""" Detach a process from the controlling terminal and run it in the
|
||||||
background as a daemon.
|
background as a daemon.
|
||||||
|
|
||||||
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278731
|
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278731
|
||||||
|
@ -102,34 +102,6 @@ def createDaemon():
|
||||||
|
|
||||||
return True
|
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):
|
def killPID(pid):
|
||||||
""" Kills the process with the given PID using the
|
""" Kills the process with the given PID using the
|
||||||
INT signal (same effect as <ctrl>+<c>).
|
INT signal (same effect as <ctrl>+<c>).
|
||||||
|
@ -151,6 +123,9 @@ def executeCmd(cmd, debug):
|
||||||
|
|
||||||
logSys.debug(cmd)
|
logSys.debug(cmd)
|
||||||
if not debug:
|
if not debug:
|
||||||
return os.system(cmd)
|
retval = os.system(cmd)
|
||||||
|
if not retval == 0:
|
||||||
|
logSys.error("'" + cmd + "' returned " + `retval`)
|
||||||
|
return retval
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
|
@ -16,18 +16,18 @@
|
||||||
|
|
||||||
# Author: Cyril Jaquier
|
# Author: Cyril Jaquier
|
||||||
#
|
#
|
||||||
# $Revision: 1.1.2.1 $
|
# $Revision: 1.1.2.2 $
|
||||||
|
|
||||||
__author__ = "Cyril Jaquier"
|
__author__ = "Cyril Jaquier"
|
||||||
__version__ = "$Revision: 1.1.2.1 $"
|
__version__ = "$Revision: 1.1.2.2 $"
|
||||||
__date__ = "$Date: 2005/07/12 13:09:47 $"
|
__date__ = "$Date: 2005/08/01 16:35:18 $"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import log4py
|
import logging
|
||||||
|
|
||||||
# Gets the instance of log4py.
|
# Gets the instance of the logger.
|
||||||
logSys = log4py.Logger().get_instance()
|
logSys = logging.getLogger("fail2ban")
|
||||||
|
|
||||||
def replaceTag(query, aInfo):
|
def replaceTag(query, aInfo):
|
||||||
""" Replace tags in query
|
""" Replace tags in query
|
||||||
|
|
|
@ -16,12 +16,12 @@
|
||||||
|
|
||||||
# Author: Cyril Jaquier
|
# Author: Cyril Jaquier
|
||||||
#
|
#
|
||||||
# $Revision: 1.12.2.4 $
|
# $Revision: 1.12.2.6 $
|
||||||
|
|
||||||
__author__ = "Cyril Jaquier"
|
__author__ = "Cyril Jaquier"
|
||||||
__version__ = "$Revision: 1.12.2.4 $"
|
__version__ = "$Revision: 1.12.2.6 $"
|
||||||
__date__ = "$Date: 2005/07/23 09:31:12 $"
|
__date__ = "$Date: 2005/08/06 15:07:11 $"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
version = "0.5.1"
|
version = "0.5.2"
|
||||||
|
|
Loading…
Reference in New Issue