--- blockhosts.py 2006-12-28 21:15:52.000000000 -0500 +++ blockhosts.erik.py 2006-12-28 21:27:22.000000000 -0500 @@ -68,6 +68,10 @@ datetime, optparse. ==== +Modified 29/12/06 by Erik Ljungström to allow for email alerts being +sent whenever a host is blocked. + +==== BlockHosts Script License This work is hereby released into the Public Domain. To view a copy of the public domain dedication, visit @@ -110,7 +114,11 @@ # and no config file provided HC_OPTIONS = { "HOSTS_BLOCKFILE": "/etc/hosts.allow", - + "SMTP_SERVER": "localhost", + "NOTIFY_ADDRESS": '', + "SMTP_USER":'', + "SMTP_PASSWD":'', + "SENDER_ADDRESS": "BlockHosts ", "LOGFILES": ( "/var/log/secure", ), # default list of logs to process, multiple files can be listed @@ -221,7 +229,7 @@ import time import errno import fcntl - + import smtplib import datetime from optparse import OptionParser, OptionGroup import ConfigParser @@ -467,9 +475,37 @@ ####################################################################### - +class MailAlert: + def __init__(self, address=["root@localhost.localdomain"], sender_address="blockhosts@localhost.localdomain", smtp_server="localhost", smtp_user='', smtp_passwd='', host=''): + self.__address = address.replace('\@', '@') + self.__sender_address = sender_address.replace('\@', '@') + self.__smtp_server = smtp_server + # If smtp_user and passwd is empty, assume no authentication is necessary + self.__smtp_user = smtp_user.replace('\@', '@') + self.__smtp_passwd = smtp_passwd + self.__host = host + def sendMail(self): + if len(self.__address) == 0: + return + session = smtplib.SMTP(self.__smtp_server) + time = datetime.datetime.now().strftime("%a %d/%m/%Y %H:%M:%S") + message = "To: " + self.__address + message += "\nFrom: "+ self.__sender_address + message += "\nSubject: Host blocked!\n\n" + message += time + message += "\nAdded abusive host: " + self.__host + " to the naughty list" + if len(self.__smtp_user) > 0: + session.login(self.__smtp_user, self.__smtp_passwd) + smtpresult = session.sendmail(self.__sender_address, self.__address, message) + if smtpresult: + errstr = "" + for recip in smtpresult.keys(): + errstr = """Unable to deliver mail to: %s Server responded: %s %s %s"""\ + % (recip, smtpresult[recip][0], smtpresult[recip][1], errstr) + raise smtplib.SMTPException, errstr + class BlockHosts: - def __init__(self, blockfile, block_services="ALL"): + def __init__(self, blockfile, block_services="ALL",address=["root@localhost.localdomain"], sender_address="blockhosts@localhost.localdomain", smtp_server="localhost", smtp_user='', smtp_passwd=''): self.__abusive_hosts = {} # hosts -> HostData [count, last seen] self.__offset_first_marker = -1L self.__remaining_lines = [] # all lines after the 2nd end marker @@ -477,6 +513,12 @@ self.__block_services = block_services self.__found_first_marker = False self.__found_second_marker = False + #Mail alert variables - purely for passing on to the MailAlert class + self.__address = address + self.__sender_address = sender_address + self.__smtp_server = smtp_server + self.__smtp_user = smtp_user + self.__smtp_passwd = smtp_passwd ####### def load_hosts_blockfile(self, logoffsets): @@ -644,10 +686,10 @@ # first collect all the lines that will go in the blockhosts # section of blockfile - this is stored in lines[] status = False - lines.append("%s\n" % HOSTS_MARKER_LINE) # first marker line - for host in new_hosts: + mailer = MailAlert(self.__address, self.__sender_address, self.__smtp_server,self.__smtp_user, self.__smtp_passwd, host) + mailer.sendMail() lines.append("%s: %15s : deny\n" % (self.__block_services, host)) if new_hosts: lines.append("\n") @@ -733,7 +775,7 @@ except NoSectionError, e: traceback.print_exc() die("Config file missing 'constants' section", e) - + keys = allitems.keys() for key in keys: if options.has_key(key): @@ -759,10 +801,15 @@ config_options = copy.copy(HC_OPTIONS) get_config(config_options) - +#DEFAULTS oparser.set_defaults(verbose=1, ignore_offset=False, dry_run=False, + notify_address=config_options["NOTIFY_ADDRESS"], + sender_address=config_options["SENDER_ADDRESS"], + smtp_server=config_options["SMTP_SERVER"], + smtp_user=config_options["SMTP_USER"], + smtp_passwd=config_options["SMTP_PASSWD"], logfiles=",".join(config_options["LOGFILES"]), blockfile=config_options["HOSTS_BLOCKFILE"], block_services=config_options["BLOCK_SERVICES"], @@ -822,9 +869,24 @@ oconfig.add_option("--lockfile", metavar="FILE", help="Prevent multiple instances from running - open this file for locking and writing (\"%s\")" % defaults.lockfile) + oconfig.add_option("--notify-address", metavar="ADDRESS", + help="Address to send notification emails to (Leave blank to disable email alerts altogether) (\"%s\")" % defaults.notify_address) + + oconfig.add_option("--sender-address", metavar="ADDRESS", + help="Address appearing as sender address in notification emails (\"%s\")" % defaults.sender_address) + + oconfig.add_option("--smtp-server", metavar="HOST", + help="SMTP server to send notification emails through (\"%s\")" % defaults.smtp_server) + + oconfig.add_option("--smtp-user", metavar="USERNAME", + help="If SMTP authentication is required, use this username (\"%s\")" % defaults.smtp_user) + oconfig.add_option("--smtp-passwd", metavar="PASSWORD", + help="If SMTP authentication is required, use this password (\"%s\")" % defaults.smtp_passwd) + oconfig.add_option("--echo", type="string", metavar="TAG", help="Prints TAG on stderr and syslog, may be used to identify a run of blockhosts (%s)" % defaults.echo) + oparser.add_option_group(oconfig) (options, remaining_args) = oparser.parse_args() @@ -873,8 +935,8 @@ prune_date = NOW_DATETIME - datetime.timedelta(0, options.discard*60*60) info(" ... will discard all host entries older than ", prune_date.strftime("%Y-%m-%d %H:%M")) - - dh = BlockHosts(options.blockfile, options.block_services) +# Had to do this.. options:: not reachable from within BlockHosts? + dh = BlockHosts(options.blockfile, options.block_services,options.notify_address, options.sender_address, options.smtp_server, options.smtp_user, options.smtp_passwd ) prev_logoffsets = {} new_logoffsets = {} ip_pid = {} @@ -947,9 +1009,8 @@ debug(" ------- finished looking into log file: ", logfile) debug(" ------- collecting block file updates --- ") - dh.update_hosts_blockfile(options.blockcount, prune_date, new_logoffsets, options.dry_run) - + if not options.dry_run: lock.unlock() #######################################################################