ARPSCAN to plugin rewrite

This commit is contained in:
Jokob-sk
2023-08-06 10:50:03 +10:00
parent ef64014100
commit c2da5c56b8
15 changed files with 303 additions and 238 deletions

View File

@@ -213,6 +213,7 @@ def main ():
nmapSchedule.last_run = timeNow()
performNmapScan(db, get_all_devices(db))
# todo replace the scans with plugins
# Perform a network scan via arp-scan or pihole
if last_network_scan + datetime.timedelta(minutes=conf.SCAN_CYCLE_MINUTES) < loop_start_time:
last_network_scan = loop_start_time
@@ -250,7 +251,7 @@ def main ():
# --------------------------------------------------
# process all the scanned data into new devices
mylog('debug', "[MAIN] start processig scan results")
process_scan (db, conf.arpscan_devices )
process_scan (db)
# Reporting
if conf.cycle in conf.check_report:

View File

@@ -14,44 +14,48 @@ from scanners.pholusscan import performPholusScan, resolve_device_name_dig, reso
#-------------------------------------------------------------------------------
def save_scanned_devices (db, p_arpscan_devices, p_cycle_interval):
def save_scanned_devices (db):
sql = db.sql #TO-DO
cycle = 1 # always 1, only one cycle supported
mylog('debug', ['[ARP Scan] Detected devices:', len(p_arpscan_devices)])
# mylog('debug', ['[ARP Scan] Detected devices:', len(p_arpscan_devices)])
# Delete previous scan data
sql.execute ("DELETE FROM CurrentScan WHERE cur_ScanCycle = ?",
(cycle,))
# handled by the ARPSCAN plugin
# # Delete previous scan data
# sql.execute ("DELETE FROM CurrentScan")
if len(p_arpscan_devices) > 0:
# Insert new arp-scan devices
sql.executemany ("INSERT INTO CurrentScan (cur_ScanCycle, cur_MAC, "+
" cur_IP, cur_Vendor, cur_ScanMethod) "+
"VALUES ("+ str(cycle) + ", :mac, :ip, :hw, 'arp-scan')",
p_arpscan_devices)
# if len(p_arpscan_devices) > 0:
# # Insert new arp-scan devices
# sql.executemany ("INSERT INTO CurrentScan (cur_ScanCycle, cur_MAC, "+
# " cur_IP, cur_Vendor, cur_ScanMethod) "+
# "VALUES (1, :mac, :ip, :hw, 'arp-scan')",
# p_arpscan_devices)
# ------------------------ TO CONVERT INTO PLUGIN
# # Insert Pi-hole devices
# startTime = timeNow()
# sql.execute ("""INSERT INTO CurrentScan (cur_ScanCycle, cur_MAC,
# cur_IP, cur_Vendor, cur_ScanMethod)
# SELECT ?, PH_MAC, PH_IP, PH_Vendor, 'Pi-hole'
# FROM PiHole_Network
# WHERE PH_LastQuery >= ?
# AND NOT EXISTS (SELECT 'X' FROM CurrentScan
# WHERE cur_MAC = PH_MAC
# AND cur_ScanCycle = ? )""",
# (cycle,
# (int(startTime.strftime('%s')) - 60 * p_cycle_interval),
# cycle) )
# ------------------------ TO CONVERT INTO PLUGIN
# Insert Pi-hole devices
startTime = timeNow()
sql.execute ("""INSERT INTO CurrentScan (cur_ScanCycle, cur_MAC,
cur_IP, cur_Vendor, cur_ScanMethod)
SELECT ?, PH_MAC, PH_IP, PH_Vendor, 'Pi-hole'
FROM PiHole_Network
WHERE PH_LastQuery >= ?
AND NOT EXISTS (SELECT 'X' FROM CurrentScan
WHERE cur_MAC = PH_MAC
AND cur_ScanCycle = ? )""",
(cycle,
(int(startTime.strftime('%s')) - 60 * p_cycle_interval),
cycle) )
# Check Internet connectivity
internet_IP = get_internet_IP( conf.DIG_GET_IP_ARG )
# TESTING - Force IP
# internet_IP = ""
if internet_IP != "" :
sql.execute ("""INSERT INTO CurrentScan (cur_ScanCycle, cur_MAC, cur_IP, cur_Vendor, cur_ScanMethod)
VALUES (?, 'Internet', ?, Null, 'queryDNS') """, (cycle, internet_IP) )
sql.execute (f"""INSERT INTO CurrentScan (cur_ScanCycle, cur_MAC, cur_IP, cur_Vendor, cur_ScanMethod)
VALUES ( 1, 'Internet', '{internet_IP}', Null, 'queryDNS') """)
# #76 Add Local MAC of default local interface
# BUGFIX #106 - Device that pialert is running
@@ -73,93 +77,67 @@ def save_scanned_devices (db, p_arpscan_devices, p_cycle_interval):
local_ip = '0.0.0.0'
# Check if local mac has been detected with other methods
sql.execute ("SELECT COUNT(*) FROM CurrentScan WHERE cur_ScanCycle = ? AND cur_MAC = ? ", (cycle, local_mac) )
sql.execute ("SELECT COUNT(*) FROM CurrentScan WHERE cur_MAC = ? ", (local_mac) )
if sql.fetchone()[0] == 0 :
sql.execute ("INSERT INTO CurrentScan (cur_ScanCycle, cur_MAC, cur_IP, cur_Vendor, cur_ScanMethod) "+
"VALUES ( ?, ?, ?, Null, 'local_MAC') ", (cycle, local_mac, local_ip) )
"VALUES ( 1, ?, ?, Null, 'local_MAC') ", (local_mac, local_ip) )
#-------------------------------------------------------------------------------
def print_scan_stats (db):
sql = db.sql #TO-DO
# Devices Detected
sql.execute ("""SELECT COUNT(*) FROM CurrentScan
WHERE cur_ScanCycle = ? """,
(conf.cycle,))
sql.execute ("""SELECT COUNT(*) FROM CurrentScan""")
mylog('verbose', ['[Scan Stats] Devices Detected.......: ', str (sql.fetchone()[0]) ])
# Devices arp-scan
sql.execute ("""SELECT COUNT(*) FROM CurrentScan
WHERE cur_ScanMethod='arp-scan' AND cur_ScanCycle = ? """,
(conf.cycle,))
sql.execute ("""SELECT COUNT(*) FROM CurrentScan WHERE cur_ScanMethod='arp-scan' """)
mylog('verbose', ['[Scan Stats] arp-scan detected..: ', str (sql.fetchone()[0]) ])
# Devices Pi-hole
sql.execute ("""SELECT COUNT(*) FROM CurrentScan
WHERE cur_ScanMethod='PiHole' AND cur_ScanCycle = ? """,
(conf.cycle,))
sql.execute ("""SELECT COUNT(*) FROM CurrentScan WHERE cur_ScanMethod='PiHole'""")
mylog('verbose', ['[Scan Stats] Pi-hole detected...: +' + str (sql.fetchone()[0]) ])
# New Devices
sql.execute ("""SELECT COUNT(*) FROM CurrentScan
WHERE cur_ScanCycle = ?
AND NOT EXISTS (SELECT 1 FROM Devices
WHERE dev_MAC = cur_MAC) """,
(conf.cycle,))
WHERE NOT EXISTS (SELECT 1 FROM Devices
WHERE dev_MAC = cur_MAC) """)
mylog('verbose', ['[Scan Stats] New Devices........: ' + str (sql.fetchone()[0]) ])
# Devices in this ScanCycle
sql.execute ("""SELECT COUNT(*) FROM Devices, CurrentScan
WHERE dev_MAC = cur_MAC AND dev_ScanCycle = cur_ScanCycle
AND dev_ScanCycle = ? """,
(conf.cycle,))
mylog('verbose', ['[Scan Stats] Devices in this cycle..: ' + str (sql.fetchone()[0]) ])
# Down Alerts
sql.execute ("""SELECT COUNT(*) FROM Devices
WHERE dev_AlertDeviceDown = 1
AND dev_ScanCycle = ?
WHERE dev_AlertDeviceDown = 1
AND NOT EXISTS (SELECT 1 FROM CurrentScan
WHERE dev_MAC = cur_MAC
AND dev_ScanCycle = cur_ScanCycle) """,
(conf.cycle,))
AND dev_ScanCycle = cur_ScanCycle) """)
mylog('verbose', ['[Scan Stats] Down Alerts........: ' + str (sql.fetchone()[0]) ])
# New Down Alerts
sql.execute ("""SELECT COUNT(*) FROM Devices
WHERE dev_AlertDeviceDown = 1
AND dev_PresentLastScan = 1
AND dev_ScanCycle = ?
AND dev_PresentLastScan = 1
AND NOT EXISTS (SELECT 1 FROM CurrentScan
WHERE dev_MAC = cur_MAC
AND dev_ScanCycle = cur_ScanCycle) """,
(conf.cycle,))
AND dev_ScanCycle = cur_ScanCycle) """)
mylog('verbose', ['[Scan Stats] New Down Alerts....: ' + str (sql.fetchone()[0]) ])
# New Connections
sql.execute ("""SELECT COUNT(*) FROM Devices, CurrentScan
WHERE dev_MAC = cur_MAC AND dev_ScanCycle = cur_ScanCycle
AND dev_PresentLastScan = 0
AND dev_ScanCycle = ? """,
(conf.cycle,))
AND dev_PresentLastScan = 0""")
mylog('verbose', ['[Scan Stats] New Connections....: ' + str ( sql.fetchone()[0]) ])
# Disconnections
sql.execute ("""SELECT COUNT(*) FROM Devices
WHERE dev_PresentLastScan = 1
AND dev_ScanCycle = ?
WHERE dev_PresentLastScan = 1
AND NOT EXISTS (SELECT 1 FROM CurrentScan
WHERE dev_MAC = cur_MAC
AND dev_ScanCycle = cur_ScanCycle) """,
(conf.cycle,))
AND dev_ScanCycle = cur_ScanCycle) """)
mylog('verbose', ['[Scan Stats] Disconnections.....: ' + str ( sql.fetchone()[0]) ])
# IP Changes
sql.execute ("""SELECT COUNT(*) FROM Devices, CurrentScan
WHERE dev_MAC = cur_MAC AND dev_ScanCycle = cur_ScanCycle
AND dev_ScanCycle = ?
AND dev_LastIP <> cur_IP """,
(conf.cycle,))
WHERE dev_MAC = cur_MAC AND dev_ScanCycle = cur_ScanCycle
AND dev_LastIP <> cur_IP """)
mylog('verbose', ['[Scan Stats] IP Changes.........: ' + str ( sql.fetchone()[0]) ])
@@ -176,20 +154,18 @@ def create_new_devices (db):
eve_PendingAlertEmail)
SELECT cur_MAC, cur_IP, ?, 'New Device', cur_Vendor, 1
FROM CurrentScan
WHERE cur_ScanCycle = ?
AND NOT EXISTS (SELECT 1 FROM Devices
WHERE NOT EXISTS (SELECT 1 FROM Devices
WHERE dev_MAC = cur_MAC) """,
(startTime, conf.cycle) )
(startTime) )
mylog('debug','[New Devices] Insert Connection into session table')
sql.execute ("""INSERT INTO Sessions (ses_MAC, ses_IP, ses_EventTypeConnection, ses_DateTimeConnection,
ses_EventTypeDisconnection, ses_DateTimeDisconnection, ses_StillConnected, ses_AdditionalInfo)
SELECT cur_MAC, cur_IP,'Connected',?, NULL , NULL ,1, cur_Vendor
FROM CurrentScan
WHERE cur_ScanCycle = ?
AND NOT EXISTS (SELECT 1 FROM Sessions
WHERE NOT EXISTS (SELECT 1 FROM Sessions
WHERE ses_MAC = cur_MAC) """,
(startTime, conf.cycle) )
(startTime) )
# arpscan - Create new devices
mylog('debug','[New Devices] 2 Create devices')
@@ -236,13 +212,12 @@ def create_new_devices (db):
SELECT cur_MAC, '(unknown)', cur_Vendor, cur_IP, ?, ?,
{newDevDefaults}
FROM CurrentScan
WHERE cur_ScanCycle = ?
AND NOT EXISTS (SELECT 1 FROM Devices
WHERE NOT EXISTS (SELECT 1 FROM Devices
WHERE dev_MAC = cur_MAC) """
mylog('debug',f'[New Devices] 2 Create devices SQL: {sqlQuery}')
sql.execute (sqlQuery, (startTime, startTime, conf.cycle) )
sql.execute (sqlQuery, (startTime, startTime) )
# Pi-hole - Insert events for new devices
# NOT STRICYLY NECESARY (Devices can be created through Current_Scan)
@@ -326,18 +301,15 @@ def update_devices_data_from_scan (db):
WHERE dev_ScanCycle = ?
AND dev_PresentLastScan = 0
AND EXISTS (SELECT 1 FROM CurrentScan
WHERE dev_MAC = cur_MAC
AND dev_ScanCycle = cur_ScanCycle) """,
(startTime, conf.cycle))
WHERE dev_MAC = cur_MAC) """,
(startTime))
# Clean no active devices
mylog('debug','[Update Devices] 2 Clean no active devices')
sql.execute ("""UPDATE Devices SET dev_PresentLastScan = 0
WHERE dev_ScanCycle = ?
AND NOT EXISTS (SELECT 1 FROM CurrentScan
WHERE NOT EXISTS (SELECT 1 FROM CurrentScan
WHERE dev_MAC = cur_MAC
AND dev_ScanCycle = cur_ScanCycle) """,
(conf.cycle,))
AND dev_ScanCycle = cur_ScanCycle) """)
# Update IP & Vendor
mylog('debug','[Update Devices] - 3 LastIP & Vendor')
@@ -348,11 +320,9 @@ def update_devices_data_from_scan (db):
dev_Vendor = (SELECT cur_Vendor FROM CurrentScan
WHERE dev_MAC = cur_MAC
AND dev_ScanCycle = cur_ScanCycle)
WHERE dev_ScanCycle = ?
AND EXISTS (SELECT 1 FROM CurrentScan
WHERE EXISTS (SELECT 1 FROM CurrentScan
WHERE dev_MAC = cur_MAC
AND dev_ScanCycle = cur_ScanCycle) """,
(conf.cycle,))
AND dev_ScanCycle = cur_ScanCycle) """)
# Pi-hole Network - Update (unknown) Name
mylog('debug','[Update Devices] - 4 Unknown Name')

View File

@@ -59,16 +59,19 @@ def importConfigs (db):
# Only import file if the file was modifed since last import.
# this avoids time zone issues as we just compare the previous timestamp to the current time stamp
fileModifiedTime = os.path.getmtime(config_file)
mylog('debug', ['[Import Config] checking config file '])
mylog('debug', ['[Import Config] lastImportedConfFile :', conf.lastImportedConfFile])
mylog('debug', ['[Import Config] file modified time :', os.path.getmtime(config_file)])
mylog('debug', ['[Import Config] file modified time :', fileModifiedTime])
if (os.path.getmtime(config_file) == conf.lastImportedConfFile) :
if (fileModifiedTime == conf.lastImportedConfFile) :
mylog('debug', ['[Import Config] skipping config file import'])
return
conf.lastImportedConfFile = os.path.getmtime(config_file)
conf.lastImportedConfFile = fileModifiedTime
mylog('debug', ['[Import Config] importing config file'])
conf.mySettings = [] # reset settings

View File

@@ -36,12 +36,13 @@ def scan_network (db):
db.commitDB()
# Moved to the ARPSCAN Plugin
# arp-scan command
conf.arpscan_devices = []
if conf.ENABLE_ARPSCAN:
mylog('verbose','[Network Scan] arp-scan start')
conf.arpscan_devices = execute_arpscan (conf.userSubnets)
mylog('verbose','[Network Scan] arp-scan ends')
# conf.arpscan_devices = []
# if conf.ENABLE_ARPSCAN:
# mylog('verbose','[Network Scan] arp-scan start')
# conf.arpscan_devices = execute_arpscan (conf.userSubnets)
# mylog('verbose','[Network Scan] arp-scan ends')
# Pi-hole method
if conf.PIHOLE_ACTIVE :
@@ -57,8 +58,7 @@ def scan_network (db):
def process_scan (db, arpscan_devices):
def process_scan (db):
# Query ScanCycle properties
scanCycle_data = query_ScanCycle_Data (db, True)
@@ -76,7 +76,7 @@ def process_scan (db, arpscan_devices):
# Load current scan data
mylog('verbose','[Process Scan] Processing scan results')
save_scanned_devices (db, arpscan_devices, cycle_interval)
save_scanned_devices (db)
db.commitDB()
@@ -85,8 +85,7 @@ def process_scan (db, arpscan_devices):
print_scan_stats(db)
mylog('none','[Process Scan] Stats end')
# Create Events
mylog('verbose','[Process Scan] Updating DB Info')
# Create Events
mylog('verbose','[Process Scan] Sessions Events (connect / discconnect)')
insert_events(db)
@@ -122,6 +121,9 @@ def process_scan (db, arpscan_devices):
# Skip repeated notifications
mylog('verbose','[Process Scan] Skipping repeated notifications')
skip_repeated_notifications (db)
# Clear current scan as processed
db.sql.execute ("DELETE FROM CurrentScan")
# Commit changes
db.commitDB()
@@ -279,12 +281,11 @@ def insert_events (db):
SELECT dev_MAC, dev_LastIP, ?, 'Device Down', '', 1
FROM Devices
WHERE dev_AlertDeviceDown = 1
AND dev_PresentLastScan = 1
AND dev_ScanCycle = ?
AND dev_PresentLastScan = 1
AND NOT EXISTS (SELECT 1 FROM CurrentScan
WHERE dev_MAC = cur_MAC
AND dev_ScanCycle = cur_ScanCycle) """,
(startTime, conf.cycle) )
(startTime) )
# Check new connections
mylog('debug','[Events] - 2 - New Connections')
@@ -294,9 +295,8 @@ def insert_events (db):
SELECT cur_MAC, cur_IP, ?, 'Connected', '', dev_AlertEvents
FROM Devices, CurrentScan
WHERE dev_MAC = cur_MAC AND dev_ScanCycle = cur_ScanCycle
AND dev_PresentLastScan = 0
AND dev_ScanCycle = ? """,
(startTime, conf.cycle) )
AND dev_PresentLastScan = 0 """,
(startTime) )
# Check disconnections
mylog('debug','[Events] - 3 - Disconnections')
@@ -308,11 +308,10 @@ def insert_events (db):
FROM Devices
WHERE dev_AlertDeviceDown = 0
AND dev_PresentLastScan = 1
AND dev_ScanCycle = ?
AND NOT EXISTS (SELECT 1 FROM CurrentScan
WHERE dev_MAC = cur_MAC
AND dev_ScanCycle = cur_ScanCycle) """,
(startTime, conf.cycle) )
(startTime) )
# Check IP Changed
mylog('debug','[Events] - 4 - IP Changes')
@@ -322,8 +321,7 @@ def insert_events (db):
SELECT cur_MAC, cur_IP, ?, 'IP Changed',
'Previous IP: '|| dev_LastIP, dev_AlertEvents
FROM Devices, CurrentScan
WHERE dev_MAC = cur_MAC AND dev_ScanCycle = cur_ScanCycle
AND dev_ScanCycle = ?
WHERE dev_MAC = cur_MAC AND dev_ScanCycle = cur_ScanCycle
AND dev_LastIP <> cur_IP """,
(startTime, conf.cycle) )
(startTime) )
mylog('debug','[Events] - Events end')

