Initial version
This commit is contained in:
commit
c894805619
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…
Reference in a new issue