Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions src/lib/onboard/setup-nim-vllm.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,62 @@ 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 queryVllmModels = vi.fn(() => JSON.stringify({ data: [{ id: "served/model" }] }));
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,
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.",
);
});

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 }));
Expand Down
30 changes: 19 additions & 11 deletions src/lib/onboard/setup-nim-vllm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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)
) {
Comment thread
coderabbitai[bot] marked this conversation as resolved.
throw new Error("Managed vLLM endpoint authorization failed.");
}
return {
Expand Down Expand Up @@ -263,6 +271,16 @@ export function createSetupNimVllmHandler(
? " ✓ Using managed vLLM endpoint"
: ` ✓ Using existing vLLM on localhost:${deps.VLLM_PORT}`,
);
let managedValidationOptions: Awaited<ReturnType<typeof managedVllmValidationOptions>> | 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`], {
Expand Down Expand Up @@ -330,16 +348,6 @@ export function createSetupNimVllmHandler(
}

const validationModel = deps.requireValue(state.model, "Expected a detected vLLM model");
let managedValidationOptions: Awaited<ReturnType<typeof managedVllmValidationOptions>> | 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",
Expand Down
Loading