diff --git a/bin/lib/onboard.js b/bin/lib/onboard.js index 02aa47904d4..a74950fdf5f 100644 --- a/bin/lib/onboard.js +++ b/bin/lib/onboard.js @@ -2748,7 +2748,20 @@ async function setupNim(gpu) { console.log(" Installing Ollama via Homebrew..."); run("brew install ollama", { ignoreError: true }); console.log(" Starting Ollama..."); - run("OLLAMA_HOST=0.0.0.0:11434 ollama serve > /dev/null 2>&1 &", { ignoreError: true }); + // On macOS, Docker Desktop routes host-gateway through the VM so + // 127.0.0.1 is reachable from containers — bind to localhost to + // avoid exposing Ollama to the LAN (CWE-668, NVBUG 6014821). + // On Linux, containers access the host via the Docker bridge IP + // so 0.0.0.0 is required for reachability. + // On WSL2, the default binding works without override. + let ollamaEnv = ""; + if (!isWsl()) { + ollamaEnv = + process.platform === "darwin" + ? "OLLAMA_HOST=127.0.0.1:11434 " + : "OLLAMA_HOST=0.0.0.0:11434 "; + } + run(`${ollamaEnv}ollama serve > /dev/null 2>&1 &`, { ignoreError: true }); sleep(2); console.log(" ✓ Using Ollama on localhost:11434"); provider = "ollama-local"; diff --git a/spark-install.md b/spark-install.md index 94cf265bfa5..ef2254d5ea2 100644 --- a/spark-install.md +++ b/spark-install.md @@ -86,11 +86,13 @@ ollama run nemotron-3-super:120b # type /bye to exit ``` -### 4. Configure Ollama to Listen on All Interfaces +### 4. Configure Ollama to Listen on All Interfaces (Linux only) -By default Ollama binds to `127.0.0.1`, which is not reachable from inside the sandbox container. Configure it to listen on all interfaces: +On Linux, Ollama's default `127.0.0.1` binding is not reachable from inside the sandbox container because Docker containers access the host via the bridge IP, not loopback. Configure Ollama to listen on all interfaces: > **Note:** `OLLAMA_HOST=0.0.0.0` exposes Ollama on your network. If you're not on a trusted LAN, restrict access with host firewall rules (`ufw`, `iptables`, etc.). +> +> **macOS users:** Docker Desktop routes `host-gateway` through the VM, so the default `127.0.0.1` binding works — skip this step. The NemoClaw onboarding wizard handles this automatically. ```bash sudo mkdir -p /etc/systemd/system/ollama.service.d diff --git a/src/lib/local-inference.test.ts b/src/lib/local-inference.test.ts index 34040c81475..13bbbcfcaec 100644 --- a/src/lib/local-inference.test.ts +++ b/src/lib/local-inference.test.ts @@ -29,9 +29,7 @@ describe("local inference helpers", () => { }); it("returns the expected base URL for ollama-local", () => { - expect(getLocalProviderBaseUrl("ollama-local")).toBe( - "http://host.openshell.internal:11434/v1", - ); + expect(getLocalProviderBaseUrl("ollama-local")).toBe("http://host.openshell.internal:11434/v1"); }); it("returns null for unknown local provider URLs", () => { @@ -91,7 +89,13 @@ describe("local inference helpers", () => { }); expect(result.ok).toBe(false); expect(result.message).toMatch(/host\.openshell\.internal:11434/); - expect(result.message).toMatch(/0\.0\.0\.0:11434/); + // Platform-aware message: macOS advises restarting Docker Desktop; + // Linux advises binding to 0.0.0.0 (CWE-668 / NVBUG 6014821). + if (process.platform === "darwin") { + expect(result.message).toMatch(/Restart Docker Desktop/); + } else { + expect(result.message).toMatch(/0\.0\.0\.0:11434/); + } }); it("returns a clear error when vllm-local is unavailable", () => { @@ -202,9 +206,10 @@ describe("local inference helpers", () => { expect( getBootstrapOllamaModelOptions({ totalMemoryMB: LARGE_OLLAMA_MIN_MEMORY_MB - 1 }), ).toEqual(["qwen2.5:7b"]); - expect( - getBootstrapOllamaModelOptions({ totalMemoryMB: LARGE_OLLAMA_MIN_MEMORY_MB }), - ).toEqual(["qwen2.5:7b", DEFAULT_OLLAMA_MODEL]); + expect(getBootstrapOllamaModelOptions({ totalMemoryMB: LARGE_OLLAMA_MIN_MEMORY_MB })).toEqual([ + "qwen2.5:7b", + DEFAULT_OLLAMA_MODEL, + ]); expect(getDefaultOllamaModel(() => "", { totalMemoryMB: 16384 })).toBe("qwen2.5:7b"); }); diff --git a/src/lib/local-inference.ts b/src/lib/local-inference.ts index 9390bb70ebb..37f939f99f3 100644 --- a/src/lib/local-inference.ts +++ b/src/lib/local-inference.ts @@ -119,7 +119,9 @@ export function validateLocalProvider( return { ok: false, message: - "Local Ollama is responding on localhost, but containers cannot reach http://host.openshell.internal:11434. Ensure Ollama listens on 0.0.0.0:11434 instead of 127.0.0.1 so sandboxes can reach it.", + process.platform === "darwin" + ? "Local Ollama is responding on localhost, but containers cannot reach http://host.openshell.internal:11434. Restart Docker Desktop and ensure host networking is enabled." + : "Local Ollama is responding on localhost, but containers cannot reach http://host.openshell.internal:11434. Ensure Ollama listens on 0.0.0.0:11434 (not 127.0.0.1) so sandboxes can reach it via the Docker bridge.", }; default: return { @@ -207,10 +209,7 @@ export function getOllamaProbeCommand( return `curl -sS --max-time ${timeoutSeconds} http://localhost:11434/api/generate -H 'Content-Type: application/json' -d ${shellQuote(payload)} 2>/dev/null`; } -export function validateOllamaModel( - model: string, - runCapture: RunCaptureFn, -): ValidationResult { +export function validateOllamaModel(model: string, runCapture: RunCaptureFn): ValidationResult { const output = runCapture(getOllamaProbeCommand(model), { ignoreError: true }); if (!output) { return { diff --git a/test/e2e/test-gpu-e2e.sh b/test/e2e/test-gpu-e2e.sh index 6d034b964ca..55d32444043 100755 --- a/test/e2e/test-gpu-e2e.sh +++ b/test/e2e/test-gpu-e2e.sh @@ -7,7 +7,7 @@ # Mirrors what a user with a GPU would actually do: # 1. Install Ollama binary # 2. Run the NemoClaw installer with NEMOCLAW_PROVIDER=ollama -# 3. Onboard starts Ollama (OLLAMA_HOST=0.0.0.0:11434), pulls model, creates sandbox +# 3. Onboard starts Ollama (OLLAMA_HOST=0.0.0.0:11434 on Linux for container reachability), pulls model, creates sandbox # 4. Verify inference works through the sandbox # 5. Destroy + uninstall # @@ -159,10 +159,10 @@ if [ "${NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE:-}" != "1" ]; then exit 1 fi -# Verify port 11434 is free (onboard needs to start Ollama on 0.0.0.0:11434) +# Verify port 11434 is free (onboard needs to start Ollama on 0.0.0.0:11434 on Linux for container reachability) if curl -sf http://localhost:11434/api/tags >/dev/null 2>&1; then info "WARNING: Something is already listening on port 11434." - info "Onboard may not be able to start Ollama on 0.0.0.0:11434." + info "Onboard may not be able to bind Ollama to 0.0.0.0:11434." info "On ephemeral runners this should not happen." # Don't fail — onboard will detect the running Ollama and use it. # The container reachability check in onboard will catch 127.0.0.1 issues. @@ -188,7 +188,7 @@ else fi # If the Ollama installer started a system service, stop it so onboard -# can start Ollama with OLLAMA_HOST=0.0.0.0:11434 (required for containers). +# can start Ollama with OLLAMA_HOST=0.0.0.0:11434 (required for container reachability on Linux). # This needs the ollama process to be owned by our user, or systemctl access. if curl -sf http://localhost:11434/api/tags >/dev/null 2>&1; then info "Ollama service is running — attempting to stop for clean onboard..."