mirror of https://github.com/fail2ban/fail2ban
- Initial 0.5 commit
git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/branches/FAIL2BAN-0_5@115 a942ae1a-1317-0410-a47c-b1dcaea8d6050.5
parent
ea9a671e88
commit
685f4aad99
|
@ -9,6 +9,8 @@ Fail2Ban (version 0.?.?) ??/??/2005
|
||||||
|
|
||||||
ver. 0.?.? (??/??/2005) - ???
|
ver. 0.?.? (??/??/2005) - ???
|
||||||
----------
|
----------
|
||||||
|
- Added firewall rules definition in the configuration file
|
||||||
|
- Cleaned a bit fail2ban.py
|
||||||
- Added an initd script for RedHat/Fedora. Thanks to Andrey
|
- Added an initd script for RedHat/Fedora. Thanks to Andrey
|
||||||
G. Grozin
|
G. Grozin
|
||||||
|
|
||||||
|
|
5
MANIFEST
5
MANIFEST
|
@ -7,15 +7,14 @@ version.py
|
||||||
fail2ban.py
|
fail2ban.py
|
||||||
firewall/__init__.py
|
firewall/__init__.py
|
||||||
firewall/firewall.py
|
firewall/firewall.py
|
||||||
firewall/iptables.py
|
|
||||||
firewall/ipfw.py
|
|
||||||
firewall/ipfwadm.py
|
|
||||||
logreader/__init__.py
|
logreader/__init__.py
|
||||||
logreader/logreader.py
|
logreader/logreader.py
|
||||||
confreader/__init__.py
|
confreader/__init__.py
|
||||||
confreader/configreader.py
|
confreader/configreader.py
|
||||||
utils/__init__.py
|
utils/__init__.py
|
||||||
utils/dns.py
|
utils/dns.py
|
||||||
|
utils/process.py
|
||||||
config/fail2ban.conf.default
|
config/fail2ban.conf.default
|
||||||
config/gentoo-initd
|
config/gentoo-initd
|
||||||
config/gentoo-confd
|
config/gentoo-confd
|
||||||
|
config/redhat-initd
|
||||||
|
|
3
README
3
README
|
@ -121,8 +121,7 @@ Thanks:
|
||||||
-------
|
-------
|
||||||
|
|
||||||
Kévin Drapel, Marvin Rouge, Sireyessire, Robert Edeker,
|
Kévin Drapel, Marvin Rouge, Sireyessire, Robert Edeker,
|
||||||
Tom Pike, Iain Lea
|
Tom Pike, Iain Lea, Andrey G. Grozin
|
||||||
|
|
||||||
|
|
||||||
License:
|
License:
|
||||||
--------
|
--------
|
||||||
|
|
|
@ -87,7 +87,25 @@ enabled = false
|
||||||
# Notes.: logfile to monitor.
|
# Notes.: logfile to monitor.
|
||||||
# Values: FILE Default: /var/log/httpd/access_log
|
# Values: FILE Default: /var/log/httpd/access_log
|
||||||
#
|
#
|
||||||
logfile = /var/log/httpd/access_log
|
logfile = /home/cyril/workspace/fail2ban/log-test/apache
|
||||||
|
|
||||||
|
# Option: fwbanrule
|
||||||
|
# Notes.: command executed when banning an IP. Take care that the
|
||||||
|
# command is executed with Fail2Ban user rights.
|
||||||
|
# <if> interface name
|
||||||
|
# <ip> IP address
|
||||||
|
# Values: CMD
|
||||||
|
# Default iptables -I INPUT 1 -i <if> -s <ip> -j DROP
|
||||||
|
fwbanrule = iptables -I INPUT 1 -i <if> -s <ip> -j DROP
|
||||||
|
|
||||||
|
# Option: fwunbanrule
|
||||||
|
# Notes.: command executed when unbanning an IP. Take care that the
|
||||||
|
# command is executed with Fail2Ban user rights.
|
||||||
|
# <if> interface name
|
||||||
|
# <ip> IP address
|
||||||
|
# Values: CMD
|
||||||
|
# Default iptables -D INPUT -i <if> -s <ip> -j DROP
|
||||||
|
fwunbanrule = iptables -D INPUT -i <if> -s <ip> -j DROP
|
||||||
|
|
||||||
# Option: timeregex
|
# Option: timeregex
|
||||||
# Notes.: regex to match timestamp in Apache logfile.
|
# Notes.: regex to match timestamp in Apache logfile.
|
||||||
|
@ -120,7 +138,25 @@ enabled = true
|
||||||
# Notes.: logfile to monitor.
|
# Notes.: logfile to monitor.
|
||||||
# Values: FILE Default: /var/log/secure
|
# Values: FILE Default: /var/log/secure
|
||||||
#
|
#
|
||||||
logfile = /var/log/secure
|
logfile = /home/cyril/workspace/fail2ban/log-test/test
|
||||||
|
|
||||||
|
# Option: fwbanrule
|
||||||
|
# Notes.: command executed when banning an IP. Take care that the
|
||||||
|
# command is executed with Fail2Ban user rights.
|
||||||
|
# <if> interface name
|
||||||
|
# <ip> IP address
|
||||||
|
# Values: CMD
|
||||||
|
# Default iptables -I INPUT 1 -i <if> -s <ip> -j DROP
|
||||||
|
fwbanrule = iptables -I INPUT 1 -i <if> -s <ip> -j DROP
|
||||||
|
|
||||||
|
# Option: fwunbanrule
|
||||||
|
# Notes.: command executed when unbanning an IP. Take care that the
|
||||||
|
# command is executed with Fail2Ban user rights.
|
||||||
|
# <if> interface name
|
||||||
|
# <ip> IP address
|
||||||
|
# Values: CMD
|
||||||
|
# Default iptables -D INPUT -i <if> -s <ip> -j DROP
|
||||||
|
fwunbanrule = iptables -D INPUT -i <if> -s <ip> -j DROP
|
||||||
|
|
||||||
# Option: timeregex
|
# Option: timeregex
|
||||||
# Notes.: regex to match timestamp in SSH logfile.
|
# Notes.: regex to match timestamp in SSH logfile.
|
||||||
|
|
|
@ -24,8 +24,13 @@ __date__ = "$Date$"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
import log4py
|
||||||
|
|
||||||
from ConfigParser import *
|
from ConfigParser import *
|
||||||
|
|
||||||
|
# Gets the instance of log4py.
|
||||||
|
logSys = log4py.Logger().get_instance()
|
||||||
|
|
||||||
class ConfigReader:
|
class ConfigReader:
|
||||||
""" This class allow the handling of the configuration options.
|
""" This class allow the handling of the configuration options.
|
||||||
The DEFAULT section contains the global information about
|
The DEFAULT section contains the global information about
|
||||||
|
@ -40,12 +45,13 @@ class ConfigReader:
|
||||||
["str", "logfile", "/dev/null"],
|
["str", "logfile", "/dev/null"],
|
||||||
["str", "timeregex", ""],
|
["str", "timeregex", ""],
|
||||||
["str", "timepattern", ""],
|
["str", "timepattern", ""],
|
||||||
["str", "failregex", ""])
|
["str", "failregex", ""],
|
||||||
|
["str", "fwbanrule", ""],
|
||||||
|
["str", "fwunbanrule", ""])
|
||||||
|
|
||||||
def __init__(self, logSys, confPath):
|
def __init__(self, confPath):
|
||||||
self.confPath = confPath
|
self.confPath = confPath
|
||||||
self.configParser = SafeConfigParser()
|
self.configParser = SafeConfigParser()
|
||||||
self.logSys = logSys
|
|
||||||
|
|
||||||
def openConf(self):
|
def openConf(self):
|
||||||
""" Opens the configuration file.
|
""" Opens the configuration file.
|
||||||
|
@ -74,7 +80,7 @@ class ConfigReader:
|
||||||
|
|
||||||
values[option[1]] = v
|
values[option[1]] = v
|
||||||
except NoOptionError:
|
except NoOptionError:
|
||||||
self.logSys.warn("No '"+option[1]+"' defined in '"+sec+"'")
|
logSys.warn("No '" + option[1] + "' defined in '" + sec + "'")
|
||||||
values[option[1]] = option[2]
|
values[option[1]] = option[2]
|
||||||
return values
|
return values
|
||||||
|
|
224
fail2ban.py
224
fail2ban.py
|
@ -26,7 +26,7 @@ __date__ = "$Date$"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import time, sys, getopt, os, signal, string
|
import time, sys, getopt, os, string, signal
|
||||||
from ConfigParser import *
|
from ConfigParser import *
|
||||||
|
|
||||||
# Checks if log4py is present.
|
# Checks if log4py is present.
|
||||||
|
@ -39,13 +39,15 @@ except:
|
||||||
# Appends our own modules path
|
# Appends our own modules path
|
||||||
sys.path.append('/usr/lib/fail2ban')
|
sys.path.append('/usr/lib/fail2ban')
|
||||||
|
|
||||||
from firewall.iptables import Iptables
|
from firewall.firewall import Firewall
|
||||||
from firewall.ipfw import Ipfw
|
|
||||||
from firewall.ipfwadm import Ipfwadm
|
|
||||||
from logreader.logreader import LogReader
|
from logreader.logreader import LogReader
|
||||||
from confreader.configreader import ConfigReader
|
from confreader.configreader import ConfigReader
|
||||||
|
from utils.process import *
|
||||||
from version import version
|
from version import version
|
||||||
|
|
||||||
|
# Gets the instance of log4py.
|
||||||
|
logSys = log4py.Logger().get_instance()
|
||||||
|
|
||||||
def usage():
|
def usage():
|
||||||
print "Usage: fail2ban.py [OPTIONS]"
|
print "Usage: fail2ban.py [OPTIONS]"
|
||||||
print
|
print
|
||||||
|
@ -79,141 +81,26 @@ def checkForRoot():
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def createDaemon():
|
|
||||||
"""Detach a process from the controlling terminal and run it in the
|
|
||||||
background as a daemon.
|
|
||||||
|
|
||||||
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278731
|
|
||||||
"""
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Fork a child process so the parent can exit. This will return control
|
|
||||||
# to the command line or shell. This is required so that the new process
|
|
||||||
# is guaranteed not to be a process group leader. We have this guarantee
|
|
||||||
# because the process GID of the parent is inherited by the child, but
|
|
||||||
# the child gets a new PID, making it impossible for its PID to equal its
|
|
||||||
# PGID.
|
|
||||||
pid = os.fork()
|
|
||||||
except OSError, e:
|
|
||||||
return((e.errno, e.strerror)) # ERROR (return a tuple)
|
|
||||||
|
|
||||||
if (pid == 0): # The first child.
|
|
||||||
|
|
||||||
# Next we call os.setsid() to become the session leader of this new
|
|
||||||
# session. The process also becomes the process group leader of the
|
|
||||||
# new process group. Since a controlling terminal is associated with a
|
|
||||||
# session, and this new session has not yet acquired a controlling
|
|
||||||
# terminal our process now has no controlling terminal. This shouldn't
|
|
||||||
# fail, since we're guaranteed that the child is not a process group
|
|
||||||
# leader.
|
|
||||||
os.setsid()
|
|
||||||
|
|
||||||
# When the first child terminates, all processes in the second child
|
|
||||||
# are sent a SIGHUP, so it's ignored.
|
|
||||||
signal.signal(signal.SIGHUP, signal.SIG_IGN)
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Fork a second child to prevent zombies. Since the first child is
|
|
||||||
# a session leader without a controlling terminal, it's possible for
|
|
||||||
# it to acquire one by opening a terminal in the future. This second
|
|
||||||
# fork guarantees that the child is no longer a session leader, thus
|
|
||||||
# preventing the daemon from ever acquiring a controlling terminal.
|
|
||||||
pid = os.fork() # Fork a second child.
|
|
||||||
except OSError, e:
|
|
||||||
return((e.errno, e.strerror)) # ERROR (return a tuple)
|
|
||||||
|
|
||||||
if (pid == 0): # The second child.
|
|
||||||
# Ensure that the daemon doesn't keep any directory in use. Failure
|
|
||||||
# to do this could make a filesystem unmountable.
|
|
||||||
os.chdir("/")
|
|
||||||
# Give the child complete control over permissions.
|
|
||||||
os.umask(0)
|
|
||||||
else:
|
|
||||||
os._exit(0) # Exit parent (the first child) of the second child.
|
|
||||||
else:
|
|
||||||
os._exit(0) # Exit parent of the first child.
|
|
||||||
|
|
||||||
# Close all open files. Try the system configuration variable, SC_OPEN_MAX,
|
|
||||||
# for the maximum number of open files to close. If it doesn't exist, use
|
|
||||||
# the default value (configurable).
|
|
||||||
try:
|
|
||||||
maxfd = os.sysconf("SC_OPEN_MAX")
|
|
||||||
except (AttributeError, ValueError):
|
|
||||||
maxfd = 256 # default maximum
|
|
||||||
|
|
||||||
for fd in range(0, maxfd):
|
|
||||||
try:
|
|
||||||
os.close(fd)
|
|
||||||
except OSError: # ERROR (ignore)
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Redirect the standard file descriptors to /dev/null.
|
|
||||||
os.open("/dev/null", os.O_RDONLY) # standard input (0)
|
|
||||||
os.open("/dev/null", os.O_RDWR) # standard output (1)
|
|
||||||
os.open("/dev/null", os.O_RDWR) # standard error (2)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def sigTERMhandler(signum, frame):
|
def sigTERMhandler(signum, frame):
|
||||||
""" Handles the TERM signal when in daemon mode in order to
|
""" Handles the TERM signal when in daemon mode in order to
|
||||||
exit properly.
|
exit properly.
|
||||||
"""
|
"""
|
||||||
logSys.debug("Signal handler called with sig "+`signum`)
|
logSys.debug("Signal handler called with sig "+`signum`)
|
||||||
killApp()
|
killApp()
|
||||||
|
|
||||||
def killApp():
|
def killApp():
|
||||||
""" Flush the ban list, remove the PID lock file and exit
|
""" Flush the ban list, remove the PID lock file and exit
|
||||||
nicely.
|
nicely.
|
||||||
"""
|
"""
|
||||||
logSys.warn("Restoring firewall rules...")
|
logSys.warn("Restoring firewall rules...")
|
||||||
fireWall.flushBanList(conf["debug"])
|
for element in logFwList:
|
||||||
|
element[2].flushBanList(conf["debug"])
|
||||||
removePID(conf["pidlock"])
|
removePID(conf["pidlock"])
|
||||||
logSys.info("Exiting...")
|
logSys.info("Exiting...")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
def checkForPID(lockfile):
|
|
||||||
""" Checks for running Fail2Ban.
|
|
||||||
|
|
||||||
Returns the current PID if Fail2Ban is running or False
|
|
||||||
if no instance found.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
fileHandler = open(lockfile)
|
|
||||||
pid = fileHandler.readline()
|
|
||||||
return pid
|
|
||||||
except IOError:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def createPID(lockfile):
|
|
||||||
""" Creates a PID lock file with the current PID.
|
|
||||||
"""
|
|
||||||
fileHandler = open(lockfile, mode='w')
|
|
||||||
pid = os.getpid()
|
|
||||||
fileHandler.write(`pid`+'\n')
|
|
||||||
fileHandler.close()
|
|
||||||
logSys.debug("Created PID lock ("+`pid`+") in "+lockfile)
|
|
||||||
|
|
||||||
def removePID(lockfile):
|
|
||||||
""" Remove PID lock.
|
|
||||||
"""
|
|
||||||
os.remove(lockfile)
|
|
||||||
logSys.debug("Removed PID lock "+lockfile)
|
|
||||||
|
|
||||||
def killPID(pid):
|
|
||||||
""" Kills the process with the given PID using the
|
|
||||||
INT signal (same effect as <ctrl>+<c>).
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
return os.kill(pid, 2)
|
|
||||||
except OSError:
|
|
||||||
logSys.error("Can not kill process " + `pid` + ". Please check that " +
|
|
||||||
"Fail2Ban is not running and remove the file " +
|
|
||||||
"'/tmp/fail2ban.pid'")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
||||||
# Gets an instance of log4py.
|
|
||||||
logSys = log4py.Logger().get_instance()
|
|
||||||
logSys.set_formatstring("%T %L %M")
|
logSys.set_formatstring("%T %L %M")
|
||||||
|
|
||||||
conf = dict()
|
conf = dict()
|
||||||
|
@ -453,38 +340,32 @@ if __name__ == "__main__":
|
||||||
|
|
||||||
# Reads the config file and create a LogReader instance for
|
# Reads the config file and create a LogReader instance for
|
||||||
# each log file to check.
|
# each log file to check.
|
||||||
confReader = ConfigReader(logSys, conf["conffile"]);
|
confReader = ConfigReader(conf["conffile"]);
|
||||||
confReader.openConf()
|
confReader.openConf()
|
||||||
logList = list()
|
logFwList = list()
|
||||||
for t in confReader.getSections():
|
for t in confReader.getSections():
|
||||||
l = confReader.getLogOptions(t)
|
l = confReader.getLogOptions(t)
|
||||||
if l["enabled"]:
|
if l["enabled"]:
|
||||||
lObj = LogReader(logSys, l["logfile"], l["timeregex"],
|
# Creates a logreader object
|
||||||
l["timepattern"], l["failregex"], conf["bantime"])
|
lObj = LogReader(l["logfile"], l["timeregex"], l["timepattern"],
|
||||||
lObj.setName(t)
|
l["failregex"], conf["bantime"])
|
||||||
logList.append(lObj)
|
# Creates a firewall object
|
||||||
|
fObj = Firewall(l["fwbanrule"], l["fwunbanrule"], conf["bantime"],
|
||||||
# Creates one instance of Iptables (thanks to Pyhton dynamic
|
conf["interface"])
|
||||||
# features).
|
# Links them into a list. I'm not really happy
|
||||||
fireWallObj = eval(fireWallName)
|
# with this :/
|
||||||
fireWall = fireWallObj(conf["bantime"], logSys, conf["interface"])
|
logFwList.append([t, lObj, fObj, dict()])
|
||||||
|
|
||||||
# IPFW needs rules number. The configuration option "ipfw-start-rule"
|
|
||||||
# defines the first rule number used by Fail2Ban.
|
|
||||||
if fireWallName == "Ipfw":
|
|
||||||
fireWall.setCrtRuleNbr(conf["ipfw-start-rule"])
|
|
||||||
|
|
||||||
# We add 127.0.0.1 to the ignore list has we do not want
|
# We add 127.0.0.1 to the ignore list has we do not want
|
||||||
# to be ban ourself.
|
# to be ban ourself.
|
||||||
for element in logList:
|
for element in logFwList:
|
||||||
element.addIgnoreIP("127.0.0.1")
|
element[1].addIgnoreIP("127.0.0.1")
|
||||||
while len(ignoreIPList) > 0:
|
while len(ignoreIPList) > 0:
|
||||||
ip = ignoreIPList.pop()
|
ip = ignoreIPList.pop()
|
||||||
for element in logList:
|
for element in logFwList:
|
||||||
element.addIgnoreIP(ip)
|
element[1].addIgnoreIP(ip)
|
||||||
|
|
||||||
logSys.info("Fail2Ban v"+version+" is running")
|
logSys.info("Fail2Ban v"+version+" is running")
|
||||||
failListFull = dict()
|
|
||||||
# Main loop
|
# Main loop
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
|
@ -493,14 +374,15 @@ if __name__ == "__main__":
|
||||||
|
|
||||||
# Checks if some IP have to be remove from ban
|
# Checks if some IP have to be remove from ban
|
||||||
# list.
|
# list.
|
||||||
fireWall.checkForUnBan(conf["debug"])
|
for element in logFwList:
|
||||||
|
element[2].checkForUnBan(conf["debug"])
|
||||||
|
|
||||||
# If the log file has not been modified since the
|
# If the log file has not been modified since the
|
||||||
# last time, we sleep for 1 second. This is active
|
# last time, we sleep for 1 second. This is active
|
||||||
# polling so not very effective.
|
# polling so not very effective.
|
||||||
modList = list()
|
modList = list()
|
||||||
for element in logList:
|
for element in logFwList:
|
||||||
if element.isModified():
|
if element[1].isModified():
|
||||||
modList.append(element)
|
modList.append(element)
|
||||||
|
|
||||||
if len(modList) == 0:
|
if len(modList) == 0:
|
||||||
|
@ -509,42 +391,32 @@ if __name__ == "__main__":
|
||||||
|
|
||||||
# Gets the failure list from the log file. For a given IP,
|
# Gets the failure list from the log file. For a given IP,
|
||||||
# takes only the service which has the most password failures.
|
# takes only the service which has the most password failures.
|
||||||
failList = dict()
|
|
||||||
for element in modList:
|
for element in modList:
|
||||||
e = element.getFailures()
|
e = element[1].getFailures()
|
||||||
for key in e.iterkeys():
|
for key in e.iterkeys():
|
||||||
if failList.has_key(key):
|
if element[3].has_key(key):
|
||||||
if failList[key][0] < e[key][0]:
|
element[3][key] = (element[3][key][0] + e[key][0],
|
||||||
failList[key] = (e[key][0], e[key][1], element)
|
e[key][1])
|
||||||
else:
|
else:
|
||||||
failList[key] = (e[key][0], e[key][1], element)
|
element[3][key] = (e[key][0], e[key][1])
|
||||||
|
|
||||||
# Add the last log failures to the global failure list.
|
|
||||||
for key in failList.iterkeys():
|
|
||||||
if failListFull.has_key(key):
|
|
||||||
failListFull[key] = (failListFull[key][0] + 1,
|
|
||||||
failList[key][1], failList[key][2])
|
|
||||||
else:
|
|
||||||
failListFull[key] = failList[key]
|
|
||||||
|
|
||||||
# Remove the oldest failure attempts from the global list.
|
# Remove the oldest failure attempts from the global list.
|
||||||
unixTime = time.time()
|
|
||||||
failListFullTemp = failListFull.copy()
|
|
||||||
for key in failListFullTemp.iterkeys():
|
|
||||||
failTime = failListFullTemp[key][2].getFindTime()
|
|
||||||
if failListFullTemp[key][1] < unixTime - failTime:
|
|
||||||
del failListFull[key]
|
|
||||||
|
|
||||||
# We iterate the failure list and ban IP that make
|
# We iterate the failure list and ban IP that make
|
||||||
# *retryAllowed* login failures.
|
# *retryAllowed* login failures.
|
||||||
failListFullTemp = failListFull.copy()
|
unixTime = time.time()
|
||||||
for key in failListFullTemp.iterkeys():
|
for element in logFwList:
|
||||||
element = failListFullTemp[key]
|
fails = element[3].copy()
|
||||||
if element[0] >= conf["maxretry"]:
|
findTime = element[1].getFindTime()
|
||||||
logSys.info(element[2].getName()+": "+key+" has "+
|
for attempt in fails:
|
||||||
`element[0]`+" login failure(s). Banned.")
|
failTime = fails[attempt][1]
|
||||||
fireWall.addBanIP(key, conf["debug"])
|
if failTime < unixTime - failTime:
|
||||||
del failListFull[key]
|
del element[3][attempt]
|
||||||
|
elif fails[attempt][0] >= conf["maxretry"]:
|
||||||
|
logSys.info(element[0] + ": " + attempt + " has " +
|
||||||
|
`element[3][attempt][0]` +
|
||||||
|
" login failure(s). Banned.")
|
||||||
|
element[2].addBanIP(attempt, conf["debug"])
|
||||||
|
del element[3][attempt]
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
# When the user press <ctrl>+<c> we exit nicely.
|
# When the user press <ctrl>+<c> we exit nicely.
|
||||||
|
|
|
@ -24,7 +24,10 @@ __date__ = "$Date$"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import time, os
|
import time, os, log4py, re
|
||||||
|
|
||||||
|
# Gets the instance of log4py.
|
||||||
|
logSys = log4py.Logger().get_instance()
|
||||||
|
|
||||||
class Firewall:
|
class Firewall:
|
||||||
""" Manages the ban list and executes the command that ban
|
""" Manages the ban list and executes the command that ban
|
||||||
|
@ -33,30 +36,31 @@ class Firewall:
|
||||||
|
|
||||||
banList = dict()
|
banList = dict()
|
||||||
|
|
||||||
def __init__(self, banTime, logSys, interface):
|
def __init__(self, banRule, unBanRule, banTime, interface):
|
||||||
|
self.banRule = banRule
|
||||||
|
self.unBanRule = unBanRule
|
||||||
self.banTime = banTime
|
self.banTime = banTime
|
||||||
self.logSys = logSys
|
|
||||||
self.interface = interface
|
self.interface = interface
|
||||||
|
|
||||||
def addBanIP(self, ip, debug):
|
def addBanIP(self, ip, debug):
|
||||||
""" Bans an IP.
|
""" Bans an IP.
|
||||||
"""
|
"""
|
||||||
if not self.inBanList(ip):
|
if not self.inBanList(ip):
|
||||||
self.logSys.warn("Ban "+ip)
|
logSys.warn("Ban "+ip)
|
||||||
self.banList[ip] = time.time()
|
self.banList[ip] = time.time()
|
||||||
self.__executeCmd(self.banIP(ip), debug)
|
self.__executeCmd(self.banIP(ip), debug)
|
||||||
else:
|
else:
|
||||||
self.logSys.error(ip+" already in ban list")
|
logSys.error(ip+" already in ban list")
|
||||||
|
|
||||||
def delBanIP(self, ip, debug):
|
def delBanIP(self, ip, debug):
|
||||||
""" Unban an IP.
|
""" Unban an IP.
|
||||||
"""
|
"""
|
||||||
if self.inBanList(ip):
|
if self.inBanList(ip):
|
||||||
self.logSys.warn("Unban "+ip)
|
logSys.warn("Unban "+ip)
|
||||||
del self.banList[ip]
|
del self.banList[ip]
|
||||||
self.__executeCmd(self.unBanIP(ip), debug)
|
self.__executeCmd(self.unBanIP(ip), debug)
|
||||||
else:
|
else:
|
||||||
self.logSys.error(ip+" not in ban list")
|
logSys.error(ip+" not in ban list")
|
||||||
|
|
||||||
def inBanList(self, ip):
|
def inBanList(self, ip):
|
||||||
""" Checks if IP is in ban list.
|
""" Checks if IP is in ban list.
|
||||||
|
@ -85,12 +89,30 @@ class Firewall:
|
||||||
def __executeCmd(self, cmd, debug):
|
def __executeCmd(self, cmd, debug):
|
||||||
""" Executes an OS command.
|
""" Executes an OS command.
|
||||||
"""
|
"""
|
||||||
self.logSys.debug(cmd)
|
logSys.debug(cmd)
|
||||||
if not debug:
|
if not debug:
|
||||||
return os.system(cmd)
|
return os.system(cmd)
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def banIP(self, ip):
|
||||||
|
""" Returns query to ban IP.
|
||||||
|
"""
|
||||||
|
query = self.replaceTag(self.banRule, ip, self.interface)
|
||||||
|
return query
|
||||||
|
|
||||||
|
def unBanIP(self, ip):
|
||||||
|
""" Returns query to unban IP.
|
||||||
|
"""
|
||||||
|
query = self.replaceTag(self.unBanRule, ip, self.interface)
|
||||||
|
return query
|
||||||
|
|
||||||
|
def replaceTag(self, query, ip, interface):
|
||||||
|
string = query
|
||||||
|
string = string.replace("<ip>", ip)
|
||||||
|
string = string.replace("<if>", interface)
|
||||||
|
return string
|
||||||
|
|
||||||
def viewBanList(self):
|
def viewBanList(self):
|
||||||
""" Prints the ban list on screen. Usefull for debugging.
|
""" Prints the ban list on screen. Usefull for debugging.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,72 +0,0 @@
|
||||||
# This file is part of Fail2Ban.
|
|
||||||
#
|
|
||||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# Fail2Ban is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with Fail2Ban; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
# Author: Cyril Jaquier
|
|
||||||
#
|
|
||||||
# $Revision$
|
|
||||||
|
|
||||||
__author__ = "Cyril Jaquier"
|
|
||||||
__version__ = "$Revision$"
|
|
||||||
__date__ = "$Date$"
|
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
|
||||||
__license__ = "GPL"
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
from firewall import Firewall
|
|
||||||
|
|
||||||
class Ipfw(Firewall):
|
|
||||||
""" This class contains specific methods and variables for the
|
|
||||||
iptables firewall. Must implements the 'abstracts' methods
|
|
||||||
banIP(ip) and unBanIP(ip).
|
|
||||||
|
|
||||||
Must adds abstract methods definition:
|
|
||||||
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/266468
|
|
||||||
"""
|
|
||||||
|
|
||||||
crtRuleNbr = 0
|
|
||||||
|
|
||||||
def getCrtRuleNbr(self):
|
|
||||||
""" Gets the current rule number.
|
|
||||||
"""
|
|
||||||
return self.crtRuleNbr
|
|
||||||
|
|
||||||
def setCrtRuleNbr(self, value):
|
|
||||||
""" Sets the current rule number.
|
|
||||||
"""
|
|
||||||
self.crtRuleNbr = value
|
|
||||||
|
|
||||||
def banIP(self, ip):
|
|
||||||
""" Returns query to ban IP.
|
|
||||||
"""
|
|
||||||
query = "ipfw -q add "+`self.crtRuleNbr`+" deny ip from "+ip+" to any"
|
|
||||||
self.crtRuleNbr = self.crtRuleNbr + 1
|
|
||||||
return query
|
|
||||||
|
|
||||||
def unBanIP(self, ip):
|
|
||||||
""" Returns query to unban IP.
|
|
||||||
"""
|
|
||||||
ruleNbr = str(self.__findRuleNumber(ip))
|
|
||||||
query = "ipfw -q delete "+ruleNbr
|
|
||||||
return query
|
|
||||||
|
|
||||||
def __findRuleNumber(self, ip):
|
|
||||||
""" Uses shell commands in order to find the rule
|
|
||||||
number we want to delete.
|
|
||||||
"""
|
|
||||||
output = os.popen("ipfw list|grep \"from "+ip+" to\"|awk '{print $1}'",
|
|
||||||
"r");
|
|
||||||
return output.read()
|
|
|
@ -1,48 +0,0 @@
|
||||||
# This file is part of Fail2Ban.
|
|
||||||
#
|
|
||||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# Fail2Ban is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with Fail2Ban; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
# Author: Cyril Jaquier
|
|
||||||
#
|
|
||||||
# $Revision$
|
|
||||||
|
|
||||||
__author__ = "Cyril Jaquier"
|
|
||||||
__version__ = "$Revision$"
|
|
||||||
__date__ = "$Date$"
|
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
|
||||||
__license__ = "GPL"
|
|
||||||
|
|
||||||
from firewall import Firewall
|
|
||||||
|
|
||||||
class Ipfwadm(Firewall):
|
|
||||||
""" This class contains specific methods and variables for the
|
|
||||||
iptables firewall. Must implements the 'abstracts' methods
|
|
||||||
banIP(ip) and unBanIP(ip).
|
|
||||||
|
|
||||||
Must adds abstract methods definition:
|
|
||||||
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/266468
|
|
||||||
"""
|
|
||||||
|
|
||||||
def banIP(self, ip):
|
|
||||||
""" Returns query to ban IP.
|
|
||||||
"""
|
|
||||||
query = "ipfwadm -I -a deny -W "+self.interface+" -S "+ip
|
|
||||||
return query
|
|
||||||
|
|
||||||
def unBanIP(self, ip):
|
|
||||||
""" Returns query to unban IP.
|
|
||||||
"""
|
|
||||||
query = "ipfwadm -I -d deny -W "+self.interface+" -S "+ip
|
|
||||||
return query
|
|
|
@ -1,48 +0,0 @@
|
||||||
# This file is part of Fail2Ban.
|
|
||||||
#
|
|
||||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# Fail2Ban is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with Fail2Ban; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
# Author: Cyril Jaquier
|
|
||||||
#
|
|
||||||
# $Revision$
|
|
||||||
|
|
||||||
__author__ = "Cyril Jaquier"
|
|
||||||
__version__ = "$Revision$"
|
|
||||||
__date__ = "$Date$"
|
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
|
||||||
__license__ = "GPL"
|
|
||||||
|
|
||||||
from firewall import Firewall
|
|
||||||
|
|
||||||
class Iptables(Firewall):
|
|
||||||
""" This class contains specific methods and variables for the
|
|
||||||
iptables firewall. Must implements the 'abstracts' methods
|
|
||||||
banIP(ip) and unBanIP(ip).
|
|
||||||
|
|
||||||
Must adds abstract methods definition:
|
|
||||||
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/266468
|
|
||||||
"""
|
|
||||||
|
|
||||||
def banIP(self, ip):
|
|
||||||
""" Returns query to ban IP.
|
|
||||||
"""
|
|
||||||
query = "iptables -I INPUT 1 -i "+self.interface+" -s "+ip+" -j DROP"
|
|
||||||
return query
|
|
||||||
|
|
||||||
def unBanIP(self, ip):
|
|
||||||
""" Returns query to unban IP.
|
|
||||||
"""
|
|
||||||
query = "iptables -D INPUT -i "+self.interface+" -s "+ip+" -j DROP"
|
|
||||||
return query
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
[Wed Mar 31 15:11:28 2005] [error] [client www.google.ch] user cyril: authentication failure for "/phpinfo": Password Mismatch
|
||||||
[Mon Jan 03 05:02:15 2005] [error] [client 81.83.248.17] File does not exist: /var/www/jaquier.dyndns.org/htdocs/msadc
|
[Mon Jan 03 05:02:15 2005] [error] [client 81.83.248.17] File does not exist: /var/www/jaquier.dyndns.org/htdocs/msadc
|
||||||
[Mon Jan 03 05:02:20 2005] [error] [client 81.83.248.17] File does not exist: /var/www/jaquier.dyndns.org/htdocs/msadc
|
[Mon Jan 03 05:02:20 2005] [error] [client 81.83.248.17] File does not exist: /var/www/jaquier.dyndns.org/htdocs/msadc
|
||||||
[Mon Jan 03 05:02:20 2005] [error] [client 81.83.248.17] File does not exist: /var/www/jaquier.dyndns.org/htdocs/msadc
|
[Mon Jan 03 05:02:20 2005] [error] [client 81.83.248.17] File does not exist: /var/www/jaquier.dyndns.org/htdocs/msadc
|
||||||
|
@ -98,5 +99,10 @@
|
||||||
[Wed Jan 05 15:14:24 2005] [error] [client 192.168.0.129] File does not exist: /var/www/jaquier.dyndns.org/htdocs/favicon.ico
|
[Wed Jan 05 15:14:24 2005] [error] [client 192.168.0.129] File does not exist: /var/www/jaquier.dyndns.org/htdocs/favicon.ico
|
||||||
[Wed Jan 05 15:14:29 2005] [error] [client 192.168.0.129] File does not exist: /var/www/jaquier.dyndns.org/htdocs/favicon.ico
|
[Wed Jan 05 15:14:29 2005] [error] [client 192.168.0.129] File does not exist: /var/www/jaquier.dyndns.org/htdocs/favicon.ico
|
||||||
[Wed Jan 05 15:14:34 2005] [error] [client 192.168.0.129] File does not exist: /var/www/jaquier.dyndns.org/htdocs/favicon.ico
|
[Wed Jan 05 15:14:34 2005] [error] [client 192.168.0.129] File does not exist: /var/www/jaquier.dyndns.org/htdocs/favicon.ico
|
||||||
[Wed Mar 05 15:08:28 2005] [error] [client 192.168.0.128] user cyril: authentication failure for "/phpinfo": Password Mismatch
|
[Wed Mar 10 15:08:28 2005] [error] [client www.google.ch] user cyril: authentication failure for "/phpinfo": Password Mismatch
|
||||||
[Wed Mar 05 15:09:28 2005] [error] [client 192.168.0.128] user cyril: authentication failure for "/phpinfo": Password Mismatch
|
[Wed Mar 10 15:09:28 2005] [error] [client www.google.ch] user cyril: authentication failure for "/phpinfo": Password Mismatch
|
||||||
|
[Wed Mar 10 15:11:28 2005] [error] [client www.google.ch] user cyril: authentication failure for "/phpinfo": Password Mismatch
|
||||||
|
[Wed Mar 30 15:11:28 2005] [error] [client www.google.ch] user cyril: authentication failure for "/phpinfo": Password Mismatch
|
||||||
|
[Wed Mar 30 15:11:28 2005] [error] [client www.google.ch] user cyril: authentication failure for "/phpinfo": Password Mismatch
|
||||||
|
[Wed Mar 30 15:11:28 2005] [error] [client www.google.ch] user cyril: authentication failure for "/phpinfo": Password Mismatch
|
||||||
|
[Wed Mar 30 15:11:28 2005] [error] [client www.google.ch] user cyril: authentication failure for "/phpinfo": Password Mismatch
|
|
@ -3,3 +3,9 @@ Jan 7 17:53:26 [sshd] (pam_unix) authentication failure; logname= uid=0 euid=0
|
||||||
Mar 7 17:53:28 [sshd] error: PAM: Authentication failure for kevin from 62.220.137.36
|
Mar 7 17:53:28 [sshd] error: PAM: Authentication failure for kevin from 62.220.137.36
|
||||||
Mar 7 17:55:28 [sshd] error: PAM: Authentication failure for kevin from 62.220.137.36
|
Mar 7 17:55:28 [sshd] error: PAM: Authentication failure for kevin from 62.220.137.36
|
||||||
Mar 7 17:57:28 [sshd] error: PAM: Authentication failure for kevin from 62.220.137.36
|
Mar 7 17:57:28 [sshd] error: PAM: Authentication failure for kevin from 62.220.137.36
|
||||||
|
Mar 7 17:57:28 [sshd] error: PAM: Authentication failure for kevin from 192.168.0.128
|
||||||
|
Mar 7 17:57:28 [sshd] error: PAM: Authentication failure for kevin from 192.168.0.128
|
||||||
|
Mar 7 17:57:28 [sshd] error: PAM: Authentication failure for kevin from 192.168.0.128
|
||||||
|
Mar 7 17:57:28 [sshd] error: PAM: Authentication failure for kevin from 192.168.0.128
|
||||||
|
Mar 7 17:57:28 [sshd] error: PAM: Authentication failure for kevin from www.google.ch
|
||||||
|
Mar 7 17:57:28 [sshd] error: PAM: Authentication failure for kevin from www.google.ch
|
||||||
|
|
|
@ -24,18 +24,21 @@ __date__ = "$Date$"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import os, sys, time, re
|
import os, sys, time, re, log4py
|
||||||
|
|
||||||
from utils.dns import *
|
from utils.dns import *
|
||||||
|
|
||||||
|
# Gets the instance of log4py.
|
||||||
|
logSys = log4py.Logger().get_instance()
|
||||||
|
|
||||||
class LogReader:
|
class LogReader:
|
||||||
""" Reads a log file and reports information about IP that make password
|
""" Reads a log file and reports information about IP that make password
|
||||||
failure, bad user or anything else that is considered as doubtful login
|
failure, bad user or anything else that is considered as doubtful login
|
||||||
attempt.
|
attempt.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, logSys, logPath, timeregex, timepattern, failregex,
|
def __init__(self, logPath, timeregex, timepattern, failregex,
|
||||||
findTime = 3600):
|
findTime = 3600):
|
||||||
self.logPath = logPath
|
self.logPath = logPath
|
||||||
self.timeregex = timeregex
|
self.timeregex = timeregex
|
||||||
self.timepattern = timepattern
|
self.timepattern = timepattern
|
||||||
|
@ -43,20 +46,9 @@ class LogReader:
|
||||||
self.findTime = findTime
|
self.findTime = findTime
|
||||||
self.ignoreIpList = []
|
self.ignoreIpList = []
|
||||||
self.lastModTime = 0
|
self.lastModTime = 0
|
||||||
self.logSys = logSys
|
|
||||||
self.lastPos = 0
|
self.lastPos = 0
|
||||||
self.lastDate = 0
|
self.lastDate = 0
|
||||||
self.logStats = None
|
self.logStats = None
|
||||||
|
|
||||||
def setName(self, name):
|
|
||||||
""" Sets the name of the log reader.
|
|
||||||
"""
|
|
||||||
self.name = name
|
|
||||||
|
|
||||||
def getName(self):
|
|
||||||
""" Gets the name of the log reader.
|
|
||||||
"""
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
def getFindTime(self):
|
def getFindTime(self):
|
||||||
""" Gets the find time.
|
""" Gets the find time.
|
||||||
|
@ -66,7 +58,7 @@ class LogReader:
|
||||||
def addIgnoreIP(self, ip):
|
def addIgnoreIP(self, ip):
|
||||||
""" Adds an IP to the ignore list.
|
""" Adds an IP to the ignore list.
|
||||||
"""
|
"""
|
||||||
self.logSys.debug("Add "+ip+" to ignore list")
|
logSys.debug("Add "+ip+" to ignore list")
|
||||||
self.ignoreIpList.append(ip)
|
self.ignoreIpList.append(ip)
|
||||||
|
|
||||||
def inIgnoreIPList(self, ip):
|
def inIgnoreIPList(self, ip):
|
||||||
|
@ -80,7 +72,7 @@ class LogReader:
|
||||||
try:
|
try:
|
||||||
fileHandler = open(self.logPath)
|
fileHandler = open(self.logPath)
|
||||||
except OSError:
|
except OSError:
|
||||||
self.logSys.error("Unable to open "+self.logPath)
|
logSys.error("Unable to open "+self.logPath)
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
return fileHandler
|
return fileHandler
|
||||||
|
|
||||||
|
@ -90,13 +82,13 @@ class LogReader:
|
||||||
try:
|
try:
|
||||||
self.logStats = os.stat(self.logPath)
|
self.logStats = os.stat(self.logPath)
|
||||||
except OSError:
|
except OSError:
|
||||||
self.logSys.error("Unable to get stat on "+self.logPath)
|
logSys.error("Unable to get stat on "+self.logPath)
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
if self.lastModTime == self.logStats.st_mtime:
|
if self.lastModTime == self.logStats.st_mtime:
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
self.logSys.debug(self.logPath+" has been modified")
|
logSys.debug(self.logPath+" has been modified")
|
||||||
self.lastModTime = self.logStats.st_mtime
|
self.lastModTime = self.logStats.st_mtime
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -107,13 +99,13 @@ class LogReader:
|
||||||
"""
|
"""
|
||||||
line = file.readline()
|
line = file.readline()
|
||||||
if self.lastDate < self.getTime(line):
|
if self.lastDate < self.getTime(line):
|
||||||
self.logSys.debug("Date " + `self.lastDate` + " is " +
|
logSys.debug("Date " + `self.lastDate` + " is " + "smaller than " +
|
||||||
"smaller than " + `self.getTime(line)`)
|
`self.getTime(line)`)
|
||||||
self.logSys.debug("Log rotation detected for " + self.logPath)
|
logSys.debug("Log rotation detected for " + self.logPath)
|
||||||
self.lastPos = 0
|
self.lastPos = 0
|
||||||
|
|
||||||
self.logSys.debug("Setting file position to " + `self.lastPos` + " for "
|
logSys.debug("Setting file position to " + `self.lastPos` + " for " +
|
||||||
+ self.logPath)
|
self.logPath)
|
||||||
file.seek(self.lastPos)
|
file.seek(self.lastPos)
|
||||||
|
|
||||||
def getFailures(self):
|
def getFailures(self):
|
||||||
|
@ -124,7 +116,7 @@ class LogReader:
|
||||||
and the latest failure time.
|
and the latest failure time.
|
||||||
"""
|
"""
|
||||||
ipList = dict()
|
ipList = dict()
|
||||||
self.logSys.debug(self.logPath)
|
logSys.debug(self.logPath)
|
||||||
logFile = self.openLogFile()
|
logFile = self.openLogFile()
|
||||||
self.setFilePos(logFile)
|
self.setFilePos(logFile)
|
||||||
lastLine = ''
|
lastLine = ''
|
||||||
|
@ -137,9 +129,9 @@ class LogReader:
|
||||||
if unixTime < time.time()-self.findTime:
|
if unixTime < time.time()-self.findTime:
|
||||||
break
|
break
|
||||||
if self.inIgnoreIPList(ip):
|
if self.inIgnoreIPList(ip):
|
||||||
self.logSys.debug("Ignore "+ip)
|
logSys.debug("Ignore "+ip)
|
||||||
continue
|
continue
|
||||||
self.logSys.debug("Found "+ip)
|
logSys.debug("Found "+ip)
|
||||||
if ipList.has_key(ip):
|
if ipList.has_key(ip):
|
||||||
ipList[ip] = (ipList[ip][0]+1, unixTime)
|
ipList[ip] = (ipList[ip][0]+1, unixTime)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -24,4 +24,4 @@ __date__ = "$Date$"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
version = "0.4.1"
|
version = "0.5.0-CVS"
|
||||||
|
|
Loading…
Reference in New Issue