From 9f63b4c46007e40a9ceb8d852b9a46f579ce87f4 Mon Sep 17 00:00:00 2001 From: Rai Butera Date: Thu, 12 Mar 2026 18:54:14 +0000 Subject: [PATCH] fix: allow remote backend clients with shared-secret auth as internal --- .../ws-connection/connect-policy.test.ts | 31 ++++++++++++++++++- .../server/ws-connection/connect-policy.ts | 9 ++++-- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/gateway/server/ws-connection/connect-policy.test.ts b/src/gateway/server/ws-connection/connect-policy.test.ts index 9c965378b84..1770fcecadb 100644 --- a/src/gateway/server/ws-connection/connect-policy.test.ts +++ b/src/gateway/server/ws-connection/connect-policy.test.ts @@ -332,6 +332,7 @@ describe("ws connect policy", () => { }), ).toBe(true); + // Remote backend client with valid shared-secret auth is now trusted (gateway.mode=remote support). expect( shouldSkipBackendSelfPairing({ connectParams: makeConnectParams( @@ -343,7 +344,7 @@ describe("ws connect policy", () => { sharedAuthOk: true, authMethod: "token", }), - ).toBe(false); + ).toBe(true); expect( shouldSkipBackendSelfPairing({ @@ -423,6 +424,34 @@ describe("ws connect policy", () => { }), ).toBe(false); + // Remote backend client (gateway.mode=remote) with valid shared-secret auth is trusted. + expect( + shouldSkipBackendSelfPairing({ + connectParams: makeConnectParams( + GATEWAY_CLIENT_IDS.GATEWAY_CLIENT, + GATEWAY_CLIENT_MODES.BACKEND, + ), + isLocalClient: false, + hasBrowserOriginHeader: false, + sharedAuthOk: true, + authMethod: "token", + }), + ).toBe(true); + + // Remote backend client with browser origin header is still rejected. + expect( + shouldSkipBackendSelfPairing({ + connectParams: makeConnectParams( + GATEWAY_CLIENT_IDS.GATEWAY_CLIENT, + GATEWAY_CLIENT_MODES.BACKEND, + ), + isLocalClient: false, + hasBrowserOriginHeader: true, + sharedAuthOk: true, + authMethod: "token", + }), + ).toBe(false); + // auth.mode="none" with browser origin header is still rejected (hasBrowserOriginHeader=true). expect( shouldSkipBackendSelfPairing({ diff --git a/src/gateway/server/ws-connection/connect-policy.ts b/src/gateway/server/ws-connection/connect-policy.ts index 66f53ee7d36..69ce10c8cc8 100644 --- a/src/gateway/server/ws-connection/connect-policy.ts +++ b/src/gateway/server/ws-connection/connect-policy.ts @@ -93,12 +93,15 @@ export function shouldSkipBackendSelfPairing(params: { const usesSharedSecretAuth = params.authMethod === "token" || params.authMethod === "password"; // When auth is disabled entirely (mode="none"), there is no shared secret to verify, but a // local client with no browser origin and the correct gateway-client/backend identity is still - // a trusted internal connection. Allow attestation in that case too. + // a trusted internal connection. const authIsDisabled = params.authMethod === "none"; + // Remote backend clients (gateway.mode=remote) connecting with a valid shared-secret credential + // are trusted too — isLocalClient is false for remote gateways but the shared secret provides + // equivalent trust. Only the auth-disabled path is restricted to local connections, because + // remote + no-auth would be a security hole. return ( - params.isLocalClient && !params.hasBrowserOriginHeader && - ((params.sharedAuthOk && usesSharedSecretAuth) || authIsDisabled) + ((params.sharedAuthOk && usesSharedSecretAuth) || (params.isLocalClient && authIsDisabled)) ); }