mirror of https://github.com/fail2ban/fail2ban
Load fail2ban-0.4.1 into debs/fail2ban/trunk.
parent
2b5c103cd0
commit
edd7ed5d30
|
@ -0,0 +1,17 @@
|
||||||
|
# quick dirty hack to avoid reading documentation for cdbs where
|
||||||
|
# install target involved only if using autotools
|
||||||
|
DESTDIR=debian/fail2ban
|
||||||
|
|
||||||
|
all::
|
||||||
|
cp fail2ban.py fail2ban
|
||||||
|
gzip -c CHANGELOG > changelog.gz
|
||||||
|
|
||||||
|
install:: all
|
||||||
|
mkdir -p $(DESTDIR)/etc/default
|
||||||
|
cp config/fail2ban.conf.default $(DESTDIR)/etc/fail2ban.conf
|
||||||
|
cp config/gentoo-confd $(DESTDIR)/etc/default/fail2ban
|
||||||
|
mkdir -p $(DESTDIR)/usr/lib/fail2ban/
|
||||||
|
cp log4py.py $(DESTDIR)/usr/lib/fail2ban/
|
||||||
|
|
||||||
|
clean::
|
||||||
|
rm -rf changelog.gz fail2ban `find -iname '*.pyc' `
|
|
@ -21,7 +21,7 @@ ipfw-start-rule = 100
|
||||||
# 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
|
||||||
#
|
#
|
||||||
background = false
|
background = true
|
||||||
|
|
||||||
# Option: debug
|
# Option: debug
|
||||||
# Notes.: enable debug mode. More verbose output and bypass root user test.
|
# Notes.: enable debug mode. More verbose output and bypass root user test.
|
||||||
|
@ -45,7 +45,7 @@ logfile = /var/log/fail2ban.log
|
||||||
# Notes.: number of retrys before IP gets banned.
|
# Notes.: number of retrys before IP gets banned.
|
||||||
# Values: NUM Default: 3
|
# Values: NUM Default: 3
|
||||||
#
|
#
|
||||||
maxretry = 3
|
maxretry = 5
|
||||||
|
|
||||||
# Option: bantime
|
# Option: bantime
|
||||||
# Notes.: number of seconds an IP will be banned.
|
# Notes.: number of seconds an IP will be banned.
|
||||||
|
@ -85,9 +85,9 @@ enabled = false
|
||||||
|
|
||||||
# Option: logfile
|
# Option: logfile
|
||||||
# Notes.: logfile to monitor.
|
# Notes.: logfile to monitor.
|
||||||
# Values: FILE Default: /var/log/httpd/access_log
|
# Values: FILE Default: /var/log/apache/access_log
|
||||||
#
|
#
|
||||||
logfile = /var/log/httpd/access_log
|
logfile = /var/log/apache/access_log
|
||||||
|
|
||||||
# Option: timeregex
|
# Option: timeregex
|
||||||
# Notes.: regex to match timestamp in Apache logfile.
|
# Notes.: regex to match timestamp in Apache logfile.
|
||||||
|
@ -118,9 +118,9 @@ enabled = true
|
||||||
|
|
||||||
# Option: logfile
|
# Option: logfile
|
||||||
# Notes.: logfile to monitor.
|
# Notes.: logfile to monitor.
|
||||||
# Values: FILE Default: /var/log/secure
|
# Values: FILE Default: /var/log/auth.log
|
||||||
#
|
#
|
||||||
logfile = /var/log/secure
|
logfile = /var/log/auth.log
|
||||||
|
|
||||||
# Option: timeregex
|
# Option: timeregex
|
||||||
# Notes.: regex to match timestamp in SSH logfile.
|
# Notes.: regex to match timestamp in SSH logfile.
|
||||||
|
@ -140,4 +140,4 @@ timepattern = %%b %%d %%H:%%M:%%S
|
||||||
# Notes.: regex to match the password failures messages in the logfile.
|
# Notes.: regex to match the password failures messages in the logfile.
|
||||||
# Values: TEXT Default: Authentication failure|Failed password|Invalid user
|
# Values: TEXT Default: Authentication failure|Failed password|Invalid user
|
||||||
#
|
#
|
||||||
failregex = Authentication failure|Failed password|Invalid user
|
failregex = Authentication failure|Failed password|Invalid user|Illegal user|Failed keyboard-interactive
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
fail2ban for Debian
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
This package is nearly 100% identical to the upstream version. It was 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
|
||||||
|
|
||||||
|
See the file TODO.Debian for more details, as well as the Debian Bug Tracking
|
||||||
|
system.
|
||||||
|
|
||||||
|
-- Yaroslav Halchenko <debian@onerussian.com>, Tue, 4 Jul 2005 00:00:00 -1000
|
|
@ -0,0 +1,3 @@
|
||||||
|
Because this is just a quick hack to package fail2ban it might be missing some crucial parts...
|
||||||
|
|
||||||
|
-- yoh@onerussian.com
|
|
@ -0,0 +1,6 @@
|
||||||
|
fail2ban (0.4.1-1) unstable; urgency=low
|
||||||
|
|
||||||
|
* First upstream release of a Debian package
|
||||||
|
|
||||||
|
-- Yaroslav Halchenko <debian@onerussian.com> Mon, 04 Jul 2005 11:47:23 +0300
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
4
|
|
@ -0,0 +1,18 @@
|
||||||
|
Source: fail2ban
|
||||||
|
Section: net
|
||||||
|
Priority: optional
|
||||||
|
Maintainer: Yaroslav Halchenko <yoh@onerussian.com>
|
||||||
|
Build-Depends-Indep: debhelper (>= 4.1.67), python, python2.3-dev
|
||||||
|
Standards-Version: 3.6.1
|
||||||
|
|
||||||
|
Package: fail2ban
|
||||||
|
Architecture: all
|
||||||
|
Depends: ${shlibs:Depends}, ${misc:Depends}, python, iptables
|
||||||
|
Description: Ban IPs that make too many password failure
|
||||||
|
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. Currently, iptables, ipfwadm and ipfw are supported.
|
||||||
|
.
|
||||||
|
Homepage: http://www.sourceforge.net/projects/fail2ban
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
This package was originally debianized by Yaroslav Halchenko
|
||||||
|
<debian@onerussian.com> on Mon Jul 4 14:41:34 HST 2005
|
||||||
|
|
||||||
|
It was downloaded from http://www.sourceforge.net/projects/fail2ban
|
||||||
|
pointed from the project page of the upstream author
|
||||||
|
Cyril Jaquier: <lostcontrol@users.sourceforge.net>
|
||||||
|
http://fail2ban.sourceforge.net
|
||||||
|
|
||||||
|
Copyright:
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, 59 Temple Place - Suite 330,
|
||||||
|
Boston, MA 02111-1307, USA.
|
||||||
|
|
||||||
|
See /usr/share/common-licenses/GPL for the full license.
|
|
@ -0,0 +1,2 @@
|
||||||
|
/etc/fail2ban.conf
|
||||||
|
/etc/default/fail2ban
|
|
@ -0,0 +1,61 @@
|
||||||
|
#! /bin/sh
|
||||||
|
#
|
||||||
|
# skeleton example file to build /etc/init.d/ scripts.
|
||||||
|
# This file should be used to construct scripts for /etc/init.d.
|
||||||
|
#
|
||||||
|
# Written by Miquel van Smoorenburg <miquels@cistron.nl>.
|
||||||
|
# Modified for Debian
|
||||||
|
# by Ian Murdock <imurdock@gnu.ai.mit.edu>.
|
||||||
|
#
|
||||||
|
# Version: @(#)skeleton 1.9 26-Feb-2001 miquels@cistron.nl
|
||||||
|
#
|
||||||
|
|
||||||
|
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
|
||||||
|
DAEMON=/usr/sbin/fail2ban
|
||||||
|
NAME=fail2ban
|
||||||
|
DESC=fail2ban
|
||||||
|
PIDFILE=/var/run/$NAME.pid
|
||||||
|
|
||||||
|
test -x $DAEMON || exit 0
|
||||||
|
|
||||||
|
# Include fail2ban defaults if available
|
||||||
|
if [ -f /etc/default/fail2ban ] ; then
|
||||||
|
. /etc/default/fail2ban
|
||||||
|
fi
|
||||||
|
DAEMON_OPTS=$FAIL2BAN_OPTS
|
||||||
|
set -e
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
start)
|
||||||
|
echo -n "Starting $DESC: "
|
||||||
|
[ -f $PIDFILE ] && [ ! -d /proc/`cat $PIDFILE` ] && rm -f $PIDFILE
|
||||||
|
start-stop-daemon --start --quiet --pidfile /var/run/$NAME.pid \
|
||||||
|
-b --exec $DAEMON -- $DAEMON_OPTS
|
||||||
|
echo "$NAME."
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
echo -n "Stopping $DESC: "
|
||||||
|
start-stop-daemon --stop --quiet --pidfile /var/run/$NAME.pid
|
||||||
|
rm -f $PIDFILE
|
||||||
|
echo "$NAME."
|
||||||
|
;;
|
||||||
|
restart|force-reload)
|
||||||
|
#
|
||||||
|
# If the "reload" option is implemented, move the "force-reload"
|
||||||
|
# option to the "reload" entry above. If not, "force-reload" is
|
||||||
|
# just the same as "restart".
|
||||||
|
#
|
||||||
|
echo -n "Restarting $DESC: "
|
||||||
|
( $0 stop )
|
||||||
|
sleep 1
|
||||||
|
$0 start
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
N=/etc/init.d/$NAME
|
||||||
|
# echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2
|
||||||
|
echo "Usage: $N {start|stop|restart|force-reload}" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit 0
|
|
@ -0,0 +1 @@
|
||||||
|
/usr/bin/fail2ban /usr/sbin/fail2ban
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/make -f
|
||||||
|
# Copyrignt 2003-04 Yaroslav Halchenko <debian@onerussian.com>
|
||||||
|
|
||||||
|
include /usr/share/cdbs/1/rules/debhelper.mk
|
||||||
|
include /usr/share/cdbs/1/class/makefile.mk
|
||||||
|
include /usr/share/cdbs/1/class/python-distutils.mk
|
||||||
|
#include /usr/share/cdbs/1/rules/buildcore.mk
|
||||||
|
#include /usr/share/cdbs/1/class/autotools.mk
|
||||||
|
|
||||||
|
#DEB_MAKE_INSTALL_TARGET := "install DESTDIR=debian/fail2ban"
|
||||||
|
# does on package by package bases so doesn't work :-/
|
||||||
|
#DEB_DH_INSTALL_ARGS := --list-missing
|
||||||
|
# DEB_OPT_FLAG :=
|
||||||
|
# DEB_CONFIGURE_EXTRA_FLAGS := --enable-debug
|
||||||
|
# tight versioning
|
||||||
|
#DEB_DH_MAKESHLIBS_ARGS := -V
|
||||||
|
common-install-prehook-impl::
|
||||||
|
make install
|
||||||
|
clean::
|
||||||
|
make clean
|
|
@ -0,0 +1,6 @@
|
||||||
|
# watch control file for uscan
|
||||||
|
# Run the "uscan" command to check for upstream updates and more.
|
||||||
|
# Site Directory Pattern Version Script
|
||||||
|
version=2
|
||||||
|
http://voxel.dl.sourceforge.net/sourceforge/fail2ban/ \
|
||||||
|
fail2ban-([0-9]+\.[0-9]+\.[0-9]*)\.tar\.bz2 debian uupdate
|
|
@ -29,6 +29,10 @@ __license__ = "GPL"
|
||||||
import time, sys, getopt, os, signal, string
|
import time, sys, getopt, os, signal, string
|
||||||
from ConfigParser import *
|
from ConfigParser import *
|
||||||
|
|
||||||
|
# Appends our own modules path
|
||||||
|
# you: moved before loading log4py so we add path to it
|
||||||
|
sys.path.append('/usr/lib/fail2ban')
|
||||||
|
|
||||||
# Checks if log4py is present.
|
# Checks if log4py is present.
|
||||||
try:
|
try:
|
||||||
import log4py
|
import log4py
|
||||||
|
@ -36,8 +40,6 @@ except:
|
||||||
print "log4py is needed (see README)"
|
print "log4py is needed (see README)"
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
# Appends our own modules path
|
|
||||||
sys.path.append('/usr/lib/fail2ban')
|
|
||||||
|
|
||||||
from firewall.iptables import Iptables
|
from firewall.iptables import Iptables
|
||||||
from firewall.ipfw import Ipfw
|
from firewall.ipfw import Ipfw
|
||||||
|
|
|
@ -0,0 +1,593 @@
|
||||||
|
"""
|
||||||
|
|
||||||
|
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)
|
2
setup.py
2
setup.py
|
@ -36,7 +36,7 @@ setup(
|
||||||
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://www.sourceforge.net/projects/fail2ban",
|
||||||
scripts = ['fail2ban.py'],
|
scripts = ['fail2ban'],
|
||||||
py_modules = ['version'],
|
py_modules = ['version'],
|
||||||
packages = ['firewall', 'logreader', 'confreader', 'utils']
|
packages = ['firewall', 'logreader', 'confreader', 'utils']
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue