diff --git a/package.json b/package.json index 8bc123d..61d0bfe 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "ava": "^5.3.1", "eslint-config-steelbrain": "^11.0.0", "node-pty": "^1.0.0", - "ssh2": "^1.14.0", + "ssh2": "github:m-team-kit/ssh2#add-certificate-support", "ssh2-streams": "^0.4.10", "ts-node": "^10.9.1", "typescript": "^5.1.6" @@ -60,7 +60,7 @@ "sb-promise-queue": "^2.1.0", "sb-scandir": "^3.1.0", "shell-escape": "^0.2.0", - "ssh2": "^1.14.0" + "ssh2": "github:m-team-kit/ssh2#add-certificate-support" }, "ava": { "files": [ diff --git a/src/index.ts b/src/index.ts index 96dc3bc..bccbe32 100644 --- a/src/index.ts +++ b/src/index.ts @@ -28,6 +28,8 @@ export type Config = ConnectConfig & { password?: string privateKey?: string privateKeyPath?: string + certificate?: string + certificatePath?: string tryKeyboard?: boolean onKeyboardInteractive?: ( name: string, @@ -230,6 +232,36 @@ export class NodeSSH { invariant(typeof config.password === 'string', 'config.password must be a valid string') } + // SSH certificate authentication (OpenSSH PROTOCOL.certkeys) + // Certificates are used with a private key for CA-signed authentication + if (config.certificate != null || config.certificatePath != null) { + if (config.certificate != null) { + invariant(typeof config.certificate === 'string', 'config.certificate must be a valid string') + invariant( + config.certificatePath == null, + 'config.certificatePath must not be specified when config.certificate is specified', + ) + } else if (config.certificatePath != null) { + invariant(typeof config.certificatePath === 'string', 'config.certificatePath must be a valid string') + invariant( + config.certificate == null, + 'config.certificate must not be specified when config.certificatePath is specified', + ) + } + + if (config.certificatePath != null) { + // Must be an fs path + try { + config.certificate = await readFile(config.certificatePath) + } catch (err) { + if (err != null && err.code === 'ENOENT') { + throw new AssertionError({ message: 'config.certificatePath does not exist at given fs path' }) + } + throw err + } + } + } + if (config.tryKeyboard != null) { invariant(typeof config.tryKeyboard === 'boolean', 'config.tryKeyboard must be a valid boolean') }