Permalink
Browse files

uff - nasty refactoring to make fail2ban loop if there is some error …

…occur
  • Loading branch information...
yarikoptic committed Oct 12, 2005
1 parent d7dc8f3 commit 03e7d722de6f7ea72b6dd9280f8762c96e4b678e
Showing with 181 additions and 112 deletions.
  1. +14 −2 config/fail2ban.conf.default
  2. +4 −1 debian/changelog
  3. +31 −6 fail2ban
  4. +113 −100 fail2ban.py
  5. +15 −3 firewall/firewall.py
  6. +4 −0 utils/process.py
@@ -185,9 +185,15 @@ fwstart = iptables -N fail2ban-http
# Values: CMD Default:
#
fwend = iptables -D INPUT -p tcp --dport http -j fail2ban-http
- iptables -D fail2ban-http -j RETURN
+ iptables -F fail2ban-http
iptables -X fail2ban-http
+# Option: fwcheck
+# Notes.: command executed once before each fwban command
+# Values: CMD Default:
+#
+fwcheck = iptables -L INPUT | grep -q fail2ban-http
+
# Option: fwban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
@@ -257,9 +263,15 @@ fwstart = iptables -N fail2ban-ssh
# Values: CMD Default:
#
fwend = iptables -D INPUT -p tcp --dport ssh -j fail2ban-ssh
- iptables -D fail2ban-ssh -j RETURN
+ iptables -F fail2ban-ssh
iptables -X fail2ban-ssh
+# Option: fwcheck
+# Notes.: command executed once before each fwban command
+# Values: CMD Default:
+#
+fwcheck = iptables -L INPUT | grep -q fail2ban-ssh
+
# Option: fwbanrule
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
View
@@ -1,4 +1,4 @@
-fail2ban (0.5.4-5.6) unstable; urgency=low
+fail2ban (0.5.4-5.7) unstable; urgency=low
* Added a notification regarding the importance of 0.5.4-5 change of
failregex in the config file
@@ -11,6 +11,9 @@ fail2ban (0.5.4-5.6) unstable; urgency=low
SSH: Illegal -> Invalid. Should match both now
* Fixed a problem of raise AttributeError exception reported as a side
effect of crash during parsing of the config file
+ * Introduced fwcheck option to verify consistency of the
+ chains. Implemented automatic restart of fail2ban main function in
+ case if check of fwban failed. Should close few bugs
-- Yaroslav Halchenko <debian@onerussian.com> Mon, 3 Oct 2005 22:26:28 -1000
View
@@ -26,24 +26,46 @@ __date__ = "$Date: 2005/08/04 20:51:14 $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
-import sys, traceback, logging
+import sys, traceback, logging, time
# Appends our own modules path.
sys.path.append("/usr/share/fail2ban")
# Now we can import our modules.
import fail2ban
from utils.pidlock import PIDLock
+from utils.process import ExternalError
+
+# Start the application. Handle all the unhandled exceptions
+# yoh: I don't think that this parameters need to be configured
+# and probably maxRestarts should be removed
+legitRestartTime = 10 # legitimate minimal restart time
+maxRestarts = 100 # max number of times to perform restart
+
+lastRestartTime = time.time()
+restarts = 0
# Get the instance of the logger.
logSys = logging.getLogger("fail2ban")
# Get PID lock file instance
pidLock = PIDLock()
-# Start the application. Handle all the unhandled exceptions
try:
- fail2ban.main()
+ while True:
+ restarts += 1
+ try:
+ fail2ban.main(restarts>1)
+ except ExternalError, e:
+ # There went something wrong while dealing with Iptables. May be chain got
+ # removed?
+ logSys.error("Fail2Ban got a problem: " + e.__str__())
+ if (time.time() - lastRestartTime > legitRestartTime) and (restarts < maxRestarts):
+ logSys.error("Restarting for the %d time "%restarts)
+ lastRestartTime = time.time()
+ else:
+ logSys.error("Exiting: restarts follow too often, or too many restart attempts")
+ sys.exit(0)
except SystemExit:
# We called sys.exit(). Nothing wrong so just pass
pass
@@ -55,6 +77,9 @@ except Exception, e:
logSys.error("Type: " + `type.__name__` + "\n" +
"Value: " + `e.args` + "\n" +
"TB: " + `tbStack`)
- # Remove the PID lock file. Should close #1239562
- pidLock.remove()
- logging.shutdown()
+
+# Remove the PID lock file. Should close #1239562
+pidLock.remove()
+logSys.info("Exiting...")
+logging.shutdown()
+
View
@@ -92,9 +92,8 @@ def sigTERMhandler(signum, frame):
logSys.debug("Signal handler called with sig "+`signum`)
killApp()
-def killApp():
- """ Flush the ban list, remove the PID lock file and exit
- nicely.
+def restoreFwRules():
+ """ Flush the ban list
"""
logSys.warn("Restoring firewall rules...")
for element in logFwList:
@@ -103,12 +102,15 @@ def killApp():
for element in logFwList:
l = element[4]
executeCmd(l["fwend"], conf["debug"])
- # Execute global start command
+ # Execute global end command
executeCmd(conf["cmdend"], conf["debug"])
- # Remove the PID lock
- pidLock.remove()
- logSys.info("Exiting...")
- logging.shutdown()
+
+def killApp():
+ """ Flush the ban list, remove and exit
+ nicely.
+ """
+ # Restore Fw rules
+ restoreFwRules()
sys.exit(0)
def getCmdLineOptions(optList):
@@ -136,9 +138,12 @@ def getCmdLineOptions(optList):
if opt[0] == "-k":
conf["kill"] = True
-def main():
+def main(secondaryStart):
""" Fail2Ban main function
"""
+ # (re)Initialize global variables
+ logFwList.__init__()
+ conf.clear()
# Add the default logging handler
stdout = logging.StreamHandler(sys.stdout)
@@ -213,106 +218,110 @@ def main():
except KeyError:
pass
- # Start Fail2Ban in daemon mode
- if conf["background"]:
- retCode = createDaemon()
- signal.signal(signal.SIGTERM, sigTERMhandler)
- if not retCode:
- logSys.error("Unable to start daemon")
- sys.exit(-1)
+ # If it is not a hot restart
+ # fork, setup logging, create pid, check for root
+ if not secondaryStart:
+ # Start Fail2Ban in daemon mode
+ if conf["background"]:
+ logSys.debug("Daemonizing")
+ retCode = createDaemon()
+ signal.signal(signal.SIGTERM, sigTERMhandler)
+ if not retCode:
+ logSys.error("Unable to start daemon")
+ sys.exit(-1)
- # First setup Log targets
- # Bug fix for #1234699
- os.umask(0077)
- for target in conf["logtargets"].split():
- # target formatter
- # By default global formatter is taken. Is different for SYSLOG
- tformatter = formatter
- if target == "STDERR":
- hdlr = logging.StreamHandler(sys.stderr)
- elif target == "SYSLOG":
- # SYSLOG target can be either
- # a socket (file, so it starts with /)
- # or hostname
- # or hostname:port
- syslogtargets = re.findall("(/[\w/]*)|([^/ ][^: ]*)(:(\d+)){,1}",
- conf["syslog-target"])
- # we are waiting for a single match
- syslogtargets = syslogtargets[0]
+ # First setup Log targets
+ # Bug fix for #1234699
+ os.umask(0077)
+ for target in conf["logtargets"].split():
+ # target formatter
+ # By default global formatter is taken. Is different for SYSLOG
+ tformatter = formatter
+ if target == "STDERR":
+ hdlr = logging.StreamHandler(sys.stderr)
+ elif target == "SYSLOG":
+ # SYSLOG target can be either
+ # a socket (file, so it starts with /)
+ # or hostname
+ # or hostname:port
+ syslogtargets = re.findall("(/[\w/]*)|([^/ ][^: ]*)(:(\d+)){,1}",
+ conf["syslog-target"])
+ # we are waiting for a single match
+ syslogtargets = syslogtargets[0]
- # assign facility if it was defined
- if conf["syslog-facility"] < 0:
- facility = handlers.SysLogHandler.LOG_USER
- else:
- facility = conf["syslog-facility"]
+ # assign facility if it was defined
+ if conf["syslog-facility"] < 0:
+ facility = handlers.SysLogHandler.LOG_USER
+ else:
+ facility = conf["syslog-facility"]
- if len(syslogtargets) == 0: # everything default
- hdlr = logging.handlers.SysLogHandler()
+ if len(syslogtargets) == 0: # everything default
+ hdlr = logging.handlers.SysLogHandler()
+ else:
+ if not ( syslogtargets[0] == "" ): # got socket
+ syslogtarget = syslogtargets[0]
+ else: # got hostname and maybe a port
+ if syslogtargets[3] == "": # no port specified
+ port = 514
+ else:
+ port = int(syslogtargets[3])
+ syslogtarget = (syslogtargets[1], port)
+ hdlr = logging.handlers.SysLogHandler(syslogtarget, facility)
+ tformatter = logging.Formatter("fail2ban[%(process)d]: " +
+ formatterstring);
else:
- if not ( syslogtargets[0] == "" ): # got socket
- syslogtarget = syslogtargets[0]
- else: # got hostname and maybe a port
- if syslogtargets[3] == "": # no port specified
- port = 514
- else:
- port = int(syslogtargets[3])
- syslogtarget = (syslogtargets[1], port)
- hdlr = logging.handlers.SysLogHandler(syslogtarget, facility)
- tformatter = logging.Formatter("fail2ban[%(process)d]: " +
- formatterstring);
- else:
- # Target should be a file
- try:
- open(target, "a")
- hdlr = logging.FileHandler(target)
- except IOError:
- logSys.error("Unable to log to " + target)
- continue
- # Set formatter and add handler to logger
- hdlr.setFormatter(tformatter)
- logSys.addHandler(hdlr)
+ # Target should be a file
+ try:
+ open(target, "a")
+ hdlr = logging.FileHandler(target)
+ except IOError:
+ logSys.error("Unable to log to " + target)
+ continue
+ # Set formatter and add handler to logger
+ hdlr.setFormatter(tformatter)
+ logSys.addHandler(hdlr)
- # Process some options
+ # Process some options
- # Verbose level
- if conf["verbose"]:
- logSys.warn("Verbose level is "+`conf["verbose"]`)
- if conf["verbose"] == 1:
- logSys.setLevel(logging.INFO)
- elif conf["verbose"] > 1:
+ # Verbose level
+ if conf["verbose"]:
+ logSys.warn("Verbose level is "+`conf["verbose"]`)
+ if conf["verbose"] == 1:
+ logSys.setLevel(logging.INFO)
+ elif conf["verbose"] > 1:
+ logSys.setLevel(logging.DEBUG)
+
+ # Set debug log level
+ if conf["debug"]:
logSys.setLevel(logging.DEBUG)
+ formatterstring = ('%(levelname)s: [%(filename)s (%(lineno)d)] ' +
+ '%(message)s')
+ formatter = logging.Formatter("%(asctime)s " + formatterstring)
+ stdout.setFormatter(formatter)
+ logSys.warn("DEBUG MODE: FIREWALL COMMANDS ARE _NOT_ EXECUTED BUT " +
+ "ONLY DISPLAYED IN THE LOG MESSAGES")
+
+ # Checks for root user. This is necessary because log files
+ # are owned by root and firewall needs root access.
+ if not checkForRoot():
+ logSys.error("You must be root")
+ if not conf["debug"]:
+ sys.exit(-1)
- # Set debug log level
- if conf["debug"]:
- logSys.setLevel(logging.DEBUG)
- formatterstring = ('%(levelname)s: [%(filename)s (%(lineno)d)] ' +
- '%(message)s')
- formatter = logging.Formatter("%(asctime)s " + formatterstring)
- stdout.setFormatter(formatter)
- logSys.warn("DEBUG MODE: FIREWALL COMMANDS ARE _NOT_ EXECUTED BUT " +
- "ONLY DISPLAYED IN THE LOG MESSAGES")
+ # Checks that no instance of Fail2Ban is currently running.
+ pid = pidLock.exists()
+ if pid:
+ logSys.error("Fail2Ban already running with PID "+pid)
+ sys.exit(-1)
+ else:
+ ret = pidLock.create()
+ if not ret:
+ # Unable to create PID lock. Exit
+ sys.exit(-1)
# Ignores IP list
ignoreIPList = conf["ignoreip"].split(' ')
- # Checks for root user. This is necessary because log files
- # are owned by root and firewall needs root access.
- if not checkForRoot():
- logSys.error("You must be root")
- if not conf["debug"]:
- sys.exit(-1)
-
- # Checks that no instance of Fail2Ban is currently running.
- pid = pidLock.exists()
- if pid:
- logSys.error("Fail2Ban already running with PID "+pid)
- sys.exit(-1)
- else:
- 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("FindTime is " + `conf["findtime"]`)
@@ -350,7 +359,8 @@ def main():
["str", "fwstart", ""],
["str", "fwend", ""],
["str", "fwban", ""],
- ["str", "fwunban", ""])
+ ["str", "fwunban", ""],
+ ["str", "fwcheck", ""])
logSys.info("Fail2Ban v" + version + " is running")
@@ -364,7 +374,7 @@ def main():
lObj = LogReader(l["logfile"], l["timeregex"], l["timepattern"],
l["failregex"], l["maxfailures"], l["findtime"])
# Creates a firewall object
- fObj = Firewall(l["fwban"], l["fwunban"], l["bantime"])
+ fObj = Firewall(l["fwban"], l["fwunban"], l["fwcheck"], l["bantime"])
# Links them into a list. I'm not really happy
# with this :/
logFwList.append([t, lObj, fObj, dict(), l])
@@ -449,7 +459,10 @@ def main():
mail.sendmail(mailConf["subject"],
mailConf["message"], aInfo)
del element[3][attempt]
-
+ except ExternalError:
+ # restore as much as possible before restart
+ restoreFwRules()
+ raise
except KeyboardInterrupt:
# When the user press <ctrl>+<c> we exit nicely.
killApp()
Oops, something went wrong.

0 comments on commit 03e7d72

Please sign in to comment.