ssh-client-wrapper 3.1.2 → 3.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -1
- package/lib/sshExec.js +14 -1
- package/lib/util.js +19 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -56,7 +56,7 @@ Creates a new SSH client instance.
|
|
|
56
56
|
* `host` (string): The hostname or IP address of the server.
|
|
57
57
|
* `user` (string): The username for authentication.
|
|
58
58
|
* `port` (number, optional): The port number. Defaults to 22.
|
|
59
|
-
* `password` (string | Function, optional): The password for authentication, or a function that returns the password.
|
|
59
|
+
* `password` (string | Function, optional): The password for authentication, or a function that returns the password. See [Wrong password](#wrong-password).
|
|
60
60
|
* `keyFile` (string, optional): The path to the private key file for key-based authentication.
|
|
61
61
|
* `passphrase` (string | Function, optional): The passphrase for the private key, or a function that returns it.
|
|
62
62
|
* `noStrictHostKeyChecking` (boolean, optional): If `true`, bypasses strict host key checking.
|
|
@@ -190,6 +190,15 @@ Checks if a connection to the remote host can be established.
|
|
|
190
190
|
|
|
191
191
|
* Returns: `Promise<boolean>` - Resolves with `true` on success.
|
|
192
192
|
|
|
193
|
+
### Wrong password
|
|
194
|
+
|
|
195
|
+
If the login is refused, the promise is rejected with an `Error` whose `message` is `Permission denied` and whose `code` is `AUTH_FAILED`. `host`, `user` and `port` tell which login failed.
|
|
196
|
+
|
|
197
|
+
* `password` / `passphrase` given as a **string**: ssh is asked to try only once (`-oNumberOfPasswordPrompts=1`), because sending the same wrong string again can not succeed (and may lock the account). The promise is rejected at once.
|
|
198
|
+
* `password` / `passphrase` given as a **function**: the function is called each time ssh asks (up to three times, the ssh default), so that it can ask the user again and return another value. If none of them is accepted the promise is rejected as above.
|
|
199
|
+
|
|
200
|
+
Whether to ask the user again after an `AUTH_FAILED` is up to the application.
|
|
201
|
+
|
|
193
202
|
### `.disconnect()`
|
|
194
203
|
Closes the master SSH connection to the remote host.
|
|
195
204
|
|
package/lib/sshExec.js
CHANGED
|
@@ -205,7 +205,12 @@ export async function connect(hostInfo, timeout = 60) {
|
|
|
205
205
|
done = true;
|
|
206
206
|
resolve();
|
|
207
207
|
} else if (/Permission denied \(/.test(output)) {
|
|
208
|
-
|
|
208
|
+
const err = new Error("Permission denied");
|
|
209
|
+
err.code = "AUTH_FAILED";
|
|
210
|
+
err.host = hostInfo.host;
|
|
211
|
+
err.user = hostInfo.user;
|
|
212
|
+
err.port = hostInfo.port;
|
|
213
|
+
reject(err);
|
|
209
214
|
} else if (/Could not resolve hostname/.test(output)) {
|
|
210
215
|
reject(new Error("Could not resolve hostname"));
|
|
211
216
|
} else if (/Bad port/.test(output)) {
|
|
@@ -251,6 +256,14 @@ export async function connect(hostInfo, timeout = 60) {
|
|
|
251
256
|
try {
|
|
252
257
|
await p;
|
|
253
258
|
debug(`connected to ${hostInfo.host}`);
|
|
259
|
+
} catch (e) {
|
|
260
|
+
//the pty of a failed connection must not be left behind. It keeps the process alive.
|
|
261
|
+
debug(`failed to connect to ${hostInfo.host}, kill master pty`);
|
|
262
|
+
if (hostInfo.masterPty) {
|
|
263
|
+
hostInfo.masterPty.kill();
|
|
264
|
+
hostInfo.masterPty = null;
|
|
265
|
+
}
|
|
266
|
+
throw e;
|
|
254
267
|
} finally {
|
|
255
268
|
if (ac !== null) {
|
|
256
269
|
ac.abort();
|
package/lib/util.js
CHANGED
|
@@ -155,6 +155,20 @@ export const sanityCheck = (hostInfo)=>{
|
|
|
155
155
|
return hostInfo;
|
|
156
156
|
};
|
|
157
157
|
|
|
158
|
+
/**
|
|
159
|
+
* check if the password and the passphrase can not change between the prompts of ssh
|
|
160
|
+
* @param {object} hostInfo - host information object defined in index.js
|
|
161
|
+
* @returns {boolean} - true if at least one of them is a string and none of them is a function
|
|
162
|
+
*/
|
|
163
|
+
function hasFixedSecret(hostInfo) {
|
|
164
|
+
const secrets = [hostInfo.password, hostInfo.passphrase];
|
|
165
|
+
return secrets.some((e)=>{
|
|
166
|
+
return typeof e === "string";
|
|
167
|
+
}) && !secrets.some((e)=>{
|
|
168
|
+
return typeof e === "function";
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
158
172
|
function getControlPersistDir(hostInfo) {
|
|
159
173
|
for (const candidate of [hostInfo.ControlPersistDir, process.env.SSH_CONTROL_PERSIST_DIR]) {
|
|
160
174
|
if (typeof candidate === "string") {
|
|
@@ -237,6 +251,11 @@ export const getSshOption = (hostInfo, withoutDestination)=>{
|
|
|
237
251
|
if (Number.isInteger(hostInfo.ConnectTimeout) && hostInfo.ConnectTimeout > 0) {
|
|
238
252
|
args.push(`-oConnectTimeout=${hostInfo.ConnectTimeout}`);
|
|
239
253
|
}
|
|
254
|
+
//ssh asks three times by default. If the secret is a fixed string, asking again with the same string is
|
|
255
|
+
//of no use. A function can give another secret each time, so it keeps the default.
|
|
256
|
+
if (hasFixedSecret(hostInfo)) {
|
|
257
|
+
args.push("-oNumberOfPasswordPrompts=1");
|
|
258
|
+
}
|
|
240
259
|
if (Array.isArray(hostInfo.sshOpt)) {
|
|
241
260
|
args.push(...hostInfo.sshOpt.filter((e)=>{
|
|
242
261
|
return typeof e === "string";
|