View File

@@ -181,8 +181,8 @@ def execute_plugin(db, plugin):
# build SQL query parameters to insert into the DB
sqlParams = []
# python-script
if plugin['data_source'] == 'python-script':
# script
if plugin['data_source'] == 'script':
# ------- prepare params --------
# prepare command from plugin settings, custom parameters
command = resolve_wildcards_arr(set_CMD.split(), params)
@@ -203,24 +203,35 @@ def execute_plugin(db, plugin):
# check the last run output
f = open(pluginsPath + '/' + plugin["code_name"] + '/last_result.log', 'r+')
newLines = f.read().split('\n')
f.close()
# Initialize newLines
newLines = []
# cleanup - select only lines containing a separator to filter out unnecessary data
newLines = list(filter(lambda x: '|' in x, newLines))
# Create the file path
file_path = os.path.join(pluginsPath, plugin["code_name"], 'last_result.log')
# # regular logging
# for line in newLines:
# append_line_to_file (pluginsPath + '/plugin.log', line +'\n')
for line in newLines:
columns = line.split("|")
# There has to be always 9 columns
if len(columns) == 9:
sqlParams.append((plugin["unique_prefix"], columns[0], columns[1], 'null', columns[2], columns[3], columns[4], columns[5], columns[6], 0, columns[7], 'null', columns[8]))
else:
mylog('none', ['[Plugins]: Skipped invalid line in the output: ', line])
# Check if the file exists
if os.path.exists(file_path):
# File exists, open it and read its contents
with open(file_path, 'r+') as f:
newLines = f.read().split('\n')
# if the script produced some outpout, clean it up to ensure it's the correct format
# cleanup - select only lines containing a separator to filter out unnecessary data
newLines = list(filter(lambda x: '|' in x, newLines))
# # regular logging
# for line in newLines:
# append_line_to_file (pluginsPath + '/plugin.log', line +'\n')
for line in newLines:
columns = line.split("|")
# There has to be always 9 columns
if len(columns) == 9:
sqlParams.append((plugin["unique_prefix"], columns[0], columns[1], 'null', columns[2], columns[3], columns[4], columns[5], columns[6], 0, columns[7], 'null', columns[8]))
else:
mylog('none', ['[Plugins]: Skipped invalid line in the output: ', line])
else:
mylog('debug', [f'[Plugins] The file {file_path} does not exist'])
# pialert-db-query
if plugin['data_source'] == 'pialert-db-query':