diff --git a/.gitignore b/.gitignore index 8535c235..b2804d3f 100755 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ __pycache__/ **/script.log **/pialert.conf_bak **/pialert.db_bak +.*.swp front/img/account/* **/account.php diff --git a/Dockerfile b/Dockerfile index 9ce5e9e2..48eb5d6f 100755 --- a/Dockerfile +++ b/Dockerfile @@ -15,7 +15,7 @@ ENV PATH="/opt/venv/bin:$PATH" COPY . ${INSTALL_DIR}/ -RUN pip install netifaces tplink-omada-client pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython \ +RUN pip install netifaces tplink-omada-client pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython librouteros \ && bash -c "find ${INSTALL_DIR} -type d -exec chmod 750 {} \;" \ && bash -c "find ${INSTALL_DIR} -type f -exec chmod 640 {} \;" \ && bash -c "find ${INSTALL_DIR} -type f \( -name '*.sh' -o -name '*.py' -o -name 'speedtest-cli' \) -exec chmod 750 {} \;" diff --git a/front/log/.gitignore b/front/log/.gitignore old mode 100755 new mode 100644 diff --git a/front/plugins/mikrotik_scan/README.md b/front/plugins/mikrotik_scan/README.md new file mode 100755 index 00000000..3d743790 --- /dev/null +++ b/front/plugins/mikrotik_scan/README.md @@ -0,0 +1,7 @@ +## Overview + +Plugin for device name discovery via the Mikrotik dhcp-server leases + +### Usage + +- Check the Settings page for details. diff --git a/front/plugins/mikrotik_scan/config.json b/front/plugins/mikrotik_scan/config.json new file mode 100755 index 00000000..baca3a15 --- /dev/null +++ b/front/plugins/mikrotik_scan/config.json @@ -0,0 +1,437 @@ +{ + "code_name": "mikrotik_scan", + "unique_prefix": "MTSCAN", + "plugin_type": "other", + "execution_order" : "Layer_4", + "enabled": true, + "data_source": "script", + "mapped_to_table": "CurrentScan", + "show_ui": true, + "localized": ["display_name", "description", "icon"], + "display_name": [ + { + "language_code": "en_us", + "string": "Mikrotik (Name discovery)" + } + ], + "icon": [ + { + "language_code": "en_us", + "string": "" + } + ], + "description": [ + { + "language_code": "en_us", + "string": "A plugin to discover device names." + } + ], + "params": [ + { + "name": "ips", + "type": "sql", + "value": "SELECT dev_LastIP from DEVICES order by dev_MAC", + "timeoutMultiplier": true + }, + { + "name": "mt_host", + "type": "setting", + "value": "MTSCAN_MT_HOST" + }, + { + "name": "mt_port", + "type": "setting", + "value": "MTSCAN_MT_PORT" + }, + { + "name": "mt_user", + "type": "setting", + "value": "MTSCAN_MT_USER" + }, + { + "name": "mt_pass", + "type": "setting", + "value": "MTSCAN_MT_PASS" + } + ], + "settings": [ + { + "function": "RUN", + "events": ["run"], + "type": { + "dataType": "string", + "elements": [ + { "elementType": "select", "elementOptions": [], "transformers": [] } + ] + }, + "default_value": "disabled", + "options": [ + "disabled", + "before_name_updates", + "on_new_device", + "once", + "schedule", + "always_after_scan" + ], + "localized": ["name", "description"], + "name": [ + { + "language_code": "en_us", + "string": "When to run" + } + ], + "description": [ + { + "language_code": "en_us", + "string": "When the plugin should be executed. If enabled this will execute the scan until there are no (unknown) or (name not found) devices. Setting this to on_new_device or a daily schedule is recommended." + } + ] + }, + { + "function": "CMD", + "type": { + "dataType": "string", + "elements": [ + { + "elementType": "input", + "elementOptions": [{ "readonly": "true" }], + "transformers": [] + } + ] + }, + "default_value": "python3 /app/front/plugins/mikrotik_scan/mikrotik.py", + "options": [], + "localized": ["name", "description"], + "name": [ + { + "language_code": "en_us", + "string": "Command" + } + ], + "description": [ + { + "language_code": "en_us", + "string": "Command to run. This can not be changed" + } + ] + }, + { + "function": "RUN_SCHD", + "type": { + "dataType": "string", + "elements": [ + { "elementType": "input", "elementOptions": [], "transformers": [] } + ] + }, + "default_value": "*/30 * * * *", + "options": [], + "localized": ["name", "description"], + "name": [ + { + "language_code": "en_us", + "string": "Schedule" + } + ], + "description": [ + { + "language_code": "en_us", + "string": "Only enabled if you select schedule in the MKTSCAN_RUN setting. Make sure you enter the schedule in the correct cron-like format (e.g. validate at crontab.guru). For example entering 0 4 * * * will run the scan after 4 am in the TIMEZONE you set above. Will be run NEXT time the time passes." + } + ] + }, + { + "function": "MT_HOST", + "type": { + "dataType": "string", + "elements": [ + { + "elementType": "input", + "elementOptions": [], + "transformers": [] + } + ] + }, + "default_value": "192.168.88.1", + "options": [], + "localized": ["name", "description"], + "name": [ + { + "language_code": "en_us", + "string": "Mikrotik Host IP" + } + ], + "description": [ + { + "language_code": "en_us", + "string": "IP for Mikrotik Router" + } + ] + }, + { + "function": "MT_PORT", + "type": { + "dataType": "integer", + "elements": [ + { + "elementType": "input", + "elementOptions": [{ "type": "number" }], + "transformers": [] + } + ] + }, + "default_value": 8728, + "options": [], + "localized": ["name", "description"], + "name": [ + { + "language_code": "en_us", + "string": "Mikrotik API Port" + } + ], + "description": [ + { + "language_code": "en_us", + "string": "API Port for Mikrotik Router" + } + ] + }, + { + "function": "MT_USER", + "type": { + "dataType": "string", + "elements": [ + { + "elementType": "input", + "elementOptions": [], + "transformers": [] + } + ] + }, + "default_value": "admin", + "options": [], + "localized": ["name", "description"], + "name": [ + { + "language_code": "en_us", + "string": "Mikrotik User" + } + ], + "description": [ + { + "language_code": "en_us", + "string": "User for Mikrotik Router" + } + ] + }, + { + "function": "MT_PASS", + "type": { + "dataType": "string", + "elements": [ + { + "elementType": "input", + "elementOptions": [{ "type": "password" }], + "transformers": [] + } + ] + }, + "default_value": "", + "options": [], + "localized": ["name", "description"], + "name": [ + { + "language_code": "en_us", + "string": "Mikrotik Password" + } + ], + "description": [ + { + "language_code": "en_us", + "string": "Password for Mikrotik Router" + } + ] + } + ], + "database_column_definitions": [ + { + "column": "Object_PrimaryID", + "mapped_to_column": "cur_MAC", + "css_classes": "col-sm-2", + "show": true, + "type": "device_name_mac", + "default_value": "", + "options": [], + "localized": ["name"], + "name": [ + { + "language_code": "en_us", + "string": "Name" + } + ] + }, + { + "column": "ForeignKey", + "css_classes": "col-sm-2", + "show": true, + "type": "device_mac", + "default_value": "", + "options": [], + "localized": ["name"], + "name": [ + { + "language_code": "en_us", + "string": "MAC" + } + ] + }, + { + "column": "Object_SecondaryID", + "mapped_to_column": "cur_IP", + "css_classes": "col-sm-2", + "show": true, + "type": "device_ip", + "default_value": "", + "options": [], + "localized": ["name"], + "name": [ + { + "language_code": "en_us", + "string": "IP" + } + ] + }, + { + "column": "Watched_Value1", + "css_classes": "col-sm-2", + "show": true, + "type": "device_ip", + "default_value": "", + "options": [], + "localized": ["name"], + "name": [ + { + "language_code": "en_us", + "string": "Lease IP" + } + ] + }, + { + "column": "Watched_Value2", + "mapped_to_column": "cur_Name", + "css_classes": "col-sm-2", + "show": true, + "type": "label", + "default_value": "", + "options": [], + "localized": ["name"], + "name": [ + { + "language_code": "en_us", + "string": "Name" + } + ] + }, + { + "column": "Watched_Value3", + "css_classes": "col-sm-2", + "show": true, + "type": "label", + "default_value": "", + "options": [], + "localized": ["name"], + "name": [ + { + "language_code": "en_us", + "string": "Host Name" + } + ] + }, + { + "column": "Watched_Value4", + "css_classes": "col-sm-2", + "show": true, + "type": "label", + "default_value": "", + "options": [], + "localized": ["name"], + "name": [ + { + "language_code": "en_us", + "string": "Last Seen" + } + ] + }, + { + "column": "HelpVal1", + "css_classes": "col-sm-2", + "show": true, + "type": "label", + "default_value": "", + "options": [], + "localized": ["name"], + "name": [ + { + "language_code": "en_us", + "string": "Comment" + } + ] + }, + { + "column": "Dummy", + "mapped_to_column": "cur_ScanMethod", + "mapped_to_column_data": { + "value": "MTSCAN" + }, + "css_classes": "col-sm-2", + "show": false, + "type": "label", + "default_value": "", + "options": [], + "localized": ["name"], + "name": [ + { + "language_code": "en_us", + "string": "Scan method" + } + ] + }, + { + "column": "DateTimeCreated", + "css_classes": "col-sm-2", + "show": true, + "type": "label", + "default_value": "", + "options": [], + "localized": ["name"], + "name": [ + { + "language_code": "en_us", + "string": "Created" + }, + { + "language_code": "es_es", + "string": "Creado" + } + ] + }, + { + "column": "DateTimeChanged", + "css_classes": "col-sm-2", + "show": true, + "type": "label", + "default_value": "", + "options": [], + "localized": ["name"], + "name": [ + { + "language_code": "en_us", + "string": "Changed" + }, + { + "language_code": "es_es", + "string": "Cambiado" + } + ] + } + ] +} diff --git a/front/plugins/mikrotik_scan/mikrotik.py b/front/plugins/mikrotik_scan/mikrotik.py new file mode 100755 index 00000000..88c2f098 --- /dev/null +++ b/front/plugins/mikrotik_scan/mikrotik.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python +# test script by running: +# tbc + +import os +import pathlib +import argparse +import subprocess +import sys +import hashlib +import csv +import sqlite3 +import re +from io import StringIO +from datetime import datetime + +# Register NetAlertX directories +INSTALL_PATH="/app" +sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) + +from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64 +from logger import mylog, append_line_to_file +from helper import timeNowTZ, get_setting_value +from const import logPath, applicationPath, fullDbPath +from database import DB +from device import Device_obj +import conf +from pytz import timezone +from librouteros import connect +from librouteros.exceptions import TrapError + +# Make sure the TIMEZONE for logging is correct +conf.tz = timezone(get_setting_value('TIMEZONE')) + +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') + +pluginName = 'NSLOOKUP' + +def main(): + + mylog('verbose', [f'[{pluginName}] In script']) + + mt_host = get_setting_value('MTSCAN_MT_HOST') + mt_port = get_setting_value('MTSCAN_MT_PORT') + mt_user = get_setting_value('MTSCAN_MT_USER') + mt_password = get_setting_value('MTSCAN_MT_PASS') + + #mylog('verbose', [f'[{pluginName}] Router: {mt_host}:{mt_port} user: {mt_user}, pass: {mt_password}']) + # Create a database connection + db = DB() # instance of class DB + db.open() + + # Initialize the Plugin obj output file + plugin_objects = Plugin_Objects(RESULT_FILE) + + # Create a Device_obj instance + device_handler = Device_obj(db) + + # Retrieve devices + #unknown_devices = device_handler.getUnknown() + #mylog('verbose', [f'[{pluginName}] Unknown devices count: {len(unknown_devices)}']) + + all_devices = device_handler.getAll() + + mylog('verbose', [f'[{pluginName}] all devices count: {len(all_devices)}']) + + device_map = {d['dev_MAC']:d['dev_LastIP'] for d in all_devices} + + try: + # connect router + api = connect(username=mt_user, password=mt_password, host=mt_host, port=mt_port) + + # get dhcp leases + leases = api('/ip/dhcp-server/lease/print') + + + + for lease in leases: + lease_id = lease.get('.id') + address = lease.get('address') + mac_address = lease.get('mac-address').lower() + host_name = lease.get('host-name') + comment = lease.get('comment') + last_seen = lease.get('last-seen') + + mylog('verbose', [f"ID: {lease_id}, Address: {address}, MAC Address: {mac_address}, Host Name: {host_name}, Comment: {comment}, Last Seen: {last_seen}"]) + if mac_address in device_map.keys(): + device_name = host_name + if comment != '': + device_name = comment + + plugin_objects.add_object( + # "Name-MAC", "LastIP", "IP", "Name","Host","LastSeen","Comment" + primaryId = mac_address, + secondaryId = device_map[mac_address], + watched1 = address, + watched2 = device_name, + watched3 = host_name, + watched4 = last_seen, + extra = '', + helpVal1 = comment, + foreignKey = mac_address) + + plugin_objects.write_result_file() + except TrapError as e: + mylog('error', [f"An error occurred: {e}"]) + except Exception as e: + mylog('error', [f"Failed to connect to MikroTik API: {e}"]) + + + #for device in unknown_devices: + # domain_name, dns_server = execute_nslookup(device['dev_LastIP'], timeout) + + # if domain_name != '': + # plugin_objects.add_object( + # # "MAC", "IP", "Server", "Name" + # primaryId = device['dev_MAC'], + # secondaryId = device['dev_LastIP'], + # watched1 = dns_server, + # watched2 = domain_name, + # watched3 = '', + # watched4 = '', + # extra = '', + # foreignKey = device['dev_MAC']) + + #plugin_objects.write_result_file() + + + mylog('verbose', [f'[{pluginName}] Script finished']) + + return 0 + + + + +#=============================================================================== +# BEGIN +#=============================================================================== +if __name__ == '__main__': + main()