UNPKG

domotz-remote-pawn

Version:

Domotz Agent

582 lines (509 loc) 25 kB
/** This file is part of Domotz Agent. * Copyright (C) 2016 Domotz Ltd * * Domotz Agent is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Domotz Agent is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Domotz Agent. If not, see <http://www.gnu.org/licenses/>. * * Created by Andrea Azzara <a.azzara@domotz.com> on 23/08/2017. */ const DEFAULT_NMAP_TIMEOUT = 28; const DEFAULT_OVERLAY_DB_FD_CYCLES = 720; const DEFAULT_OVERLAY_DB_STARTUP_FD_CYCLES = 30; const DEFAULT_OVERLAY_DEVICE_DB_FD_CYCLES = 720; const DEFAULT_OVERLAY_DEVICE_DB_STARTUP_FD_CYCLES = 40; function SettingsManager(resourceLocator) { 'use strict'; var fileUtils = resourceLocator.utils.file; var myConsole = resourceLocator.log.decorateLogs(); var fileName = process.env.DOMOTZ_CONF_DIR + '/agent_settings.json'; var configurationUpdater = resourceLocator.configurationUpdater; var fs = require('fs'); var domotzPlatform = require('../utils/platform').factory(fs); var semver = require('semver'); function getSetting(path, defaultValue) { return resourceLocator.lodash.get(resourceLocator, path, defaultValue); } function updateCustomInterfaces(refreshBindingsList) { myConsole.info('Updating custom settings: %s', JSON.stringify(resourceLocator.configuration.settings)); var updateHostInfo = function (ifaces) { resourceLocator.discovery.host.loadCustomInterfaces(ifaces); if (refreshBindingsList) { resourceLocator.interfacesBindingStorage.invalidateList(); } }; if ( !resourceLocator.configuration.settings.external_subnets && !resourceLocator.configuration.settings.external_hosts && !isForcedL3DiscoveredAddressesEnabled() ) { myConsole.info('No external subnets nor hosts found'); updateHostInfo({}); return; } var interfaces = {}; if (resourceLocator.configuration.settings.external_subnets) { myConsole.info('Updating external subnets: %s', JSON.stringify(resourceLocator.configuration.settings.external_subnets)); resourceLocator.configuration.settings.external_subnets.forEach(function (i) { interfaces['ext' + i.id] = { custom: true, netmask: i.subnet_mask, ip: i.address, mac: null, discovery: i.discovery, }; }); } else { myConsole.info('No external subnets found'); } if (resourceLocator.configuration.settings.external_hosts) { myConsole.info('Updating external hosts: %s', JSON.stringify(resourceLocator.configuration.settings.external_hosts)); resourceLocator.configuration.settings.external_hosts.forEach(function (h) { interfaces['ext' + h.id] = { custom: true, netmask: 32, ip: h.ip_address, mac: null, hostname: h.hostname, discovery_hw_address: h.hw_address, }; }); } else { myConsole.info('No external host found'); } if (isForcedL3DiscoveredAddressesEnabled()) { myConsole.info( 'Updating external hosts: %s', JSON.stringify(resourceLocator.configuration.settings.forced_l3_discovered_addresses.ip_list) ); resourceLocator.configuration.settings.forced_l3_discovered_addresses.ip_list.forEach(function (h, index) { interfaces['ext-forced-' + index] = { custom: true, netmask: 32, ip: h, mac: null, }; }); } else { myConsole.info('No external forced host found'); } updateHostInfo(interfaces); } function checkConfigurationVersion() { myConsole.debug('Checking required conf version'); var requiredConfigVersion = resourceLocator.lodash.get(resourceLocator, 'configuration.settings.required_conf_version'); if (!requiredConfigVersion) { myConsole.debug('No required conf version found, ignore'); return; } var confVersion = resourceLocator.lodash.get(resourceLocator.configuration, 'conf_version', null); myConsole.debug('Required conf version found %s, current one is %s', requiredConfigVersion, confVersion); if (requiredConfigVersion !== confVersion) { myConsole.info('Required conf version is different from current one ' + requiredConfigVersion); var engineEndpoint = resourceLocator.configuration.engine.api_endpoints; var baseURL = engineEndpoint.agent || engineEndpoint.hub; var confURL = baseURL + 'agent/' + resourceLocator.configuration.id; configurationUpdater.updateConfiguration(requiredConfigVersion, confURL); } } function loadSettings() { myConsole.info('Reading settings from cache: %s', fileName); var cachedSettings = fileUtils.readFile(fileName); if (cachedSettings) { resourceLocator.configuration.settings = JSON.parse(cachedSettings); myConsole.info('Found settings from cache: %s', JSON.stringify(resourceLocator.configuration.settings)); updateCustomInterfaces(); } else { myConsole.info('Settings not found in cache, requiring settings from server'); fetchSettings() .then(updateCustomInterfaces) .catch(function () { myConsole.info('Unable to retrieve settings from server'); }); } } function writeSettings(settings) { var fileContents = JSON.stringify(settings, null, ' '); myConsole.info('Writing agent settings: %s', fileName); fileUtils.writeFileSafe(fileName, fileContents); } function isSettingChanged(pathToCheck, defaultValue, oldValue) { return oldValue !== getSetting(pathToCheck, defaultValue); } function restartIfRequired(oldAmqpEnabledSettings) { if (isSettingChanged('configuration.settings.amqp.enabled', true, oldAmqpEnabledSettings)) { resourceLocator.utils.proc.gracefulShutdown(); } } function fetchSettings(refreshBindingsList, callback) { var engineRequest = resourceLocator.engineRequest; var oldAmqpEnabledSettings = getSetting('configuration.settings.amqp.enabled', true); return engineRequest .sendWithPromise('/settings', 'GET', {}, null, false) .then(function (result) { myConsole.info('Updating settings with ' + JSON.stringify(result)); resourceLocator.configuration.settings = result; writeSettings(result); updateCustomInterfaces(refreshBindingsList); checkConfigurationVersion(); if (callback) { callback(); } restartIfRequired(oldAmqpEnabledSettings); }) .catch(function (error) { myConsole.error(error.message + '\n' + error.stack); }); } function isOnvifUnicastDiscoveryEnabled() { return resourceLocator.lodash.get(resourceLocator, 'configuration.settings.onvif_unicast_discovery', false); } function isForcedL3DiscoveredAddressesEnabled() { return ( resourceLocator.lodash.get(resourceLocator, 'configuration.settings.forced_l3_discovered_addresses', false) && resourceLocator.lodash.get(resourceLocator, 'configuration.settings.forced_l3_discovered_addresses.enabled', true) && resourceLocator.lodash.get(resourceLocator, 'configuration.settings.forced_l3_discovered_addresses.ip_list', false) ); } function isLayer3ScanEnabled() { return resourceLocator.lodash.get(resourceLocator, 'configuration.settings.layer_3_scan', false); } function isBroadcastDiscoveryDisabled() { return !resourceLocator.lodash.get(resourceLocator, 'configuration.settings.broadcast_discovery', true); } function isLowThrottleMode() { return resourceLocator.lodash.get(resourceLocator, 'configuration.settings.http_call_throttle', '') === 'LOW'; } function getEyesInterval() { var frequency = resourceLocator.lodash.get(resourceLocator, 'configuration.settings.eyes_hourly_frequency'); if (frequency) { return parseInt(120 / frequency); } return null; } function getMibDiscoveryInterval() { return parseInt(resourceLocator.lodash.get(resourceLocator, 'configuration.settings.oid_discovery.oid_scan_period_fast_discoveries', 30)); } function getMibDiscoveryStartupInterval() { return parseInt(resourceLocator.lodash.get(resourceLocator, 'configuration.settings.oid_discovery.oid_scan_delay_fast_discoveries', 12)); } function getOverlayDBInterval() { return parseInt(resourceLocator.lodash.get(resourceLocator, 'configuration.settings.overlay.db.fd_cycles', DEFAULT_OVERLAY_DB_FD_CYCLES)); } function getOverlayDBStartupInterval() { return parseInt( resourceLocator.lodash.get(resourceLocator, 'configuration.settings.overlay.db.startup_fd_cycles', DEFAULT_OVERLAY_DB_STARTUP_FD_CYCLES) ); } function getOverlayDeviceDBInterval() { return parseInt( resourceLocator.lodash.get(resourceLocator, 'configuration.settings.overlay.device_db.fd_cycles', DEFAULT_OVERLAY_DEVICE_DB_FD_CYCLES) ); } function getOverlayDeviceDBStartupInterval() { return parseInt( resourceLocator.lodash.get( resourceLocator, 'configuration.settings.overlay.device_db.startup_fd_cycles', DEFAULT_OVERLAY_DEVICE_DB_STARTUP_FD_CYCLES ) ); } function getNMAPPerformanceSettings(hostTimeout) { var lodash = resourceLocator.lodash; var nmap_performance = lodash.get(resourceLocator, 'configuration.settings.discovery.host.nmap_performance', {}); return { 'min-parallelism': lodash.get(nmap_performance, 'parallelism.min', undefined), 'max-parallelism': lodash.get(nmap_performance, 'parallelism.max', undefined), 'min-rtt-timeout': lodash.get(nmap_performance, 'rtt_timeout_ms.min', undefined) ? nmap_performance.rtt_timeout_ms.min + 'ms' : undefined, 'max-rtt-timeout': lodash.get(nmap_performance, 'rtt_timeout_ms.max', undefined) ? nmap_performance.rtt_timeout_ms.max + 'ms' : undefined, 'initial-rtt-timeout': lodash.get(nmap_performance, 'rtt_timeout_ms.initial', undefined) ? nmap_performance.rtt_timeout_ms.initial + 'ms' : undefined, 'scan-delay': lodash.get(nmap_performance, 'scan_delay_ms', undefined) ? nmap_performance.scan_delay_ms + 'ms' : undefined, 'max-scan-delay': lodash.get(nmap_performance, 'scan_delay_max_ms', undefined) ? nmap_performance.scan_delay_max_ms + 'ms' : undefined, 'min-rate': lodash.get(nmap_performance, 'rate.min', undefined), 'max-rate': lodash.get(nmap_performance, 'rate.max', undefined), T: lodash.get(nmap_performance, 'time_template', undefined), 'host-timeout': (hostTimeout || lodash.get(nmap_performance, 'host_timeout_s', DEFAULT_NMAP_TIMEOUT)) + 's', }; } function getRTDConfiguration() { if (!resourceLocator.lodash.get(resourceLocator, 'configuration.settings.rtd_scan', true)) { return null; } return resourceLocator.lodash.get(resourceLocator, 'configuration.settings.rtd_discovery'); } function getDiscoverySettings() { return resourceLocator.lodash.get(resourceLocator, 'configuration.settings.discovery', { max_ip_netmask: 22, ports: [22, 23, 80, 443, 3000, 3389, 5000, 5900, 8000, 8001, 8080, 8888], udp_ports: [161], udp_port_status_mapping: { 'CLOSED|FILTERED': 'CLOSED', 'OPEN|FILTERED': 'CLOSED', }, refresh: 30, }); } function getMTRRemoteHost() { return resourceLocator.lodash.get(resourceLocator, 'configuration.settings.mtr.remote_host_for_mtr_computation', '8.8.8.8'); } function getMTRPacketNumber() { return parseInt(resourceLocator.lodash.get(resourceLocator, 'configuration.settings.mtr.number_of_packets_for_mtr', 10)); } function getOidsToDiscover() { return resourceLocator.lodash.get(resourceLocator, 'configuration.settings.oids_to_discover', { '1.3.6.1.2.1.1.1.0': 'string', // sysDescr '1.3.6.1.2.1.1.2.0': 'string', // sysOID '1.3.6.1.2.1.1.4.0': 'string', // sysContact '1.3.6.1.2.1.1.5.0': 'string', // sysName '1.3.6.1.2.1.1.6.0': 'string', // sysLocation '1.3.6.1.2.1.1.7.0': 'int', // sysServices '1.3.6.1.2.1.2.1.0': 'int', // ifNumber }); } function isOsInspectionsEnabled() { return resourceLocator.lodash.get(resourceLocator, 'configuration.settings.os_inspection.enabled', false); } function osInspectionsPollingInterval() { return resourceLocator.lodash.get(resourceLocator, 'configuration.settings.os_inspection.candidate_devices_polling_interval_hours', 3); } function isExperimentEnabled(experiment) { var experiments = resourceLocator.lodash.get(resourceLocator, 'configuration.settings.experiments', []); return experiments.indexOf(experiment) !== -1; } function getNetworkInterfacesPolicies() { return resourceLocator.lodash.get(resourceLocator, 'configuration.settings.interfaces_policy', { policy: 'deny', rules: [], }); } function isEventHandlerEnabled(handlerName) { var ret = resourceLocator.lodash.get(resourceLocator, 'configuration.settings.event_handler.' + handlerName, true); if (!ret) { myConsole.info(handlerName + ' handler disabled from settings'); } return ret; } function isCommandHandlerEnabled(commandName) { var ret = resourceLocator.lodash.get(resourceLocator, 'configuration.settings.command_handler.' + commandName, true); if (!ret) { myConsole.info(commandName + ' handler disabled from settings'); } return ret; } function useSnmpOverlayDiscoveryGetChunked() { return resourceLocator.lodash.get(resourceLocator, 'configuration.settings.snmp.discovery.overlay.use_snmp_get_chunked', true); } function useSnmpBasicDiscoveryGetChunked() { return resourceLocator.lodash.get(resourceLocator, 'configuration.settings.snmp.discovery.basic.use_snmp_get_chunked', true); } function isBigNetworksEnabled() { var bigNetworks = resourceLocator.lodash.get(resourceLocator, 'configuration.settings.big_networks', { enabled: false }); return bigNetworks && bigNetworks.enabled === true; } function bigNetworksBitmask() { if (!isBigNetworksEnabled()) { return null; } return resourceLocator.lodash.get(resourceLocator, 'configuration.settings.big_networks.netmask_bits', 16); } function getBigNetworksPerformanceParams() { return resourceLocator.lodash.get(resourceLocator, 'configuration.settings.big_networks.performance_params', { t_value: 4, timeout: 10, max_subnetwork_mask: 24, parallel_executions: 4, cool_down_before_restart: 30000, // seconds of cool down before a big scan and another }); } function getStallWatcherSettings() { return resourceLocator.lodash.get(resourceLocator, 'configuration.settings.stall_watcher', { enabled: true, timeout: 5, }); } function getInterfaceDiscoveryTypesSettings() { return resourceLocator.lodash.get(resourceLocator, 'configuration.settings.discovery.interfaces.if_types', [ 0, // fallback for non-compliant devices 6, // ethernetCsmacd(6) 62, // fastEther(62) 69, // fastEtherFX(69) 117, // gigabitEthernet(117) ]); } function getUDPScanSettings() { return resourceLocator.lodash.get(resourceLocator, 'configuration.settings.udp_scan', { use_nmap_custom_scripts: true, }); } function dhcpScanOnDefaultInterface() { if (resourceLocator.utils.platform.isWindows()) { return false; } return resourceLocator.lodash.get(resourceLocator, 'configuration.settings.dhcp.scan_on_default_interface', true); } function dhcpScanTimeout() { return resourceLocator.lodash.get(resourceLocator, 'configuration.settings.dhcp.scan_timeout', null); } function getIpPolicy() { return { forced_ip_addresses: getSetting('configuration.settings.forced_ip_addresses', []), max_ip_per_nmap: getSetting('configuration.settings.max_ip_per_nmap', 50), forced_ip_ranges: getSetting('configuration.settings.forced_ip_ranges', []), }; } function getL3DiscoveryDenyList() { return getSetting('configuration.settings.discovery.l3_discovery_deny_list', []); } function isBonjour2Enabled() { return resourceLocator.lodash.get(resourceLocator, 'configuration.settings.protocols_discovery.bounjour_2.enabled', true); } function isSNMPStatusOnRequiredForSNMPDiscoveries() { return resourceLocator.lodash.get(resourceLocator, 'configuration.settings.snmp.is_snmp_status_on_required_for_snmp_discoveries', true); } function isFreshBindingsCheckDisabled() { return resourceLocator.lodash.get(resourceLocator, 'configuration.settings.disable_check_for_fresh_bindings', true); } function getSSHConfiguration() { var nodeVersion = domotzPlatform.getNodeVersion(); var fallBack; var defaultCipher = [ '3des-cbc', 'aes128-cbc', 'aes192-cbc', 'aes256-cbc', 'aes128-ctr', 'aes192-ctr', 'aes256-ctr', 'aes128-gcm@openssh.com', 'aes256-gcm@openssh.com', ]; var defaultSettings = { '0.11.12': { kex: ['diffie-hellman-group1-sha1', 'diffie-hellman-group14-sha1'], cipher: ['3des-cbc', 'aes128-cbc', 'aes192-cbc', 'aes256-cbc', 'aes128-ctr', 'aes192-ctr', 'aes256-ctr'], }, '6.9.2': { kex: [ 'diffie-hellman-group1-sha1', 'diffie-hellman-group14-sha1', 'diffie-hellman-group-exchange-sha1', 'diffie-hellman-group-exchange-sha256', 'ecdh-sha2-nistp256', 'ecdh-sha2-nistp384', 'ecdh-sha2-nistp521', 'diffie-hellman-group1-sha1', ], cipher: defaultCipher, }, '14.0.0': { kex: [ 'diffie-hellman-group1-sha1', 'diffie-hellman-group14-sha1', 'diffie-hellman-group-exchange-sha1', 'diffie-hellman-group-exchange-sha256', 'ecdh-sha2-nistp256', 'ecdh-sha2-nistp384', 'ecdh-sha2-nistp521', 'diffie-hellman-group1-sha1', ], cipher: defaultCipher, }, }; if (semver.lt(semver.clean(nodeVersion), Object.keys(defaultSettings)[0])) { myConsole.debug('nodeVersion < 0.11.12'); fallBack = defaultSettings['0.11.12']; } else if (semver.gt(semver.clean(nodeVersion), Object.keys(defaultSettings)[2])) { myConsole.debug('nodeVersion > 14.0.0'); fallBack = defaultSettings['14.0.0']; } else { myConsole.debug('0.11.12 < nodeVersion < 14.0.0'); fallBack = defaultSettings['6.9.2']; } return resourceLocator.lodash.get(resourceLocator, 'configuration.settings.ssh_configuration.' + nodeVersion.replace(/\./g, '_'), fallBack); } function useSystemSSHShellSequence() { var useSystemSSH = getSetting('configuration.settings.ssh.use_system_ssh', false); var platformsUsingSystemSSH = getSetting('configuration.settings.ssh.platforms_using_system_ssh', []); return useSystemSSH || platformsUsingSystemSSH.indexOf(resourceLocator.utils.platform.getFullPlatform()) > -1; } function getAgentSettings() { myConsole.info('Reading agent_settings.json : %s', fileName); var settings = fileUtils.readFile(fileName); if (settings) { myConsole.info('File agent_settings.json found'); return JSON.parse(settings); } else { myConsole.info('File agent_settings.json not found'); return null; } } function getExternalHostsChunkSize() { return getSetting('configuration.settings.discovery.external_hosts_chunk_size', 50); } function isNdt7SuiteEnabled() { return getSetting('configuration.settings.speed_test.ndt7_enabled', true); } return { bigNetworksBitmask: bigNetworksBitmask, dhcpScanOnDefaultInterface: dhcpScanOnDefaultInterface, dhcpScanTimeout: dhcpScanTimeout, fetchSettings: fetchSettings, getAgentSettings: getAgentSettings, getBigNetworksPerformanceParams: getBigNetworksPerformanceParams, getDiscoverySettings: getDiscoverySettings, getExternalHostsChunkSize: getExternalHostsChunkSize, getEyesInterval: getEyesInterval, getInterfaceDiscoveryTypesSettings: getInterfaceDiscoveryTypesSettings, getIpPolicy: getIpPolicy, getL3DiscoveryDenyList: getL3DiscoveryDenyList, getMibDiscoveryInterval: getMibDiscoveryInterval, getMibDiscoveryStartupInterval: getMibDiscoveryStartupInterval, getMTRPacketNumber: getMTRPacketNumber, getMTRRemoteHost: getMTRRemoteHost, getNetworkInterfacesPolicies: getNetworkInterfacesPolicies, getNMAPPerformanceSettings: getNMAPPerformanceSettings, getOidsToDiscover: getOidsToDiscover, getOverlayDBInterval: getOverlayDBInterval, getOverlayDBStartupInterval: getOverlayDBStartupInterval, getOverlayDeviceDBInterval: getOverlayDeviceDBInterval, getOverlayDeviceDBStartupInterval: getOverlayDeviceDBStartupInterval, getRTDConfiguration: getRTDConfiguration, getSetting: getSetting, getSSHConfiguration: getSSHConfiguration, getStallWatcherSettings: getStallWatcherSettings, getUDPScanSettings: getUDPScanSettings, isBigNetworksEnabled: isBigNetworksEnabled, isBonjour2Enabled: isBonjour2Enabled, isBroadcastDiscoveryDisabled: isBroadcastDiscoveryDisabled, isCommandHandlerEnabled: isCommandHandlerEnabled, isEventHandlerEnabled: isEventHandlerEnabled, isExperimentEnabled: isExperimentEnabled, isFreshBindingsCheckDisabled: isFreshBindingsCheckDisabled, isLayer3ScanEnabled: isLayer3ScanEnabled, isLowThrottleMode: isLowThrottleMode, isNdt7SuiteEnabled: isNdt7SuiteEnabled, isOnvifUnicastDiscoveryEnabled: isOnvifUnicastDiscoveryEnabled, isOsInspectionsEnabled: isOsInspectionsEnabled, isSettingChanged: isSettingChanged, isSNMPStatusOnRequiredForSNMPDiscoveries: isSNMPStatusOnRequiredForSNMPDiscoveries, loadSettings: loadSettings, osInspectionsPollingInterval: osInspectionsPollingInterval, useSnmpBasicDiscoveryGetChunked: useSnmpBasicDiscoveryGetChunked, useSnmpOverlayDiscoveryGetChunked: useSnmpOverlayDiscoveryGetChunked, useSystemSSHShellSequence: useSystemSSHShellSequence, writeSettings: writeSettings, }; } module.exports.settingsManager = SettingsManager;