domotz-remote-pawn
Version:
Domotz Agent
582 lines (509 loc) • 25 kB
JavaScript
/** 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;