Initial version
This commit is contained in:
		
						commit
						c894805619
					
				
					 4 changed files with 278 additions and 0 deletions
				
			
		
							
								
								
									
										6
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,6 @@
 | 
				
			||||||
 | 
					.idea/
 | 
				
			||||||
 | 
					venv/
 | 
				
			||||||
 | 
					*.pyc
 | 
				
			||||||
 | 
					settings.py
 | 
				
			||||||
 | 
					smbspy*.txt
 | 
				
			||||||
 | 
					apachespy*.txt
 | 
				
			||||||
							
								
								
									
										263
									
								
								lsof_spy.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										263
									
								
								lsof_spy.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,263 @@
 | 
				
			||||||
 | 
					#!/usr/bin/env python3
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import subprocess
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					import requests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try:
 | 
				
			||||||
 | 
					    from settings import *
 | 
				
			||||||
 | 
					except ImportError:
 | 
				
			||||||
 | 
					    print("Loading of settings failed. Please check your settings (copy settings.py.default to settings.py)")
 | 
				
			||||||
 | 
					    sys.exit(2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# lsof output columns statics
 | 
				
			||||||
 | 
					FILE = HOST = 8
 | 
				
			||||||
 | 
					DEVICE = 3
 | 
				
			||||||
 | 
					TYPE = 4
 | 
				
			||||||
 | 
					PID = 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def telegram_send(s):
 | 
				
			||||||
 | 
					    context = {
 | 
				
			||||||
 | 
					        'chat_id': TELEGRAM_CHAT_ID,
 | 
				
			||||||
 | 
					        'text': s,
 | 
				
			||||||
 | 
					        'parse_mode': 'HTML',
 | 
				
			||||||
 | 
					        'disable_notification': True
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    r = requests.post("https://api.telegram.org/bot{}/sendMessage".format(TELEGRAM_BOT_KEY), data=context)
 | 
				
			||||||
 | 
					    with open("/tmp/spitfire_smbspy_result.txt", 'w', encoding='utf=8') as f:
 | 
				
			||||||
 | 
					        f.write("Result: {}, Text: {}".format(r.status_code, r.text))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					###
 | 
				
			||||||
 | 
					# SMBD monitoring
 | 
				
			||||||
 | 
					###
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					smb_data = {}
 | 
				
			||||||
 | 
					smbd_output = ""
 | 
				
			||||||
 | 
					smb_result = ""
 | 
				
			||||||
 | 
					smb_users = 0
 | 
				
			||||||
 | 
					smb_active = 0
 | 
				
			||||||
 | 
					smb_files = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try:
 | 
				
			||||||
 | 
					    smbd_output = subprocess.check_output(["lsof", "-c", "/^"+SMBD+"$/", "-Pcnt"], stderr=subprocess.DEVNULL).decode()
 | 
				
			||||||
 | 
					except subprocess.CalledProcessError as e:
 | 
				
			||||||
 | 
					    # Error running process.
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					conns = list(y.split() for y in filter(
 | 
				
			||||||
 | 
					    lambda x: ("IPv4" in x or "IPv6" in x) and HOSTNAME in x and "smbd" in x, smbd_output
 | 
				
			||||||
 | 
					))
 | 
				
			||||||
 | 
					for conn in conns:
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        pid = conn[PID]
 | 
				
			||||||
 | 
					    except IndexError:
 | 
				
			||||||
 | 
					        pid = "-1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        hoststr = conn[HOST]
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            host = hoststr.split("->")[1].split(":")[0]
 | 
				
			||||||
 | 
					        except IndexError:
 | 
				
			||||||
 | 
					            host = hoststr
 | 
				
			||||||
 | 
					    except IndexError:
 | 
				
			||||||
 | 
					        host = "Unknown"
 | 
				
			||||||
 | 
					    smb_data[pid] = {'host': host, 'pid': pid, 'dirs': set(), 'files': set()}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# All lines which are in the base dir and which is a directory or a regular file
 | 
				
			||||||
 | 
					files = list(y.split() for y in filter(
 | 
				
			||||||
 | 
					    lambda x: BASE_DIR in x and (("DIR" in x and "cwd" not in x) or "REG" in x), smbd_output
 | 
				
			||||||
 | 
					))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					for file in files:
 | 
				
			||||||
 | 
					    pid = file[PID]
 | 
				
			||||||
 | 
					    entry = " ".join(file[FILE:])
 | 
				
			||||||
 | 
					    if file[TYPE] == "DIR":
 | 
				
			||||||
 | 
					        smb_data[pid]['dirs'].add(entry)
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        smb_data[pid]['files'].add(entry)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Reformat data to index on host instead of PID
 | 
				
			||||||
 | 
					host_data = {}
 | 
				
			||||||
 | 
					for data in smb_data.values():
 | 
				
			||||||
 | 
					    host = data['host']
 | 
				
			||||||
 | 
					    if host not in host_data.keys():
 | 
				
			||||||
 | 
					        host_data[host] = {'pids': [data['pid']], 'dirs': set(data['dirs']), 'files': set(data['files'])}
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        host_data[host]['pids'].append(data['pid'])
 | 
				
			||||||
 | 
					        host_data[host]['dirs'].union(data['dirs'])
 | 
				
			||||||
 | 
					        host_data[host]['files'].union(data['files'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Filter dirs to remove any dirs that are parents of another entry in the list (not useful)
 | 
				
			||||||
 | 
					for host, data in host_data.items():
 | 
				
			||||||
 | 
					    olddirs = sorted(list(data['dirs']))
 | 
				
			||||||
 | 
					    newdirs = []
 | 
				
			||||||
 | 
					    for d in olddirs:
 | 
				
			||||||
 | 
					        if not any(d in x for x in olddirs if d != x):
 | 
				
			||||||
 | 
					            newdirs.append(d)
 | 
				
			||||||
 | 
					    data['dirs'] = newdirs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					for host in sorted(host_data.keys()):
 | 
				
			||||||
 | 
					    data = host_data[host]
 | 
				
			||||||
 | 
					    flen = len(data['files'])
 | 
				
			||||||
 | 
					    dlen = len(data['dirs'])
 | 
				
			||||||
 | 
					    plen = len(data['pids'])
 | 
				
			||||||
 | 
					    smb_users += 1
 | 
				
			||||||
 | 
					    amounts = ["{} connection(s)".format(plen)]
 | 
				
			||||||
 | 
					    if dlen > 0:
 | 
				
			||||||
 | 
					        amounts.append("{} dir(s)".format(dlen))
 | 
				
			||||||
 | 
					    if flen > 0:
 | 
				
			||||||
 | 
					        smb_active += 1
 | 
				
			||||||
 | 
					        smb_files += flen
 | 
				
			||||||
 | 
					        amounts.append("{} file(s)".format(flen))
 | 
				
			||||||
 | 
					    if dlen == 0 and flen == 0:
 | 
				
			||||||
 | 
					        amounts.append("Nothing")
 | 
				
			||||||
 | 
					    smb_result += "- {}: <i>{} open</i>\n".format(host, ", ".join(amounts))
 | 
				
			||||||
 | 
					    if flen > 0:
 | 
				
			||||||
 | 
					        for f in sorted(data['files']):
 | 
				
			||||||
 | 
					            smb_result += "  - {}\n".format(f)
 | 
				
			||||||
 | 
					    elif dlen > 0:
 | 
				
			||||||
 | 
					        for d in sorted(data['dirs']):
 | 
				
			||||||
 | 
					            smb_result += "  <i>- {}</i>\n".format(d)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					###
 | 
				
			||||||
 | 
					# HTTP Monitoring
 | 
				
			||||||
 | 
					###
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					http_data = {}
 | 
				
			||||||
 | 
					http_output = ""
 | 
				
			||||||
 | 
					http_result = ""
 | 
				
			||||||
 | 
					http_users = 0
 | 
				
			||||||
 | 
					http_files = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try:
 | 
				
			||||||
 | 
					    http_output = subprocess.check_output(["lsof", "-c", "/^"+APACHE+"$/", "-Pcnt"], stderr=subprocess.DEVNULL).decode()
 | 
				
			||||||
 | 
					except subprocess.CalledProcessError as e:
 | 
				
			||||||
 | 
					    # Error running process.
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					conns = list(y.split() for y in filter(
 | 
				
			||||||
 | 
					    lambda x: ("IPv4" in x or "IPv6" in x) and HOSTNAME in x and "apache2" in x, http_output
 | 
				
			||||||
 | 
					))
 | 
				
			||||||
 | 
					for conn in conns:
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        pid = conn[PID]
 | 
				
			||||||
 | 
					    except IndexError:
 | 
				
			||||||
 | 
					        pid = "-1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        hoststr = conn[HOST]
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            host = hoststr.split("->")[1].split(":")[0]
 | 
				
			||||||
 | 
					        except IndexError:
 | 
				
			||||||
 | 
					            host = hoststr
 | 
				
			||||||
 | 
					    except IndexError:
 | 
				
			||||||
 | 
					        host = "Unknown"
 | 
				
			||||||
 | 
					    http_data[pid] = {'host': host, 'pid': pid, 'files': set()}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# All lines which are in the base dir and which is a directory or a regular file
 | 
				
			||||||
 | 
					files = list(y.split() for y in filter(lambda x: BASE_DIR in x and "REG" in x, http_output))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					for file in files:
 | 
				
			||||||
 | 
					    pid = file[PID]
 | 
				
			||||||
 | 
					    entry = " ".join(file[FILE:])
 | 
				
			||||||
 | 
					    http_data[pid]['files'].add(entry)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Reformat data to index on host instead of PID
 | 
				
			||||||
 | 
					http_host_data = {}
 | 
				
			||||||
 | 
					for data in http_data.values():
 | 
				
			||||||
 | 
					    host = data['host']
 | 
				
			||||||
 | 
					    if host not in http_host_data.keys():
 | 
				
			||||||
 | 
					        http_host_data[host] = {'pids': [data['pid']], 'files': set(data['files'])}
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        http_host_data[host]['pids'].append(data['pid'])
 | 
				
			||||||
 | 
					        http_host_data[host]['files'].union(data['files'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					for host in sorted(http_host_data.keys()):
 | 
				
			||||||
 | 
					    data = http_host_data[host]
 | 
				
			||||||
 | 
					    flen = len(data['files'])
 | 
				
			||||||
 | 
					    plen = len(data['pids'])
 | 
				
			||||||
 | 
					    http_users += 1
 | 
				
			||||||
 | 
					    amounts = ["{} connection(s)".format(plen)]
 | 
				
			||||||
 | 
					    if flen > 0:
 | 
				
			||||||
 | 
					        http_files += flen
 | 
				
			||||||
 | 
					        amounts.append("{} file(s)".format(flen))
 | 
				
			||||||
 | 
					    if flen == 0:
 | 
				
			||||||
 | 
					        amounts.append("Nothing")
 | 
				
			||||||
 | 
					    http_result += "- {}: <i>{} open</i>\n".format(host, ", ".join(amounts))
 | 
				
			||||||
 | 
					    if flen > 0:
 | 
				
			||||||
 | 
					        for f in sorted(data['files']):
 | 
				
			||||||
 | 
					            http_result += "  - {}\n".format(f)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Construct final telegram string
 | 
				
			||||||
 | 
					final_result = ""
 | 
				
			||||||
 | 
					if smb_result != "":
 | 
				
			||||||
 | 
					    final_result += "<b>{} SMB</b> has <b>{}</b> file(s) opened by " \
 | 
				
			||||||
 | 
					                    "<b>{}</b> user(s) (<b>{}</b> active users).\n".format(
 | 
				
			||||||
 | 
					        SERVER_NAME, smb_files, smb_users, smb_active
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    final_result += smb_result
 | 
				
			||||||
 | 
					else:
 | 
				
			||||||
 | 
					    final_result += "<b>{} SMB</b> has no open files or command failure.\n".format(SERVER_NAME)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if http_result != "":
 | 
				
			||||||
 | 
					    final_result += "<b>{} HTTP</b> has <b>{}</b> active transfer(s) by <b>{}</b> user(s).\n".format(
 | 
				
			||||||
 | 
					        SERVER_NAME, http_files, http_users
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    final_result += http_result
 | 
				
			||||||
 | 
					else:
 | 
				
			||||||
 | 
					    final_result += "<b>{} HTTP</b> has no open files or command failure.".format(SERVER_NAME)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Report to Telegram if changed
 | 
				
			||||||
 | 
					if os.path.isfile("/tmp/spitfire_smbspy_status.txt"):
 | 
				
			||||||
 | 
					    with open('/tmp/spitfire_smbspy_status.txt', 'r', encoding='utf-8') as f:
 | 
				
			||||||
 | 
					        old_str = "".join(f.readlines())
 | 
				
			||||||
 | 
					else:
 | 
				
			||||||
 | 
					    old_str = ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if old_str != final_result:
 | 
				
			||||||
 | 
					    with open('/tmp/spitfire_smbspy_status.txt', 'w', encoding='utf=8') as f:
 | 
				
			||||||
 | 
					        f.write(final_result)
 | 
				
			||||||
 | 
					        telegram_send(final_result)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Copy results to SpitfireSeries
 | 
				
			||||||
 | 
					import shutil, stat
 | 
				
			||||||
 | 
					try:
 | 
				
			||||||
 | 
					  shutil.copyfile("/tmp/spitfire_smbspy_status.txt", "/var/www/spitfireseries/spitfire_smbspy_status.txt")
 | 
				
			||||||
 | 
					  os.chmod("/var/www/spitfireseries/spitfire_smbspy_status.txt", 0o666)
 | 
				
			||||||
 | 
					except Exception as e:
 | 
				
			||||||
 | 
					  import syslog
 | 
				
			||||||
 | 
					  syslog.syslog("Could not copy SMB status to SpitfireSeries: {}".format(e))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Report to Nagios
 | 
				
			||||||
 | 
					limits = {
 | 
				
			||||||
 | 
					    'users': [10, 20],
 | 
				
			||||||
 | 
					    'files': [20, 50]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if smb_active + http_users >= limits['users'][1] or smb_files + http_files >= limits['files'][1]:
 | 
				
			||||||
 | 
					    status = "CRITICAL"
 | 
				
			||||||
 | 
					    return_code = 2
 | 
				
			||||||
 | 
					elif smb_active + http_users >= limits['users'][0] or smb_files + http_files >= limits['files'][0]:
 | 
				
			||||||
 | 
					    status = "WARNING"
 | 
				
			||||||
 | 
					    return_code = 1
 | 
				
			||||||
 | 
					else:
 | 
				
			||||||
 | 
					    status = "OK"
 | 
				
			||||||
 | 
					    return_code = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Construct final monitoring string
 | 
				
			||||||
 | 
					monitoring_result = "{}: ".format(status)
 | 
				
			||||||
 | 
					if smb_result != "":
 | 
				
			||||||
 | 
					    monitoring_result += "{} files opened on SMB ({}/{} users), ".format(smb_files, smb_active, smb_users)
 | 
				
			||||||
 | 
					else:
 | 
				
			||||||
 | 
					    monitoring_result += "SMB no open files or failure, ".format(SERVER_NAME)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if http_result != "":
 | 
				
			||||||
 | 
					    monitoring_result += "{} active HTTP transfers ({} users).".format(http_files, http_users)
 | 
				
			||||||
 | 
					else:
 | 
				
			||||||
 | 
					    monitoring_result += "HTTP no open files or failure.".format(SERVER_NAME)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Print monitoring string and exit with proper exit code
 | 
				
			||||||
 | 
					print(monitoring_result)
 | 
				
			||||||
 | 
					sys.exit(return_code)
 | 
				
			||||||
							
								
								
									
										1
									
								
								requirements.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								requirements.txt
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					requests
 | 
				
			||||||
							
								
								
									
										8
									
								
								settings.py.default
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								settings.py.default
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,8 @@
 | 
				
			||||||
 | 
					SERVER_NAME = "SomeServer"
 | 
				
			||||||
 | 
					SMBD = "smbd"
 | 
				
			||||||
 | 
					APACHE = "apache2"
 | 
				
			||||||
 | 
					BASE_DIR = "/media/share_root"
 | 
				
			||||||
 | 
					HOSTNAME = "some.hostname.nl"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TELEGRAM_BOT_KEY = ""
 | 
				
			||||||
 | 
					TELEGRAM_CHAT_ID = ""
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue