- 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) - ??? 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

View File

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

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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