diff --git a/src/backend/ssh/terminal.ts b/src/backend/ssh/terminal.ts index a6a42466..c93edcea 100644 --- a/src/backend/ssh/terminal.ts +++ b/src/backend/ssh/terminal.ts @@ -1,5 +1,15 @@ import { WebSocketServer, WebSocket, type RawData } from "ws"; -import { Client, type ClientChannel, type PseudoTtyOptions } from "ssh2"; +import { + Client, + type ClientChannel, + type PseudoTtyOptions, + BaseAgent, + utils as ssh2Utils, + type ParsedKey, + type SignCallback, + type SigningRequestOptions, + type IdentityCallback, +} from "ssh2"; import net from "net"; import dgram from "dgram"; import { SSH_ALGORITHMS } from "../utils/ssh-algorithms.js"; @@ -25,6 +35,42 @@ import { queryNewestTmuxSession, } from "./tmux-helper.js"; +class MemoryAgent extends BaseAgent { + private key: ParsedKey; + + constructor(key: ParsedKey) { + super(); + this.key = key; + } + + getIdentities(cb: IdentityCallback): void { + cb(null, [this.key]); + } + + sign( + pubKey: ParsedKey | Buffer | string, + data: Buffer, + optionsOrCb: SigningRequestOptions | SignCallback, + cb?: SignCallback, + ): void { + const callback = typeof optionsOrCb === "function" ? optionsOrCb : cb!; + const options = + typeof optionsOrCb === "function" ? {} : optionsOrCb; + try { + const algo = + options.hash === "sha256" + ? "rsa-sha2-256" + : options.hash === "sha512" + ? "rsa-sha2-512" + : undefined; + const signature = this.key.sign(data, algo); + callback(null, signature); + } catch (err) { + callback(err instanceof Error ? err : new Error(String(err))); + } + } +} + async function performPortKnocking( host: string, sequence: Array<{ port: number; protocol?: string; delay?: number }>, @@ -2313,6 +2359,28 @@ wss.on("connection", async (ws: WebSocket, req) => { return; } + if ( + hostConfig.terminalConfig?.agentForwarding && + connectConfig.privateKey + ) { + try { + const parsed = ssh2Utils.parseKey( + connectConfig.privateKey as Buffer, + connectConfig.passphrase as string | undefined, + ); + if (parsed && !(parsed instanceof Error)) { + connectConfig.agent = new MemoryAgent(parsed); + connectConfig.agentForward = true; + sendLog("auth", "info", "SSH agent forwarding enabled"); + } + } catch { + sshLogger.warn("Failed to set up agent forwarding", { + operation: "agent_forward_setup", + hostId: id, + }); + } + } + if ( hostConfig.portKnockSequence && hostConfig.portKnockSequence.length > 0