From 8743e6daa2b20984935d53f7ee1251592da451c9 Mon Sep 17 00:00:00 2001 From: ZacharyZcR Date: Tue, 28 Jul 2026 01:48:31 +0800 Subject: [PATCH] fix: support Tailscale auth in tmux monitor (#1113) --- src/backend/hosts/tmux/auth-utils.ts | 11 ++++++++ src/backend/hosts/tmux/index.ts | 6 +++-- .../tests/hosts/tmux/auth-utils.test.ts | 25 +++++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 src/backend/hosts/tmux/auth-utils.ts create mode 100644 src/backend/tests/hosts/tmux/auth-utils.test.ts diff --git a/src/backend/hosts/tmux/auth-utils.ts b/src/backend/hosts/tmux/auth-utils.ts new file mode 100644 index 00000000..726b1f8d --- /dev/null +++ b/src/backend/hosts/tmux/auth-utils.ts @@ -0,0 +1,11 @@ +import type { SSHHost } from "../../../types/index.js"; + +export function getTmuxAuthBehavior(authType: SSHHost["authType"]): { + credentialless: boolean; + tryKeyboard: boolean; +} { + return { + credentialless: authType === "none" || authType === "tailscale", + tryKeyboard: authType !== "tailscale", + }; +} diff --git a/src/backend/hosts/tmux/index.ts b/src/backend/hosts/tmux/index.ts index 1755c5cb..c4446105 100644 --- a/src/backend/hosts/tmux/index.ts +++ b/src/backend/hosts/tmux/index.ts @@ -40,6 +40,7 @@ import { type PaneMetrics, } from "./monitor-helpers.js"; import type { SSHHost, AuthenticatedRequest } from "../../../types/index.js"; +import { getTmuxAuthBehavior } from "./auth-utils.js"; const PANE_ID_RE = /^%\d+$/; // tmux session names cannot contain ":" or "."; keep to a conservative @@ -59,11 +60,12 @@ interface TmuxSessionOverview extends TmuxSessionSummary { // and docker; jump hosts and SOCKS5 reuse the shared helpers) async function buildSshConfig(host: SSHHost): Promise { + const authBehavior = getTmuxAuthBehavior(host.authType); const base: ConnectConfig = { host: (host.ip || "").replace(/^\[|\]$/g, ""), port: host.port, username: host.username, - tryKeyboard: true, + tryKeyboard: authBehavior.tryKeyboard, keepaliveInterval: 30000, keepaliveCountMax: 3, readyTimeout: 60000, @@ -94,7 +96,7 @@ async function buildSshConfig(host: SSHHost): Promise { if (host.keyPassword) { (base as Record).passphrase = host.keyPassword; } - } else if (host.authType === "none") { + } else if (authBehavior.credentialless) { // no credentials needed } else if (host.authType === "vault") { // cert auth setup happens in connectToHost (needs client instance) diff --git a/src/backend/tests/hosts/tmux/auth-utils.test.ts b/src/backend/tests/hosts/tmux/auth-utils.test.ts new file mode 100644 index 00000000..23488c86 --- /dev/null +++ b/src/backend/tests/hosts/tmux/auth-utils.test.ts @@ -0,0 +1,25 @@ +import { describe, expect, it } from "vitest"; +import { getTmuxAuthBehavior } from "../../../hosts/tmux/auth-utils.js"; + +describe("getTmuxAuthBehavior", () => { + it("uses credentialless non-interactive authentication for Tailscale SSH", () => { + expect(getTmuxAuthBehavior("tailscale")).toEqual({ + credentialless: true, + tryKeyboard: false, + }); + }); + + it("preserves keyboard-interactive fallback for none authentication", () => { + expect(getTmuxAuthBehavior("none")).toEqual({ + credentialless: true, + tryKeyboard: true, + }); + }); + + it("does not treat password authentication as credentialless", () => { + expect(getTmuxAuthBehavior("password")).toEqual({ + credentialless: false, + tryKeyboard: true, + }); + }); +});