mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2025-12-07 09:36:05 -08:00
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
# !/usr/bin/env python
|
||||
|
||||
import os
|
||||
import argparse
|
||||
@@ -9,13 +9,13 @@ import subprocess
|
||||
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
|
||||
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
|
||||
|
||||
from plugin_helper import Plugin_Objects
|
||||
from logger import mylog, Logger, append_line_to_file
|
||||
from utils.datetime_utils import timeNowDB
|
||||
from helper import get_setting_value
|
||||
from const import logPath
|
||||
import conf
|
||||
from pytz import timezone
|
||||
from plugin_helper import Plugin_Objects # noqa: E402 [flake8 lint suppression]
|
||||
from logger import mylog, Logger, append_line_to_file # noqa: E402 [flake8 lint suppression]
|
||||
from utils.datetime_utils import timeNowDB # noqa: E402 [flake8 lint suppression]
|
||||
from helper import get_setting_value # noqa: E402 [flake8 lint suppression]
|
||||
from const import logPath # noqa: E402 [flake8 lint suppression]
|
||||
import conf # noqa: E402 [flake8 lint suppression]
|
||||
from pytz import timezone # noqa: E402 [flake8 lint suppression]
|
||||
|
||||
# Make sure the TIMEZONE for logging is correct
|
||||
conf.tz = timezone(get_setting_value('TIMEZONE'))
|
||||
@@ -32,7 +32,8 @@ RESULT_FILE = os.path.join(LOG_PATH, f'last_result.{pluginName}.log')
|
||||
# Initialize the Plugin obj output file
|
||||
plugin_objects = Plugin_Objects(RESULT_FILE)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Scan ports of devices specified by IP addresses'
|
||||
@@ -85,7 +86,7 @@ def main():
|
||||
|
||||
mylog('verbose', [f'[{pluginName}] Total number of ports found by NMAP: ', len(entries)])
|
||||
|
||||
for entry in entries:
|
||||
for entry in entries:
|
||||
|
||||
plugin_objects.add_object(
|
||||
primaryId = entry.mac, # MAC (Device Name)
|
||||
@@ -94,14 +95,14 @@ def main():
|
||||
watched2 = entry.service,
|
||||
watched3 = entry.ip + ":" + entry.port,
|
||||
watched4 = "",
|
||||
extra = entry.extra,
|
||||
foreignKey = entry.mac
|
||||
extra = entry.extra,
|
||||
foreignKey = entry.mac
|
||||
)
|
||||
|
||||
plugin_objects.write_result_file()
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
class nmap_entry:
|
||||
def __init__(self, ip, mac, time, port, state, service, name = '', extra = '', index = 0):
|
||||
self.ip = ip
|
||||
@@ -109,13 +110,13 @@ class nmap_entry:
|
||||
self.time = time
|
||||
self.port = port
|
||||
self.state = state
|
||||
self.service = service
|
||||
self.service = service
|
||||
self.extra = extra
|
||||
self.index = index
|
||||
self.hash = str(mac) + str(port)+ str(state)+ str(service)
|
||||
self.hash = str(mac) + str(port) + str(state) + str(service)
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# -------------------------------------------------------------------------------
|
||||
def parse_kv_args(raw_args):
|
||||
"""
|
||||
Converts ['ips=a,b,c', 'macs=x,y,z', 'timeout=5'] to a dict.
|
||||
@@ -125,26 +126,28 @@ def parse_kv_args(raw_args):
|
||||
|
||||
for item in raw_args:
|
||||
if '=' not in item:
|
||||
mylog('none', [f"[{pluginName}] Scan: Invalid parameter (missing '='): {item}"])
|
||||
mylog('none', [f"[{pluginName}] Scan: Invalid parameter (missing '='): {item}"])
|
||||
|
||||
key, value = item.split('=', 1)
|
||||
|
||||
if key in parsed:
|
||||
mylog('none', [f"[{pluginName}] Scan: Duplicate parameter supplied: {key}"])
|
||||
mylog('none', [f"[{pluginName}] Scan: Duplicate parameter supplied: {key}"])
|
||||
|
||||
parsed[key] = value
|
||||
|
||||
return parsed
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
def safe_split_list(value, keyname):
|
||||
"""Split comma list safely and ensure no empty items."""
|
||||
items = [x.strip() for x in value.split(',') if x.strip()]
|
||||
if not items:
|
||||
mylog('none', [f"[{pluginName}] Scan: {keyname} list is empty or invalid"])
|
||||
mylog('none', [f"[{pluginName}] Scan: {keyname} list is empty or invalid"])
|
||||
return items
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
def performNmapScan(deviceIPs, deviceMACs, timeoutSec, args):
|
||||
"""
|
||||
run nmap scan on a list of devices
|
||||
@@ -154,15 +157,12 @@ def performNmapScan(deviceIPs, deviceMACs, timeoutSec, args):
|
||||
# collect ports / new Nmap Entries
|
||||
newEntriesTmp = []
|
||||
|
||||
|
||||
if len(deviceIPs) > 0:
|
||||
if len(deviceIPs) > 0:
|
||||
|
||||
devTotal = len(deviceIPs)
|
||||
|
||||
|
||||
mylog('verbose', [f'[{pluginName}] Scan: Nmap for max ', str(timeoutSec), 's ('+ str(round(int(timeoutSec) / 60, 1)) +'min) per device'])
|
||||
mylog('verbose', ["[NMAP Scan] Estimated max delay: ", (devTotal * int(timeoutSec)), 's ', '(', round((devTotal * int(timeoutSec))/60,1) , 'min)' ])
|
||||
|
||||
mylog('verbose', [f'[{pluginName}] Scan: Nmap for max ', str(timeoutSec), 's (' + str(round(int(timeoutSec) / 60, 1)) + 'min) per device'])
|
||||
mylog('verbose', ["[NMAP Scan] Estimated max delay: ", (devTotal * int(timeoutSec)), 's ', '(', round((devTotal * int(timeoutSec)) / 60, 1) , 'min)'])
|
||||
|
||||
devIndex = 0
|
||||
for ip in deviceIPs:
|
||||
@@ -171,67 +171,63 @@ def performNmapScan(deviceIPs, deviceMACs, timeoutSec, args):
|
||||
# prepare arguments from user supplied ones
|
||||
nmapArgs = ['nmap'] + args.split() + [ip]
|
||||
|
||||
progress = ' (' + str(devIndex+1) + '/' + str(devTotal) + ')'
|
||||
progress = ' (' + str(devIndex + 1) + '/' + str(devTotal) + ')'
|
||||
|
||||
try:
|
||||
# try runnning a subprocess with a forced (timeout) in case the subprocess hangs
|
||||
output = subprocess.check_output (nmapArgs, universal_newlines=True, stderr=subprocess.STDOUT, timeout=(float(timeoutSec)))
|
||||
output = subprocess.check_output(
|
||||
nmapArgs,
|
||||
universal_newlines=True,
|
||||
stderr=subprocess.STDOUT,
|
||||
timeout=(float(timeoutSec))
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
# An error occured, handle it
|
||||
mylog('none', ["[NMAP Scan] " ,e.output])
|
||||
mylog('none', ["[NMAP Scan] ⚠ ERROR - Nmap Scan - check logs", progress])
|
||||
mylog('none', ["[NMAP Scan] ", e.output])
|
||||
mylog('none', ["[NMAP Scan] ⚠ ERROR - Nmap Scan - check logs", progress])
|
||||
except subprocess.TimeoutExpired:
|
||||
mylog('verbose', [f'[{pluginName}] Nmap TIMEOUT - the process forcefully terminated as timeout reached for ', ip, progress])
|
||||
mylog('verbose', [f'[{pluginName}] Nmap TIMEOUT - the process forcefully terminated as timeout reached for ', ip, progress])
|
||||
|
||||
if output == "": # check if the subprocess failed
|
||||
mylog('minimal', [f'[{pluginName}] Nmap FAIL for ', ip, progress ,' check logs for details'])
|
||||
else:
|
||||
if output == "": # check if the subprocess failed
|
||||
mylog('minimal', [f'[{pluginName}] Nmap FAIL for ', ip, progress, ' check logs for details'])
|
||||
else:
|
||||
mylog('verbose', [f'[{pluginName}] Nmap SUCCESS for ', ip, progress])
|
||||
|
||||
|
||||
|
||||
# check the last run output
|
||||
# check the last run output
|
||||
newLines = output.split('\n')
|
||||
|
||||
# regular logging
|
||||
for line in newLines:
|
||||
append_line_to_file (logPath + '/app_nmap.log', line +'\n')
|
||||
|
||||
append_line_to_file(logPath + '/app_nmap.log', line + '\n')
|
||||
|
||||
index = 0
|
||||
startCollecting = False
|
||||
duration = ""
|
||||
duration = ""
|
||||
newPortsPerDevice = 0
|
||||
for line in newLines:
|
||||
for line in newLines:
|
||||
if 'Starting Nmap' in line:
|
||||
if len(newLines) > index+1 and 'Note: Host seems down' in newLines[index+1]:
|
||||
break # this entry is empty
|
||||
if len(newLines) > index + 1 and 'Note: Host seems down' in newLines[index + 1]:
|
||||
break # this entry is empty
|
||||
elif 'PORT' in line and 'STATE' in line and 'SERVICE' in line:
|
||||
startCollecting = True
|
||||
elif 'PORT' in line and 'STATE' in line and 'SERVICE' in line:
|
||||
startCollecting = False # end reached
|
||||
elif startCollecting and len(line.split()) == 3:
|
||||
elif 'PORT' in line and 'STATE' in line and 'SERVICE' in line:
|
||||
startCollecting = False # end reached
|
||||
elif startCollecting and len(line.split()) == 3:
|
||||
newEntriesTmp.append(nmap_entry(ip, deviceMACs[devIndex], timeNowDB(), line.split()[0], line.split()[1], line.split()[2]))
|
||||
newPortsPerDevice += 1
|
||||
elif 'Nmap done' in line:
|
||||
duration = line.split('scanned in ')[1]
|
||||
|
||||
mylog('verbose', [f'[{pluginName}] {newPortsPerDevice} ports found on {deviceMACs[devIndex]}'])
|
||||
duration = line.split('scanned in ')[1]
|
||||
|
||||
mylog('verbose', [f'[{pluginName}] {newPortsPerDevice} ports found on {deviceMACs[devIndex]} after {duration}'])
|
||||
|
||||
index += 1
|
||||
devIndex += 1
|
||||
|
||||
|
||||
|
||||
#end for loop
|
||||
|
||||
return newEntriesTmp
|
||||
|
||||
#===============================================================================
|
||||
|
||||
# ===============================================================================
|
||||
# BEGIN
|
||||
#===============================================================================
|
||||
# ===============================================================================
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user