Dockerfile: bump OpenClaw to 2026.3.22, widen CORS and trusted proxies#740
Dockerfile: bump OpenClaw to 2026.3.22, widen CORS and trusted proxies#740cluster2600 wants to merge 1 commit intoNVIDIA:mainfrom
Conversation
- Update openclaw from 2026.3.11 to 2026.3.22 - Add wildcard and port 3333 to allowedOrigins so remote dashboard access (Mission Control, SSH tunnels) works out of the box - Add RFC 1918 private network CIDRs to trustedProxies for reverse proxy and SSH tunnel setups common on headless DGX Spark deployments Fixes: NVIDIA#739 Signed-off-by: Maxime Grenu <maxime.grenu@gmail.com>
📝 WalkthroughWalkthroughThe Dockerfile updates the OpenClaw CLI version from 2026.3.11 to 2026.3.22 and modifies the generated Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
Dockerfile (1)
59-60: Expose the OpenClaw version as a build arg to support rebuild-time upgrades without file edits.The Dockerfile already uses ARGs for "config that varies per deployment" (line 82 comment). The OpenClaw version fits this pattern perfectly—it differs between the main Dockerfile (2026.3.22) and test Dockerfile (2026.3.11), and operators need a way to upgrade without editing the file.
♻️ Suggested change
+# Allows rebuild-time OpenClaw upgrades without editing this file. +ARG OPENCLAW_VERSION=2026.3.22 + # Install OpenClaw CLI and PyYAML for blueprint runner (single layer) -RUN npm install -g openclaw@2026.3.22 \ +RUN npm install -g openclaw@${OPENCLAW_VERSION} \ && pip3 install --no-cache-dir --break-system-packages "pyyaml==6.0.3"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Dockerfile` around lines 59 - 60, Add a build ARG for the OpenClaw version and use it in the npm install command so operators can override the version at build time; introduce ARG OPENCLAW_VERSION with the current default value (e.g., 2026.3.22) before the RUN that installs openclaw and replace the hard-coded literal in the RUN npm install -g openclaw@... with openclaw@${OPENCLAW_VERSION}; also update any companion test Dockerfile or build scripts that currently rely on the hard-coded version to pass a different --build-arg OPENCLAW_VERSION when needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@Dockerfile`:
- Around line 109-110: The default CORS origins list currently includes the
wildcard '*' (variable origins) which permits any browser origin; remove '*'
from the hardcoded default origins and only include chat_origin (derived from
CHAT_UI_URL) and any explicitly configured opt-in origins. Update the origins
assignment (the origins variable and the subsequent list(dict.fromkeys(origins +
[chat_origin])) logic) to: 1) omit '*' from the initial list, 2) append
chat_origin if present, and 3) optionally append extra origins only when an
explicit env/config (e.g., ALLOWED_ORIGINS or a configured extra_origins value)
is provided—preserve the uniqueness dedup step and keep
dangerouslyDisableDeviceAuth and allowInsecureAuth handling unchanged.
- Line 135: The current default for 'trustedProxies' includes all RFC1918 ranges
which allows any host on those networks to supply X-Forwarded-For; change the
Dockerfile/default config so 'trustedProxies' defaults to only loopback
(['127.0.0.1', '::1']) and add a build-time argument (e.g., PROXY_CIDRS) to pass
the actual proxy IPs/CIDRs into the image, then populate 'trustedProxies' from
that arg at build/startup (if provided) so deployments explicitly supply gateway
IPs/CIDRs rather than trusting entire private ranges.
---
Nitpick comments:
In `@Dockerfile`:
- Around line 59-60: Add a build ARG for the OpenClaw version and use it in the
npm install command so operators can override the version at build time;
introduce ARG OPENCLAW_VERSION with the current default value (e.g., 2026.3.22)
before the RUN that installs openclaw and replace the hard-coded literal in the
RUN npm install -g openclaw@... with openclaw@${OPENCLAW_VERSION}; also update
any companion test Dockerfile or build scripts that currently rely on the
hard-coded version to pass a different --build-arg OPENCLAW_VERSION when needed.
| origins = ['*', 'http://127.0.0.1:18789', 'http://127.0.0.1:3333']; \ | ||
| origins = list(dict.fromkeys(origins + [chat_origin])); \ |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Does OpenClaw's gateway.controlUi.allowedOriginstreat'*'as a wildcard, and are there documented security caveats whenallowInsecureAuthanddangerouslyDisableDeviceAuth are enabled?
💡 Result:
-
Yes.
gateway.controlUi.allowedOrigins: ["*"]is treated as “allow any browser origin” (i.e., a wildcard). The docs explicitly warn it does not mean “match whatever host I’m using,” and say to avoid it except for tightly controlled local testing. [1] -
Documented security caveats:
gateway.controlUi.allowInsecureAuthis described as a local compatibility toggle only: it allows localhost Control UI sessions to proceed without device identity in non-secure HTTP contexts, does not bypass pairing, and does not relax remote (non-localhost) device-identity requirements. [1]gateway.controlUi.dangerouslyDisableDeviceAuthdisables Control UI device identity checks and is documented as a severe security downgrade (“break-glass only; revert quickly”). It is also flagged as a critical security finding in the Gateway security guidance. [1][2]
🏁 Script executed:
#!/bin/bash
# Read the Dockerfile around lines 109-110 with broader context
head -130 Dockerfile | tail -30Repository: NVIDIA/NemoClaw
Length of output: 1625
🏁 Script executed:
#!/bin/bash
# Search for gateway config setup and flag enablement
rg -n "allowInsecureAuth|dangerouslyDisableDeviceAuth|controlUi" Dockerfile -B 2 -A 2Repository: NVIDIA/NemoClaw
Length of output: 304
🏁 Script executed:
#!/bin/bash
# Look for any environment variable patterns and build args
rg -n "ENV|ARG" Dockerfile | head -20Repository: NVIDIA/NemoClaw
Length of output: 349
Remove the wildcard from the default CORS origins list.
The '*' at line 109 allows any browser origin to access the Control UI (confirmed by OpenClaw docs). Combined with dangerouslyDisableDeviceAuth: True enabled at line 132, this removes device identity validation entirely—a severe security downgrade. The allowInsecureAuth flag only applies to localhost, not cross-origin requests.
chat_origin derived from CHAT_UI_URL already covers the remote dashboard case. Instead of shipping '*' as a default, make extra origins opt-in:
Suggested approach
-origins = ['*', 'http://127.0.0.1:18789', 'http://127.0.0.1:3333']; \
+extra_origins = [o.strip() for o in os.environ.get('OPENCLAW_ALLOWED_ORIGINS', '').split(',') if o.strip()]; \
+origins = ['http://127.0.0.1:18789', 'http://127.0.0.1:3333', *extra_origins]; \
origins = list(dict.fromkeys(origins + [chat_origin])); \📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| origins = ['*', 'http://127.0.0.1:18789', 'http://127.0.0.1:3333']; \ | |
| origins = list(dict.fromkeys(origins + [chat_origin])); \ | |
| extra_origins = [o.strip() for o in os.environ.get('OPENCLAW_ALLOWED_ORIGINS', '').split(',') if o.strip()]; \ | |
| origins = ['http://127.0.0.1:18789', 'http://127.0.0.1:3333', *extra_origins]; \ | |
| origins = list(dict.fromkeys(origins + [chat_origin])); \ |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Dockerfile` around lines 109 - 110, The default CORS origins list currently
includes the wildcard '*' (variable origins) which permits any browser origin;
remove '*' from the hardcoded default origins and only include chat_origin
(derived from CHAT_UI_URL) and any explicitly configured opt-in origins. Update
the origins assignment (the origins variable and the subsequent
list(dict.fromkeys(origins + [chat_origin])) logic) to: 1) omit '*' from the
initial list, 2) append chat_origin if present, and 3) optionally append extra
origins only when an explicit env/config (e.g., ALLOWED_ORIGINS or a configured
extra_origins value) is provided—preserve the uniqueness dedup step and keep
dangerouslyDisableDeviceAuth and allowInsecureAuth handling unchanged.
| 'allowedOrigins': origins, \ | ||
| }, \ | ||
| 'trustedProxies': ['127.0.0.1', '::1'], \ | ||
| 'trustedProxies': ['127.0.0.1', '::1', '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'], \ |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Does OpenClaw's gateway.trustedProxiesaccept CIDR ranges, and if so, does trusting all RFC1918 ranges causeX-Forwarded-For from any private-network client to be honored?
💡 Result:
Yes—gateway.trustedProxies effectively accepts CIDR ranges.
- In OpenClaw’s gateway code, proxy trust is checked with
isIpInCidr(normalized, candidate), where eachcandidateis an entry fromtrustedProxies(so entries like10.0.0.0/8work, not just single IPs). [1] - When
trustedProxiesis set, the Gateway usesX-Forwarded-Forto determine the client IP (and ignoresX-Real-IPunless explicitly enabled). [2]
And yes—if you “trust” all RFC1918 ranges (e.g., 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16), then any client connecting from those private address ranges will be treated as a trusted proxy, meaning their X-Forwarded-For chain will be honored for client-IP resolution. That’s why the docs emphasize only adding your actual proxy IPs. [1][3]
Important nuance: this does not automatically make such clients “local”; OpenClaw fails closed if a request arrives from a “trusted proxy” IP but forwarded headers are missing/invalid, and it still has additional “local direct” checks beyond just trusting X-Forwarded-For. [1]
Sources:
[1] https://raw.githubusercontent.com/openclaw/openclaw/refs/heads/main/src/gateway/net.ts
[2] https://docs.openclaw.ai/gateway/security
[3] https://docs.openclaw.ai/gateway/trusted-proxy-auth
Scope trustedProxies to the real proxy hop.
Line 135 trusts all RFC 1918 ranges by default. Since CIDR ranges are honored for proxy trust checks, this means any host on those private networks can provide X-Forwarded-For headers that will be used for client IP resolution—weakening client IP attribution and IP-based controls in deployments where the gateway is reachable directly from the LAN/VPC. Default to loopback only and pass actual proxy IPs/CIDRs via a build argument.
🛡️ Suggested approach
- 'trustedProxies': ['127.0.0.1', '::1', '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'], \
+ 'trustedProxies': ['127.0.0.1', '::1'] + [p.strip() for p in os.environ.get('OPENCLAW_TRUSTED_PROXIES', '').split(',') if p.strip()], \🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Dockerfile` at line 135, The current default for 'trustedProxies' includes
all RFC1918 ranges which allows any host on those networks to supply
X-Forwarded-For; change the Dockerfile/default config so 'trustedProxies'
defaults to only loopback (['127.0.0.1', '::1']) and add a build-time argument
(e.g., PROXY_CIDRS) to pass the actual proxy IPs/CIDRs into the image, then
populate 'trustedProxies' from that arg at build/startup (if provided) so
deployments explicitly supply gateway IPs/CIDRs rather than trusting entire
private ranges.
|
Thanks for submitting this PR, it addresses the issue with the pinned OpenClaw version and restrictive allowedOrigins in the Dockerfile configuration, and suggests enhancements to improve the setup process and remote dashboard access. |
Summary
2026.3.11to2026.3.22— picks up recent fixesallowedOrigins— add'*'andhttp://127.0.0.1:3333so that remote dashboard access (Mission Control, SSH tunnels from LAN machines) works without manual config changes. The sandbox config is read-only at runtime, so users currently have no way to fix this themselves without rebuilding the image.trustedProxies—10.0.0.0/8,172.16.0.0/12,192.168.0.0/16so thatX-Forwarded-Forheaders are trusted correctly in reverse proxy and SSH tunnel setups, which are the standard access pattern for headless DGX Spark deployments.Motivation
On a headless DGX Spark accessed via SSH tunnel or LAN, the current config blocks legitimate dashboard connections because:
allowedOriginsonly includeshttp://127.0.0.1:18789trustedProxiesonly includes loopback addressesSince
openclaw.jsonis intentionally immutable in the sandbox (Landlock + DAC), users cannot fix this at runtime — it must be correct at image build time.Fixes #739
Test plan
openclaw --versionSummary by CodeRabbit