use node-sshpk for better key parsing (fixes #1956, fixes #1612, fixes #1191, fixes #1788, fixes #1300)

This commit is contained in:
Eugene Pankov 2020-01-03 18:40:43 +01:00
parent b5a985b8a3
commit 280c421ae4
3 changed files with 78 additions and 23 deletions

View file

@ -21,6 +21,7 @@
"@types/ssh2": "^0.5.35",
"ssh2": "^0.8.2",
"ssh2-streams": "^0.4.2",
"sshpk": "^1.16.1",
"terminus-terminal": "^1.0.98-nightly.0"
},
"peerDependencies": {

View file

@ -3,6 +3,7 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { Client } from 'ssh2'
import * as fs from 'mz/fs'
import * as path from 'path'
import * as sshpk from 'sshpk'
import { ToastrService } from 'ngx-toastr'
import { AppService, HostAppService, Platform, Logger, LogService } from 'terminus-core'
import { SSHConnection, SSHSession } from '../api'
@ -50,7 +51,6 @@ export class SSHService {
async connectSession (session: SSHSession, logCallback?: (s: any) => void): Promise<void> {
let privateKey: string|null = null
let privateKeyPassphrase: string|null = null
let privateKeyPath = session.connection.privateKey
if (!logCallback) {
@ -71,6 +71,7 @@ export class SSHService {
}
if (privateKeyPath) {
log(`Loading private key from ${privateKeyPath}`)
try {
privateKey = (await fs.readFile(privateKeyPath)).toString()
} catch (error) {
@ -79,24 +80,31 @@ export class SSHService {
}
if (privateKey) {
log(`Loading private key from ${privateKeyPath}`)
let parsedKey: any = null
try {
parsedKey = sshpk.parsePrivateKey(privateKey, 'auto')
} catch (e) {
if (e instanceof sshpk.KeyEncryptedError) {
const modal = this.ngbModal.open(PromptModalComponent)
log('Key requires passphrase')
modal.componentInstance.prompt = 'Private key passphrase'
modal.componentInstance.password = true
let passphrase = ''
try {
const result = await modal.result
passphrase = result?.value
} catch (e) { }
parsedKey = sshpk.parsePrivateKey(
privateKey,
'auto',
{ passphrase: passphrase }
)
} else {
throw e
}
}
let encrypted = privateKey.includes('ENCRYPTED')
if (privateKeyPath.toLowerCase().endsWith('.ppk')) {
encrypted = encrypted || privateKey.includes('Encryption:') && !privateKey.includes('Encryption: none')
}
if (encrypted) {
const modal = this.ngbModal.open(PromptModalComponent)
log('Key requires passphrase')
modal.componentInstance.prompt = 'Private key passphrase'
modal.componentInstance.password = true
try {
const result = await modal.result
if (result) {
privateKeyPassphrase = result.value
}
} catch (e) { }
}
privateKey = parsedKey!.toString('ssh')
}
}
@ -167,7 +175,6 @@ export class SSHService {
username: session.connection.user,
password: session.connection.privateKey ? undefined : '',
privateKey: privateKey || undefined,
passphrase: privateKeyPassphrase || undefined,
tryKeyboard: true,
agent: agent || undefined,
agentForward: !!agent,

View file

@ -27,21 +27,53 @@
"@types/node" "*"
"@types/ssh2-streams" "*"
asn1@~0.2.0:
asn1@~0.2.0, asn1@~0.2.3:
version "0.2.4"
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
dependencies:
safer-buffer "~2.1.0"
bcrypt-pbkdf@^1.0.2:
assert-plus@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
bcrypt-pbkdf@^1.0.0, bcrypt-pbkdf@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=
dependencies:
tweetnacl "^0.14.3"
safer-buffer@~2.1.0:
dashdash@^1.12.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=
dependencies:
assert-plus "^1.0.0"
ecc-jsbn@~0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=
dependencies:
jsbn "~0.1.0"
safer-buffer "^2.1.0"
getpass@^0.1.1:
version "0.1.7"
resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=
dependencies:
assert-plus "^1.0.0"
jsbn@~0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
@ -62,6 +94,21 @@ ssh2@^0.8.2:
dependencies:
ssh2-streams "~0.4.8"
sshpk@^1.16.1:
version "1.16.1"
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==
dependencies:
asn1 "~0.2.3"
assert-plus "^1.0.0"
bcrypt-pbkdf "^1.0.0"
dashdash "^1.12.0"
ecc-jsbn "~0.1.1"
getpass "^0.1.1"
jsbn "~0.1.0"
safer-buffer "^2.0.2"
tweetnacl "~0.14.0"
streamsearch@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a"
@ -72,7 +119,7 @@ terminus-terminal@^1.0.98-nightly.0:
resolved "https://registry.yarnpkg.com/terminus-terminal/-/terminus-terminal-1.0.98-nightly.0.tgz#10df71b0a81adf76a076fb21a91c859dd2f8bef7"
integrity sha512-JLxkeoQkORcfe6cRW6BJF5ZPSbvKA8IWUAb7fzBONVmNfRKj2Mq/uYPy76UXsdmb9F1n+rYIg+DShNp57asMKA==
tweetnacl@^0.14.3:
tweetnacl@^0.14.3, tweetnacl@~0.14.0:
version "0.14.5"
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=