From fdca83ff278669f428f06a3ab3414c0b5bcdb8fa Mon Sep 17 00:00:00 2001 From: Eugene Pankov Date: Tue, 22 Feb 2022 22:03:52 +0100 Subject: [PATCH] ssh: try other OpenSSH key types besides rsa --- tabby-electron/src/index.ts | 5 ++- tabby-electron/src/sshImporters.ts | 24 +++++++++++- tabby-ssh/src/api/importer.ts | 4 ++ .../sshProfileSettings.component.ts | 1 + tabby-ssh/src/session/ssh.ts | 38 ++++++++----------- 5 files changed, 47 insertions(+), 25 deletions(-) diff --git a/tabby-electron/src/index.ts b/tabby-electron/src/index.ts index 733e4006..09bddc44 100644 --- a/tabby-electron/src/index.ts +++ b/tabby-electron/src/index.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core' import { PlatformService, LogService, UpdaterService, DockingService, HostAppService, ThemesService, Platform, AppService, ConfigService, WIN_BUILD_FLUENT_BG_SUPPORTED, isWindowsBuild, HostWindowService, HotkeyProvider, ConfigProvider, FileProvider } from 'tabby-core' import { TerminalColorSchemeProvider } from 'tabby-terminal' -import { SFTPContextMenuItemProvider, SSHProfileImporter } from 'tabby-ssh' +import { SFTPContextMenuItemProvider, SSHProfileImporter, AutoPrivateKeyLocator } from 'tabby-ssh' import { auditTime } from 'rxjs' import { HyperColorSchemes } from './colorSchemes' @@ -17,7 +17,7 @@ import { ElectronService } from './services/electron.service' import { ElectronHotkeyProvider } from './hotkeys' import { ElectronConfigProvider } from './config' import { EditSFTPContextMenu } from './sftpContextMenu' -import { OpenSSHImporter, StaticFileImporter } from './sshImporters' +import { OpenSSHImporter, PrivateKeyLocator, StaticFileImporter } from './sshImporters' @NgModule({ providers: [ @@ -34,6 +34,7 @@ import { OpenSSHImporter, StaticFileImporter } from './sshImporters' { provide: SFTPContextMenuItemProvider, useClass: EditSFTPContextMenu, multi: true }, { provide: SSHProfileImporter, useExisting: OpenSSHImporter, multi: true }, { provide: SSHProfileImporter, useExisting: StaticFileImporter, multi: true }, + { provide: AutoPrivateKeyLocator, useExisting: PrivateKeyLocator, multi: true }, ], }) export default class ElectronModule { diff --git a/tabby-electron/src/sshImporters.ts b/tabby-electron/src/sshImporters.ts index e8eb3a57..457bd727 100644 --- a/tabby-electron/src/sshImporters.ts +++ b/tabby-electron/src/sshImporters.ts @@ -5,7 +5,7 @@ import slugify from 'slugify' import * as yaml from 'js-yaml' import { Injectable } from '@angular/core' import { PartialProfile } from 'tabby-core' -import { SSHProfileImporter, PortForwardType, SSHProfile, SSHProfileOptions } from 'tabby-ssh' +import { SSHProfileImporter, PortForwardType, SSHProfile, SSHProfileOptions, AutoPrivateKeyLocator } from 'tabby-ssh' import { ElectronService } from './services/electron.service' @@ -163,3 +163,25 @@ export class StaticFileImporter extends SSHProfileImporter { })) } } + + +@Injectable({ providedIn: 'root' }) +export class PrivateKeyLocator extends AutoPrivateKeyLocator { + async getKeys (): Promise<[string, Buffer][]> { + const results: [string, Buffer][] = [] + const keysPath = path.join(process.env.HOME!, '.ssh') + if (!fsSync.existsSync(keysPath)) { + return results + } + for (const file of await fs.readdir(keysPath)) { + if (/^id_[\w\d]+$/.test(file)) { + const privateKeyContents = await fs.readFile( + path.join(keysPath, file), + { encoding: null } + ) + results.push([file, privateKeyContents]) + } + } + return results + } +} diff --git a/tabby-ssh/src/api/importer.ts b/tabby-ssh/src/api/importer.ts index 4b7c6d36..941c8d57 100644 --- a/tabby-ssh/src/api/importer.ts +++ b/tabby-ssh/src/api/importer.ts @@ -4,3 +4,7 @@ import { SSHProfile } from './interfaces' export abstract class SSHProfileImporter { abstract getProfiles (): Promise[]> } + +export abstract class AutoPrivateKeyLocator { + abstract getKeys (): Promise<[string, Buffer][]> +} diff --git a/tabby-ssh/src/components/sshProfileSettings.component.ts b/tabby-ssh/src/components/sshProfileSettings.component.ts index 8100a00f..5d3dc35e 100644 --- a/tabby-ssh/src/components/sshProfileSettings.component.ts +++ b/tabby-ssh/src/components/sshProfileSettings.component.ts @@ -134,6 +134,7 @@ export class SSHProfileSettingsComponent { proxyCommand: 'Proxy command', jumpHost: 'Jump host', socksProxy: 'SOCKS proxy', + httpProxy: 'HTTP proxy', }[this.connectionMode] } } diff --git a/tabby-ssh/src/session/ssh.ts b/tabby-ssh/src/session/ssh.ts index 33789177..26a2c13b 100644 --- a/tabby-ssh/src/session/ssh.ts +++ b/tabby-ssh/src/session/ssh.ts @@ -1,6 +1,5 @@ import * as fs from 'mz/fs' import * as crypto from 'crypto' -import * as path from 'path' // eslint-disable-next-line @typescript-eslint/no-duplicate-imports, no-duplicate-imports import * as sshpk from 'sshpk' import colors from 'ansi-colors' @@ -17,7 +16,7 @@ import { PasswordStorageService } from '../services/passwordStorage.service' import { SSHKnownHostsService } from '../services/sshKnownHosts.service' import { promisify } from 'util' import { SFTPSession } from './sftp' -import { ALGORITHM_BLACKLIST, SSHAlgorithmType, PortForwardType, SSHProfile, SSHProxyStream } from '../api' +import { ALGORITHM_BLACKLIST, SSHAlgorithmType, PortForwardType, SSHProfile, SSHProxyStream, AutoPrivateKeyLocator } from '../api' import { ForwardedPort } from './forwards' import { X11Socket } from './x11' @@ -93,6 +92,7 @@ export class SSHSession { private config: ConfigService private translate: TranslateService private knownHosts: SSHKnownHostsService + private privateKeyImporters: AutoPrivateKeyLocator[] constructor ( private injector: Injector, @@ -110,6 +110,7 @@ export class SSHSession { this.config = injector.get(ConfigService) this.translate = injector.get(TranslateService) this.knownHosts = injector.get(SSHKnownHostsService) + this.privateKeyImporters = injector.get(AutoPrivateKeyLocator, []) this.willDestroy$.subscribe(() => { for (const port of this.forwardedPorts) { @@ -155,10 +156,15 @@ export class SSHSession { } } } else { - this.remainingAuthMethods.push({ - type: 'publickey', - name: 'auto', - }) + for (const importer of this.privateKeyImporters) { + for (const [name, contents] of await importer.getKeys()) { + this.remainingAuthMethods.push({ + type: 'publickey', + name, + contents, + }) + } + } } } if (!this.profile.options.auth || this.profile.options.auth === 'agent') { @@ -497,9 +503,9 @@ export class SSHSession { continue } } - if (method.type === 'publickey') { + if (method.type === 'publickey' && method.contents) { try { - const key = await this.loadPrivateKey(method.contents) + const key = await this.loadPrivateKey(method.name!, method.contents) return { type: 'publickey', username: this.authUsername, @@ -599,20 +605,8 @@ export class SSHSession { }) } - async loadPrivateKey (privateKeyContents?: Buffer): Promise { - if (!privateKeyContents) { - const userKeyPath = path.join(process.env.HOME!, '.ssh', 'id_rsa') - if (await fs.exists(userKeyPath)) { - this.emitServiceMessage('Using user\'s default private key') - privateKeyContents = await fs.readFile(userKeyPath, { encoding: null }) - } - } - - if (!privateKeyContents) { - return null - } - - this.emitServiceMessage('Loading private key') + async loadPrivateKey (name: string, privateKeyContents: Buffer): Promise { + this.emitServiceMessage(`Loading private key: ${name}`) try { const parsedKey = await this.parsePrivateKey(privateKeyContents.toString()) this.activePrivateKey = parsedKey.toString('openssh')