diff --git a/CHANGELOG b/CHANGELOG index 7c477030..de0736a0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,9 +4,25 @@ |_| \__,_|_|_/___|_.__/\__,_|_||_| ============================================================= -Fail2Ban (version 0.5.2) 2005/08/06 +Fail2Ban (version 0.5.3) 2005/09/08 ============================================================= +ver. 0.5.3 (2005/09/08) - beta +---------- +- Fixed a bug when overriding "maxfailures" or "bantime". + Thanks to Yaroslav Halchenko +- Added more debug output if an error occurs when sending + mail. Thanks to Stephen Gildea +- Renamed "maxretry" to "maxfailures" and changed default + value to 5. Thanks to Stephen Gildea +- Hopefully fixed bug #1256075 +- Fixed bug #1262345 +- Fixed exception handling in PIDLock +- Removed warning when using "-V" or "-h" with no config + file. Thanks to Yaroslav Halchenko +- Removed "-i eth0" from config file. Thanks to Yaroslav + Halchenko + ver. 0.5.2 (2005/08/06) - beta ---------- - Better PID lock file handling. Should close #1239562 diff --git a/PKG-INFO b/PKG-INFO index 35bfe63d..d08ff836 100644 --- a/PKG-INFO +++ b/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 1.0 Name: fail2ban -Version: 0.5.2 +Version: 0.5.3 Summary: Ban IPs that make too many password failure Home-page: http://fail2ban.sourceforge.net Author: Cyril Jaquier @@ -11,5 +11,5 @@ Description: /var/log/apache/error_log and bans IP that makes too many password failures. It updates firewall rules to reject the IP address or executes user defined - commands. It needs log4py. + commands. Platform: Posix diff --git a/README b/README index 4997742a..ba03b945 100644 --- a/README +++ b/README @@ -4,7 +4,7 @@ |_| \__,_|_|_/___|_.__/\__,_|_||_| ============================================================= -Fail2Ban (version 0.5.2) 2005/08/06 +Fail2Ban (version 0.5.3) 2005/09/08 ============================================================= Fail2Ban scans log files like /var/log/pwdfail and bans IP @@ -58,15 +58,16 @@ Require: python-2.3 (http://www.python.org) To install, just do: -> tar xvfj fail2ban-0.5.2.tar.bz2 -> cd fail2ban-0.5.2 +> tar xvfj fail2ban-0.5.3.tar.bz2 +> cd fail2ban-0.5.3 > python setup.py install This will install Fail2Ban into /usr/lib/fail2ban. The fail2ban executable is placed into /usr/bin. -Gentoo: an ebuild is available on the website. -Debian: a package is available on the website. +Gentoo: ebuilds are available on the website. +Debian: Fail2Ban is in Debian unstable. +RedHat: packages are available on the website. Fail2Ban should now be correctly installed. Just type: @@ -121,7 +122,8 @@ Thanks: ------- Kévin Drapel, Marvin Rouge, Sireyessire, Robert Edeker, -Tom Pike, Iain Lea, Andrey G. Grozin, Yaroslav Halchenko +Tom Pike, Iain Lea, Andrey G. Grozin, Yaroslav Halchenko, +Jonathan Kamens, Stephen Gildea License: -------- diff --git a/TODO b/TODO index 5b383282..bfdb1a90 100644 --- a/TODO +++ b/TODO @@ -10,3 +10,5 @@ ToDo See Feature Request Tracking System at SourceForge.net - improve installation process (better prefix support) +- install Fail2ban into /usr/share +- better configuration files diff --git a/config/fail2ban.conf.default b/config/fail2ban.conf.default index e13e4045..d1dd0732 100644 --- a/config/fail2ban.conf.default +++ b/config/fail2ban.conf.default @@ -1,6 +1,6 @@ # Fail2Ban configuration file # -# $Revision: 1.8.2.9 $ +# $Revision: 1.8.2.11 $ # # 2005.06.21 modified for readability Iain Lea iain@bricbrac.de @@ -29,11 +29,11 @@ logtargets = /var/log/fail2ban.log # pidlock = /var/run/fail2ban.pid -# Option: maxretry -# Notes.: number of retrys before IP gets banned. -# Values: NUM Default: 3 +# Option: maxfailures +# Notes.: number of failures before IP gets banned. +# Values: NUM Default: 5 # -maxretry = 3 +maxfailures = 5 # Option: bantime # Notes.: number of seconds an IP will be banned. @@ -45,9 +45,9 @@ bantime = 600 # Notes.: space separated list of IP's to be ignored by fail2ban. # You can use CIDR mask in order to specify a range. # Example: ignoreip = 192.168.0.1/24 123.45.235.65 -# Values: IP Default: 192.168.0.0/24 +# Values: IP Default: 192.168.0.0/16 # -ignoreip = 192.168.0.0/24 +ignoreip = 192.168.0.0/16 # Option: cmdstart # Notes.: command executed once at the start of Fail2Ban @@ -145,14 +145,14 @@ logfile = /var/log/httpd/access_log # Values: CMD Default: # fwstart = iptables -N fail2ban-http - iptables -I INPUT -i eth0 -p tcp --dport http -j fail2ban-http + iptables -I INPUT -p tcp --dport http -j fail2ban-http iptables -A fail2ban-http -j RETURN # Option: fwend # Notes.: command executed once at the end of Fail2Ban # Values: CMD Default: # -fwend = iptables -D INPUT -i eth0 -p tcp --dport http -j fail2ban-http +fwend = iptables -D INPUT -p tcp --dport http -j fail2ban-http iptables -D fail2ban-http -j RETURN iptables -X fail2ban-http @@ -164,9 +164,9 @@ fwend = iptables -D INPUT -i eth0 -p tcp --dport http -j fail2ban-http # unix timestamp of the last failure # unix timestamp of the ban time # Values: CMD -# Default: iptables -I INPUT 1 -i eth0 -s -j DROP +# Default: iptables -I INPUT 1 -s -j DROP # -fwban = iptables -I fail2ban-http 1 -i eth0 -s -j DROP +fwban = iptables -I fail2ban-http 1 -s -j DROP # Option: fwunban # Notes.: command executed when unbanning an IP. Take care that the @@ -175,9 +175,9 @@ fwban = iptables -I fail2ban-http 1 -i eth0 -s -j DROP # unix timestamp of the ban time # unix timestamp of the unban time # Values: CMD -# Default: iptables -D INPUT -i eth0 -s -j DROP +# Default: iptables -D INPUT -s -j DROP # -fwunban = iptables -D fail2ban-http -i eth0 -s -j DROP +fwunban = iptables -D fail2ban-http -s -j DROP # Option: timeregex # Notes.: regex to match timestamp in Apache logfile. @@ -217,14 +217,14 @@ logfile = /var/log/secure # Values: CMD Default: # fwstart = iptables -N fail2ban-ssh - iptables -I INPUT -i eth0 -p tcp --dport ssh -j fail2ban-ssh + iptables -I INPUT -p tcp --dport ssh -j fail2ban-ssh iptables -A fail2ban-ssh -j RETURN # Option: fwend # Notes.: command executed once at the end of Fail2Ban # Values: CMD Default: # -fwend = iptables -D INPUT -i eth0 -p tcp --dport ssh -j fail2ban-ssh +fwend = iptables -D INPUT -p tcp --dport ssh -j fail2ban-ssh iptables -D fail2ban-ssh -j RETURN iptables -X fail2ban-ssh @@ -236,9 +236,9 @@ fwend = iptables -D INPUT -i eth0 -p tcp --dport ssh -j fail2ban-ssh # unix timestamp of the last failure # unix timestamp of the ban time # Values: CMD -# Default: iptables -I INPUT 1 -i eth0 -s -j DROP +# Default: iptables -I INPUT 1 -s -j DROP # -fwban = iptables -I fail2ban-ssh 1 -i eth0 -s -j DROP +fwban = iptables -I fail2ban-ssh 1 -s -j DROP # Option: fwunbanrule # Notes.: command executed when unbanning an IP. Take care that the @@ -247,9 +247,9 @@ fwban = iptables -I fail2ban-ssh 1 -i eth0 -s -j DROP # unix timestamp of the ban time # unix timestamp of the unban time # Values: CMD -# Default: iptables -D INPUT -i eth0 -s -j DROP +# Default: iptables -D INPUT -s -j DROP # -fwunban = iptables -D fail2ban-ssh -i eth0 -s -j DROP +fwunban = iptables -D fail2ban-ssh -s -j DROP # Option: timeregex # Notes.: regex to match timestamp in SSH logfile. diff --git a/fail2ban.py b/fail2ban.py index 883e9e82..7e17c032 100755 --- a/fail2ban.py +++ b/fail2ban.py @@ -16,11 +16,11 @@ # Author: Cyril Jaquier # -# $Revision: 1.20.2.13 $ +# $Revision: 1.20.2.16 $ __author__ = "Cyril Jaquier" -__version__ = "$Revision: 1.20.2.13 $" -__date__ = "$Date: 2005/08/06 18:44:06 $" +__version__ = "$Revision: 1.20.2.16 $" +__date__ = "$Date: 2005/09/05 21:12:08 $" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" @@ -114,10 +114,6 @@ def getCmdLineOptions(optList): """ Gets the command line options """ for opt in optList: - if opt[0] in ["-h", "--help"]: - dispUsage() - if opt[0] in ["-V", "--version"]: - dispVersion() if opt[0] == "-v": conf["verbose"] = conf["verbose"] + 1 if opt[0] == "-b": @@ -133,7 +129,7 @@ def getCmdLineOptions(optList): if opt[0] == "-i": conf["ignoreip"] = opt[1] if opt[0] == "-r": - conf["maxretry"] = int(opt[1]) + conf["maxfailures"] = int(opt[1]) if opt[0] == "-p": conf["pidlock"] = opt[1] if opt[0] == "-k": @@ -166,6 +162,10 @@ def main(): for opt in optList: if opt[0] == "-c": conf["conffile"] = opt[1] + if opt[0] in ["-h", "--help"]: + dispUsage() + if opt[0] in ["-V", "--version"]: + dispVersion() # Reads the config file and create a LogReader instance for # each log file to check. @@ -177,7 +177,7 @@ def main(): ["str", "logtargets", "/var/log/fail2ban.log"], ["bool", "debug", False], ["str", "pidlock", "/var/run/fail2ban.pid"], - ["int", "maxretry", 3], + ["int", "maxfailures", 5], ["int", "bantime", 600], ["str", "ignoreip", ""], ["int", "polltime", 1], @@ -257,12 +257,6 @@ def main(): # Ignores IP list ignoreIPList = conf["ignoreip"].split(' ') - # maxretry option - maxRetry = conf["maxretry"] - - # bantime option - banTime = conf["bantime"] - # Checks for root user. This is necessary because log files # are owned by root and firewall needs root access. if not checkForRoot(): @@ -276,11 +270,14 @@ def main(): logSys.error("Fail2Ban already running with PID "+pid) sys.exit(-1) else: - pidLock.create() + ret = pidLock.create() + if not ret: + # Unable to create PID lock. Exit + sys.exit(-1) logSys.debug("ConfFile is " + conf["conffile"]) logSys.debug("BanTime is " + `conf["bantime"]`) - logSys.debug("retryAllowed is " + `conf["maxretry"]`) + logSys.debug("MaxFailure is " + `conf["maxfailures"]`) # Options optionValues = (["bool", "enabled", False], @@ -305,7 +302,7 @@ def main(): # Options optionValues = (["bool", "enabled", False], ["str", "logfile", "/dev/null"], - ["int", "maxretry", None], + ["int", "maxfailures", None], ["int", "bantime", None], ["str", "timeregex", ""], ["str", "timepattern", ""], @@ -319,17 +316,21 @@ def main(): for t in confReader.getSections(): l = confReader.getLogOptions(t, optionValues) if l["enabled"]: - # Override maxretry option - if not l["maxretry"] == None: - maxRetry = l["maxretry"] + # Override maxfailures option + if not l["maxfailures"] == None: + maxFailures = l["maxfailures"] + else: + maxFailures = conf["maxfailures"] # Override bantime option if not l["bantime"] == None: banTime = l["bantime"] + else: + banTime = conf["bantime"] # Creates a logreader object lObj = LogReader(l["logfile"], l["timeregex"], l["timepattern"], - l["failregex"], maxRetry, banTime) + l["failregex"], maxFailures, banTime) # Creates a firewall object fObj = Firewall(l["fwban"], l["fwunban"], banTime) # Links them into a list. I'm not really happy @@ -346,6 +347,8 @@ def main(): if isValidIP(ip): for element in logFwList: element[1].addIgnoreIP(ip) + else: + logSys.warn(ip + " is not a valid IP address") logSys.info("Fail2Ban v" + version + " is running") # Execute global start command diff --git a/logreader/logreader.py b/logreader/logreader.py index 7a27ea29..1e6ec979 100644 --- a/logreader/logreader.py +++ b/logreader/logreader.py @@ -16,11 +16,11 @@ # Author: Cyril Jaquier # -# $Revision: 1.13.2.7 $ +# $Revision: 1.13.2.8 $ __author__ = "Cyril Jaquier" -__version__ = "$Revision: 1.13.2.7 $" -__date__ = "$Date: 2005/08/06 18:43:11 $" +__version__ = "$Revision: 1.13.2.8 $" +__date__ = "$Date: 2005/09/05 21:06:15 $" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" @@ -134,11 +134,13 @@ class LogReader: logSys.debug(self.logPath) logFile = self.openLogFile() self.setFilePos(logFile) - lastLine = '' + lastLine = None for line in logFile: + if not self.hasTime(line): + # There is no valid time in this line + continue lastLine = line - failList = self.findFailure(line) - for element in failList: + for element in self.findFailure(line): ip = element[0] unixTime = element[1] if unixTime < time.time()-self.findTime: @@ -152,7 +154,8 @@ class LogReader: else: ipList[ip] = (1, unixTime) self.lastPos = logFile.tell() - self.lastDate = self.getTime(lastLine) + if lastLine: + self.lastDate = self.getTime(lastLine) logFile.close() return ipList @@ -175,6 +178,15 @@ class LogReader: failList.append([ip, date]) return failList + def hasTime(self, line): + """ Return true if the line contains a date + """ + timeMatch = re.search(self.timeregex, line) + if timeMatch: + return True + else: + return False + def getTime(self, line): """ Gets the time of a log message. """ diff --git a/setup.py b/setup.py index e71e23ee..ac833c79 100755 --- a/setup.py +++ b/setup.py @@ -18,11 +18,11 @@ # Author: Cyril Jaquier # -# $Revision: 1.4.2.3 $ +# $Revision: 1.4.2.4 $ __author__ = "Cyril Jaquier" -__version__ = "$Revision: 1.4.2.3 $" -__date__ = "$Date: 2005/07/28 20:30:34 $" +__version__ = "$Revision: 1.4.2.4 $" +__date__ = "$Date: 2005/08/07 13:10:39 $" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" @@ -36,7 +36,7 @@ Fail2Ban scans log files like /var/log/pwdfail or /var/log/apache/error_log and bans IP that makes too many password failures. It updates firewall rules to reject the IP address or executes user defined -commands. It needs log4py.''' +commands.''' setup( name = "fail2ban", diff --git a/utils/dns.py b/utils/dns.py index 45b091bc..814e2ed9 100644 --- a/utils/dns.py +++ b/utils/dns.py @@ -16,11 +16,11 @@ # Author: Cyril Jaquier # -# $Revision: 1.7.2.2 $ +# $Revision: 1.7.2.3 $ __author__ = "Cyril Jaquier" -__version__ = "$Revision: 1.7.2.2 $" -__date__ = "$Date: 2005/07/22 21:11:42 $" +__version__ = "$Revision: 1.7.2.3 $" +__date__ = "$Date: 2005/08/17 19:26:49 $" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" @@ -58,8 +58,9 @@ def searchIP(text): def isValidIP(str): """ Return true if str is a valid IP """ + s = str.split('/', 1) try: - socket.inet_aton(str) + socket.inet_aton(s[0]) return True except socket.error: return False diff --git a/utils/mail.py b/utils/mail.py index 44b0954b..5ae2fc43 100644 --- a/utils/mail.py +++ b/utils/mail.py @@ -16,11 +16,11 @@ # Author: Cyril Jaquier # -# $Revision: 1.1.2.2 $ +# $Revision: 1.1.2.3 $ __author__ = "Cyril Jaquier" -__version__ = "$Revision: 1.1.2.2 $" -__date__ = "$Date: 2005/08/01 16:35:18 $" +__version__ = "$Revision: 1.1.2.3 $" +__date__ = "$Date: 2005/09/08 18:05:59 $" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" @@ -64,8 +64,8 @@ class Mail: server.sendmail(self.fromAddr, self.toAddr, mail) logSys.debug("Email sent to " + `self.toAddr`) server.quit() - except Exception: + except Exception, e: logSys.error("Unable to send mail to " + self.host + ":" + `self.port` + " from " + self.fromAddr + " to " + - `self.toAddr`) + `self.toAddr` + ": " + `e` + ": " + `e.args`) \ No newline at end of file diff --git a/utils/pidlock.py b/utils/pidlock.py index 7df9945b..40ca5385 100644 --- a/utils/pidlock.py +++ b/utils/pidlock.py @@ -16,11 +16,11 @@ # Author: Cyril Jaquier # -# $Revision: 1.1.2.1 $ +# $Revision: 1.1.2.2 $ __author__ = "Cyril Jaquier" -__version__ = "$Revision: 1.1.2.1 $" -__date__ = "$Date: 2005/08/04 20:48:30 $" +__version__ = "$Revision: 1.1.2.2 $" +__date__ = "$Date: 2005/08/07 13:08:18 $" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" @@ -51,17 +51,25 @@ class PIDLock: def create(self): """ Create PID lock. """ - fileHandler = open(self.path, mode='w') - pid = os.getpid() - fileHandler.write(`pid` + '\n') - fileHandler.close() - logSys.debug("Created PID lock (" + `pid` + ") in " + self.path) + try: + fileHandler = open(self.path, mode='w') + pid = os.getpid() + fileHandler.write(`pid` + '\n') + fileHandler.close() + logSys.debug("Created PID lock (" + `pid` + ") in " + self.path) + return True + except: + logSys.error("Unable to create PID lock " + self.path) + return False def remove(self): """ Remove PID lock. """ - os.remove(self.path) - logSys.debug("Removed PID lock " + self.path) + try: + os.remove(self.path) + logSys.debug("Removed PID lock " + self.path) + except OSError: + logSys.error("Unable to remove PID lock " + self.path) def exists(self): """ Returns the current PID if Fail2Ban is running or False diff --git a/version.py b/version.py index 214d99fe..30da7d75 100644 --- a/version.py +++ b/version.py @@ -16,12 +16,12 @@ # Author: Cyril Jaquier # -# $Revision: 1.12.2.6 $ +# $Revision: 1.12.2.8 $ __author__ = "Cyril Jaquier" -__version__ = "$Revision: 1.12.2.6 $" -__date__ = "$Date: 2005/08/06 15:07:11 $" +__version__ = "$Revision: 1.12.2.8 $" +__date__ = "$Date: 2005/09/08 18:20:51 $" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" -version = "0.5.2" +version = "0.5.3"