Skip to content

fix: add curl/node/python3 to local-inference binary allowlist - #2295

Merged
ericksoa merged 7 commits into
NVIDIA:mainfrom
BenediktSchackenberg:fix/local-inference-ports-plugins-2199-2203
Apr 24, 2026
Merged

fix: add curl/node/python3 to local-inference binary allowlist#2295
ericksoa merged 7 commits into
NVIDIA:mainfrom
BenediktSchackenberg:fix/local-inference-ports-plugins-2199-2203

Conversation

@BenediktSchackenberg

@BenediktSchackenberg BenediktSchackenberg commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

Two unrelated fixes in one PR.

1. local-inference preset — missing binaries (fixes #2199)

The preset only listed openclaw and claude as allowed binaries for the Ollama endpoints. curl, node, and python3 were blocked — causing HTTP 403 when users test with curl http://host.openshell.internal:11434/api/tags or use Python/Node inference clients directly.

Added: /usr/local/bin/node, /usr/bin/curl, /usr/local/bin/python3, /usr/bin/python3.

2. Dockerfile — missing extensions symlink and data dir (fixes #2203)

openclaw plugins install fails with Invalid extensions directory: base directory must be a real directory because ~/.openclaw/extensions is a broken symlink. The /sandbox/.openclaw-data/extensions target directory is never created and the symlink is never established.

Added extensions to the existing RUN block that creates .openclaw-data subdirs and sets up symlinks under ~/.openclaw/, matching the pattern used for logs, credentials, sandbox, and media.

Signed-off-by: Benedikt Schackenberg 6381261+BenediktSchackenberg@users.noreply.github.com

Summary by CodeRabbit

  • Configuration Updates
    • Expanded local inference configuration allowlist to permit additional host binaries (additional Node, curl, and Python interpreter locations), improving compatibility for locally executed inference workflows.

… symlink

Two fixes:

1. local-inference preset (fixes NVIDIA#2199):
   The local-inference policy preset only allowed openclaw and claude
   binaries to reach the Ollama endpoints. curl, node, and python3 were
   blocked, causing 403 when testing with curl or using Python/Node
   inference clients. Added common inference client binaries.

2. Dockerfile extensions symlink (fixes NVIDIA#2203):
   openclaw plugins install fails with 'Invalid extensions directory:
   base directory must be a real directory' because ~/.openclaw/extensions
   is a broken symlink — the /sandbox/.openclaw-data/extensions target
   directory is never created and the symlink is never added.

   Added extensions to the RUN block that creates .openclaw-data
   subdirectories and establishes the ~/.openclaw/extensions symlink,
   matching how logs/credentials/sandbox/media are handled.

Signed-off-by: Benedikt Schackenberg <6381261+BenediktSchackenberg@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 22, 2026 19:20
@copy-pr-bot

copy-pr-bot Bot commented Apr 22, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 9f3ab3fb-3156-47ff-a501-e7cf2524b6b3

📥 Commits

Reviewing files that changed from the base of the PR and between b0a6eac and 1ad758b.

📒 Files selected for processing (1)
  • nemoclaw-blueprint/policies/presets/local-inference.yaml

📝 Walkthrough

Walkthrough

The local-inference policy preset’s host binary allowlist was extended to include additional executable paths for Node.js, curl, and Python, adding entries for /usr/local/bin/node, /usr/bin/node, /usr/bin/curl, and /usr/bin/python3. No other policy sections were modified.

Changes

Cohort / File(s) Summary
Local-inference policy preset
nemoclaw-blueprint/policies/presets/local-inference.yaml
Added four binary allowlist entries under network_policies.local_inference.binaries: /usr/local/bin/node, /usr/bin/node, /usr/bin/curl, /usr/bin/python3. Review sandbox host binary execution rules and any comments about alternate apt locations.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

Possibly related PRs

Poem

🐰 Soft paws tap on keys tonight,

Paths extended, binaries in sight.
Node, curl, and Python stride,
Through sandbox walls they now can glide.
A happy hop — inference takes flight!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding curl, node, and python3 binaries to the local-inference policy preset's allowlist.
Linked Issues check ✅ Passed The PR fully addresses issue #2199 by adding required client binaries (curl, node, python3) to the local-inference binary allowlist, allowing sandboxed processes to reach Ollama endpoints.
Out of Scope Changes check ✅ Passed All changes are in-scope: only the local-inference.yaml binary allowlist was modified; the Dockerfile extensions change was reverted per reviewer feedback, keeping the PR focused on #2199.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes two sandbox usability issues: (1) allow common client binaries to reach local inference endpoints under the local-inference preset, and (2) ensure the sandbox image creates the extensions state directory and wiring so plugin installs don’t fail due to missing paths.

Changes:

  • Extend local-inference preset binary allowlist to include node, curl, and python3.
  • Ensure /sandbox/.openclaw-data/extensions exists and is included in the .openclaw state symlink setup in the Docker image.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
nemoclaw-blueprint/policies/presets/local-inference.yaml Adds additional allowed binaries for the local inference network policy preset.
Dockerfile Creates/chowns the extensions state dir and includes it in the symlink migration/creation loop.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

binaries:
- { path: /usr/local/bin/openclaw }
- { path: /usr/local/bin/claude }
- { path: /usr/local/bin/node }

Copilot AI Apr 22, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The preset adds /usr/local/bin/node but not /usr/bin/node (or a node* glob). Other presets (e.g., nemoclaw-blueprint/policies/presets/npm.yaml:27-33) allow both locations, and Debian/alternative Node installs may place node under /usr/bin. To avoid the local-inference preset still returning 403 for node-based clients in those environments, include /usr/bin/node (or /usr/bin/node*).

Suggested change
- { path: /usr/local/bin/node }
- { path: /usr/local/bin/node }
- { path: /usr/bin/node }

Copilot uses AI. Check for mistakes.
Comment thread Dockerfile Outdated
Comment on lines +331 to +337
/sandbox/.openclaw-data/extensions \
&& chown sandbox:sandbox /sandbox/.openclaw-data/logs \
/sandbox/.openclaw-data/credentials \
/sandbox/.openclaw-data/sandbox \
/sandbox/.openclaw-data/media \
&& for dir in logs credentials sandbox media; do \
/sandbox/.openclaw-data/extensions \
&& for dir in logs credentials sandbox media extensions; do \

Copilot AI Apr 22, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change makes sure /sandbox/.openclaw-data/extensions exists and then symlinks /sandbox/.openclaw/extensions to it (via the for-loop). If OpenClaw’s plugin installer requires the extensions base directory to be a non-symlink (as reported in #2203 / OpenClaw’s safe-path checks), this layout will still fail even after the target directory exists. Consider switching extensions to a real directory under /sandbox/.openclaw (and handling persistence another way), or updating the OpenClaw config/runtime to use a real directory path for extensions so openclaw plugins install succeeds.

Copilot uses AI. Check for mistakes.
@wscurran

Copy link
Copy Markdown
Contributor

✨ Thanks for submitting this PR that proposes fixes for the local-inference binaries and extensions symlinks issues — these could help improve the overall functionality of NemoClaw.


Related open issues:

@prekshivyas

prekshivyas commented Apr 22, 2026

Copy link
Copy Markdown
Collaborator

Hey @BenediktSchackenberg, thanks for the work here — I want to flag a concern with the extensions part and suggest a path forward.

The extensions change doesn't fix #2203. Two reasons:

  1. The symlink already exists. Dockerfile.base line 97 creates /sandbox/.openclaw-data/extensions and Dockerfile.base line 113 establishes ~/.openclaw/extensions -> .openclaw-data/extensions. The issue reporter's own ls output in #2203 confirms it:

    lrwxrwxrwx 1 root root 34 Apr 14 01:21 extensions -> /sandbox/.openclaw-data/extensions
    

    So when the for-loop runs on a current base image, the [ -L ... ] branch fires first and the change is a no-op.

  2. Even if the symlink were missing, creating one doesn't help. #2203's root cause is OpenClaw's install-safe-path validator rejecting symlinks:

    const baseLstat = await fs.lstat(baseDir);
    if (!baseLstat.isDirectory() || baseLstat.isSymbolicLink()) {
      throw new Error(`Invalid ${params.boundaryLabel}: base directory must be a real directory`);
    }

    lstat reports on the link itself, so any symlink (valid or dangling) trips the isSymbolicLink() branch. Creating another symlink via the for-loop's else branch wouldn't pass the validator either.

The real fix likely needs a bind mount of .openclaw-data/extensions onto ~/.openclaw/extensions — passes the validator (isDirectory()=true, isSymbolicLink()=false) and preserves persistence. Alternative: patch the compiled install-safe-path.js locally, same pattern NemoClaw already uses for fetch-guard and exec-approvals. Untested — happy to dig in separately.

Proposal: drop the Dockerfile changes from this PR and keep it focused on #2199 (local-inference binary allowlist). That half is clean and can land immediately.


Review of the local-inference part:


Possible I'm missing context on the extensions side — if you've tested this branch and openclaw plugins install @openclaw/diagnostics-otel succeeds where main fails, please share the repro state and I'll re-check. Otherwise I'd suggest descoping this PR to just the yaml change.

@prekshivyas

Copy link
Copy Markdown
Collaborator

Long-term fix tracked in NVIDIA/OpenShell#925 — proposes provisioning ~/.openclaw/extensions as a pod-spec-level persistent volume so it's a real directory inside the container (passes OpenClaw's validator, preserves plugin persistence, no in-container CAP_SYS_ADMIN needed). Suggest keeping this PR scoped to the #2199 local-inference fix and handling the extensions symlink problem on the OpenShell side.

The Dockerfile.base already creates /sandbox/.openclaw-data/extensions
and the symlink ~/.openclaw/extensions. The fix in the previous commit
was redundant and does not address the root cause of NVIDIA#2203 — which is
that OpenClaw's validator expects a real directory, not a symlink.

The extensions issue is being tracked in NVIDIA/OpenShell#925 for a
pod-spec-level fix on the OpenShell side.

PR NVIDIA#2295 is now scoped to the local-inference binary fix only (NVIDIA#2199).

Signed-off-by: Benedikt Schackenberg <6381261+BenediktSchackenberg@users.noreply.github.com>
@BenediktSchackenberg
BenediktSchackenberg force-pushed the fix/local-inference-ports-plugins-2199-2203 branch from dfd2753 to 7f0e8cf Compare April 23, 2026 11:45
@BenediktSchackenberg

Copy link
Copy Markdown
Contributor Author

Thanks for the analysis @prekshivyas — you're right that the extensions change was wrong. I've reverted the Dockerfile change in this PR. The OpenShell#925 approach (pod-spec-level persistent volume) is the correct fix for #2203.

PR #2295 is now scoped to just the local-inference binary fix (#2199).

@prekshivyas prekshivyas left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review — scoped to current state (YAML-only, Dockerfile reverted)

Scope reduction was the right call. The local-inference binary fix is on the right track but has two open items before it can land.

1. Missing /usr/bin/node (blocking)

Every other preset in this repo that allows node includes both paths:

Preset Paths
slack.yaml /usr/local/bin/node, /usr/bin/node
discord.yaml /usr/local/bin/node, /usr/bin/node
telegram.yaml /usr/local/bin/node, /usr/bin/node
this PR /usr/local/bin/node only

Copilot's inline comment flagged this too — it hasn't been addressed. Please add /usr/bin/node for consistency and to cover alternative Node install locations.

2. /usr/local/bin/python3 — dead path in current image (blocking)

Dockerfile.base installs python3 via apt (python3=3.11.2-1+b1) → goes to /usr/bin/python3. Nothing in the build chain puts a python3 at /usr/local/bin/python3. This entry is currently a no-op.

@prekshivyas already asked about this — if it's intentional for a variant image or user-installed venv scenario, a short inline comment in the YAML would help future readers. Otherwise drop it.

3. PR title is stale (non-blocking)

Title still references "extensions symlink" which was reverted. Suggest updating to something like:
fix: add curl/node/python3 to local-inference binary allowlist (#2199)


Once /usr/bin/node is added and the python3 path is clarified/dropped, this is good to go — the endpoint side of the policy was already correct, and the binary additions align with the paths in Dockerfile.base.

- Add /usr/bin/node alongside /usr/local/bin/node for consistency
  with all other presets (slack, discord, telegram all include both paths)
- Replace /usr/local/bin/python3 with /usr/bin/python3 — Dockerfile.base
  installs python3 via apt (python3=3.11.2-1+b1) which goes to /usr/bin/python3.
  /usr/local/bin/python3 is not present in the base image.
- Added inline comments explaining each binary path's origin.

Per @prekshivyas review on NVIDIA#2295.

Signed-off-by: Benedikt Schackenberg <6381261+BenediktSchackenberg@users.noreply.github.com>
@BenediktSchackenberg
BenediktSchackenberg force-pushed the fix/local-inference-ports-plugins-2199-2203 branch from 0172906 to 4f169ab Compare April 23, 2026 16:58
@BenediktSchackenberg BenediktSchackenberg changed the title fix: add curl/node/python3 to local-inference binaries and extensions symlink fix: add curl/node/python3 to local-inference binary allowlist Apr 23, 2026
@BenediktSchackenberg

Copy link
Copy Markdown
Contributor Author

Addressed all three points:

  1. Added /usr/bin/node alongside /usr/local/bin/node — matches slack/discord/telegram preset pattern ✅
  2. Replaced /usr/local/bin/python3 with /usr/bin/python3 (apt path from Dockerfile.base) and added inline comment explaining the origin ✅
  3. Updated PR title to remove the stale 'extensions symlink' reference ✅

@prekshivyas

Copy link
Copy Markdown
Collaborator

Thanks for addressing the earlier feedback — /usr/bin/node added, /usr/local/bin/python3/usr/bin/python3, title fixed. One small bug in the latest push:

/usr/bin/python3 is duplicated. The last two lines of the diff:

- { path: /usr/bin/python3 }     # apt-installed python3 (Dockerfile.base: python3=3.11.2)
- { path: /usr/bin/python3 }

Same path twice. The uncommented second one should be removed.

Minor (non-blocking): the /usr/bin/node comment says "apt-installed", but node isn't apt-installed in this image — base is node:22-slim and the apt install list has no nodejs. The real reason to list /usr/bin/node is variant-image coverage. Suggested rewording:

- { path: /usr/bin/node }  # variant image fallback (default base uses /usr/local/bin/node)

Drop the duplicate and this is good to go.

@prekshivyas prekshivyas left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI failure (checks job) is from a stale unit test, not a real regression:

FAIL  test/policies.test.ts > policies > loadPreset > local-inference preset restricts binaries to openclaw and claude
AssertionError: expected '# SPDX-FileCopyrightText...' not to contain '/usr/local/bin/node'

The test pins the old "openclaw + claude only" allowlist, but this PR's whole purpose is to add node/curl/python3 to that preset. The assertion and the test name both need updating — the name itself ("restricts binaries to openclaw and claude") is now a lie.

Quick fix: swap the not to containto contain for the three new binaries, rename the test to reflect the expanded allowlist.

Signed-off-by: Prekshi Vyas prekshiv@nvidia.com

@ericksoa ericksoa left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost there — one bug in the diff.

Blocking: duplicate /usr/bin/python3 entry

The last two lines are identical:

      - { path: /usr/bin/python3 }     # apt-installed python3 (Dockerfile.base: python3=3.11.2)
      - { path: /usr/bin/python3 }

Drop the duplicate.

Otherwise looks good

The 4 new binary paths (node x2, curl, python3) are the right additions for users testing Ollama with curl or Python/Node inference clients. The Dockerfile extensions symlink fix was correctly reverted in commit 2.

One line fix and this is ready.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ericksoa
ericksoa merged commit fbf8279 into NVIDIA:main Apr 24, 2026
1 check was pending
ericksoa added a commit that referenced this pull request Apr 24, 2026
…2295

PR #2295 added node, curl, and python3 to the local-inference preset
binaries but did not update the corresponding test assertion, causing
CI failures after the merge into this branch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@cv cv added the v0.0.25 label Apr 24, 2026
DemianHeyGen pushed a commit to DemianHeyGen/NemoClaw that referenced this pull request Apr 30, 2026
…A#2295)

Two unrelated fixes in one PR.

## 1. local-inference preset — missing binaries (fixes NVIDIA#2199)

The preset only listed `openclaw` and `claude` as allowed binaries for
the Ollama endpoints. `curl`, `node`, and `python3` were blocked —
causing HTTP 403 when users test with `curl
http://host.openshell.internal:11434/api/tags` or use Python/Node
inference clients directly.

Added: `/usr/local/bin/node`, `/usr/bin/curl`, `/usr/local/bin/python3`,
`/usr/bin/python3`.

## 2. Dockerfile — missing extensions symlink and data dir (fixes NVIDIA#2203)

`openclaw plugins install` fails with `Invalid extensions directory:
base directory must be a real directory` because
`~/.openclaw/extensions` is a broken symlink. The
`/sandbox/.openclaw-data/extensions` target directory is never created
and the symlink is never established.

Added `extensions` to the existing `RUN` block that creates
`.openclaw-data` subdirs and sets up symlinks under `~/.openclaw/`,
matching the pattern used for `logs`, `credentials`, `sandbox`, and
`media`.

Signed-off-by: Benedikt Schackenberg
<6381261+BenediktSchackenberg@users.noreply.github.com>

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Configuration Updates**
* Updated local inference configuration by expanding the allowlist for
binary execution paths, now including additional locations for Node,
curl, and Python interpreters.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Benedikt Schackenberg <6381261+BenediktSchackenberg@users.noreply.github.com>
Co-authored-by: Prekshi Vyas <34834085+prekshivyas@users.noreply.github.com>
Co-authored-by: Aaron Erickson <aerickson@nvidia.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
cv pushed a commit that referenced this pull request May 5, 2026
## Summary
- allowlist private RFC1918 host-gateway IP ranges on the
`local-inference` preset endpoints for Ollama, the auth proxy, and vLLM
- keep the existing host/port/binary restrictions in place for
`host.openshell.internal`
- add a regression test so the Ollama/proxy/vLLM endpoints cannot lose
those `allowed_ips` entries silently

## Root cause
PR #2295 added the missing binaries, but OpenShell v0.0.36 performs a
second SSRF check after the policy endpoint/binary match.
`host.openshell.internal` resolves to a Docker/Colima/WSL host-gateway
address, which is private by design. Without `allowed_ips`, OpenShell
still returns `403 ssrf_denied` for `host.openshell.internal:11434` and
`:11435` even when `local-inference` appears active.

## Live OpenShell validation
Tested with OpenShell `0.0.36` on dedicated gateway `issue2199test`.
Host services were reachable before sandbox testing:

- host `127.0.0.1:11434/api/tags` -> `200`
- host `127.0.0.1:11435/api/tags` -> `200`

Using the `origin/main` local-inference policy reproduced the reopened
issue:

- sandbox `host.openshell.internal:11434/api/tags` -> `403`,
`{"error":"ssrf_denied"}`
- sandbox `host.openshell.internal:11435/api/tags` -> `403`,
`{"error":"ssrf_denied"}`

Using this PR's local-inference policy fixed the same calls:

- sandbox `host.openshell.internal:11434/api/tags` -> `200`
- sandbox `host.openshell.internal:11435/api/tags` -> `200`

## Other validation
- `npm ci --ignore-scripts`
- `npm run build:cli`
- `npx vitest run test/policies.test.ts test/validate-blueprint.test.ts`
- `git diff --check`

Closes #2199


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Enhanced local-inference network policy: added explicit allowlisting
of RFC1918 private IP ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)
for host endpoints on ports 11434, 11435, and 8000.
* Included a note clarifying SSRF guard behavior for the 11434 endpoint.

* **Tests**
* Added tests validating the local-inference preset now enforces the
expected IP allowlists for the specified endpoints.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
@wscurran wscurran added area: local-models Local model providers, downloads, launch, or connectivity area: packaging Packages, images, registries, installers, or distribution area: providers Inference provider integrations and provider behavior bug-fix PR fixes a bug or regression platform: container Affects Docker, containerd, Podman, or images and removed Local Models labels Jun 3, 2026
@wscurran wscurran added the NV QA Bugs found by the NVIDIA QA Team label Jun 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: local-models Local model providers, downloads, launch, or connectivity area: packaging Packages, images, registries, installers, or distribution area: providers Inference provider integrations and provider behavior bug-fix PR fixes a bug or regression NV QA Bugs found by the NVIDIA QA Team platform: container Affects Docker, containerd, Podman, or images

Projects

None yet

6 participants