- Initial 0.5 commit

git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/branches/FAIL2BAN-0_5@115 a942ae1a-1317-0410-a47c-b1dcaea8d605
0.5
Cyril Jaquier 2005-07-05 12:57:20 +00:00
parent ea9a671e88
commit 685f4aad99
14 changed files with 165 additions and 393 deletions

View File

@ -9,6 +9,8 @@ Fail2Ban (version 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
G. Grozin

View File

@ -7,15 +7,14 @@ version.py
fail2ban.py
firewall/__init__.py
firewall/firewall.py
firewall/iptables.py
firewall/ipfw.py
firewall/ipfwadm.py
logreader/__init__.py
logreader/logreader.py
confreader/__init__.py
confreader/configreader.py
utils/__init__.py
utils/dns.py
utils/process.py
config/fail2ban.conf.default
config/gentoo-initd
config/gentoo-confd
config/redhat-initd

3
README
View File

@ -121,8 +121,7 @@ Thanks:
-------
Kévin Drapel, Marvin Rouge, Sireyessire, Robert Edeker,
Tom Pike, Iain Lea
Tom Pike, Iain Lea, Andrey G. Grozin
License:
--------

View File

@ -87,7 +87,25 @@ enabled = false
# Notes.: logfile to monitor.
# 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
# Notes.: regex to match timestamp in Apache logfile.
@ -120,7 +138,25 @@ enabled = true
# Notes.: logfile to monitor.
# 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
# Notes.: regex to match timestamp in SSH logfile.

View File

@ -24,8 +24,13 @@ __date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import log4py
from ConfigParser import *
# Gets the instance of log4py.
logSys = log4py.Logger().get_instance()
class ConfigReader:
""" This class allow the handling of the configuration options.
The DEFAULT section contains the global information about
@ -40,12 +45,13 @@ class ConfigReader:
["str", "logfile", "/dev/null"],
["str", "timeregex", ""],
["str", "timepattern", ""],
["str", "failregex", ""])
["str", "failregex", ""],
["str", "fwbanrule", ""],
["str", "fwunbanrule", ""])
def __init__(self, logSys, confPath):
def __init__(self, confPath):
self.confPath = confPath
self.configParser = SafeConfigParser()
self.logSys = logSys
def openConf(self):
""" Opens the configuration file.
@ -74,7 +80,7 @@ class ConfigReader:
values[option[1]] = v
except NoOptionError:
self.logSys.warn("No '"+option[1]+"' defined in '"+sec+"'")
logSys.warn("No '" + option[1] + "' defined in '" + sec + "'")
values[option[1]] = option[2]
return values

View File

@ -26,7 +26,7 @@ __date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import time, sys, getopt, os, signal, string
import time, sys, getopt, os, string, signal
from ConfigParser import *
# Checks if log4py is present.
@ -39,13 +39,15 @@ except:
# Appends our own modules path
sys.path.append('/usr/lib/fail2ban')
from firewall.iptables import Iptables
from firewall.ipfw import Ipfw
from firewall.ipfwadm import Ipfwadm
from firewall.firewall import Firewall
from logreader.logreader import LogReader
from confreader.configreader import ConfigReader
from utils.process import *
from version import version
# Gets the instance of log4py.
logSys = log4py.Logger().get_instance()
def usage():
print "Usage: fail2ban.py [OPTIONS]"
print
@ -79,141 +81,26 @@ def checkForRoot():
else:
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):
""" Handles the TERM signal when in daemon mode in order to
exit properly.
"""
logSys.debug("Signal handler called with sig "+`signum`)
killApp()
killApp()
def killApp():
""" Flush the ban list, remove the PID lock file and exit
nicely.
"""
logSys.warn("Restoring firewall rules...")
fireWall.flushBanList(conf["debug"])
for element in logFwList:
element[2].flushBanList(conf["debug"])
removePID(conf["pidlock"])
logSys.info("Exiting...")
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__":
# Gets an instance of log4py.
logSys = log4py.Logger().get_instance()
logSys.set_formatstring("%T %L %M")
conf = dict()
@ -453,38 +340,32 @@ if __name__ == "__main__":
# Reads the config file and create a LogReader instance for
# each log file to check.
confReader = ConfigReader(logSys, conf["conffile"]);
confReader = ConfigReader(conf["conffile"]);
confReader.openConf()
logList = list()
logFwList = list()
for t in confReader.getSections():
l = confReader.getLogOptions(t)
if l["enabled"]:
lObj = LogReader(logSys, l["logfile"], l["timeregex"],
l["timepattern"], l["failregex"], conf["bantime"])
lObj.setName(t)
logList.append(lObj)
# Creates one instance of Iptables (thanks to Pyhton dynamic
# features).
fireWallObj = eval(fireWallName)
fireWall = fireWallObj(conf["bantime"], logSys, conf["interface"])
# IPFW needs rules number. The configuration option "ipfw-start-rule"
# defines the first rule number used by Fail2Ban.
if fireWallName == "Ipfw":
fireWall.setCrtRuleNbr(conf["ipfw-start-rule"])
# Creates a logreader object
lObj = LogReader(l["logfile"], l["timeregex"], l["timepattern"],
l["failregex"], conf["bantime"])
# Creates a firewall object
fObj = Firewall(l["fwbanrule"], l["fwunbanrule"], conf["bantime"],
conf["interface"])
# Links them into a list. I'm not really happy
# with this :/
logFwList.append([t, lObj, fObj, dict()])
# We add 127.0.0.1 to the ignore list has we do not want
# to be ban ourself.
for element in logList:
element.addIgnoreIP("127.0.0.1")
for element in logFwList:
element[1].addIgnoreIP("127.0.0.1")
while len(ignoreIPList) > 0:
ip = ignoreIPList.pop()
for element in logList:
element.addIgnoreIP(ip)
for element in logFwList:
element[1].addIgnoreIP(ip)
logSys.info("Fail2Ban v"+version+" is running")
failListFull = dict()
# Main loop
while True:
try:
@ -493,14 +374,15 @@ if __name__ == "__main__":
# Checks if some IP have to be remove from ban
# list.
fireWall.checkForUnBan(conf["debug"])
for element in logFwList:
element[2].checkForUnBan(conf["debug"])
# If the log file has not been modified since the
# last time, we sleep for 1 second. This is active
# polling so not very effective.
modList = list()
for element in logList:
if element.isModified():
for element in logFwList:
if element[1].isModified():
modList.append(element)
if len(modList) == 0:
@ -509,42 +391,32 @@ if __name__ == "__main__":
# Gets the failure list from the log file. For a given IP,
# takes only the service which has the most password failures.
failList = dict()
for element in modList:
e = element.getFailures()
e = element[1].getFailures()
for key in e.iterkeys():
if failList.has_key(key):
if failList[key][0] < e[key][0]:
failList[key] = (e[key][0], e[key][1], element)
if element[3].has_key(key):
element[3][key] = (element[3][key][0] + e[key][0],
e[key][1])
else:
failList[key] = (e[key][0], e[key][1], element)
# 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]
element[3][key] = (e[key][0], e[key][1])
# 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
# *retryAllowed* login failures.
failListFullTemp = failListFull.copy()
for key in failListFullTemp.iterkeys():
element = failListFullTemp[key]
if element[0] >= conf["maxretry"]:
logSys.info(element[2].getName()+": "+key+" has "+
`element[0]`+" login failure(s). Banned.")
fireWall.addBanIP(key, conf["debug"])
del failListFull[key]
unixTime = time.time()
for element in logFwList:
fails = element[3].copy()
findTime = element[1].getFindTime()
for attempt in fails:
failTime = fails[attempt][1]
if failTime < unixTime - failTime:
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:
# When the user press <ctrl>+<c> we exit nicely.

