From 0de46864a1ba5443e719043d718e7d3c1e523996 Mon Sep 17 00:00:00 2001 From: Cyril Jaquier Date: Sun, 10 Oct 2004 13:34:02 +0000 Subject: [PATCH 01/99] - update some dates git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@8 a942ae1a-1317-0410-a47c-b1dcaea8d605 --- log-test/test | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/log-test/test b/log-test/test index 81e0e87f..30fc4868 100644 --- a/log-test/test +++ b/log-test/test @@ -425,7 +425,7 @@ Oct 7 01:03:12 [sshd] Failed password for illegal user tata from 128.178.164.52 - Last output repeated 2 times - Oct 7 01:03:20 [sshd] Failed password for illegal user tata from 128.178.164.52 port 37187 ssh2 - Last output repeated 2 times - -Oct 8 11:47:08 [sshd] Failed password for illegal user test from 69.182.27.122 port 34015 ssh2 -Oct 8 11:47:09 [sshd] Failed password for illegal user guest from 69.182.27.122 port 34068 ssh2 -Oct 8 11:47:11 [sshd] Failed password for illegal user admin from 69.182.27.122 port 34127 ssh2 -Oct 9 21:54:11 yellow sshd[16069]: Failed password for cyril from 212.41.79.210 port 29404 ssh2 +Oct 11 11:47:08 [sshd] Failed password for illegal user test from 69.182.27.122 port 34015 ssh2 +Oct 11 11:47:09 [sshd] Failed password for illegal user guest from 69.182.27.122 port 34068 ssh2 +Oct 11 11:47:11 [sshd] Failed password for illegal user admin from 69.182.27.122 port 34127 ssh2 +Oct 12 21:54:11 yellow sshd[16069]: Failed password for cyril from 212.41.79.210 port 29404 ssh2 From 68ab4b0b26cee2c96ff8751e94b396692b0ecce4 Mon Sep 17 00:00:00 2001 From: Cyril Jaquier Date: Sun, 10 Oct 2004 13:35:11 +0000 Subject: [PATCH 02/99] - a few changes and corrections git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@9 a942ae1a-1317-0410-a47c-b1dcaea8d605 --- firewall/firewall.py | 38 ++++++++++++++++++++++++++++++++++---- firewall/iptables.py | 14 +++++--------- 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/firewall/firewall.py b/firewall/firewall.py index 0a937d77..76632a75 100644 --- a/firewall/firewall.py +++ b/firewall/firewall.py @@ -24,24 +24,54 @@ __date__ = "$Date$" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" +import time + class Firewall: banList = dict() - def addBanIP(self, ip, time): - self.banList[ip] = time + def __init__(self, banTime): + self.banTime = banTime + + def addBanIP(self, ip): + if not self.inBanList(ip): + self.banList[ip] = time.time() + self.executeCmd(self.banIP(ip)) + else: + print ip, "already in ban list" def delBanIP(self, ip): - del self.banList[ip] + if self.inBanList(ip): + del self.banList[ip] + self.executeCmd(self.unBanIP(ip)) + else: + print ip, "not in ban list" + + def inBanList(self, ip): + return self.banList.has_key(ip) + + def checkForUnBan(self): + """ Check for user to remove from ban list. + """ + banListTemp = self.banList.copy() + iterBanList = banListTemp.iteritems() + for i in range(len(self.banList)): + element = iterBanList.next() + ip = element[0] + btime = element[1] + if btime < time.time()-self.banTime: + self.delBanIP(ip) + print '`->', time.time() def flushBanList(self): iterBanList = self.banList.iteritems() for i in range(len(self.banList)): element = iterBanList.next() ip = element[0] - self.unBanIP(ip) + self.delBanIP(ip) def executeCmd(self, cmd): + print cmd return #os.system(cmd) def viewBanList(self): diff --git a/firewall/iptables.py b/firewall/iptables.py index fcee9652..8206e631 100644 --- a/firewall/iptables.py +++ b/firewall/iptables.py @@ -28,14 +28,10 @@ from firewall import Firewall class Iptables(Firewall): - def banIP(self, ip, time): - query = 'iptables -I INPUT 1 -i eth0 -s '+str(ip)+' -j DROP' - self.addBanIP(ip, time) - self.executeCmd(query) - print query + def banIP(self, ip): + query = 'iptables -I INPUT 1 -i eth0 -s '+ip+' -j DROP' + return query def unBanIP(self, ip): - query = 'iptables -D INPUT -i eth0 -s '+str(ip)+' -j DROP' - self.delBanIP(ip) - self.executeCmd(query) - print query + query = 'iptables -D INPUT -i eth0 -s '+ip+' -j DROP' + return query From 012301b64411850e449bbc7d0aa42b68553350fa Mon Sep 17 00:00:00 2001 From: Cyril Jaquier Date: Sun, 10 Oct 2004 13:35:52 +0000 Subject: [PATCH 03/99] - Complete rewrite to use the log-reader and firewall classes git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@10 a942ae1a-1317-0410-a47c-b1dcaea8d605 --- fail2ban | 127 +++++++------------------------------------------------ 1 file changed, 15 insertions(+), 112 deletions(-) diff --git a/fail2ban b/fail2ban index 278a118f..c2cae5df 100755 --- a/fail2ban +++ b/fail2ban @@ -26,10 +26,10 @@ __date__ = "$Date$" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" -import posix,sys,os -import string,re,time +import posix, time, sys from firewall.iptables import Iptables +from logreader.metalog import Metalog def checkForRoot(): """ Check for root user. @@ -40,132 +40,35 @@ def checkForRoot(): else: return False -# start: To be removed -def executeCmd(cmd): - return #os.system(cmd) - -def unBanIP(ip): - iptables = 'iptables -D INPUT -i eth0 -s '+ip+' -j DROP' - executeCmd(iptables) - print iptables - -def banIP(ip): - iptables = 'iptables -I INPUT 1 -i eth0 -s '+ip+' -j DROP' - executeCmd(iptables) - print iptables -# end: - -def checkForUnBan(banList, currentTime, banTime): - """ Check for user to remove from ban list. - """ - iterBanList = banList.iteritems() - for i in range(len(banList)): - element = iterBanList.next() - ip = element[0] - btime = element[1] - if btime < currentTime-banTime: - del banList[ip] - unBanIP(ip) - print '`->', currentTime - return banList - -def checkForBan(retryList, banList, currentTime): - iterRetry = retryList.iteritems() - for i in range(len(retryList)): - element = iterRetry.next() - retry = element[1][0] - ip = element[0] - if element[1][0] > 2 and not banList.has_key(ip): - banList[ip] = currentTime - banIP(ip) - print '`->', currentTime - return banList - -def flushBanList(banList): - iterBanList = banList.iteritems() - for i in range(len(banList)): - element = iterBanList.next() - ip = element[0] - unBanIP(ip) - -def parseLogLine(line): - """ Match sshd failed password log - """ - if re.search("Failed password", line): - matchIP = re.search("(?:\d{1,3}\.){3}\d{1,3}", line) - return matchIP - if __name__ == "__main__": - # start: For object oriented testing - f = Iptables() - f.banIP('11', 1231) - f.banIP('13', 1232) - f.banIP('13', 1233) - f.unBanIP('11') - f.viewBanList() - f.flushBanList() - # end: + fireWall = Iptables(600) + logFile = Metalog("./log-test/test", 600) if not checkForRoot(): print "You must be root." #sys.exit(-1) - logPath = './log-test/test' - banTime = 60 - ignoreIPs = '127.0.0.1' + logFile.addIgnoreIP("127.0.0.1") - lastModTime = 0 - banList = dict() while True: - try: - currentTime = time.time() + try: + fireWall.checkForUnBan() - banList = checkForUnBan(banList, currentTime, banTime) - - try: - pwdFailStats = os.stat(logPath) - except OSError: - print "Unable to get stat on", logPath - sys.exit(-1) - - if lastModTime == pwdFailStats.st_mtime: + if not logFile.isModified(): time.sleep(1) continue - print logPath, 'has been modified' - lastModTime = pwdFailStats.st_mtime + failList = logFile.getPwdFailure() - try: - pwdfail = open(logPath) - except OSError: - print "Unable to open", logPath - sys.exit(-1) + iterFailList = failList.iteritems() + for i in range(len(failList)): + element = iterFailList.next() + if element[1][0] > 2: + fireWall.addBanIP(element[0]) - retryList = dict() - for line in pwdfail.readlines(): - match = parseLogLine(line) - if match: - ip = match.group() - date = list(time.strptime(line[0:15], "%b %d %H:%M:%S")) - date[0] = time.gmtime()[0] - unixTime = time.mktime(date) - if unixTime < currentTime-banTime: - continue - if re.search(ip, ignoreIPs): - print 'Ignore ', ip - continue - print 'Found', ip, 'at', unixTime - if retryList.has_key(`ip`): - retryList[`ip`] = (retryList[`ip`][0]+1,unixTime) - else: - retryList[`ip`] = (1,unixTime) - - pwdfail.close() - - banList = checkForBan(retryList, banList, currentTime) except KeyboardInterrupt: print 'Restoring iptables...' - flushBanList(banList) + fireWall.flushBanList() print 'Exiting...' sys.exit(0) From 5fc0a651635377fe1faa5bdcdb56aa0835382636 Mon Sep 17 00:00:00 2001 From: Cyril Jaquier Date: Sun, 10 Oct 2004 23:41:07 +0000 Subject: [PATCH 04/99] - removing this file. Replaced by fail2ban.py git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@11 a942ae1a-1317-0410-a47c-b1dcaea8d605 --- fail2ban | 74 -------------------------------------------------------- 1 file changed, 74 deletions(-) delete mode 100755 fail2ban diff --git a/fail2ban b/fail2ban deleted file mode 100755 index c2cae5df..00000000 --- a/fail2ban +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env python - -# 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 posix, time, sys - -from firewall.iptables import Iptables -from logreader.metalog import Metalog - -def checkForRoot(): - """ Check for root user. - """ - uid = `posix.getuid()` - if uid == '0': - return True - else: - return False - -if __name__ == "__main__": - - fireWall = Iptables(600) - logFile = Metalog("./log-test/test", 600) - - if not checkForRoot(): - print "You must be root." - #sys.exit(-1) - - logFile.addIgnoreIP("127.0.0.1") - - while True: - try: - fireWall.checkForUnBan() - - if not logFile.isModified(): - time.sleep(1) - continue - - failList = logFile.getPwdFailure() - - iterFailList = failList.iteritems() - for i in range(len(failList)): - element = iterFailList.next() - if element[1][0] > 2: - fireWall.addBanIP(element[0]) - - except KeyboardInterrupt: - print 'Restoring iptables...' - fireWall.flushBanList() - print 'Exiting...' - sys.exit(0) From 4602bb1bfc8ce9f63aaaa323e1b1b9ecd293cf30 Mon Sep 17 00:00:00 2001 From: Cyril Jaquier Date: Sun, 10 Oct 2004 23:44:24 +0000 Subject: [PATCH 05/99] - Add this file with .py extension in order to by recognize as Python source file with pydev (Eclipse plugin) - Add command line options: -v (verbose mode), -h (help), -b (background) - Add daemon function found on aspn.activestate.com git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@14 a942ae1a-1317-0410-a47c-b1dcaea8d605 --- fail2ban.py | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100755 fail2ban.py diff --git a/fail2ban.py b/fail2ban.py new file mode 100755 index 00000000..4acae8e3 --- /dev/null +++ b/fail2ban.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python + +# 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 posix, time, sys, getopt, os, signal + +from firewall.iptables import Iptables +from logreader.metalog import Metalog + +def usage(): + print "fail2ban [-h][-v][-b]" + sys.exit(0) + +def checkForRoot(): + """ Check for root user. + """ + uid = `posix.getuid()` + if uid == '0': + return True + 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("/tmp/fail2ban.log", os.O_CREAT|os.O_APPEND|os.O_RDWR) # standard output (1) + #os.open("/dev/null", os.O_RDWR) # standard error (2) + os.open("/tmp/fail2ban.log", os.O_CREAT|os.O_APPEND|os.O_RDWR) # standard error (2) + + return(0) + + +if __name__ == "__main__": + + try: + optList, args = getopt.getopt(sys.argv[1:], 'hvb') + except getopt.GetoptError: + usage() + + verbose = False + for opt in optList: + if opt[0] == "-h": + usage() + if opt[0] == "-v": + verbose = True + if opt[0] == "-b": + retCode = createDaemon() + if retCode != 0: + print "Unable to start daemon" + sys.exit(-1) + + if not checkForRoot(): + print "You must be root." + #sys.exit(-1) + + fireWall = Iptables(600, verbose = verbose) + logFile = Metalog("./log-test/test", 600, verbose = verbose) + + logFile.addIgnoreIP("127.0.0.1") + + while True: + try: + sys.stdout.flush() + sys.stderr.flush() + + fireWall.checkForUnBan() + + if not logFile.isModified(): + time.sleep(1) + continue + + failList = logFile.getPwdFailure() + + iterFailList = failList.iteritems() + for i in range(len(failList)): + element = iterFailList.next() + if element[1][0] > 2: + fireWall.addBanIP(element[0]) + + except KeyboardInterrupt: + print 'Restoring iptables...' + fireWall.flushBanList() + print 'Exiting...' + sys.exit(0) From e561e39583ad4b866dbd2153e001b6dbf8201264 Mon Sep 17 00:00:00 2001 From: Cyril Jaquier Date: Sun, 10 Oct 2004 23:46:58 +0000 Subject: [PATCH 06/99] - Add verbose option git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@15 a942ae1a-1317-0410-a47c-b1dcaea8d605 --- firewall/firewall.py | 15 ++++++++++----- logreader/logreader.py | 6 ++++-- logreader/metalog.py | 6 ++++-- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/firewall/firewall.py b/firewall/firewall.py index 76632a75..09e9ccae 100644 --- a/firewall/firewall.py +++ b/firewall/firewall.py @@ -30,22 +30,25 @@ class Firewall: banList = dict() - def __init__(self, banTime): + def __init__(self, banTime, verbose = False): self.banTime = banTime + self.verbose = verbose def addBanIP(self, ip): if not self.inBanList(ip): self.banList[ip] = time.time() self.executeCmd(self.banIP(ip)) else: - print ip, "already in ban list" + if self.verbose: + print ip, "already in ban list" def delBanIP(self, ip): if self.inBanList(ip): del self.banList[ip] self.executeCmd(self.unBanIP(ip)) else: - print ip, "not in ban list" + if self.verbose: + print ip, "not in ban list" def inBanList(self, ip): return self.banList.has_key(ip) @@ -61,7 +64,8 @@ class Firewall: btime = element[1] if btime < time.time()-self.banTime: self.delBanIP(ip) - print '`->', time.time() + if self.verbose: + print '`->', time.time() def flushBanList(self): iterBanList = self.banList.iteritems() @@ -71,7 +75,8 @@ class Firewall: self.delBanIP(ip) def executeCmd(self, cmd): - print cmd + if self.verbose: + print cmd return #os.system(cmd) def viewBanList(self): diff --git a/logreader/logreader.py b/logreader/logreader.py index 492ed76d..f17711f8 100644 --- a/logreader/logreader.py +++ b/logreader/logreader.py @@ -28,11 +28,12 @@ import os, sys class LogReader: - def __init__(self, logPath, findTime = 3600): + def __init__(self, logPath, findTime = 3600, verbose = False): self.logPath = logPath self.findTime = findTime self.ignoreIpList = [] self.lastModTime = 0 + self.verbose = verbose def addIgnoreIP(self, ip): self.ignoreIpList.append(ip) @@ -58,7 +59,8 @@ class LogReader: if self.lastModTime == logStats.st_mtime: return False else: - print self.logPath, 'has been modified' + if self.verbose: + print self.logPath, 'has been modified' self.lastModTime = logStats.st_mtime return True diff --git a/logreader/metalog.py b/logreader/metalog.py index 20a64766..c8e3db12 100644 --- a/logreader/metalog.py +++ b/logreader/metalog.py @@ -41,9 +41,11 @@ class Metalog(LogReader): if unixTime < time.time()-self.findTime: continue if self.inIgnoreIPList(ip): - print 'Ignore', ip + if self.verbose: + print 'Ignore', ip continue - print 'Found', ip, 'at', unixTime + if self.verbose: + print 'Found', ip, 'at', unixTime if ipList.has_key(ip): ipList[ip] = (ipList[ip][0]+1, unixTime) else: From be1755cac12afb93d9c74ce9c5e0295c8ba1a090 Mon Sep 17 00:00:00 2001 From: Cyril Jaquier Date: Mon, 11 Oct 2004 10:21:56 +0000 Subject: [PATCH 07/99] - Change quoting style git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@16 a942ae1a-1317-0410-a47c-b1dcaea8d605 --- firewall/iptables.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firewall/iptables.py b/firewall/iptables.py index 8206e631..c06a2dd3 100644 --- a/firewall/iptables.py +++ b/firewall/iptables.py @@ -29,9 +29,9 @@ from firewall import Firewall class Iptables(Firewall): def banIP(self, ip): - query = 'iptables -I INPUT 1 -i eth0 -s '+ip+' -j DROP' + query = "iptables -I INPUT 1 -i eth0 -s "+ip+" -j DROP" return query def unBanIP(self, ip): - query = 'iptables -D INPUT -i eth0 -s '+ip+' -j DROP' + query = "iptables -D INPUT -i eth0 -s "+ip+" -j DROP" return query From c286d568555896e33cade457d870630e35ea16c4 Mon Sep 17 00:00:00 2001 From: Cyril Jaquier Date: Mon, 11 Oct 2004 10:22:41 +0000 Subject: [PATCH 08/99] - Add log4py support - Remove old verbose mode - Add debug feature git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@17 a942ae1a-1317-0410-a47c-b1dcaea8d605 --- firewall/firewall.py | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/firewall/firewall.py b/firewall/firewall.py index 09e9ccae..72ecc7cd 100644 --- a/firewall/firewall.py +++ b/firewall/firewall.py @@ -24,36 +24,36 @@ __date__ = "$Date$" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" -import time +import time, os class Firewall: banList = dict() - def __init__(self, banTime, verbose = False): + def __init__(self, banTime, logSys): self.banTime = banTime - self.verbose = verbose + self.logSys = logSys - def addBanIP(self, ip): + def addBanIP(self, ip, debug): if not self.inBanList(ip): + self.logSys.info("Ban "+ip) self.banList[ip] = time.time() - self.executeCmd(self.banIP(ip)) + self.executeCmd(self.banIP(ip), debug) else: - if self.verbose: - print ip, "already in ban list" + self.logSys.info(ip+" already in ban list") - def delBanIP(self, ip): + def delBanIP(self, ip, debug): if self.inBanList(ip): + self.logSys.info("Unban "+ip) del self.banList[ip] - self.executeCmd(self.unBanIP(ip)) + self.executeCmd(self.unBanIP(ip), debug) else: - if self.verbose: - print ip, "not in ban list" + self.logSys.info(ip+" not in ban list") def inBanList(self, ip): return self.banList.has_key(ip) - def checkForUnBan(self): + def checkForUnBan(self, debug): """ Check for user to remove from ban list. """ banListTemp = self.banList.copy() @@ -63,21 +63,21 @@ class Firewall: ip = element[0] btime = element[1] if btime < time.time()-self.banTime: - self.delBanIP(ip) - if self.verbose: - print '`->', time.time() + self.delBanIP(ip, debug) - def flushBanList(self): + def flushBanList(self, debug): iterBanList = self.banList.iteritems() for i in range(len(self.banList)): element = iterBanList.next() ip = element[0] - self.delBanIP(ip) + self.delBanIP(ip, debug) - def executeCmd(self, cmd): - if self.verbose: - print cmd - return #os.system(cmd) + def executeCmd(self, cmd, debug): + self.logSys.debug(cmd) + if not debug: + return os.system(cmd) + else: + return None def viewBanList(self): iterBanList = self.banList.iteritems() From a2ea1164b3ed987c8ef9e30874a1c7e4adf079bc Mon Sep 17 00:00:00 2001 From: Cyril Jaquier Date: Mon, 11 Oct 2004 10:23:53 +0000 Subject: [PATCH 09/99] - Add log4py support - Remove old verbose mode git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@18 a942ae1a-1317-0410-a47c-b1dcaea8d605 --- logreader/logreader.py | 11 +++++------ logreader/metalog.py | 6 ++---- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/logreader/logreader.py b/logreader/logreader.py index f17711f8..9c134307 100644 --- a/logreader/logreader.py +++ b/logreader/logreader.py @@ -28,12 +28,12 @@ import os, sys class LogReader: - def __init__(self, logPath, findTime = 3600, verbose = False): + def __init__(self, logPath, logSys, findTime = 3600): self.logPath = logPath self.findTime = findTime self.ignoreIpList = [] self.lastModTime = 0 - self.verbose = verbose + self.logSys = logSys def addIgnoreIP(self, ip): self.ignoreIpList.append(ip) @@ -45,7 +45,7 @@ class LogReader: try: fileHandler = open(self.logPath) except OSError: - print "Unable to open", self.logPath + self.logSys.error("Unable to open "+self.logPath) sys.exit(-1) return fileHandler @@ -53,14 +53,13 @@ class LogReader: try: logStats = os.stat(self.logPath) except OSError: - print "Unable to get stat on", logPath + self.logSys.error("Unable to get stat on "+self.logPath) sys.exit(-1) if self.lastModTime == logStats.st_mtime: return False else: - if self.verbose: - print self.logPath, 'has been modified' + self.logSys.debug(self.logPath+" has been modified") self.lastModTime = logStats.st_mtime return True diff --git a/logreader/metalog.py b/logreader/metalog.py index c8e3db12..31b4d564 100644 --- a/logreader/metalog.py +++ b/logreader/metalog.py @@ -41,11 +41,9 @@ class Metalog(LogReader): if unixTime < time.time()-self.findTime: continue if self.inIgnoreIPList(ip): - if self.verbose: - print 'Ignore', ip + self.logSys.debug("Ignore "+ip) continue - if self.verbose: - print 'Found', ip, 'at', unixTime + self.logSys.debug("Found "+ip) if ipList.has_key(ip): ipList[ip] = (ipList[ip][0]+1, unixTime) else: From 8eb470019c3884af2f9196a149974f0fd91270e1 Mon Sep 17 00:00:00 2001 From: Cyril Jaquier Date: Mon, 11 Oct 2004 10:26:39 +0000 Subject: [PATCH 10/99] - Add log4py support - Remove old verbose mode - Add debug feature - Add option -f . This is the log file to read from - Add option -l . This is the file to log fail2ban messages - Add option -d. Allow fail2ban to run without root permissions. Do not execute OS command git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@19 a942ae1a-1317-0410-a47c-b1dcaea8d605 --- fail2ban.py | 57 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/fail2ban.py b/fail2ban.py index 4acae8e3..895d2092 100755 --- a/fail2ban.py +++ b/fail2ban.py @@ -27,12 +27,13 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" import posix, time, sys, getopt, os, signal +import log4py from firewall.iptables import Iptables from logreader.metalog import Metalog def usage(): - print "fail2ban [-h][-v][-b]" + print "fail2ban [-h][-v][-b][-d][-f ][-l ]" sys.exit(0) def checkForRoot(): @@ -90,7 +91,7 @@ def createDaemon(): 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("/") + os.chdir("/") # Give the child complete control over permissions. os.umask(0) else: @@ -114,39 +115,57 @@ def createDaemon(): # 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("/tmp/fail2ban.log", os.O_CREAT|os.O_APPEND|os.O_RDWR) # standard output (1) - #os.open("/dev/null", os.O_RDWR) # standard error (2) - os.open("/tmp/fail2ban.log", os.O_CREAT|os.O_APPEND|os.O_RDWR) # standard error (2) + os.open("/dev/null", os.O_RDWR) # standard output (1) + os.open("/dev/null", os.O_RDWR) # standard error (2) return(0) if __name__ == "__main__": + logSys = log4py.Logger().get_instance() + logSys.set_formatstring("%T %L %M") + try: - optList, args = getopt.getopt(sys.argv[1:], 'hvb') + optList, args = getopt.getopt(sys.argv[1:], 'hvbdf:l:') except getopt.GetoptError: usage() - verbose = False + debug = False + logFilePath = "/var/log/pwdfail/current" + for opt in optList: if opt[0] == "-h": usage() if opt[0] == "-v": - verbose = True + logSys.set_loglevel(log4py.LOGLEVEL_VERBOSE) if opt[0] == "-b": retCode = createDaemon() + logSys.set_target("/tmp/fail2ban.log") if retCode != 0: - print "Unable to start daemon" + logSys.error("Unable to start daemon") sys.exit(-1) + if opt[0] == "-d": + debug = True + logSys.set_loglevel(log4py.LOGLEVEL_DEBUG) + logSys.set_formatstring(log4py.FMT_DEBUG) + if opt[0] == "-f": + logFilePath = opt[1] + if opt[0] == "-l": + try: + open(opt[1], "a") + logSys.set_target(opt[1]) + except IOError: + logSys.error("Unable to log to "+opt[1]) + logSys.error("Use default output for logging") if not checkForRoot(): - print "You must be root." - #sys.exit(-1) + logSys.error("You must be root") + if not debug: + sys.exit(-1) - fireWall = Iptables(600, verbose = verbose) - logFile = Metalog("./log-test/test", 600, verbose = verbose) + fireWall = Iptables(600, logSys) + logFile = Metalog(logFilePath, logSys, 600) logFile.addIgnoreIP("127.0.0.1") @@ -155,7 +174,7 @@ if __name__ == "__main__": sys.stdout.flush() sys.stderr.flush() - fireWall.checkForUnBan() + fireWall.checkForUnBan(debug) if not logFile.isModified(): time.sleep(1) @@ -167,10 +186,10 @@ if __name__ == "__main__": for i in range(len(failList)): element = iterFailList.next() if element[1][0] > 2: - fireWall.addBanIP(element[0]) + fireWall.addBanIP(element[0], debug) except KeyboardInterrupt: - print 'Restoring iptables...' - fireWall.flushBanList() - print 'Exiting...' + logSys.info("Restoring iptables...") + fireWall.flushBanList(debug) + logSys.info("Exiting...") sys.exit(0) From bb896fb391ed6fcb63d256eb4c87702b3b323442 Mon Sep 17 00:00:00 2001 From: Cyril Jaquier Date: Tue, 12 Oct 2004 21:40:50 +0000 Subject: [PATCH 11/99] - Add a debug message when adding ip to ignore list git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@20 a942ae1a-1317-0410-a47c-b1dcaea8d605 --- logreader/logreader.py | 1 + 1 file changed, 1 insertion(+) diff --git a/logreader/logreader.py b/logreader/logreader.py index 9c134307..1e52ace2 100644 --- a/logreader/logreader.py +++ b/logreader/logreader.py @@ -36,6 +36,7 @@ class LogReader: self.logSys = logSys def addIgnoreIP(self, ip): + self.logSys.debug("Add "+ip+" to ignore list") self.ignoreIpList.append(ip) def inIgnoreIPList(self, ip): From 4eeb61c0e163a429ffeccfa10fb1270e6af98bd4 Mon Sep 17 00:00:00 2001 From: Cyril Jaquier Date: Tue, 12 Oct 2004 21:44:09 +0000 Subject: [PATCH 12/99] - Update help message - Add -i option: ignore ip list. Space separated ip list - Add -t option: ban time in seconds. 600 to ban ip for 10 minutes - Add a info message saying that fail2ban is running git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@21 a942ae1a-1317-0410-a47c-b1dcaea8d605 --- fail2ban.py | 44 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/fail2ban.py b/fail2ban.py index 895d2092..5b813a3a 100755 --- a/fail2ban.py +++ b/fail2ban.py @@ -29,11 +29,28 @@ __license__ = "GPL" import posix, time, sys, getopt, os, signal import log4py +# Appends our own modules path +sys.path.append('/usr/lib/fail2ban') + from firewall.iptables import Iptables from logreader.metalog import Metalog +from version import version def usage(): - print "fail2ban [-h][-v][-b][-d][-f ][-l ]" + print "Usage: fail2ban.py [OPTIONS]" + print + print "Fail2Ban v"+version+" reads log file that contains password failure report" + print "and bans the corresponding IP address using iptables." + print + print " -b start fail2ban in background" + print " -d start fail2ban in debug mode" + print " -f read password failure from FILE" + print " -h display this help message" + print " -l log message in FILE" + print " -t