From 3aa9aad85472c8e896b1185e81a08600fc5621e4 Mon Sep 17 00:00:00 2001 From: Eugene Date: Tue, 20 Aug 2024 23:55:56 +0200 Subject: [PATCH] agent forwarding --- app/package.json | 2 +- app/yarn.lock | 8 ++-- tabby-ssh/src/session/ssh.ts | 87 +++++++++++++++++++++++------------- 3 files changed, 60 insertions(+), 37 deletions(-) diff --git a/app/package.json b/app/package.json index 1aac9cab..fb2b93fc 100644 --- a/app/package.json +++ b/app/package.json @@ -30,7 +30,7 @@ "native-process-working-directory": "^1.0.2", "npm": "6", "rxjs": "^7.5.7", - "russh": "^0.0.1-alpha.11", + "russh": "^0.0.1-alpha.12", "source-map-support": "^0.5.20", "v8-compile-cache": "^2.3.0", "yargs": "^17.7.2" diff --git a/app/yarn.lock b/app/yarn.lock index a7182591..d489bf22 100644 --- a/app/yarn.lock +++ b/app/yarn.lock @@ -3613,10 +3613,10 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -russh@^0.0.1-alpha.11: - version "0.0.1-alpha.11" - resolved "https://registry.yarnpkg.com/russh/-/russh-0.0.1-alpha.11.tgz#6b5fe686c74aecabd77d22d456dd3092729914bc" - integrity sha512-za66Eo723+xvAxGdNMkMNixDY2R3k4I6/aHgr+Gpbnyih20sRrr2Wyq9/9QQnvn24AZ5vC1BTZLyJbfz7+D4Kg== +russh@^0.0.1-alpha.12: + version "0.0.1-alpha.12" + resolved "https://registry.yarnpkg.com/russh/-/russh-0.0.1-alpha.12.tgz#fc8d54870fef24ce8b5c33fb372199027c6572a2" + integrity sha512-nxfOSaa5V6+lutNwhLQuemIeAgzK8TXwOXcoEsvmd90dQ3TyrTUoNLOifbv5x0SYCg9dPRUP3OSwqnGPBhchug== dependencies: "@napi-rs/cli" "^2.18.3" diff --git a/tabby-ssh/src/session/ssh.ts b/tabby-ssh/src/session/ssh.ts index bd4952bc..eb64f381 100644 --- a/tabby-ssh/src/session/ssh.ts +++ b/tabby-ssh/src/session/ssh.ts @@ -157,40 +157,15 @@ export class SSHSession { } } } + if (!this.profile.options.auth || this.profile.options.auth === 'agent') { - if (this.hostApp.platform === Platform.Windows) { - if (this.config.store.ssh.agentType === 'auto') { - if (await fs.exists(WINDOWS_OPENSSH_AGENT_PIPE)) { - this.remainingAuthMethods.push({ - type: 'agent', - kind: 'named-pipe', - path: WINDOWS_OPENSSH_AGENT_PIPE, - }) - } else if (russh.isPageantRunning()) { - this.remainingAuthMethods.push({ - type: 'agent', - kind: 'pageant', - }) - } else { - this.emitServiceMessage(colors.bgYellow.yellow.black(' ! ') + ` Agent auth selected, but no running Agent process is found`) - } - } else if (this.config.store.ssh.agentType === 'pageant') { - this.remainingAuthMethods.push({ - type: 'agent', - kind: 'pageant', - }) - } else { - this.remainingAuthMethods.push({ - type: 'agent', - kind: 'named-pipe', - path: this.config.store.ssh.agentPath || WINDOWS_OPENSSH_AGENT_PIPE, - }) - } + const spec = await this.getAgentConnectionSpec() + if (!spec) { + this.emitServiceMessage(colors.bgYellow.yellow.black(' ! ') + ` Agent auth selected, but no running Agent process is found`) } else { this.remainingAuthMethods.push({ type: 'agent', - kind: 'unix-socket', - path: process.env.SSH_AUTH_SOCK!, + ...spec, }) } } @@ -203,6 +178,40 @@ export class SSHSession { this.remainingAuthMethods.push({ type: 'hostbased' }) } + private async getAgentConnectionSpec (): Promise { + if (this.hostApp.platform === Platform.Windows) { + if (this.config.store.ssh.agentType === 'auto') { + if (await fs.exists(WINDOWS_OPENSSH_AGENT_PIPE)) { + return { + kind: 'named-pipe', + path: WINDOWS_OPENSSH_AGENT_PIPE, + } + } else if (russh.isPageantRunning()) { + return { + kind: 'pageant', + } + } else { + this.emitServiceMessage(colors.bgYellow.yellow.black(' ! ') + ` Agent auth selected, but no running Agent process is found`) + } + } else if (this.config.store.ssh.agentType === 'pageant') { + return { + kind: 'pageant', + } + } else { + return { + kind: 'named-pipe', + path: this.config.store.ssh.agentPath || WINDOWS_OPENSSH_AGENT_PIPE, + } + } + } else { + return { + kind: 'unix-socket', + path: process.env.SSH_AUTH_SOCK!, + } + } + return null + } + async openSFTP (): Promise { if (!(this.ssh instanceof russh.AuthenticatedSSHClient)) { throw new Error('Cannot open SFTP session before auth') @@ -334,8 +343,6 @@ export class SSHSession { // ssh.connect({ - // agent: this.agentPath, - // agentForward: this.profile.options.agentForward && !!this.agentPath, // keepaliveInterval: this.profile.options.keepaliveInterval ?? 15000, // keepaliveCountMax: this.profile.options.keepaliveCountMax, // readyTimeout: this.profile.options.readyTimeout, @@ -408,6 +415,19 @@ export class SSHSession { event.channel.close() } }) + + this.ssh.agentChannelOpen$.subscribe(async channel => { + const spec = await this.getAgentConnectionSpec() + if (!spec) { + await channel.close() + return + } + + const agent = await russh.SSHAgentStream.connect(spec) + channel.data$.subscribe(data => agent.write(data)) + agent.data$.subscribe(data => channel.write(data), undefined, () => channel.close()) + channel.closed$.subscribe(() => agent.close()) + }) } private async verifyHostKey (key: russh.SshPublicKey): Promise { @@ -668,6 +688,9 @@ export class SSHSession { screenNumber: 0, }) } + if (this.profile.options.agentForward) { + await ch.requestAgentForwarding() + } await ch.requestShell() return ch }