ARPSCAN to plugin rewrite
This commit is contained in:
@@ -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",
|
||||
...
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -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"],
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user