diff --git a/back/pialert.py b/back/pialert.py index 2c2b0c94..e9e492b1 100755 --- a/back/pialert.py +++ b/back/pialert.py @@ -50,7 +50,6 @@ confPath = "/config/pialert.conf" dbPath = '/db/pialert.db' fullConfPath = pialertPath + confPath fullDbPath = pialertPath + dbPath -STOPARPSCAN = pialertPath + "/db/setting_stoparpscan" # Global variables @@ -205,6 +204,8 @@ piholeDhcpleases = '/etc/pihole/dhcp.leases' # GENERAL settings # ---------------------- +ENABLE_ARPSCAN = True +SCAN_SUBNETS = ['192.168.1.0/24 --interface=eth1', '192.168.1.0/24 --interface=eth0'] PRINT_LOG = False TIMEZONE = 'Europe/Berlin' PIALERT_WEB_PROTECTION = False @@ -212,7 +213,7 @@ PIALERT_WEB_PASSWORD = '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020 INCLUDED_SECTIONS = ['internet', 'new_devices', 'down_devices', 'events'] SCAN_CYCLE_MINUTES = 5 -SCAN_SUBNETS = ['192.168.1.0/24 --interface=eth1', '192.168.1.0/24 --interface=eth0'] + DAYS_TO_KEEP_EVENTS = 90 @@ -354,7 +355,7 @@ def importConfig (): # Specify globals so they can be overwritten with the new config global lastTimeImported # General - global SCAN_SUBNETS, PRINT_LOG, TIMEZONE, PIALERT_WEB_PROTECTION, PIALERT_WEB_PASSWORD, INCLUDED_SECTIONS, SCAN_CYCLE_MINUTES, DAYS_TO_KEEP_EVENTS, REPORT_DASHBOARD_URL + global ENABLE_ARPSCAN, SCAN_SUBNETS, PRINT_LOG, TIMEZONE, PIALERT_WEB_PROTECTION, PIALERT_WEB_PASSWORD, INCLUDED_SECTIONS, SCAN_CYCLE_MINUTES, DAYS_TO_KEEP_EVENTS, REPORT_DASHBOARD_URL # Email global REPORT_MAIL, SMTP_SERVER, SMTP_PORT, REPORT_TO, REPORT_FROM, SMTP_SKIP_LOGIN, SMTP_USER, SMTP_PASS, SMTP_SKIP_TLS # Webhooks @@ -391,6 +392,8 @@ def importConfig (): # Import setting if found in the dictionary # General + + ENABLE_ARPSCAN = check_config_dict('ENABLE_ARPSCAN', ENABLE_ARPSCAN , config_dict) SCAN_SUBNETS = check_config_dict('SCAN_SUBNETS', SCAN_SUBNETS , config_dict) PRINT_LOG = check_config_dict('PRINT_LOG', PRINT_LOG , config_dict) TIMEZONE = check_config_dict('TIMEZONE', TIMEZONE , config_dict) @@ -475,6 +478,7 @@ def importConfig (): settings = [ # General + ('ENABLE_ARPSCAN', 'Enable arpscan', '', 'boolean', '', '' , str(ENABLE_ARPSCAN) , 'General'), ('SCAN_SUBNETS', 'Subnets to scan', '', 'subnets', '', '' , str(SCAN_SUBNETS) , 'General'), ('PRINT_LOG', 'Print additional logging', '', 'boolean', '', '' , str(PRINT_LOG) , 'General'), ('TIMEZONE', 'Time zone', '', 'text', '', '' ,str(TIMEZONE) , 'General'), @@ -704,27 +708,27 @@ def main (): performNmapScan(get_all_devices()) # Perform an arp-scan if not disabled with a file - if last_network_scan + datetime.timedelta(minutes=SCAN_CYCLE_MINUTES) < time_started and os.path.exists(STOPARPSCAN) == False: + if last_network_scan + datetime.timedelta(minutes=SCAN_CYCLE_MINUTES) < time_started: last_network_scan = time_started cycle = 1 # network scan scan_network() - - # Check if new devices need to be scanned with Nmap - if NMAP_ACTIVE: - sql.execute ("""SELECT eve_IP as dev_LastIP, eve_MAC as dev_MAC FROM Events_Devices - WHERE eve_PendingAlertEmail = 1 - AND eve_EventType = 'New Device' - ORDER BY eve_DateTime""") - - rows = sql.fetchall() - commitDB() - - performNmapScan(rows) # Reporting if cycle in check_report: send_notifications() + # Check if new devices need to be scanned with Nmap + if NMAP_ACTIVE: + sql.execute ("""SELECT eve_IP as dev_LastIP, eve_MAC as dev_MAC FROM Events_Devices + WHERE eve_PendingAlertEmail = 1 + AND eve_EventType = 'New Device' + ORDER BY eve_DateTime""") + + rows = sql.fetchall() + commitDB() + + performNmapScan(rows) + # clean up the DB once a day if last_cleanup + datetime.timedelta(hours = 24) < time_started: last_cleanup = time_started @@ -1094,10 +1098,12 @@ def scan_network (): # ScanCycle data cycle_interval = scanCycle_data['cic_EveryXmin'] - # arp-scan command - file_print(' arp-scan start') - arpscan_devices = execute_arpscan () - print_log ('arp-scan ends') + # arp-scan command + arpscan_devices = [] + if ENABLE_ARPSCAN: + file_print(' arp-scan start') + arpscan_devices = execute_arpscan () + print_log ('arp-scan ends') # DEBUG - print number of rows updated # file_print('aspr-scan result:', len(arpscan_devices)) @@ -1292,11 +1298,12 @@ def save_scanned_devices (p_arpscan_devices, p_cycle_interval): sql.execute ("DELETE FROM CurrentScan WHERE cur_ScanCycle = ?", (cycle,)) - # 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 ("+ str(cycle) + ", :mac, :ip, :hw, 'arp-scan')", + p_arpscan_devices) # Insert Pi-hole devices sql.execute ("""INSERT INTO CurrentScan (cur_ScanCycle, cur_MAC, @@ -1706,8 +1713,6 @@ def update_devices_names (): foundDig = 0 foundPholus = 0 - # Devices without name - file_print(' Trying to resolve devices without name') # BUGFIX #97 - Updating name of Devices w/o IP sql.execute ("SELECT * FROM Devices WHERE dev_Name IN ('(unknown)','', '(name not found)') AND dev_LastIP <> '-'") unknownDevices = sql.fetchall() @@ -1717,6 +1722,13 @@ def update_devices_names (): if PHOLUS_ACTIVE and (len(unknownDevices) > 0 or PHOLUS_FORCE): performPholusScan(PHOLUS_TIMEOUT) + # skip checks if no unknown devices + if len(unknownDevices) == 0 and PHOLUS_FORCE == False: + return + + # Devices without name + file_print(' Trying to resolve devices without name') + # get names from Pholus scan sql.execute ('SELECT * FROM Pholus_Scan where "Record_Type"="Answer"') pholusResults = list(sql.fetchall()) @@ -1764,65 +1776,68 @@ def update_devices_names (): #------------------------------------------------------------------------------- def performNmapScan(devicesToScan): - timeoutSec = NMAP_TIMEOUT - updateState("Scan: Nmap") + if len(devicesToScan) > 0: - file_print('[', timeNow(), '] Scan: Nmap for max ', str(timeoutSec), 's ('+ str(round(int(timeoutSec) / 60, 1)) +'min) per device') + timeoutSec = NMAP_TIMEOUT - file_print(" Estimated max delay: ", (len(devicesToScan) * int(timeoutSec)), 's ', '(', round((len(devicesToScan) * int(timeoutSec))/60,1) , 'min)' ) + updateState("Scan: Nmap") - for device in devicesToScan: - # Execute command - output = "" - # prepare arguments from user supplied ones - nmapArgs = ['nmap'] + NMAP_ARGS.split() + [device["dev_LastIP"]] + file_print('[', timeNow(), '] Scan: Nmap for max ', str(timeoutSec), 's ('+ str(round(int(timeoutSec) / 60, 1)) +'min) per device') - try: - # try runnning a subprocess with a forced (timeout + 30 seconds) in case the subprocess hangs - output = subprocess.check_output (nmapArgs, universal_newlines=True, stderr=subprocess.STDOUT, timeout=(timeoutSec + 30)) - except subprocess.CalledProcessError as e: - # An error occured, handle it - file_print(e.output) - file_print(" Error - Nmap Scan - check logs") - except subprocess.TimeoutExpired as timeErr: - file_print(' Nmap TIMEOUT - the process forcefully terminated as timeout reached for ', device["dev_LastIP"]) + file_print(" Estimated max delay: ", (len(devicesToScan) * int(timeoutSec)), 's ', '(', round((len(devicesToScan) * int(timeoutSec))/60,1) , 'min)' ) - if output == "": # check if the subprocess failed - file_print('[', timeNow(), '] Scan: Nmap FAIL - check logs') - else: - file_print('[', timeNow(), '] Scan: Nmap SUCCESS for ', device["dev_LastIP"]) - - # check the last run output - newLines = output.split('\n') + for device in devicesToScan: + # Execute command + output = "" + # prepare arguments from user supplied ones + nmapArgs = ['nmap'] + NMAP_ARGS.split() + [device["dev_LastIP"]] - # regular logging - for line in newLines: - append_line_to_file (logPath + '/pialert_nmap.log', line +'\n') - - # collect ports - params = [] + try: + # try runnning a subprocess with a forced (timeout + 30 seconds) in case the subprocess hangs + output = subprocess.check_output (nmapArgs, universal_newlines=True, stderr=subprocess.STDOUT, timeout=(timeoutSec + 30)) + except subprocess.CalledProcessError as e: + # An error occured, handle it + file_print(e.output) + file_print(" Error - Nmap Scan - check logs") + except subprocess.TimeoutExpired as timeErr: + file_print(' Nmap TIMEOUT - the process forcefully terminated as timeout reached for ', device["dev_LastIP"]) - index = 0 - startCollecting = False - duration = "" - 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 - 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: - params.append((device["dev_MAC"], timeNow(), line.split()[0], line.split()[1], line.split()[2], '')) - elif 'Nmap done' in line: - duration = line.split('scanned in ')[1] - index += 1 + if output == "": # check if the subprocess failed + file_print('[', timeNow(), '] Scan: Nmap FAIL - check logs') + else: + file_print('[', timeNow(), '] Scan: Nmap SUCCESS for ', device["dev_LastIP"]) + + # check the last run output + newLines = output.split('\n') - if len(params) > 0: - sql.executemany ("""INSERT INTO Nmap_Scan ("MAC", "Time", "Port", "State", "Service", "Extra") VALUES (?, ?, ?, ?, ?, ?)""", params) - commitDB () + # regular logging + for line in newLines: + append_line_to_file (logPath + '/pialert_nmap.log', line +'\n') + + # collect ports + params = [] + + index = 0 + startCollecting = False + duration = "" + 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 + 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: + params.append((device["dev_MAC"], timeNow(), line.split()[0], line.split()[1], line.split()[2], '')) + elif 'Nmap done' in line: + duration = line.split('scanned in ')[1] + index += 1 + + if len(params) > 0: + sql.executemany ("""INSERT INTO Nmap_Scan ("MAC", "Time", "Port", "State", "Service", "Extra") VALUES (?, ?, ?, ?, ?, ?)""", params) + commitDB () #------------------------------------------------------------------------------- diff --git a/front/js/handle_version.js b/front/js/handle_version.js index 5cf44897..cf072961 100644 --- a/front/js/handle_version.js +++ b/front/js/handle_version.js @@ -11,13 +11,21 @@ function handleVersion(){ if(release_timestamp > build_timestamp + 600 ) { console.log("New release!") + // handling the navigation menu icon $('#version').attr("class", $('#version').attr("class").replace("myhidden", "")) - $('#new-version-text').attr("class", $('#new-version-text').attr("class").replace("myhidden", "")) - + + maintenanceDiv = $('#new-version-text') } else{ - console.log("All up-to-date!") - $('#current-version-text').attr("class", $('#current-version-text').attr("class").replace("myhidden", "")) + console.log("All up-to-date!") + + maintenanceDiv = $('#current-version-text') + } + + // handling the maintenance section message + if(emptyArr.includes(maintenanceDiv) == false && $(maintenanceDiv).length != 0) + { + $(maintenanceDiv).attr("class", $(maintenanceDiv).attr("class").replace("myhidden", "")) } } diff --git a/front/js/pialert_common.js b/front/js/pialert_common.js index c2d6a807..644cdde1 100755 --- a/front/js/pialert_common.js +++ b/front/js/pialert_common.js @@ -10,6 +10,7 @@ // ----------------------------------------------------------------------------- var timerRefreshData = '' var modalCallbackFunction = ''; +var emptyArr = ['undefined', "", undefined, null]; // urlParams = new Proxy(new URLSearchParams(window.location.search), { // get: (searchParams, prop) => searchParams.get(prop.toString()), @@ -33,65 +34,6 @@ function setCache(key, data) } -// ----------------------------------------------------------------------------- - -function handleVersion(){ - - release_timestamp = getCookie("release_timestamp") - - if(release_timestamp != "") - { - - build_timestamp = parseInt($('#version').attr("data-build-time").match( /\d+/g ).join('')) - - // if the release_timestamp is older by 10 min or more as the build timestamp then there is a new release available - if(release_timestamp > build_timestamp + 600 ) - { - console.log("New release!") - $('#version').attr("class", $('#version').attr("class").replace("myhidden", "")) - - } - else{ - console.log("All up-to-date!") - $('#current-version-text').attr("class", $('#current-version-text').attr("class").replace("myhidden", "")) - // new-version-text current-version-text - } - } - -} - -//-------------------------------------------------------------- - -function getVersion() -{ - release_timestamp = getCookie("release_timestamp") - - // no cached value available - if(release_timestamp == "") - { - // get parameter value - $.get('https://api.github.com/repos/jokob-sk/Pi.Alert/releases', function(data) { - - var releases = data; - - if(releases.length > 0) - { - release_datetime = releases[0].published_at; - release_timestamp = new Date(release_datetime).getTime() / 1000; - - // cache value - setCookie("release_timestamp", release_timestamp ,5); - - handleVersion(); - } - }); - } else - { - // cache is available, just call the handler - handleVersion() - } -} - // ----------------------------------------------------------------------------- function setCookie (cookie, value, expirationMinutes='') { // Calc expiration date diff --git a/front/maintenance.php b/front/maintenance.php index dc3faaa4..a45546a6 100755 --- a/front/maintenance.php +++ b/front/maintenance.php @@ -73,16 +73,6 @@ $pia_db = str_replace('front', 'db', getcwd()).'/pialert.db'; $pia_db_size = number_format((filesize($pia_db) / 1000000),2,",",".") . ' MB'; $pia_db_mod = date ("F d Y H:i:s", filemtime($pia_db)); -// Pause Arp Scan --------------------------------------------------------------- - -if (!file_exists('../db/setting_stoparpscan')) { - $execstring = 'ps -f -u root | grep "sudo arp-scan" 2>&1'; - $pia_arpscans = ""; - exec($execstring, $pia_arpscans); - $pia_arpscans_result = sizeof($pia_arpscans).' '.lang('Maintenance_arp_status_on'); -} else { - $pia_arpscans_result = 'arp-Scan '.lang('Maintenance_arp_status_off') .''; -} // Count and Calc Backups ------------------------------------------------------- @@ -183,12 +173,7 @@ if (isset($_POST['submit']) && submit && isset($_POST['skinselector_set'])) {
- -
-
-
-
-
+ @@ -264,13 +249,7 @@ if (isset($_POST['submit']) && submit && isset($_POST['skinselector_set'])) {
- -
-
- -
-
-
+
diff --git a/front/php/server/devices.php b/front/php/server/devices.php index dc4f8998..7dae1119 100755 --- a/front/php/server/devices.php +++ b/front/php/server/devices.php @@ -559,25 +559,6 @@ function PiaEnableDarkmode() { } } - -//------------------------------------------------------------------------------ -// Toggle on/off Arp-Scans -//------------------------------------------------------------------------------ -function PiaToggleArpScan() { - $file = '../../../db/setting_stoparpscan'; - global $pia_lang; - - if (file_exists($file)) { - echo lang('BackDevices_Arpscan_enabled'); - unlink($file); - echo(""); - } else { - echo lang('BackDevices_Arpscan_disabled'); - $startarpscan = fopen($file, 'w'); - echo(""); - } - } - //------------------------------------------------------------------------------ // Query total numbers of Devices by status //------------------------------------------------------------------------------ diff --git a/front/php/templates/header.php b/front/php/templates/header.php index c54fc57e..a14d7e7b 100755 --- a/front/php/templates/header.php +++ b/front/php/templates/header.php @@ -276,4 +276,3 @@ if ($ENABLED_DARKMODE === True) { - diff --git a/front/php/templates/language/en_us.php b/front/php/templates/language/en_us.php index fc71b90b..ea466abf 100755 --- a/front/php/templates/language/en_us.php +++ b/front/php/templates/language/en_us.php @@ -438,6 +438,8 @@ $lang['en_us'] = array( 'settings_imported' => 'Last time settings were imported from the pialert.conf file:', //General +'ENABLE_ARPSCAN_name' => 'Enable ARP scan', +'ENABLE_ARPSCAN_description' => 'Arp-scan is a command-line tool that uses the ARP protocol to discover and fingerprint IP hosts on the local network. An alternative to ARP scan is to enable the PIHOLE_ACTIVEPiHole integration settings.', 'SCAN_SUBNETS_name' => 'Subnets to scan', 'SCAN_SUBNETS_description' => ' diff --git a/front/settings.php b/front/settings.php index 197e9789..2c1585ef 100644 --- a/front/settings.php +++ b/front/settings.php @@ -247,7 +247,7 @@ CommitDB();