View File

@ -24,7 +24,10 @@ __date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import time, os
import time, os, log4py, re
# Gets the instance of log4py.
logSys = log4py.Logger().get_instance()
class Firewall:
""" Manages the ban list and executes the command that ban
@ -33,30 +36,31 @@ class Firewall:
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.logSys = logSys
self.interface = interface
def addBanIP(self, ip, debug):
""" Bans an IP.
"""
if not self.inBanList(ip):
self.logSys.warn("Ban "+ip)
logSys.warn("Ban "+ip)
self.banList[ip] = time.time()
self.__executeCmd(self.banIP(ip), debug)
else:
self.logSys.error(ip+" already in ban list")
logSys.error(ip+" already in ban list")
def delBanIP(self, ip, debug):
""" Unban an IP.
"""
if self.inBanList(ip):
self.logSys.warn("Unban "+ip)
logSys.warn("Unban "+ip)
del self.banList[ip]
self.__executeCmd(self.unBanIP(ip), debug)
else:
self.logSys.error(ip+" not in ban list")
logSys.error(ip+" not in ban list")
def inBanList(self, ip):
""" Checks if IP is in ban list.
@ -85,12 +89,30 @@ class Firewall:
def __executeCmd(self, cmd, debug):
""" Executes an OS command.
"""
self.logSys.debug(cmd)
logSys.debug(cmd)
if not debug:
return os.system(cmd)
else:
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):
""" Prints the ban list on screen. Usefull for debugging.
"""

