remoteshell
This commit is contained in:
parent
37fa4cf020
commit
97614a9079
78
kit/package-lock.json
generated
78
kit/package-lock.json
generated
@ -18,6 +18,7 @@
|
||||
"node-forge": "^1.3.1",
|
||||
"physical-cpu-count": "^2.0.0",
|
||||
"sm-crypto": "^0.3.13",
|
||||
"ssh2": "^1.16.0",
|
||||
"systeminformation": "^5.30.6",
|
||||
"webdav": "^5.3.1"
|
||||
},
|
||||
@ -56,6 +57,15 @@
|
||||
"resolved": "https://registry.npmmirror.com/applescript/-/applescript-1.0.0.tgz",
|
||||
"integrity": "sha512-yvtNHdWvtbYEiIazXAdp/NY+BBb65/DAseqlNiJQjOx9DynuzOYDbVLBJvuc0ve0VL9x6B3OHF6eH52y9hCBtQ=="
|
||||
},
|
||||
"node_modules/asn1": {
|
||||
"version": "0.2.6",
|
||||
"resolved": "https://registry.npmmirror.com/asn1/-/asn1-0.2.6.tgz",
|
||||
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": "~2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/auto-launch": {
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/auto-launch/-/auto-launch-5.0.6.tgz",
|
||||
@ -81,6 +91,15 @@
|
||||
"resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz",
|
||||
"integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg=="
|
||||
},
|
||||
"node_modules/bcrypt-pbkdf": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
||||
"integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"tweetnacl": "^0.14.3"
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
@ -97,6 +116,15 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/buildcheck": {
|
||||
"version": "0.0.7",
|
||||
"resolved": "https://registry.npmmirror.com/buildcheck/-/buildcheck-0.0.7.tgz",
|
||||
"integrity": "sha512-lHblz4ahamxpTmnsk+MNTRWsjYKv965MwOrSJyeD588rR3Jcu7swE+0wN5F+PbL5cjgu/9ObkhfzEPuofEMwLA==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/byte-length": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/byte-length/-/byte-length-1.0.2.tgz",
|
||||
@ -110,6 +138,20 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/cpu-features": {
|
||||
"version": "0.0.10",
|
||||
"resolved": "https://registry.npmmirror.com/cpu-features/-/cpu-features-0.0.10.tgz",
|
||||
"integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==",
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"buildcheck": "~0.0.6",
|
||||
"nan": "^2.19.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-port-killer": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/cross-port-killer/-/cross-port-killer-1.4.0.tgz",
|
||||
@ -417,6 +459,13 @@
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/nan": {
|
||||
"version": "2.27.0",
|
||||
"resolved": "https://registry.npmmirror.com/nan/-/nan-2.27.0.tgz",
|
||||
"integrity": "sha512-hC+0LidcL3XE4rp1C4H54KujgXKzbfyTngZTwBByQxsOxCEKZT0MPQ4hOKUH2jU1OYstqdDH4onyHPDzcV0XdQ==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/nested-property": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/nested-property/-/nested-property-4.0.0.tgz",
|
||||
@ -523,6 +572,12 @@
|
||||
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz",
|
||||
"integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA=="
|
||||
},
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/sm-crypto": {
|
||||
"version": "0.3.13",
|
||||
"resolved": "https://registry.npmjs.org/sm-crypto/-/sm-crypto-0.3.13.tgz",
|
||||
@ -531,6 +586,23 @@
|
||||
"jsbn": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ssh2": {
|
||||
"version": "1.17.0",
|
||||
"resolved": "https://registry.npmmirror.com/ssh2/-/ssh2-1.17.0.tgz",
|
||||
"integrity": "sha512-wPldCk3asibAjQ/kziWQQt1Wh3PgDFpC0XpwclzKcdT1vql6KeYxf5LIt4nlFkUeR8WuphYMKqUA56X4rjbfgQ==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"asn1": "^0.2.6",
|
||||
"bcrypt-pbkdf": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.16.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"cpu-features": "~0.0.10",
|
||||
"nan": "^2.23.0"
|
||||
}
|
||||
},
|
||||
"node_modules/streamroller": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz",
|
||||
@ -575,6 +647,12 @@
|
||||
"url": "https://www.buymeacoffee.com/systeminfo"
|
||||
}
|
||||
},
|
||||
"node_modules/tweetnacl": {
|
||||
"version": "0.14.5",
|
||||
"resolved": "https://registry.npmmirror.com/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
||||
"integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
|
||||
"license": "Unlicense"
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "5.26.5",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
"node-forge": "^1.3.1",
|
||||
"physical-cpu-count": "^2.0.0",
|
||||
"sm-crypto": "^0.3.13",
|
||||
"ssh2": "^1.16.0",
|
||||
"systeminformation": "^5.30.6",
|
||||
"webdav": "^5.3.1"
|
||||
}
|
||||
|
||||
@ -335,12 +335,155 @@ function scp(from, to, params = {}) {
|
||||
}));
|
||||
}
|
||||
|
||||
function loadSsh2Client() {
|
||||
try {
|
||||
return require("ssh2").Client;
|
||||
} catch (e) {
|
||||
throw new Error("remoteShell requires pure JS dependency ssh2. Please run npm install ssh2 in kit project.");
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeRemoteShellConfig(config = {}) {
|
||||
const host = config.host || config.hostname;
|
||||
if (!host) {
|
||||
throw new Error("remoteShell host is required");
|
||||
}
|
||||
|
||||
const username = config.username || config.user;
|
||||
if (!username) {
|
||||
throw new Error("remoteShell username is required");
|
||||
}
|
||||
|
||||
const password = resolveScpPassword(config);
|
||||
const privateKey = config.privateKey || config.keyText || (config.keyFile ? fs.readFileSync(path.resolve(config.keyFile), "utf-8") : undefined);
|
||||
const connectConfig = Object.assign({}, config, {
|
||||
host,
|
||||
username,
|
||||
port: config.port || 22,
|
||||
password,
|
||||
privateKey,
|
||||
passphrase: resolveValue(config.passphrase)
|
||||
});
|
||||
|
||||
delete connectConfig.user;
|
||||
delete connectConfig.hostname;
|
||||
delete connectConfig.pwd;
|
||||
delete connectConfig.plainPwd;
|
||||
delete connectConfig.plainPassword;
|
||||
delete connectConfig.plaintextPwd;
|
||||
delete connectConfig.plaintextPassword;
|
||||
delete connectConfig.keyText;
|
||||
delete connectConfig.keyFile;
|
||||
|
||||
return connectConfig;
|
||||
}
|
||||
|
||||
function remoteShell(config = {}) {
|
||||
const Client = loadSsh2Client();
|
||||
const connectConfig = normalizeRemoteShellConfig(config);
|
||||
let client = null;
|
||||
let connectPromise = null;
|
||||
|
||||
function connect() {
|
||||
if (client) {
|
||||
return Promise.resolve(client);
|
||||
}
|
||||
if (connectPromise) {
|
||||
return connectPromise;
|
||||
}
|
||||
|
||||
connectPromise = new Promise((resolve, reject) => {
|
||||
const conn = new Client();
|
||||
conn.once("ready", () => {
|
||||
client = conn;
|
||||
resolve(conn);
|
||||
});
|
||||
conn.once("error", (e) => {
|
||||
connectPromise = null;
|
||||
reject(e);
|
||||
});
|
||||
conn.once("close", () => {
|
||||
client = null;
|
||||
connectPromise = null;
|
||||
});
|
||||
conn.connect(connectConfig);
|
||||
});
|
||||
return connectPromise;
|
||||
}
|
||||
|
||||
async function exec(cmd, options = {}) {
|
||||
if (!cmd) {
|
||||
throw new Error("remoteShell exec cmd is required");
|
||||
}
|
||||
|
||||
const conn = await connect();
|
||||
return new Promise((resolve, reject) => {
|
||||
const execOptions = Object.assign({}, options);
|
||||
delete execOptions.stdio;
|
||||
delete execOptions.rejectOnError;
|
||||
conn.exec(cmd, execOptions, (err, stream) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
|
||||
let stdout = "";
|
||||
let stderr = "";
|
||||
stream.on("error", reject);
|
||||
stream.on("close", (code, signal) => {
|
||||
const result = { code, signal, stdout, stderr };
|
||||
if (options.rejectOnError === false || code === 0) {
|
||||
resolve(result);
|
||||
} else {
|
||||
const error = new Error(`Remote command failed with code ${code}: ${cmd}`);
|
||||
error.result = result;
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
stream.on("data", (data) => {
|
||||
const text = data.toString();
|
||||
stdout += text;
|
||||
if (options.stdio === "inherit") {
|
||||
process.stdout.write(text);
|
||||
}
|
||||
});
|
||||
stream.stderr.on("data", (data) => {
|
||||
const text = data.toString();
|
||||
stderr += text;
|
||||
if (options.stdio === "inherit") {
|
||||
process.stderr.write(text);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function execText(cmd, options = {}) {
|
||||
const result = await exec(cmd, options);
|
||||
return result.stdout;
|
||||
}
|
||||
|
||||
async function call(cmd, options = {}) {
|
||||
return exec(cmd, Object.assign({ stdio: "inherit" }, options));
|
||||
}
|
||||
|
||||
function close() {
|
||||
if (client) {
|
||||
client.end();
|
||||
}
|
||||
client = null;
|
||||
connectPromise = null;
|
||||
}
|
||||
|
||||
return { connect, exec, execText, call, close };
|
||||
}
|
||||
|
||||
function exit(code = 0) {
|
||||
process.exit(code);
|
||||
}
|
||||
|
||||
function isSecretLogKey(key) {
|
||||
return /^(pwd|password|plainPwd|plainPassword|plaintextPwd|plaintextPassword)$/i.test(key);
|
||||
return /^(pwd|password|plainPwd|plainPassword|plaintextPwd|plaintextPassword|privateKey|passphrase|keyText)$/i.test(key);
|
||||
}
|
||||
|
||||
function redactLogValue(value) {
|
||||
@ -403,6 +546,7 @@ module.exports = {
|
||||
readJson: withCallLog("readJson", readJson),
|
||||
writeJson: withCallLog("writeJson", writeJson),
|
||||
scp: withCallLog("scp", scp),
|
||||
remoteShell: withCallLog("remoteShell", remoteShell),
|
||||
exit: withCallLog("exit", exit),
|
||||
setVar: withCallLog("setVar", setVar),
|
||||
getVar: withCallLog("getVar", getVar),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user