From 7616b02bb10632769064a44af4142aa008b693e3 Mon Sep 17 00:00:00 2001 From: Roshan Singh Date: Tue, 13 Jan 2026 03:55:04 +0000 Subject: [PATCH] Fix tailscale allowTailscale bypass in token mode --- src/gateway/auth.test.ts | 22 +++++++++++++++++++++ src/gateway/auth.ts | 41 +++++++++++++++++----------------------- 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/src/gateway/auth.test.ts b/src/gateway/auth.test.ts index ad7c91c0e..a6d2e145e 100644 --- a/src/gateway/auth.test.ts +++ b/src/gateway/auth.test.ts @@ -92,4 +92,26 @@ describe("gateway auth", () => { expect(missingProxy.ok).toBe(false); expect(missingProxy.reason).toBe("tailscale_proxy_missing"); }); + + it("allows tailscale identity to satisfy token mode auth", async () => { + const res = await authorizeGatewayConnect({ + auth: { mode: "token", token: "secret", allowTailscale: true }, + connectAuth: null, + req: { + socket: { remoteAddress: "127.0.0.1" }, + headers: { + host: "gateway.local", + "x-forwarded-for": "100.64.0.1", + "x-forwarded-proto": "https", + "x-forwarded-host": "ai-hub.bone-egret.ts.net", + "tailscale-user-login": "peter", + "tailscale-user-name": "Peter", + }, + } as never, + }); + + expect(res.ok).toBe(true); + expect(res.method).toBe("tailscale"); + expect(res.user).toBe("peter"); + }); }); diff --git a/src/gateway/auth.ts b/src/gateway/auth.ts index 91577a342..8d7702f9a 100644 --- a/src/gateway/auth.ts +++ b/src/gateway/auth.ts @@ -146,21 +146,29 @@ export async function authorizeGatewayConnect(params: { const { auth, connectAuth, req } = params; const localDirect = isLocalDirectRequest(req); - if (auth.mode === "none") { - if (auth.allowTailscale && !localDirect) { - const tailscaleUser = getTailscaleUser(req); - if (!tailscaleUser) { - return { ok: false, reason: "tailscale_user_missing" }; - } - if (!isTailscaleProxyRequest(req)) { - return { ok: false, reason: "tailscale_proxy_missing" }; - } + if (auth.allowTailscale && !localDirect) { + const tailscaleUser = getTailscaleUser(req); + const tailscaleProxy = isTailscaleProxyRequest(req); + + if (tailscaleUser && tailscaleProxy) { return { ok: true, method: "tailscale", user: tailscaleUser.login, }; } + + if (auth.mode === "none") { + if (!tailscaleUser) { + return { ok: false, reason: "tailscale_user_missing" }; + } + if (!tailscaleProxy) { + return { ok: false, reason: "tailscale_proxy_missing" }; + } + } + } + + if (auth.mode === "none") { return { ok: true, method: "none" }; } @@ -191,20 +199,5 @@ export async function authorizeGatewayConnect(params: { return { ok: true, method: "password" }; } - if (auth.allowTailscale) { - const tailscaleUser = getTailscaleUser(req); - if (!tailscaleUser) { - return { ok: false, reason: "tailscale_user_missing" }; - } - if (!isTailscaleProxyRequest(req)) { - return { ok: false, reason: "tailscale_proxy_missing" }; - } - return { - ok: true, - method: "tailscale", - user: tailscaleUser.login, - }; - } - return { ok: false, reason: "unauthorized" }; }