View File

@ -1,72 +0,0 @@
# This file is part of Fail2Ban.
#
# Fail2Ban is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# Fail2Ban is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Fail2Ban; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# Author: Cyril Jaquier
#
# $Revision$
__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()

View File

@ -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

View File

@ -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

View File

@ -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: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: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 Mar 05 15:08:28 2005] [error] [client 192.168.0.128] 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:08:28 2005] [error] [client www.google.ch] 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

View File

@ -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: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 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

View File

@ -24,18 +24,21 @@ __date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import os, sys, time, re
import os, sys, time, re, log4py
from utils.dns import *
# Gets the instance of log4py.
logSys = log4py.Logger().get_instance()
class LogReader:
""" Reads a log file and reports information about IP that make password
failure, bad user or anything else that is considered as doubtful login
attempt.
"""
def __init__(self, logSys, logPath, timeregex, timepattern, failregex,
findTime = 3600):
def __init__(self, logPath, timeregex, timepattern, failregex,
findTime = 3600):
self.logPath = logPath
self.timeregex = timeregex
self.timepattern = timepattern
@ -43,20 +46,9 @@ class LogReader:
self.findTime = findTime
self.ignoreIpList = []
self.lastModTime = 0
self.logSys = logSys
self.lastPos = 0
self.lastDate = 0
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):
""" Gets the find time.
@ -66,7 +58,7 @@ class LogReader:
def addIgnoreIP(self, ip):
""" 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)
def inIgnoreIPList(self, ip):
@ -80,7 +72,7 @@ class LogReader:
try:
fileHandler = open(self.logPath)
except OSError:
self.logSys.error("Unable to open "+self.logPath)
logSys.error("Unable to open "+self.logPath)
sys.exit(-1)
return fileHandler
@ -90,13 +82,13 @@ class LogReader:
try:
self.logStats = os.stat(self.logPath)
except OSError:
self.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:
return False
else:
self.logSys.debug(self.logPath+" has been modified")
logSys.debug(self.logPath+" has been modified")
self.lastModTime = self.logStats.st_mtime
return True
@ -107,13 +99,13 @@ class LogReader:
"""
line = file.readline()
if self.lastDate < self.getTime(line):
self.logSys.debug("Date " + `self.lastDate` + " is " +
"smaller than " + `self.getTime(line)`)
self.logSys.debug("Log rotation detected for " + self.logPath)
logSys.debug("Date " + `self.lastDate` + " is " + "smaller than " +
`self.getTime(line)`)
logSys.debug("Log rotation detected for " + self.logPath)
self.lastPos = 0
self.logSys.debug("Setting file position to " + `self.lastPos` + " for "
+ self.logPath)
logSys.debug("Setting file position to " + `self.lastPos` + " for " +
self.logPath)
file.seek(self.lastPos)
def getFailures(self):
@ -124,7 +116,7 @@ class LogReader:
and the latest failure time.
"""
ipList = dict()
self.logSys.debug(self.logPath)
logSys.debug(self.logPath)
logFile = self.openLogFile()
self.setFilePos(logFile)
lastLine = ''
@ -137,9 +129,9 @@ class LogReader:
if unixTime < time.time()-self.findTime:
break
if self.inIgnoreIPList(ip):
self.logSys.debug("Ignore "+ip)
logSys.debug("Ignore "+ip)
continue
self.logSys.debug("Found "+ip)
logSys.debug("Found "+ip)
if ipList.has_key(ip):
ipList[ip] = (ipList[ip][0]+1, unixTime)
else:

View File

@ -24,4 +24,4 @@ __date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
version = "0.4.1"
version = "0.5.0-CVS"