domotz-remote-pawn
Version:
Domotz Agent
196 lines (168 loc) • 6.95 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 Tommaso Latini <tommaso@domotz.com> on 06/09/16.
*
*/
const MODULE = 'SSH_COMMAND_EXECUTOR';
const USER = 'domotz';
const NULL_PASSWORD = null;
const NULL_PRIVATE_KEY = null;
const HOST = 'localhost';
const SSH_BINARY = 'ssh';
const SSHPASS_BINARY = 'domotz_sshpass';
var os = require('os');
var path = require('path');
var q = require('q');
var semver = require('semver');
const sshCommon = require("./sshCommon");
module.exports.factory = function (procUtils, fileUtils, resourceLocator) {
var optionsMap = {
bind_address: '-b',
cipher_spec: '-c',
cipher: '-c',
escape_char: '-e',
config_file: '-F',
dynamic_port_forwarding: '-D',
identity_file: '-i',
pkcs11: '-I',
local_port_forwarding: '-L',
mac_spec: '-m',
disable_remote_command: '-N',
host_key_checking: '-oStrictHostKeyChecking=',
user_known_host_file: '-oUserKnownHostsFile=',
server_alive_interval: '-oServerAliveInterval=',
server_alive_count_max: '-oServerAliveCountMax=',
pubkey_accepted_key_types: '-oPubkeyAcceptedKeyTypes=',
host_key_algorithms: '-oHostKeyAlgorithms=',
kex_algorithm: '-oKexAlgorithms=',
ctl_cmd: '-O',
port: '-p',
remote_port_forwarding: '-R',
ctl_path: '-S',
disable_pseudo_tty_alloc: '-T'
}
var USER_KNOWN_HOST_FILE = optionsMap.user_known_host_file + '/dev/null';
var HOST_KEY_CHECKING = optionsMap.host_key_checking + 'no';
var myConsole = resourceLocator.log.decorateLogs();
var openSSHVersion = resourceLocator.utils.platform.getOpenSSHVersion();
var sshInfo = resourceLocator.utils.platform.getSSHInfo();
if (openSSHVersion && semver.clean(openSSHVersion)) {
myConsole.debug("OpenSSH version is: " + semver.clean(openSSHVersion));
//https://www.openssh.com/txt/release-7.0
if (semver.lt(semver.clean(openSSHVersion), '7.0.0')) {
delete optionsMap["pubkey_accepted_key_types"];
delete optionsMap["host_key_algorithms"];
}
}
function describe(args, options) {
// Removing the password arguments from the logs
return args.join(' ') + (options ? '. Options: ' + JSON.stringify(options) : '');
}
/**
* SSH is an external tool. It must be linked with system libraries!
*/
function getCleanEnv() {
var cleanedEnv = Object.create(process.env);
cleanedEnv.LD_LIBRARY_PATH = "";
return cleanedEnv;
}
function getOpt(options, names, dflt) {
for (var i = 0; i < names.length; i++) {
var name = names[i];
var o = options[name];
if (typeof o !== 'undefined') {
delete options[name];
return o;
}
}
return dflt;
}
function getArgsForAuthentication(password, privateKey, identityFile) {
var args = [];
if (privateKey !== NULL_PRIVATE_KEY) {
myConsole.debug("Private Key is present. use identity file: %s", identityFile)
//0600 cannot be changed to 0o0600 on node 0.10
if (!fileUtils.createFile(identityFile, privateKey, {mode: 0600, encoding: 'ascii'})) { /* eslint-disable-line */
privateKey = NULL_PRIVATE_KEY;
} else {
args.push(SSH_BINARY);
args.push('-i');
if (process.env.DPLATFORM === 'win') {
args.push("'" + identityFile + "'");
} else {
args.push(identityFile);
}
}
}
if (privateKey === NULL_PRIVATE_KEY && password !== NULL_PASSWORD) {
myConsole.debug("Fallback in sshpass");
args.push(SSHPASS_BINARY);
args.push('-e');
args.push(SSH_BINARY);
}
return args;
}
function exec(command, options, correlationId) {
var sshCommon = require('./sshCommon');
var logId = getOpt(options, ['log_prefix'], MODULE + ' - ' + correlationId + ' - ');
var userKnownHostFile = getOpt(options, 'user_known_host_file', USER_KNOWN_HOST_FILE);
var hostKeyChecking = getOpt(options, 'host_key_checking', HOST_KEY_CHECKING);
var host = getOpt(options, ['host', 'hostname'], HOST);
var user = getOpt(options, ['user', 'username'], USER);
var password = getOpt(options, ['password', 'pwd'], NULL_PASSWORD);
var privateKey = getOpt(options, ['private_key'], NULL_PRIVATE_KEY);
var identityFile = path.join(os.tmpdir(), '.' + user);
var args = getArgsForAuthentication(password, privateKey, identityFile);
if (args.length === 0) {
myConsole.error('%s - Missing Authentication Method', logId);
return q.reject('Missing Authentication Method');
}
args = args.concat(sshCommon.getArgsFromOptions(options, optionsMap, sshInfo, myConsole));
args.push(userKnownHostFile);
args.push(hostKeyChecking);
args.push(user + '@' + host);
if (command) {
args.push(command);
}
var ssh = args.shift();
if (password && privateKey === NULL_PRIVATE_KEY) {
options.description = describe(args, options);
}
options.env = getCleanEnv();
if (password !== NULL_PASSWORD && privateKey === NULL_PRIVATE_KEY) {
options.env.SSHPASS = password;
}
options.timeout = null;
setTimeout(function () {
fileUtils.deleteFile(identityFile);
myConsole.debug('%s - Successfully deleted file [%s]', logId, identityFile);
}, options.ttl || 60000);
if (process.env.DPLATFORM === 'win') {
options.shell = process.env.DOMOTZ_ROOT_DIR + '\\lib\\portable-git\\bin\\domotz_bash.exe';
}
return procUtils.spawn(ssh, args, options);
}
return {
// For testing
_describe: describe,
_getCleanedEnv: getCleanEnv,
_getOpt: getOpt,
_getArgsForAuthentication: getArgsForAuthentication,
// -----------
exec: exec
};
};