X-Git-Url: https://git.ucc.asn.au/?a=blobdiff_plain;f=src%2Flink%2Fpexpect%2Fexamples%2Ftopip.py;fp=src%2Flink%2Fpexpect%2Fexamples%2Ftopip.py;h=0000000000000000000000000000000000000000;hb=edf8e5b569e75692d61a44f2c3241fb6410e4fbd;hp=5bd63e2ef224fea4a14ef67cb45ce8c4b26b7bfe;hpb=35ff18a5beda685e59ca898026570d67b7ead333;p=progcomp10.git diff --git a/src/link/pexpect/examples/topip.py b/src/link/pexpect/examples/topip.py deleted file mode 100755 index 5bd63e2..0000000 --- a/src/link/pexpect/examples/topip.py +++ /dev/null @@ -1,267 +0,0 @@ -#!/usr/bin/env python - -""" This runs netstat on a local or remote server. It calculates some simple -statistical information on the number of external inet connections. It groups -by IP address. This can be used to detect if one IP address is taking up an -excessive number of connections. It can also send an email alert if a given IP -address exceeds a threshold between runs of the script. This script can be used -as a drop-in Munin plugin or it can be used stand-alone from cron. I used this -on a busy web server that would sometimes get hit with denial of service -attacks. This made it easy to see if a script was opening many multiple -connections. A typical browser would open fewer than 10 connections at once. A -script might open over 100 simultaneous connections. - -./topip.py [-s server_hostname] [-u username] [-p password] {-a from_addr,to_addr} {-n N} {-v} {--ipv6} - - -s : hostname of the remote server to login to. - -u : username to user for login. - -p : password to user for login. - -n : print stddev for the the number of the top 'N' ipaddresses. - -v : verbose - print stats and list of top ipaddresses. - -a : send alert if stddev goes over 20. - -l : to log message to /var/log/topip.log - --ipv6 : this parses netstat output that includes ipv6 format. - Note that this actually only works with ipv4 addresses, but for versions of - netstat that print in ipv6 format. - --stdev=N : Where N is an integer. This sets the trigger point for alerts and logs. - Default is to trigger if max value is above 5 standard deviations. - -Example: - - This will print stats for the top IP addresses connected to the given host: - - ./topip.py -s www.example.com -u mylogin -p mypassword -n 10 -v - - This will send an alert email if the maxip goes over the stddev trigger value and - the the current top ip is the same as the last top ip (/tmp/topip.last): - - ./topip.py -s www.example.com -u mylogin -p mypassword -n 10 -v -a alert@example.com,user@example.com - - This will print the connection stats for the localhost in Munin format: - - ./topip.py - -Noah Spurrier - -$Id: topip.py 489 2007-11-28 23:40:34Z noah $ -""" - -import pexpect, pxssh # See http://pexpect.sourceforge.net/ -import os, sys, time, re, getopt, pickle, getpass, smtplib -import traceback -from pprint import pprint - -TOPIP_LOG_FILE = '/var/log/topip.log' -TOPIP_LAST_RUN_STATS = '/var/run/topip.last' - -def exit_with_usage(): - - print globals()['__doc__'] - os._exit(1) - -def stats(r): - - """This returns a dict of the median, average, standard deviation, min and max of the given sequence. - - >>> from topip import stats - >>> print stats([5,6,8,9]) - {'med': 8, 'max': 9, 'avg': 7.0, 'stddev': 1.5811388300841898, 'min': 5} - >>> print stats([1000,1006,1008,1014]) - {'med': 1008, 'max': 1014, 'avg': 1007.0, 'stddev': 5.0, 'min': 1000} - >>> print stats([1,3,4,5,18,16,4,3,3,5,13]) - {'med': 4, 'max': 18, 'avg': 6.8181818181818183, 'stddev': 5.6216817577237475, 'min': 1} - >>> print stats([1,3,4,5,18,16,4,3,3,5,13,14,5,6,7,8,7,6,6,7,5,6,4,14,7]) - {'med': 6, 'max': 18, 'avg': 7.0800000000000001, 'stddev': 4.3259218670706474, 'min': 1} - """ - - total = sum(r) - avg = float(total)/float(len(r)) - sdsq = sum([(i-avg)**2 for i in r]) - s = list(r) - s.sort() - return dict(zip(['med', 'avg', 'stddev', 'min', 'max'] , (s[len(s)//2], avg, (sdsq/len(r))**.5, min(r), max(r)))) - -def send_alert (message, subject, addr_from, addr_to, smtp_server='localhost'): - - """This sends an email alert. - """ - - message = 'From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n' % (addr_from, addr_to, subject) + message - server = smtplib.SMTP(smtp_server) - server.sendmail(addr_from, addr_to, message) - server.quit() - -def main(): - - ###################################################################### - ## Parse the options, arguments, etc. - ###################################################################### - try: - optlist, args = getopt.getopt(sys.argv[1:], 'h?valqs:u:p:n:', ['help','h','?','ipv6','stddev=']) - except Exception, e: - print str(e) - exit_with_usage() - options = dict(optlist) - - munin_flag = False - if len(args) > 0: - if args[0] == 'config': - print 'graph_title Netstat Connections per IP' - print 'graph_vlabel Socket connections per IP' - print 'connections_max.label max' - print 'connections_max.info Maximum number of connections per IP' - print 'connections_avg.label avg' - print 'connections_avg.info Average number of connections per IP' - print 'connections_stddev.label stddev' - print 'connections_stddev.info Standard deviation' - return 0 - elif args[0] != '': - print args, len(args) - return 0 - exit_with_usage() - if [elem for elem in options if elem in ['-h','--h','-?','--?','--help']]: - print 'Help:' - exit_with_usage() - if '-s' in options: - hostname = options['-s'] - else: - # if host was not specified then assume localhost munin plugin. - munin_flag = True - hostname = 'localhost' - # If localhost then don't ask for username/password. - if hostname != 'localhost' and hostname != '127.0.0.1': - if '-u' in options: - username = options['-u'] - else: - username = raw_input('username: ') - if '-p' in options: - password = options['-p'] - else: - password = getpass.getpass('password: ') - else: - use_localhost = True - - if '-l' in options: - log_flag = True - else: - log_flag = False - if '-n' in options: - average_n = int(options['-n']) - else: - average_n = None - if '-v' in options: - verbose = True - else: - verbose = False - if '-a' in options: - alert_flag = True - (alert_addr_from, alert_addr_to) = tuple(options['-a'].split(',')) - else: - alert_flag = False - if '--ipv6' in options: - ipv6_flag = True - else: - ipv6_flag = False - if '--stddev' in options: - stddev_trigger = float(options['--stddev']) - else: - stddev_trigger = 5 - - if ipv6_flag: - netstat_pattern = '(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+::ffff:(\S+):(\S+)\s+.*?\r' - else: - netstat_pattern = '(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(?:::ffff:)*(\S+):(\S+)\s+.*?\r' - #netstat_pattern = '(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+):(\S+)\s+.*?\r' - - # run netstat (either locally or via SSH). - if use_localhost: - p = pexpect.spawn('netstat -n -t') - PROMPT = pexpect.TIMEOUT - else: - p = pxssh.pxssh() - p.login(hostname, username, password) - p.sendline('netstat -n -t') - PROMPT = p.PROMPT - - # loop through each matching netstat_pattern and put the ip address in the list. - ip_list = {} - try: - while 1: - i = p.expect([PROMPT, netstat_pattern]) - if i == 0: - break - k = p.match.groups()[4] - if k in ip_list: - ip_list[k] = ip_list[k] + 1 - else: - ip_list[k] = 1 - except: - pass - - # remove a few common, uninteresting addresses from the dictionary. - ip_list = dict([ (key,value) for key,value in ip_list.items() if '192.168.' not in key]) - ip_list = dict([ (key,value) for key,value in ip_list.items() if '127.0.0.1' not in key]) - - # sort dict by value (count) - #ip_list = sorted(ip_list.iteritems(),lambda x,y:cmp(x[1], y[1]),reverse=True) - ip_list = ip_list.items() - if len(ip_list) < 1: - if verbose: print 'Warning: no networks connections worth looking at.' - return 0 - ip_list.sort(lambda x,y:cmp(y[1],x[1])) - - # generate some stats for the ip addresses found. - if average_n <= 1: - average_n = None - s = stats(zip(*ip_list[0:average_n])[1]) # The * unary operator treats the list elements as arguments - s['maxip'] = ip_list[0] - - # print munin-style or verbose results for the stats. - if munin_flag: - print 'connections_max.value', s['max'] - print 'connections_avg.value', s['avg'] - print 'connections_stddev.value', s['stddev'] - return 0 - if verbose: - pprint (s) - print - pprint (ip_list[0:average_n]) - - # load the stats from the last run. - try: - last_stats = pickle.load(file(TOPIP_LAST_RUN_STATS)) - except: - last_stats = {'maxip':None} - - if s['maxip'][1] > (s['stddev'] * stddev_trigger) and s['maxip']==last_stats['maxip']: - if verbose: print 'The maxip has been above trigger for two consecutive samples.' - if alert_flag: - if verbose: print 'SENDING ALERT EMAIL' - send_alert(str(s), 'ALERT on %s' % hostname, alert_addr_from, alert_addr_to) - if log_flag: - if verbose: print 'LOGGING THIS EVENT' - fout = file(TOPIP_LOG_FILE,'a') - #dts = time.strftime('%Y:%m:%d:%H:%M:%S', time.localtime()) - dts = time.asctime() - fout.write ('%s - %d connections from %s\n' % (dts,s['maxip'][1],str(s['maxip'][0]))) - fout.close() - - # save state to TOPIP_LAST_RUN_STATS - try: - pickle.dump(s, file(TOPIP_LAST_RUN_STATS,'w')) - os.chmod (TOPIP_LAST_RUN_STATS, 0664) - except: - pass - # p.logout() - -if __name__ == '__main__': - try: - main() - sys.exit(0) - except SystemExit, e: - raise e - except Exception, e: - print str(e) - traceback.print_exc() - os._exit(1) -