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

@@ -97,7 +97,7 @@ More on specifics below.
Currently, only 3 data sources are supported (valid `data_source` value).
- Script (`python-script`)
- Script (`script`)
- SQL query on the PiAlert database (`pialert-db-query`)
- Template (`template`)
@@ -107,9 +107,9 @@ Currently, only 3 data sources are supported (valid `data_source` value).
>```
Any of the above data sources have to return a "table" of the exact structure as outlined above.
### "data_source": "python-script"
### "data_source": "script"
If the `data_source` is set to `python-script` the `CMD` setting (that you specify in the `settings` array section in the `config.json`) needs to contain an executable Linux command, that generates a `last_result.log` file. This file needs to be stored in the same folder as the plugin.
If the `data_source` is set to `script` the `CMD` setting (that you specify in the `settings` array section in the `config.json`) needs to contain an executable Linux command, that generates a `last_result.log` file. This file needs to be stored in the same folder as the plugin.
The content of the `last_result.log` file needs to contain the columns as defined in the "Column order and values" section above. The order of columns can't be changed. After every scan it should contain only the results from the latest scan/execution.
@@ -264,7 +264,7 @@ This approach is used to implement the `DHCPLSS` plugin. The script parses all s
"code_name": "dhcp_leases",
"unique_prefix": "DHCPLSS",
...
"data_source": "python-script",
"data_source": "script",
"localized": ["display_name", "description", "icon"],
"mapped_to_table": "DHCP_Leases",
...

View File

@@ -2,34 +2,34 @@
"code_name": "arpscan",
"unique_prefix": "ARPSCAN",
"enabled": true,
"data_source": "python-script",
"mapped_to_table": "DHCP_Leases",
"data_source": "script",
"mapped_to_table": "CurrentScan",
"localized": ["display_name", "description", "icon"],
"display_name": [
{
"language_code": "en_us",
"string": "Un-Discoverable Devices"
"string": "Network scan (Arp-Scan)"
}
],
"icon": [
{
"language_code": "en_us",
"string": "<i class=\"fa-solid fa-binoculars\"></i>"
"string": "<i class=\"fa-solid fa-search\"></i>"
}
],
"description": [
{
"language_code": "en_us",
"string": "This plugin is to import undiscoverable devices from a file."
"string": "This plugin is to execute an arp-scan on the local network"
}
],
"params" : [
{
"name" : "devices",
"name" : "subnets",
"type" : "setting",
"value" : "UNDIS_devices_to_import"
"value" : "SCAN_SUBNETS"
}],
"settings": [
@@ -37,7 +37,7 @@
"function": "RUN",
"type": "text.select",
"default_value":"disabled",
"options": ["disabled", "once", "always_after_scan"],
"options": ["disabled", "once", "schedule", "scan_cycle", "always_after_scan", "on_new_device"],
"localized": ["name", "description"],
"name" :[{
"language_code":"en_us",
@@ -50,8 +50,8 @@
},
{
"function": "CMD",
"type": "text",
"default_value": "python3 /home/pi/pialert/front/plugins/undiscoverables/script.py devices={devices}",
"type": "readonly",
"default_value": "python3 /home/pi/pialert/front/plugins/arp_scan/script.py userSubnets={subnets}",
"options": [],
"localized": ["name", "description"],
"name": [
@@ -63,7 +63,7 @@
"description": [
{
"language_code": "en_us",
"string": "Command to run. This can not be changed"
"string": "Command to run. This should not be changed"
}
]
},
@@ -71,7 +71,7 @@
{
"function": "RUN_TIMEOUT",
"type": "integer",
"default_value": 10,
"default_value": 300,
"options": [],
"localized": ["name", "description"],
"name": [
@@ -88,28 +88,39 @@
]
},
{
"function": "WATCH",
"type": "readonly",
"default_value": [],
"options": [],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "Watched"
}
],
"description": [
{
"language_code": "en_us",
"string": "Undiscoverable Devices can not change their status, no watch is enabled."
}
]
"function": "RUN_SCHD",
"type": "text",
"default_value":"*/3 * * * *",
"options": [],
"localized": ["name", "description"],
"name" : [{
"language_code":"en_us",
"string" : "Schedule"
}],
"description": [{
"language_code":"en_us",
"string" : "Only enabled if you select <code>schedule</code> in the <a href=\"#ARPSCAN_RUN\"><code>ARPSCAN_RUN</code> setting</a>. Make sure you enter the schedule in the correct cron-like format (e.g. validate at <a href=\"https://crontab.guru/\" target=\"_blank\">crontab.guru</a>). For example entering <code>*/3 * * * *</code> will run the scan every 3 minutes. Will be run NEXT time the time passes."
}]
},
{
"function": "WATCH",
"type": "text.multiselect",
"default_value":["Watched_Value1", "Watched_Value2"],
"options": ["Watched_Value1","Watched_Value2","Watched_Value3","Watched_Value4"],
"localized": ["name", "description"],
"name" :[{
"language_code":"en_us",
"string" : "Watched"
}] ,
"description":[{
"language_code":"en_us",
"string" : "Send a notification if selected values change. Use <code>CTRL + Click</code> to select/deselect. <ul> <li><code>Watched_Value1</code> is IP</li><li><code>Watched_Value2</code> is Vendor</li><li><code>Watched_Value3</code> is Interface </li><li><code>Watched_Value4</code> is N/A </li></ul>"
}]
},
{
"function": "REPORT_ON",
"type": "readonly",
"default_value": [],
"type": "text.multiselect",
"default_value": ["new", "watched-changed"],
"options": ["new", "watched-changed", "watched-not-changed"],
"localized": ["name", "description"],
"name": [
@@ -121,60 +132,31 @@
"description": [
{
"language_code": "en_us",
"string": "No notifications will be sent."
"string": "When should notification be sent out."
}
]
},
{
"function": "devices_to_import",
"type": "list",
"default_value":["dummy_router"],
"options": [],
"localized": ["name", "description"],
"name" : [{
"language_code":"en_us",
"string" : "UnDiscoverable Devices"
}],
"description": [{
"language_code":"en_us",
"string" : "Devices to be added to the devices list."
}]
}
}
],
"database_column_definitions":
[
{
"column": "Watched_Value1",
"mapped_to_column": "DHCP_Name",
"column": "Object_PrimaryID",
"mapped_to_column": "cur_MAC",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"type": "devicemac",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Device Name"
"string" : "MAC"
}]
},
{
"column": "Object_PrimaryID",
"mapped_to_column": "DHCP_MAC",
"css_classes": "col-sm-2",
"show": true,
"type": "devicemac",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "MAC address"
}]
},
{
"column": "Object_SecondaryID",
"mapped_to_column": "DHCP_IP",
"column": "Watched_Value1",
"mapped_to_column": "cur_IP",
"css_classes": "col-sm-2",
"show": true,
"type": "deviceip",
@@ -185,6 +167,34 @@
"language_code":"en_us",
"string" : "IP"
}]
},
{
"column": "Watched_Value2",
"mapped_to_column": "cur_Vendor",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Vendor"
}]
} ,
{
"column": "Extra",
"mapped_to_column": "cur_ScanMethod",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Scan method"
}]
} ,
{
"column": "DateTimeCreated",
@@ -200,8 +210,7 @@
}]
},
{
"column": "DateTimeChanged",
"mapped_to_column": "DHCP_DateTime",
"column": "DateTimeChanged",
"css_classes": "col-sm-2",
"show": true,
"type": "label",

View File

@@ -1,48 +1,121 @@
#!/usr/bin/env python
# test script by running python script.py devices=test,dummy
import os
import pathlib
import argparse
import sys
import re
import subprocess
from time import strftime
sys.path.append("/home/pi/pialert/front/plugins")
from plugin_helper import Plugin_Objects
from plugin_helper import Plugin_Object, Plugin_Objects
CUR_PATH = str(pathlib.Path(__file__).parent.resolve())
LOG_FILE = os.path.join(CUR_PATH , 'script.log')
RESULT_FILE = os.path.join(CUR_PATH , 'last_result.log')
LOG_FILE = os.path.join(CUR_PATH, 'script.log')
RESULT_FILE = os.path.join(CUR_PATH, 'last_result.log')
def main():
# the script expects a parameter in the format of devices=device1,device2,...
# the script expects a parameter in the format of userSubnets=subnet1,subnet2,...
parser = argparse.ArgumentParser(description='Import devices from settings')
parser.add_argument('devices', action="store", help="list of device names separated by ','")
parser.add_argument('userSubnets', nargs='+', help="list of subnets with options")
values = parser.parse_args()
UNDIS_devices = Plugin_Objects( RESULT_FILE )
devices = Plugin_Objects(RESULT_FILE)
if values.devices:
for fake_dev in values.devices.split('=')[1].split(','):
UNDIS_devices.add_object(
primaryId=fake_dev, # MAC (Device Name)
secondaryId="0.0.0.0", # IP Address (always 0.0.0.0)
watched1=fake_dev, # Device Name
watched2="",
watched3="",
watched4="",
extra="",
foreignKey="")
subnets_list = []
UNDIS_devices.write_result_file()
if isinstance(values.userSubnets, list):
subnets_list = values.userSubnets
else:
subnets_list = [values.userSubnets]
unique_devices = execute_arpscan(subnets_list)
for device in unique_devices:
devices.add_object(
primaryId=device['mac'], # MAC (Device Name)
secondaryId=device['ip'], # IP Address
watched1=device['ip'], # Device Name
watched2=device.get('hw', ''), # Vendor (assuming it's in the 'hw' field)
watched3=device.get('interface', ''), # Add the interface
watched4='',
extra='arp-scan',
foreignKey="")
devices.write_result_file()
return 0
def execute_arpscan(userSubnets):
# output of possible multiple interfaces
arpscan_output = ""
devices_list = []
# scan each interface
for interface in userSubnets :
arpscan_output = execute_arpscan_on_interface (interface)
print(arpscan_output)
# Search IP + MAC + Vendor as regular expresion
re_ip = r'(?P<ip>((2[0-5]|1[0-9]|[0-9])?[0-9]\.){3}((2[0-5]|1[0-9]|[0-9])?[0-9]))'
re_mac = r'(?P<mac>([0-9a-fA-F]{2}[:-]){5}([0-9a-fA-F]{2}))'
re_hw = r'(?P<hw>.*)'
re_pattern = re.compile (re_ip + '\s+' + re_mac + '\s' + re_hw)
devices_list_tmp = [
{**device.groupdict(), "interface": interface}
for device in re.finditer(re_pattern, arpscan_output)
]
devices_list += devices_list_tmp
# mylog('debug', ['[ARP Scan] Found: Devices including duplicates ', len(devices_list) ])
# Delete duplicate MAC
unique_mac = []
unique_devices = []
for device in devices_list :
if device['mac'] not in unique_mac:
unique_mac.append(device['mac'])
unique_devices.append(device)
# return list
# mylog('debug', ['[ARP Scan] Found: Devices without duplicates ', len(unique_devices) ])
print("Devices List len:", len(devices_list)) # Add this line to print devices_list
print("Devices List:", devices_list) # Add this line to print devices_list
return devices_list
def execute_arpscan_on_interface(interface):
# Prepare command arguments
arpscan_args = ['sudo', 'arp-scan', '--ignoredups', '--retry=6'] + interface.split()
# Execute command
try:
# try running a subprocess safely
result = subprocess.check_output(arpscan_args, universal_newlines=True)
except subprocess.CalledProcessError as e:
# An error occurred, handle it
error_type = type(e).__name__ # Capture the error type
result = ""
return result
#===============================================================================
# BEGIN
#===============================================================================
if __name__ == '__main__':
main()
main()

View File

@@ -2,7 +2,7 @@
"code_name": "dhcp_leases",
"unique_prefix": "DHCPLSS",
"enabled": true,
"data_source": "python-script",
"data_source": "script",
"data_filters": [
{
"compare_column" : "Object_PrimaryID",

View File

@@ -2,7 +2,7 @@
"code_name": "dhcp_servers",
"unique_prefix": "DHCPSRVS",
"enabled": true,
"data_source": "python-script",
"data_source": "script",
"localized": ["display_name", "description", "icon"],
"display_name" : [{
"language_code":"en_us",

View File

@@ -48,9 +48,8 @@
],
"settings":[
{
"function": "flows",
"type": "json",
"maxLength": 50,
"function": "FLOW",
"type": "json",
"default_value": [{
"name":"apply_template",
"trigger": [
@@ -121,13 +120,13 @@
"name": [
{
"language_code": "en_us",
"string": "Flows"
"string": "Plugin flow"
}
],
"description": [
{
"language_code": "en_us",
"string": "The flow."
"string": "This flow makes sure the template is applied to devices that are older than 3 days."
}
]
},

View File

@@ -2,7 +2,7 @@
"code_name": "undiscoverables",
"unique_prefix": "UNDIS",
"enabled": true,
"data_source": "python-script",
"data_source": "script",
"mapped_to_table": "DHCP_Leases",
"localized": ["display_name", "description", "icon"],

View File

@@ -2,7 +2,7 @@
"code_name": "unifi_import",
"unique_prefix": "UNFIMP",
"enabled": true,
"data_source": "python-script",
"data_source": "script",
"data_filters": [
{
"compare_column" : "Object_PrimaryID",

View File

@@ -2,7 +2,7 @@
"code_name": "website_monitor",
"unique_prefix": "WEBMON",
"enabled": true,
"data_source": "python-script",
"data_source": "script",
"localized": ["display_name", "description", "icon"],
"display_name" : [{
"language_code":"en_us",