diff --git a/CHANGELOG b/CHANGELOG index fd74f7ce..ebcac024 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,9 +4,20 @@ |_| \__,_|_|_/___|_.__/\__,_|_||_| ============================================================= -Fail2Ban (version 0.6.1) 2006/03/16 +Fail2Ban (version 0.7.0) 2006/07/?? ============================================================= +ver. 0.7.0 (2006/07/??) - alpha +---------- +- Almost a complete rewrite :) Fail2ban design is really + better (IMHO). There is a lot of new features +- Client/Server architecture +- Multithreading. Each jail has its own threads: one for the + log reading and another for the actions +- Execute several actions +- Split configuration files. They are more readable and easy + to use + ver. 0.6.1 (2006/03/16) - stable ---------- - Added permanent banning. Set banTime to a negative value to diff --git a/MANIFEST b/MANIFEST index 22731947..22b3eca8 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1,29 +1,52 @@ README CHANGELOG TODO -setup.cfg +fail2ban-client +fail2ban-server +fail2ban-testcases +client/__init__.py +client/actionreader.py +client/configreader.py +client/configurator.py +client/csocket.py +client/fail2banreader.py +client/filterreader.py +client/jailreader.py +client/jailsreader.py +server/__init__.py +server/action.py +server/actions.py +server/banmanager.py +server/banticket.py +server/faildata.py +server/failmanager.py +server/failticket.py +server/filter.py +server/jail.py +server/jailthread.py +server/server.py +server/ssocket.py +server/ticket.py +server/transmitter.py setup.py -version.py -fail2ban -fail2ban.py -firewall/__init__.py -firewall/firewall.py -logreader/__init__.py -logreader/logreader.py -confreader/__init__.py -confreader/configreader.py +testcases/__init__.py +testcases/banmanagertestcase.py +testcases/failmanagertestcase.py +testcases/filtertestcase.py +testcases/servertestcase.py utils/__init__.py utils/dns.py -utils/process.py utils/mail.py -utils/strings.py utils/pidlock.py -config/fail2ban.conf.iptables -config/fail2ban.conf.shorewall -config/fail2ban.conf.hostsdeny -config/gentoo-initd -config/gentoo-confd -config/redhat-initd -config/debian-initd +utils/process.py +utils/strings.py +version.py +config/fail2ban.conf +config/fail2ban.local +config/jail.conf +config/action.d/dummy.conf +config/action.d/iptables.conf +config/filter.d/apache-auth.conf +config/filter.d/sshd.conf man/fail2ban.8 man/fail2ban.conf.5 diff --git a/TODO b/TODO index 7c388e85..81ed4bbf 100644 --- a/TODO +++ b/TODO @@ -1,2 +1,2 @@ - Don't close socket after a send -- Multiple actions !!! \ No newline at end of file +- Refactoring in server.py, actions.py, filter.py \ No newline at end of file diff --git a/client/actionreader.py b/client/actionreader.py index 5486b90e..5b620d82 100644 --- a/client/actionreader.py +++ b/client/actionreader.py @@ -53,8 +53,7 @@ class ActionReader(ConfigReader): ConfigReader.read(self, "action.d/" + self.file) def getOptions(self, pOpts): - opts = [["string", "bantime", "600"], - ["string", "actionstart", ""], + opts = [["string", "actionstart", ""], ["string", "actionstop", ""], ["string", "actioncheck", ""], ["string", "actionban", ""], @@ -62,19 +61,19 @@ class ActionReader(ConfigReader): self.opts = ConfigReader.getOptions(self, "DEFAULT", opts, pOpts) def convert(self): + head = ["set", self.name] stream = list() + stream.append(head + ["addaction", self.file]) for opt in self.opts: - if opt == "bantime": - stream.append(["set", self.name, "bantime", self.opts[opt]]) - elif opt == "actionstart": - stream.append(["set", self.name, "actionstart", self.opts[opt]]) + if opt == "actionstart": + stream.append(head + ["actionstart", self.file, self.opts[opt]]) elif opt == "actionstop": - stream.append(["set", self.name, "actionstop", self.opts[opt]]) + stream.append(head + ["actionstop", self.file, self.opts[opt]]) elif opt == "actioncheck": - stream.append(["set", self.name, "actioncheck", self.opts[opt]]) + stream.append(head + ["actioncheck", self.file, self.opts[opt]]) elif opt == "actionban": - stream.append(["set", self.name, "actionban", self.opts[opt]]) + stream.append(head + ["actionban", self.file, self.opts[opt]]) elif opt == "actionunban": - stream.append(["set", self.name, "actionunban", self.opts[opt]]) + stream.append(head + ["actionunban", self.file, self.opts[opt]]) return stream \ No newline at end of file diff --git a/client/jailreader.py b/client/jailreader.py index b839172c..52a62d16 100644 --- a/client/jailreader.py +++ b/client/jailreader.py @@ -38,7 +38,7 @@ class JailReader(ConfigReader): ConfigReader.__init__(self) self.name = name self.filter = None - self.action = None + self.actions = list() def setName(self, value): self.name = value @@ -55,7 +55,7 @@ class JailReader(ConfigReader): def getOptions(self): opts = [["bool", "enabled", "false"], ["int", "maxretry", None], - ["int", "bantime", None], + ["int", "bantime", 600], ["string", "filter", ""], ["string", "action", ""]] self.opts = ConfigReader.getOptions(self, self.name, opts) @@ -67,9 +67,11 @@ class JailReader(ConfigReader): self.filter.getOptions(self.opts) # Read action - self.action = ActionReader(self.opts["action"], self.name) - self.action.read() - self.action.getOptions(self.opts) + for act in self.opts["action"].split(): + action = ActionReader(act, self.name) + action.read() + action.getOptions(self.opts) + self.actions.append(action) def convert(self): stream = [["add", self.name]] @@ -79,6 +81,7 @@ class JailReader(ConfigReader): elif opt == "bantime": stream.append(["set", self.name, "bantime", self.opts[opt]]) stream.extend(self.filter.convert()) - stream.extend(self.action.convert()) + for action in self.actions: + stream.extend(action.convert()) return stream \ No newline at end of file diff --git a/config/action.d/dummy.conf b/config/action.d/dummy.conf index 1be6253b..9f95871f 100644 --- a/config/action.d/dummy.conf +++ b/config/action.d/dummy.conf @@ -1,7 +1,5 @@ [DEFAULT] -bantime = 1234 - name = temporary # Option: protocol diff --git a/config/action.d/foo.conf b/config/action.d/foo.conf new file mode 100644 index 00000000..6be4c389 --- /dev/null +++ b/config/action.d/foo.conf @@ -0,0 +1,50 @@ +[DEFAULT] + +name = temporary + +# Option: protocol +# Notes.: internally used by config reader for interpolations. +# Values: [ tcp | udp | icmp | all ] Default: tcp +# +protocol = tcp + +# Option: fwstart +# Notes.: command executed once at the start of Fail2Ban. +# Values: CMD Default: +# +actionstart = touch /tmp/fail2ban.foo + +# Option: fwend +# Notes.: command executed once at the end of Fail2Ban +# Values: CMD Default: +# +actionstop = rm /tmp/fail2ban.foo + +# Option: fwcheck +# Notes.: command executed once before each fwban command +# Values: CMD Default: +# +actioncheck = + +# Option: fwban +# Notes.: command executed when banning an IP. Take care that the +# command is executed with Fail2Ban user rights. +# Tags: IP address +# number of failures +# unix timestamp of the last failure +# unix timestamp of the ban time +# Values: CMD +# Default: iptables -I INPUT 1 -s -j DROP +# +actionban = echo "+" >> /tmp/fail2ban.foo + +# Option: fwunban +# Notes.: command executed when unbanning an IP. Take care that the +# command is executed with Fail2Ban user rights. +# Tags: IP address +# unix timestamp of the ban time +# unix timestamp of the unban time +# Values: CMD +# Default: iptables -D INPUT -s -j DROP +# +actionunban = echo "-" >> /tmp/fail2ban.foo diff --git a/config/fail2ban.conf b/config/fail2ban.conf index 32a33411..4c29363d 100644 --- a/config/fail2ban.conf +++ b/config/fail2ban.conf @@ -1,4 +1,4 @@ [DEFAULT] -loglevel = 4 +loglevel = 3 diff --git a/config/fail2ban.local b/config/fail2ban.local index 12b8a6f5..db4574b5 100644 --- a/config/fail2ban.local +++ b/config/fail2ban.local @@ -1,4 +1 @@ [DEFAULT] - -test = 4567 -prout = fuck you diff --git a/config/jail.conf b/config/jail.conf index 66853517..ffa314e1 100644 --- a/config/jail.conf +++ b/config/jail.conf @@ -2,9 +2,9 @@ enabled = true filter = sshd -action = dummy +action = dummy foo maxretry = 2 -#bantime = 10 +bantime = 5 [SSH] diff --git a/fail2ban-client b/fail2ban-client index fa395b6e..f88f52bf 100755 --- a/fail2ban-client +++ b/fail2ban-client @@ -32,6 +32,7 @@ import sys, string, os, pickle, re, logging, getopt, time sys.path.insert(1, "/usr/lib/fail2ban") # Now we can import our modules +from version import version from client.csocket import CSocket from client.configurator import Configurator @@ -57,7 +58,7 @@ class Fail2banClient: """ print "Usage: "+self.argv[0]+" [OPTIONS] " print - print "Fail2Ban v0.7 reads log file that contains password failure report" + print "Fail2Ban v" + version + " reads log file that contains password failure report" print "and bans the corresponding IP addresses using firewall rules." print print " -b start in background" @@ -126,6 +127,10 @@ class Fail2banClient: self.startServer(self.conf["background"]) # Configure the server self.processCmd(self.stream) + elif cmd[0] == "loadconf" and len(cmd) == 1: + self.readConfig() + # Configure the server + self.processCmd(self.stream) else: try: client = CSocket() diff --git a/fail2ban-server b/fail2ban-server index 1d8ece42..c23c528e 100755 --- a/fail2ban-server +++ b/fail2ban-server @@ -25,9 +25,15 @@ __date__ = "$Date: 2004/10/10 13:33:40 $" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" +import locale, getopt, logging, sys + +# Inserts our own modules path first in the list +# fix for bug #343821 +sys.path.insert(1, "/usr/lib/fail2ban") + +from version import version from server.server import Server from utils.process import * -import locale, getopt, logging, sys # Gets the instance of the logger. logSys = logging.getLogger("fail2ban") @@ -53,7 +59,7 @@ class Fail2banServer: """ print "Usage: "+self.argv[0]+" [OPTIONS]" print - print "Fail2Ban v0.7 reads log file that contains password failure report" + print "Fail2Ban v" + version + " reads log file that contains password failure report" print "and bans the corresponding IP addresses using firewall rules." print print " -b start in background" diff --git a/fail2ban-testcases b/fail2ban-testcases index 3c427fe9..59f34461 100755 --- a/fail2ban-testcases +++ b/fail2ban-testcases @@ -28,6 +28,11 @@ __license__ = "GPL" import unittest, logging, sys +# Inserts our own modules path first in the list +# fix for bug #343821 +sys.path.insert(1, "/usr/lib/fail2ban") + +from version import version from testcases import filtertestcase from testcases import servertestcase from testcases import failmanagertestcase @@ -40,7 +45,7 @@ stdout = logging.StreamHandler(sys.stdout) logSys.addHandler(stdout) logSys.setLevel(logging.FATAL) -print "Fail2ban test suite. Please wait..." +print "Fail2ban " + version + " test suite. Please wait..." tests = unittest.TestSuite() diff --git a/server/action.py b/server/action.py index f47fe579..69ce903e 100644 --- a/server/action.py +++ b/server/action.py @@ -24,13 +24,10 @@ __date__ = "$Date: 2004/10/10 13:33:40 $" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" -from banmanager import BanManager -from failmanager import FailManager, FailManagerEmpty -from jailthread import JailThread import time, logging, os # Gets the instance of the logger. -logSys = logging.getLogger("fail2ban.action") +logSys = logging.getLogger("fail2ban.actions.action") ## # Execute commands. @@ -39,20 +36,10 @@ logSys = logging.getLogger("fail2ban.action") # action has to be taken. A BanManager take care of the banned IP # addresses. -class Action(JailThread): +class Action: - ## - # Constructor. - # - # Initialize the filter object with default values. - # @param jail the jail object - - def __init__(self, jail): - JailThread.__init__(self, jail) - ## The jail which contains this action. - self.jail = jail - ## The ban manager. - self.banManager = BanManager() + def __init__(self, name): + self.name = name ## Command executed in order to initialize the system. self.actionStart = '' ## Command executed when an IP address gets banned. @@ -64,7 +51,13 @@ class Action(JailThread): ## Command executed in order to stop the system. self.actionStop = '' logSys.debug("Created Action") - + + def setName(self, name): + self.name = name + + def getName(self): + return self.name + ## # Set the "start" command. # @@ -82,6 +75,9 @@ class Action(JailThread): def getActionStart(self): return self.actionStart + def execActionStart(self, aInfo): + return self.executeCmd(self.actionStart, aInfo); + ## # Set the "ban" command. # @@ -99,6 +95,9 @@ class Action(JailThread): def getActionBan(self): return self.actionBan + def execActionBan(self, aInfo): + return self.executeCmd(self.actionBan, aInfo); + ## # Set the "unban" command. # @@ -116,6 +115,9 @@ class Action(JailThread): def getActionUnban(self): return self.actionUnban + def execActionUnban(self, aInfo): + return self.executeCmd(self.actionUnban, aInfo); + ## # Set the "check" command. # @@ -133,6 +135,9 @@ class Action(JailThread): def getActionCheck(self): return self.actionCheck + def execActionCheck(self, aInfo): + return self.executeCmd(self.actionCheck, aInfo); + ## # Set the "stop" command. # @@ -150,104 +155,8 @@ class Action(JailThread): def getActionStop(self): return self.actionStop - ## - # Set the ban time. - # - # @param value the time - - def setBanTime(self, value): - self.banManager.setBanTime(value) - logSys.info("Set banTime = %s" % value) - - ## - # Get the ban time. - # - # @return the time - - def getBanTime(self): - return self.banManager.getBanTime() - - ## - # Main loop. - # - # This function is the main loop of the thread. It checks the Jail - # queue and executes commands when an IP address is banned. - # @return True when the thread exits nicely - - def run(self): - self.executeCmd(self.actionStart) - self.setActive(True) - while self.isActive(): - if not self.isIdle: - #logSys.debug(self.jail.getName() + ": action") - ret = self.checkBan() - if not ret: - self.checkUnBan() - time.sleep(self.sleepTime) - else: - time.sleep(self.sleepTime) - self.flushBan() - self.executeCmd(self.actionStop) - logSys.debug(self.jail.getName() + ": action terminated") - return True - - ## - # Check for IP address to ban. - # - # Look in the Jail queue for FailTicket. If a ticket is available, - # it executes the "ban" command and add a ticket to the BanManager. - # @return True if an IP address get banned - - def checkBan(self): - logSys.debug("Check for IP address to ban") - ticket = self.jail.getFailTicket() - if ticket != False: - aInfo = dict() - bTicket = BanManager.createBanTicket(ticket) - aInfo["ip"] = bTicket.getIP() - logSys.info("Ban %s" % aInfo["ip"]) - self.executeCmd(self.replaceTag(self.actionBan, aInfo)) - self.banManager.addBanTicket(bTicket) - return True - return False - - ## - # Check for IP address to unban. - # - # Unban IP address which are outdated. - - def checkUnBan(self): - logSys.debug("Check for IP address to unban") - for ticket in self.banManager.unBanList(time.time()): - aInfo = dict() - aInfo["ip"] = ticket.getIP() - logSys.info("Unban %s" % aInfo["ip"]) - self.executeCmd(self.replaceTag(self.actionUnban, aInfo)) - - ## - # Flush the ban list. - # - # Unban all IP address which are still in the banning list. - - def flushBan(self): - logSys.debug("Flush ban list") - for ticket in self.banManager.flushBanList(): - aInfo = dict() - aInfo["ip"] = ticket.getIP() - logSys.info("Unban %s" % aInfo["ip"]) - self.executeCmd(self.replaceTag(self.actionUnban, aInfo)) - - ## - # Get the status of the filter. - # - # Get some informations about the filter state such as the total - # number of failures. - # @return a list with tuple - - def status(self): - ret = [("Currently banned", self.banManager.size()), - ("Total banned", self.banManager.getBanTotal())] - return ret + def execActionStop(self, aInfo): + return self.executeCmd(self.actionStop, aInfo); @staticmethod def replaceTag(query, aInfo): @@ -261,21 +170,26 @@ class Action(JailThread): return string @staticmethod - def executeCmd(cmd): + def executeCmd(cmd, aInfo = None): """ Executes an OS command. """ if cmd == "": logSys.debug("Nothing to do") return True - logSys.debug(cmd) - retval = os.system(cmd) + # Replace tags + if not aInfo == None: + realCmd = Action.replaceTag(cmd, aInfo) + else: + realCmd = cmd + + logSys.debug(realCmd) + retval = os.system(realCmd) #if not retval == 0: # logSys.error("'" + cmd + "' returned " + `retval`) # raise Exception("Execution of command '%s' failed" % cmd) if retval == 0: return True else: - logSys.error("%s returned %x" % (cmd, retval)) - return False - \ No newline at end of file + logSys.error("%s returned %x" % (realCmd, retval)) + return False \ No newline at end of file diff --git a/server/actions.py b/server/actions.py new file mode 100644 index 00000000..707272d4 --- /dev/null +++ b/server/actions.py @@ -0,0 +1,183 @@ +# 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: 1.1 $ + +__author__ = "Cyril Jaquier" +__version__ = "$Revision: 1.1 $" +__date__ = "$Date: 2004/10/10 13:33:40 $" +__copyright__ = "Copyright (c) 2004 Cyril Jaquier" +__license__ = "GPL" + +from banmanager import BanManager +from failmanager import FailManager, FailManagerEmpty +from jailthread import JailThread +from action import Action +import time, logging, os + +# Gets the instance of the logger. +logSys = logging.getLogger("fail2ban.actions") + +## +# Execute commands. +# +# This class reads the failures from the Jail queue and decide if an +# action has to be taken. A BanManager take care of the banned IP +# addresses. + +class Actions(JailThread): + + ## + # Constructor. + # + # Initialize the filter object with default values. + # @param jail the jail object + + def __init__(self, jail): + JailThread.__init__(self, jail) + ## The jail which contains this action. + self.jail = jail + self.actions = list() + ## The ban manager. + self.banManager = BanManager() + + def addAction(self, name): + action = Action(name) + self.actions.append(action) + + def delAction(self, name): + for action in self.actions: + if action.getName() == name: + self.actions.remove(action) + break + + def getAction(self, name): + for action in self.actions: + if action.getName() == name: + return action + raise KeyError + + def getLastAction(self): + action = self.actions.pop() + self.actions.append(action) + return action + + ## + # Set the ban time. + # + # @param value the time + + def setBanTime(self, value): + self.banManager.setBanTime(value) + logSys.info("Set banTime = %s" % value) + + ## + # Get the ban time. + # + # @return the time + + def getBanTime(self): + return self.banManager.getBanTime() + + ## + # Main loop. + # + # This function is the main loop of the thread. It checks the Jail + # queue and executes commands when an IP address is banned. + # @return True when the thread exits nicely + + def run(self): + for action in self.actions: + action.execActionStart(None) + self.setActive(True) + while self.isActive(): + if not self.isIdle: + #logSys.debug(self.jail.getName() + ": action") + ret = self.checkBan() + if not ret: + self.checkUnBan() + time.sleep(self.sleepTime) + else: + time.sleep(self.sleepTime) + self.flushBan() + for action in self.actions: + action.execActionStop(None) + logSys.debug(self.jail.getName() + ": action terminated") + return True + + ## + # Check for IP address to ban. + # + # Look in the Jail queue for FailTicket. If a ticket is available, + # it executes the "ban" command and add a ticket to the BanManager. + # @return True if an IP address get banned + + def checkBan(self): + logSys.debug("Check for IP address to ban") + ticket = self.jail.getFailTicket() + if ticket != False: + aInfo = dict() + bTicket = BanManager.createBanTicket(ticket) + aInfo["ip"] = bTicket.getIP() + logSys.info("Ban %s" % aInfo["ip"]) + for action in self.actions: + action.execActionBan(aInfo) + self.banManager.addBanTicket(bTicket) + return True + return False + + ## + # Check for IP address to unban. + # + # Unban IP address which are outdated. + + def checkUnBan(self): + logSys.debug("Check for IP address to unban") + for ticket in self.banManager.unBanList(time.time()): + aInfo = dict() + aInfo["ip"] = ticket.getIP() + logSys.info("Unban %s" % aInfo["ip"]) + for action in self.actions: + action.execActionUnban(aInfo) + + ## + # Flush the ban list. + # + # Unban all IP address which are still in the banning list. + + def flushBan(self): + logSys.debug("Flush ban list") + for ticket in self.banManager.flushBanList(): + aInfo = dict() + aInfo["ip"] = ticket.getIP() + logSys.info("Unban %s" % aInfo["ip"]) + for action in self.actions: + action.execActionUnban(aInfo) + + ## + # Get the status of the filter. + # + # Get some informations about the filter state such as the total + # number of failures. + # @return a list with tuple + + def status(self): + ret = [("Currently banned", self.banManager.size()), + ("Total banned", self.banManager.getBanTotal())] + return ret + \ No newline at end of file diff --git a/server/jail.py b/server/jail.py index 06b1229d..10dd22df 100644 --- a/server/jail.py +++ b/server/jail.py @@ -24,7 +24,7 @@ __date__ = "$Date: 2004/10/10 13:33:40 $" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" -from action import Action +from actions import Actions from filter import Filter import Queue @@ -35,7 +35,7 @@ class Jail: self.name = name self.queue = Queue.Queue() self.filter = Filter(self) - self.action = Action(self) + self.action = Actions(self) def setName(self, name): self.name = name diff --git a/server/server.py b/server/server.py index 7ef3886f..31f9d97d 100644 --- a/server/server.py +++ b/server/server.py @@ -162,7 +162,25 @@ class Server: else: raise ServerUnknownJail(name) - # Action + # Action + def addAction(self, name, value): + if self.jails.has_key(name): + self.jails[name].getAction().addAction(value) + else: + raise ServerUnknownJail(name) + + def getLastAction(self, name): + if self.jails.has_key(name): + return self.jails[name].getAction().getLastAction() + else: + raise ServerUnknownJail(name) + + def delAction(self, name, value): + if self.jails.has_key(name): + self.jails[name].getAction().delAction(value) + else: + raise ServerUnknownJail(name) + def setBanTime(self, name, value): if self.jails.has_key(name): self.jails[name].getAction().setBanTime(value) @@ -175,63 +193,63 @@ class Server: else: raise ServerUnknownJail(name) - def setActionStart(self, name, value): + def setActionStart(self, name, action, value): if self.jails.has_key(name): - self.jails[name].getAction().setActionStart(value) + self.jails[name].getAction().getAction(action).setActionStart(value) else: raise ServerUnknownJail(name) - def getActionStart(self, name): + def getActionStart(self, name, action): if self.jails.has_key(name): - return self.jails[name].getAction().getActionStart() + return self.jails[name].getAction().getAction(action).getActionStart() else: raise ServerUnknownJail(name) - def setActionStop(self, name, value): + def setActionStop(self, name, action, value): if self.jails.has_key(name): - self.jails[name].getAction().setActionStop(value) + self.jails[name].getAction().getAction(action).setActionStop(value) else: raise ServerUnknownJail(name) - def getActionStop(self, name): + def getActionStop(self, name, action): if self.jails.has_key(name): - return self.jails[name].getAction().getActionStop() + return self.jails[name].getAction().getAction(action).getActionStop() else: raise ServerUnknownJail(name) - def setActionCheck(self, name, value): + def setActionCheck(self, name, action, value): if self.jails.has_key(name): - self.jails[name].getAction().setActionCheck(value) + self.jails[name].getAction().getAction(action).setActionCheck(value) else: raise ServerUnknownJail(name) - def getActionCheck(self, name): + def getActionCheck(self, name, action): if self.jails.has_key(name): - return self.jails[name].getAction().getActionCheck() + return self.jails[name].getAction().getAction(action).getActionCheck() else: raise ServerUnknownJail(name) - def setActionBan(self, name, value): + def setActionBan(self, name, action, value): if self.jails.has_key(name): - self.jails[name].getAction().setActionBan(value) + self.jails[name].getAction().getAction(action).setActionBan(value) else: raise ServerUnknownJail(name) - def getActionBan(self, name): + def getActionBan(self, name, action): if self.jails.has_key(name): - return self.jails[name].getAction().getActionBan() + return self.jails[name].getAction().getAction(action).getActionBan() else: raise ServerUnknownJail(name) - def setActionUnban(self, name, value): + def setActionUnban(self, name, action, value): if self.jails.has_key(name): - self.jails[name].getAction().setActionUnban(value) + self.jails[name].getAction().getAction(action).setActionUnban(value) else: raise ServerUnknownJail(name) - def getActionUnban(self, name): + def getActionUnban(self, name, action): if self.jails.has_key(name): - return self.jails[name].getAction().getActionUnban() + return self.jails[name].getAction().getAction(action).getActionUnban() else: raise ServerUnknownJail(name) diff --git a/server/transmitter.py b/server/transmitter.py index a3de9a23..c10157c9 100644 --- a/server/transmitter.py +++ b/server/transmitter.py @@ -104,7 +104,7 @@ class Transmitter: self.server.setLogLevel(value) return self.server.getLogLevel() # Jail - if action[1] == "idle": + elif action[1] == "idle": if action[2] == "on": self.server.setIdleJail(name, True) elif action[2] == "off": @@ -140,26 +140,38 @@ class Transmitter: value = action[2] self.server.setBanTime(name, int(value)) return self.server.getBanTime(name) + elif action[1] == "addaction": + value = action[2] + self.server.addAction(name, value) + return self.server.getLastAction(name).getName() + elif action[1] == "delaction": + self.server.delAction(name, value) + return None elif action[1] == "actionstart": - value = action[2] - self.server.setActionStart(name, value) - return self.server.getActionStart(name) + act = action[2] + value = action[3] + self.server.setActionStart(name, act, value) + return self.server.getActionStart(name, act) elif action[1] == "actionstop": - value = action[2] - self.server.setActionStop(name, value) - return self.server.getActionStop(name) + act = action[2] + value = action[3] + self.server.setActionStop(name, act, value) + return self.server.getActionStop(name, act) elif action[1] == "actioncheck": - value = action[2] - self.server.setActionCheck(name, value) - return self.server.getActionCheck(name) + act = action[2] + value = action[3] + self.server.setActionCheck(name, act, value) + return self.server.getActionCheck(name, act) elif action[1] == "actionban": - value = action[2] - self.server.setActionBan(name, value) - return self.server.getActionBan(name) + act = action[2] + value = action[3] + self.server.setActionBan(name, act, value) + return self.server.getActionBan(name, act) elif action[1] == "actionunban": - value = action[2] - self.server.setActionUnban(name, value) - return self.server.getActionUnban(name) + act = action[2] + value = action[3] + self.server.setActionUnban(name, act, value) + return self.server.getActionUnban(name, act) raise Exception("Invalid command (no set action)") def actionGet(self, action): diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 00000000..fba97f88 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,5 @@ +[install] +install-purelib=/usr/lib/fail2ban + +[sdist] +formats=bztar diff --git a/setup.py b/setup.py new file mode 100755 index 00000000..f894f3a2 --- /dev/null +++ b/setup.py @@ -0,0 +1,84 @@ +#!/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: 1.6 $ + +__author__ = "Cyril Jaquier" +__version__ = "$Revision: 1.6 $" +__date__ = "$Date: 2006/01/22 11:08:42 $" +__copyright__ = "Copyright (c) 2004 Cyril Jaquier" +__license__ = "GPL" + +from distutils.core import setup +from version import version +from os.path import isfile, join +from sys import exit, argv + +longdesc = ''' +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.''' + +setup( + name = "fail2ban", + version = version, + description = "Ban IPs that make too many password failure", + long_description = longdesc, + author = "Cyril Jaquier", + author_email = "lostcontrol@users.sourceforge.net", + url = "http://fail2ban.sourceforge.net", + license = "GPL", + platforms = "Posix", + scripts = ['fail2ban-client', 'fail2ban-server', + 'fail2ban-testcases'], + py_modules = ['version'], + packages = ['client', 'server', 'testcases', 'utils'] +) + +# Do some checks after installation +# Search for obsolete files. +obsoleteFiles = [] +elements = {"/etc/": ["fail2ban.conf"], + "/usr/bin/": ["fail2ban.py"], + "/usr/lib/fail2ban/firewall/": ["iptables.py", "ipfwadm.py", + "ipfw.py"]} +for dir in elements: + for f in elements[dir]: + path = join(dir, f) + if isfile(path): + obsoleteFiles.append(path) +if obsoleteFiles: + print + print "Obsolete files from previous Fail2Ban versions were found on " \ + "your system." + print "Please delete them:" + print + for f in obsoleteFiles: + print "\t" + f + print + +# Update config file +if argv[1] == "install": + print + print "Please do not forget to update your configuration files." + print "Use config/ as an example." + print diff --git a/version.py b/version.py new file mode 100644 index 00000000..d4fe1137 --- /dev/null +++ b/version.py @@ -0,0 +1,27 @@ +# 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: 1.15.2.1 $ + +__author__ = "Cyril Jaquier" +__version__ = "$Revision: 1.15.2.1 $" +__date__ = "$Date: 2006/03/19 18:35:21 $" +__copyright__ = "Copyright (c) 2004 Cyril Jaquier" +__license__ = "GPL" + +version = "0.7.0-SVN"