auth: allow local loopback connections to bypass trusted-proxy auth

This commit is contained in:
michael.zhang 2026-03-20 12:14:55 +08:00
parent 1ba70c3707
commit c14cd23c50
2 changed files with 51 additions and 0 deletions

View File

@ -649,4 +649,46 @@ describe("trusted-proxy auth", () => {
expect(res.ok).toBe(true);
expect(res.user).toBe("nick@example.com");
});
it("allows local direct loopback connections to bypass trusted-proxy auth", async () => {
const res = await authorizeGatewayConnect({
auth: {
mode: "trusted-proxy",
allowTailscale: false,
trustedProxy: trustedProxyConfig,
},
connectAuth: null,
trustedProxies: ["10.0.0.1"],
req: {
socket: { remoteAddress: "127.0.0.1" },
headers: {
host: "127.0.0.1:18789",
},
} as never,
});
expect(res.ok).toBe(true);
expect(res.method).toBe("none");
});
it("does not bypass trusted-proxy auth for non-loopback connections", async () => {
const res = await authorizeGatewayConnect({
auth: {
mode: "trusted-proxy",
allowTailscale: false,
trustedProxy: trustedProxyConfig,
},
connectAuth: null,
trustedProxies: ["10.0.0.1"],
req: {
socket: { remoteAddress: "192.168.1.50" },
headers: {
host: "192.168.1.50:18789",
},
} as never,
});
expect(res.ok).toBe(false);
expect(res.reason).toBe("trusted_proxy_untrusted_source");
});
});

View File

@ -380,6 +380,15 @@ export async function authorizeGatewayConnect(
);
if (auth.mode === "trusted-proxy") {
// Allow local direct connections (loopback, no proxy headers) to bypass
// trusted-proxy auth. Local CLI commands connect directly to 127.0.0.1
// without going through a reverse proxy, so they lack proxy identity
// headers. This is safe because only processes on the same machine can
// reach the loopback interface.
if (localDirect) {
return { ok: true, method: "none" };
}
if (!auth.trustedProxy) {
return { ok: false, reason: "trusted_proxy_config_missing" };
}