From da21895e4064212b77737cfee4e63095fce7e2d6 Mon Sep 17 00:00:00 2001 From: Eugene Pankov Date: Wed, 29 Jul 2020 19:18:57 +0200 Subject: [PATCH] more explicit SSH auth options and agent forwarding - fixes #2284, fixes #2511, fixes #2717, fixes #2184 --- terminus-ssh/src/api.ts | 2 +- .../editConnectionModal.component.pug | 35 ++++++++++--- .../editConnectionModal.component.ts | 1 + .../src/components/sshTab.component.ts | 3 ++ terminus-ssh/src/services/ssh.service.ts | 52 +++++++++++++++++-- 5 files changed, 81 insertions(+), 12 deletions(-) diff --git a/terminus-ssh/src/api.ts b/terminus-ssh/src/api.ts index 9f39f794..8fd2069d 100644 --- a/terminus-ssh/src/api.ts +++ b/terminus-ssh/src/api.ts @@ -24,6 +24,7 @@ export interface SSHConnection { host: string port: number user: string + auth?: null|'password'|'publicKey'|'agent'|'keyboardInteractive' password?: string privateKey?: string group: string | null @@ -36,7 +37,6 @@ export interface SSHConnection { skipBanner?: boolean disableDynamicTitle?: boolean jumpHost?: string - agentForward?: boolean algorithms?: {[t: string]: string[]} } diff --git a/terminus-ssh/src/components/editConnectionModal.component.pug b/terminus-ssh/src/components/editConnectionModal.component.pug index cc993f78..9470cca9 100644 --- a/terminus-ssh/src/components/editConnectionModal.component.pug +++ b/terminus-ssh/src/components/editConnectionModal.component.pug @@ -43,6 +43,34 @@ ) .form-line + .header + .title Authentication + .btn-group.w-100( + [(ngModel)]='connection.auth', + ngbRadioGroup + ) + label.btn.btn-outline-secondary(ngbButtonLabel) + input(type='radio', ngbButton, [value]='null') + i.far.fa-lightbulb + .m-0 Auto + label.btn.btn-outline-secondary(ngbButtonLabel) + input(type='radio', ngbButton, [value]='"password"') + i.fas.fa-font + .m-0 Password + label.btn.btn-outline-secondary(ngbButtonLabel) + input(type='radio', ngbButton, [value]='"publicKey"') + i.fas.fa-key + .m-0 Key + label.btn.btn-outline-secondary(ngbButtonLabel) + input(type='radio', ngbButton, [value]='"agent"') + i.fas.fa-user-secret + .m-0 Agent + label.btn.btn-outline-secondary(ngbButtonLabel) + input(type='radio', ngbButton, [value]='"keyboardInteractive"') + i.far.fa-keyboard + .m-0 Interactive + + .form-line(*ngIf='!connection.auth || connection.auth === "password"') .header .title Password .description(*ngIf='!hasSavedPassword') Save a password in the keychain @@ -54,7 +82,7 @@ i.fas.fa-trash-alt span Forget - .form-line + .form-line(*ngIf='!connection.auth || connection.auth === "publicKey"') .header .title Private key .description Path to the private key file @@ -83,11 +111,6 @@ .title X11 forwarding toggle([(ngModel)]='connection.x11') - .form-line - .header - .title Allow Agent Forwarding - toggle([(ngModel)]='connection.agentForward') - .form-line .header .title Tab color diff --git a/terminus-ssh/src/components/editConnectionModal.component.ts b/terminus-ssh/src/components/editConnectionModal.component.ts index 49d8be3c..5ad18cc2 100644 --- a/terminus-ssh/src/components/editConnectionModal.component.ts +++ b/terminus-ssh/src/components/editConnectionModal.component.ts @@ -49,6 +49,7 @@ export class EditConnectionModalComponent { this.hasSavedPassword = !!await this.passwordStorage.loadPassword(this.connection) this.connection.algorithms = this.connection.algorithms || {} this.connection.scripts = this.connection.scripts || [] + this.connection.auth = this.connection.auth || null for (const k of Object.values(SSHAlgorithmType)) { if (!this.connection.algorithms[k]) { diff --git a/terminus-ssh/src/components/sshTab.component.ts b/terminus-ssh/src/components/sshTab.component.ts index bae253c1..67aa8780 100644 --- a/terminus-ssh/src/components/sshTab.component.ts +++ b/terminus-ssh/src/components/sshTab.component.ts @@ -160,6 +160,9 @@ export class SSHTabComponent extends BaseTerminalTabComponent { } async canClose (): Promise { + if (!this.session?.open) { + return true + } return (await this.electron.showMessageBox( this.hostApp.getWindow(), { diff --git a/terminus-ssh/src/services/ssh.service.ts b/terminus-ssh/src/services/ssh.service.ts index 5748e6e9..28fcd8ca 100644 --- a/terminus-ssh/src/services/ssh.service.ts +++ b/terminus-ssh/src/services/ssh.service.ts @@ -165,8 +165,13 @@ export class SSHService { const modal = this.ngbModal.open(PromptModalComponent) modal.componentInstance.prompt = prompt.prompt modal.componentInstance.password = !prompt.echo - const result = await modal.result - results.push(result ? result.value : '') + + try { + const result = await modal.result + results.push(result ? result.value : '') + } catch { + results.push('') + } } finish(results) })) @@ -194,6 +199,29 @@ export class SSHService { agent = process.env.SSH_AUTH_SOCK as string } + const authMethodsLeft = ['none'] + if (!session.connection.auth || session.connection.auth === 'password') { + authMethodsLeft.push('password') + } + if (!session.connection.auth || session.connection.auth === 'publicKey') { + if (!privateKey) { + log('\r\nPrivate key auth selected, but no key is loaded\r\n') + } else { + authMethodsLeft.push('publickey') + } + } + if (!session.connection.auth || session.connection.auth === 'agent') { + if (!agent) { + log('\r\nAgent auth selected, but no running agent is detected\r\n') + } else { + authMethodsLeft.push('agent') + } + } + if (!session.connection.auth || session.connection.auth === 'keyboardInteractive') { + authMethodsLeft.push('keyboard-interactive') + } + authMethodsLeft.push('hostbased') + try { ssh.connect({ host: session.connection.host, @@ -202,8 +230,8 @@ export class SSHService { password: session.connection.privateKey ? undefined : '', privateKey: privateKey || undefined, tryKeyboard: true, - agent: session.connection.agentForward && agent || undefined, - agentForward: session.connection.agentForward && !!agent, + agent: agent || undefined, + agentForward: (!session.connection.auth || session.connection.auth === 'agent') && !!agent, keepaliveInterval: session.connection.keepaliveInterval, keepaliveCountMax: session.connection.keepaliveCountMax, readyTimeout: session.connection.readyTimeout, @@ -215,7 +243,21 @@ export class SSHService { hostHash: 'sha256' as any, algorithms: session.connection.algorithms, sock: session.jumpStream, - }) + authHandler: methodsLeft => { + while (true) { + let method = authMethodsLeft.shift() + if (!method) { + return false + } + if (methodsLeft && !methodsLeft.includes(method) && method !== 'agent') { + // Agent can still be used even if not in methodsLeft + this.logger.info('Server does not support auth method', method) + continue + } + return method + } + }, + } as any) } catch (e) { this.toastr.error(e.message) reject(e)