From f2b89e319889621faec3749f657baae6f5ada6af Mon Sep 17 00:00:00 2001 From: Tinson Lai Date: Fri, 7 Aug 2026 10:33:49 +0000 Subject: [PATCH 1/2] fix(onboard): authorize managed loopback vLLM endpoints The managed host-local runtime always publishes on 127.0.0.1, and the shared SSRF preflight admits an explicit loopback host without minting a trusted-private capability, so the unconditional capability demand could never pass and every managed vLLM onboard exited before creating a sandbox. A loopback endpoint now satisfies the check on its own, while any other host still requires a provenance-checked capability. Signed-off-by: Tinson Lai --- src/lib/onboard/setup-nim-vllm.test.ts | 54 ++++++++++++++++++++++++++ src/lib/onboard/setup-nim-vllm.ts | 10 ++++- 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/lib/onboard/setup-nim-vllm.test.ts b/src/lib/onboard/setup-nim-vllm.test.ts index 5041cfe56b8..c015c3b3751 100644 --- a/src/lib/onboard/setup-nim-vllm.test.ts +++ b/src/lib/onboard/setup-nim-vllm.test.ts @@ -134,6 +134,60 @@ describe("setupNim vLLM route containment", () => { expect(renderedOutput).not.toContain("localhost:8000"); }); + it("authorizes a managed loopback endpoint without a trusted-private capability (#8539)", async () => { + const apiKey = "a".repeat(64); + const queryVllmModels = vi.fn(() => JSON.stringify({ data: [{ id: "served/model" }] })); + const validateOpenAiLikeSelection = vi.fn(async () => ({ + ok: true, + api: "openai-completions", + })); + const handler = createSetupNimVllmHandler( + deps({ + getLocalProviderBaseUrl: () => "http://127.0.0.1:8000/v1", + getLocalProviderValidationBaseUrl: () => "http://127.0.0.1:8000/v1", + getManagedVllmProviderBinding: () => ({ + baseUrl: "http://127.0.0.1:8000/v1", + apiKey, + }), + queryVllmModels, + validateOpenAiLikeSelection, + }), + ); + + await expect(handler(state(null))).resolves.toBe("selected"); + expect(validateOpenAiLikeSelection).toHaveBeenCalledWith( + "Local vLLM", + "http://127.0.0.1:8000/v1", + "served/model", + null, + undefined, + undefined, + { apiKey, pinnedAddresses: [], trustedPrivateCapability: undefined }, + ); + }); + + it("fails closed for a managed endpoint that is neither loopback nor operator-trusted private", async () => { + const validateOpenAiLikeSelection = vi.fn(async () => ({ ok: true })); + const handler = createSetupNimVllmHandler( + deps({ + getLocalProviderBaseUrl: () => "http://93.184.216.34:8000/v1", + getLocalProviderValidationBaseUrl: () => "http://93.184.216.34:8000/v1", + getManagedVllmProviderBinding: () => ({ + baseUrl: "http://93.184.216.34:8000/v1", + apiKey: "a".repeat(64), + }), + queryVllmModels: () => JSON.stringify({ data: [{ id: "served/model" }] }), + validateOpenAiLikeSelection, + }), + ); + + await expect(handler(state(null))).rejects.toThrow("exit 1"); + expect(validateOpenAiLikeSelection).not.toHaveBeenCalled(); + expect(console.error).toHaveBeenCalledWith( + " Managed vLLM endpoint authorization could not be verified.", + ); + }); + it("rejects a root-matched alias with topology-neutral recovery for a managed dual endpoint", async () => { const selection = state("required/model"); const validateOpenAiLikeSelection = vi.fn(async () => ({ ok: true })); diff --git a/src/lib/onboard/setup-nim-vllm.ts b/src/lib/onboard/setup-nim-vllm.ts index 4459c252146..2fc3884860f 100644 --- a/src/lib/onboard/setup-nim-vllm.ts +++ b/src/lib/onboard/setup-nim-vllm.ts @@ -3,9 +3,11 @@ import { assertEndpointResolvesPublic, + isTrustedPrivateEndpointCapability, type TrustedPrivateEndpointCapability, } from "../inference/endpoint-ssrf-preflight"; import { VLLM_MODELS } from "../inference/vllm-models"; +import { isLoopbackHostname } from "../private-networks"; import { cliName } from "./branding"; import type { SetupNimSelectionResult, SetupNimSelectionState } from "./setup-nim-flow"; @@ -77,7 +79,13 @@ async function managedVllmValidationOptions(baseUrl: string, apiKey: string) { const preflight = await assertEndpointResolvesPublic(baseUrl, undefined, { trustedPrivateHosts: [hostname], }); - if (!preflight.ok || !preflight.trustedPrivateCapability) { + if (!preflight.ok) { + throw new Error("Managed vLLM endpoint authorization failed."); + } + if ( + !isLoopbackHostname(hostname) && + !isTrustedPrivateEndpointCapability(preflight.trustedPrivateCapability) + ) { throw new Error("Managed vLLM endpoint authorization failed."); } return { From 832374d356d161ae5602ec0c5c9c29765c7202d2 Mon Sep 17 00:00:00 2001 From: Apurv Kumaria Date: Fri, 7 Aug 2026 03:57:20 -0700 Subject: [PATCH 2/2] fix(onboard): authorize vLLM before model discovery Signed-off-by: Apurv Kumaria --- src/lib/onboard/setup-nim-vllm.test.ts | 4 +++- src/lib/onboard/setup-nim-vllm.ts | 20 ++++++++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/lib/onboard/setup-nim-vllm.test.ts b/src/lib/onboard/setup-nim-vllm.test.ts index c015c3b3751..a83fe7a746e 100644 --- a/src/lib/onboard/setup-nim-vllm.test.ts +++ b/src/lib/onboard/setup-nim-vllm.test.ts @@ -167,6 +167,7 @@ describe("setupNim vLLM route containment", () => { }); it("fails closed for a managed endpoint that is neither loopback nor operator-trusted private", async () => { + const queryVllmModels = vi.fn(() => JSON.stringify({ data: [{ id: "served/model" }] })); const validateOpenAiLikeSelection = vi.fn(async () => ({ ok: true })); const handler = createSetupNimVllmHandler( deps({ @@ -176,12 +177,13 @@ describe("setupNim vLLM route containment", () => { baseUrl: "http://93.184.216.34:8000/v1", apiKey: "a".repeat(64), }), - queryVllmModels: () => JSON.stringify({ data: [{ id: "served/model" }] }), + queryVllmModels, validateOpenAiLikeSelection, }), ); await expect(handler(state(null))).rejects.toThrow("exit 1"); + expect(queryVllmModels).not.toHaveBeenCalled(); expect(validateOpenAiLikeSelection).not.toHaveBeenCalled(); expect(console.error).toHaveBeenCalledWith( " Managed vLLM endpoint authorization could not be verified.", diff --git a/src/lib/onboard/setup-nim-vllm.ts b/src/lib/onboard/setup-nim-vllm.ts index 2fc3884860f..5d276679342 100644 --- a/src/lib/onboard/setup-nim-vllm.ts +++ b/src/lib/onboard/setup-nim-vllm.ts @@ -271,6 +271,16 @@ export function createSetupNimVllmHandler( ? " ✓ Using managed vLLM endpoint" : ` ✓ Using existing vLLM on localhost:${deps.VLLM_PORT}`, ); + let managedValidationOptions: Awaited> | null = + null; + if (apiKey) { + try { + managedValidationOptions = await managedVllmValidationOptions(validationBaseUrl, apiKey); + } catch { + console.error(" Managed vLLM endpoint authorization could not be verified."); + deps.exitProcess(1); + } + } const raw = apiKey ? deps.queryVllmModels(validationBaseUrl, apiKey) : deps.runCapture(["curl", "-sf", `${validationBaseUrl}/models`], { @@ -338,16 +348,6 @@ export function createSetupNimVllmHandler( } const validationModel = deps.requireValue(state.model, "Expected a detected vLLM model"); - let managedValidationOptions: Awaited> | null = - null; - if (apiKey) { - try { - managedValidationOptions = await managedVllmValidationOptions(validationBaseUrl, apiKey); - } catch { - console.error(" Managed vLLM endpoint authorization could not be verified."); - deps.exitProcess(1); - } - } const validation = apiKey ? await deps.validateOpenAiLikeSelection( "Local vLLM",