fix(image): add missing openclaw-data symlinks for runtime-writable paths#1519
Conversation
…legram, credentials OpenClaw writes to exec-approvals.json, telegram/, and credentials/ at runtime, but these paths had no symlinks from the root-owned .openclaw/ directory to the sandbox-writable .openclaw-data/. This caused EACCES permission denied errors for tool approvals, Telegram offset persistence, and WhatsApp credential storage. Fixes #1027 Refs #975, #1114 Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe Docker base image is extended with two new state subdirectories ( Changes
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~2 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
…aths (NVIDIA#1519) ## Summary - Add symlinks for `exec-approvals.json`, `telegram/`, and `credentials/` from `.openclaw/` to `.openclaw-data/` - These paths are written by OpenClaw at runtime but had no writable target, causing EACCES errors - Follows the exact same pattern as the 11 existing symlinks in `Dockerfile.base` ## Security posture Unchanged. The `.openclaw/` directory remains root-owned (755), all symlinks are root-owned and `chattr +i` locked at runtime by `nemoclaw-start.sh`. The agent can only write file contents through the symlinks to sandbox-owned `.openclaw-data/`. ## Fixes - Fixes NVIDIA#1027 — `exec-approvals.json` EACCES - Refs NVIDIA#975 — Telegram offset persistence EACCES - Refs NVIDIA#1114 — WhatsApp credentials EACCES <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Extended Docker image filesystem with new data storage directories for telegram and credentials management. * Added approvals configuration file to the Docker environment setup. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
… (#1305) ## Summary Fixes the four issues reported in #1114 — EACCES permission errors and missing gateway token when running inside the NemoClaw sandbox. ### Issue mapping | # | Reported error | Fix | |---|----------------|-----| | 1 | `EACCES: open '/sandbox/.openclaw/openclaw.json.*.tmp'` | `install_configure_guard` — intercepts `openclaw configure` with a clear error and directs users to `nemoclaw onboard --resume` on the host | | 2 | Same as #1 (different PID) | Same fix | | 3 | `EACCES: mkdir '/sandbox/.openclaw/credentials'` | Already resolved on main via #1519 (credentials symlink to `.openclaw-data/`) | | 4 | No WhatsApp QR code | Consequence of #3, also resolved by #1519 | ### Root cause (issues 1 & 2) OpenClaw's `configure` command performs atomic writes — it creates a temp file (`openclaw.json.PID.UUID.tmp`) in the same directory as the config. Since `/sandbox/.openclaw/` is Landlock read-only at the kernel level, file creation is rejected with EACCES. This is by design: the sandbox config is intentionally immutable at runtime. Rather than weakening Landlock (security regression), we intercept the command in the sandbox shell and guide users to the correct host-side workflow. ### Changes **1. `install_configure_guard()`** — Writes a shell function wrapper to `.bashrc`/`.profile` that intercepts `openclaw configure` and prints: ``` Error: 'openclaw configure' cannot modify config inside the sandbox. The sandbox config is read-only (Landlock enforced) for security. To change your configuration, exit the sandbox and run: nemoclaw onboard --resume This rebuilds the sandbox with your updated settings. ``` All other `openclaw` subcommands pass through to the real binary. **2. `export_gateway_token()`** — Reads `gateway.auth.token` from `openclaw.json` and exports it as `OPENCLAW_GATEWAY_TOKEN`, so interactive sessions (`openshell sandbox connect`) can authenticate with the gateway. Persists to `.bashrc`/`.profile` using idempotent marker blocks and cleans stale tokens on revocation. **3. `_read_gateway_token()` helper** — Shared Python snippet used by both `export_gateway_token` and `print_dashboard_urls` (deduplication, uses `with open()` context manager). All three are called in both root and non-root startup paths. ## Security properties preserved - `/sandbox/.openclaw` remains root-owned, Landlock read-only - `openclaw.json` remains chmod 444 (immutable) - No new attack surface — token is read-only from existing config - `command openclaw` bypass preserves all non-configure functionality Fixes #1114 Signed-off-by: Dongni Yang <dongniy@nvidia.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Signed-off-by: Dongni Yang <dongniy@nvidia.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…aths (NVIDIA#1519) ## Summary - Add symlinks for `exec-approvals.json`, `telegram/`, and `credentials/` from `.openclaw/` to `.openclaw-data/` - These paths are written by OpenClaw at runtime but had no writable target, causing EACCES errors - Follows the exact same pattern as the 11 existing symlinks in `Dockerfile.base` ## Security posture Unchanged. The `.openclaw/` directory remains root-owned (755), all symlinks are root-owned and `chattr +i` locked at runtime by `nemoclaw-start.sh`. The agent can only write file contents through the symlinks to sandbox-owned `.openclaw-data/`. ## Fixes - Fixes NVIDIA#1027 — `exec-approvals.json` EACCES - Refs NVIDIA#975 — Telegram offset persistence EACCES - Refs NVIDIA#1114 — WhatsApp credentials EACCES <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Extended Docker image filesystem with new data storage directories for telegram and credentials management. * Added approvals configuration file to the Docker environment setup. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
…IA#1114) (NVIDIA#1305) ## Summary Fixes the four issues reported in NVIDIA#1114 — EACCES permission errors and missing gateway token when running inside the NemoClaw sandbox. ### Issue mapping | # | Reported error | Fix | |---|----------------|-----| | 1 | `EACCES: open '/sandbox/.openclaw/openclaw.json.*.tmp'` | `install_configure_guard` — intercepts `openclaw configure` with a clear error and directs users to `nemoclaw onboard --resume` on the host | | 2 | Same as NVIDIA#1 (different PID) | Same fix | | 3 | `EACCES: mkdir '/sandbox/.openclaw/credentials'` | Already resolved on main via NVIDIA#1519 (credentials symlink to `.openclaw-data/`) | | 4 | No WhatsApp QR code | Consequence of NVIDIA#3, also resolved by NVIDIA#1519 | ### Root cause (issues 1 & 2) OpenClaw's `configure` command performs atomic writes — it creates a temp file (`openclaw.json.PID.UUID.tmp`) in the same directory as the config. Since `/sandbox/.openclaw/` is Landlock read-only at the kernel level, file creation is rejected with EACCES. This is by design: the sandbox config is intentionally immutable at runtime. Rather than weakening Landlock (security regression), we intercept the command in the sandbox shell and guide users to the correct host-side workflow. ### Changes **1. `install_configure_guard()`** — Writes a shell function wrapper to `.bashrc`/`.profile` that intercepts `openclaw configure` and prints: ``` Error: 'openclaw configure' cannot modify config inside the sandbox. The sandbox config is read-only (Landlock enforced) for security. To change your configuration, exit the sandbox and run: nemoclaw onboard --resume This rebuilds the sandbox with your updated settings. ``` All other `openclaw` subcommands pass through to the real binary. **2. `export_gateway_token()`** — Reads `gateway.auth.token` from `openclaw.json` and exports it as `OPENCLAW_GATEWAY_TOKEN`, so interactive sessions (`openshell sandbox connect`) can authenticate with the gateway. Persists to `.bashrc`/`.profile` using idempotent marker blocks and cleans stale tokens on revocation. **3. `_read_gateway_token()` helper** — Shared Python snippet used by both `export_gateway_token` and `print_dashboard_urls` (deduplication, uses `with open()` context manager). All three are called in both root and non-root startup paths. ## Security properties preserved - `/sandbox/.openclaw` remains root-owned, Landlock read-only - `openclaw.json` remains chmod 444 (immutable) - No new attack surface — token is read-only from existing config - `command openclaw` bypass preserves all non-configure functionality Fixes NVIDIA#1114 Signed-off-by: Dongni Yang <dongniy@nvidia.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Signed-off-by: Dongni Yang <dongniy@nvidia.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
, NVIDIA#514) The original code fix for GatewayRequestError EACCES on exec-approvals.json landed via NVIDIA#1519, which added just the two symlink/touch lines in Dockerfile.base without any regression tests. This adds static assertions on the image-build sources so a future refactor that drops the provisioning steps fails fast in CI, before a docker build runs. Covers: - NVIDIA#1027/NVIDIA#1519: exec-approvals.json + update-check.json backing files and symlinks in Dockerfile.base (including ordering: data file created before the symlink that points at it). - NVIDIA#514: openclaw.json stays mode 0444, .config-hash stays root:root 0444, and /sandbox/.openclaw/ itself stays root:root 0755 so the agent cannot replace symlinks or forge an integrity hash. Signed-off-by: Tony Luo <xialuo@nvidia.com> Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
) (#1236) ## Summary Reboot of this PR after #1519 merged the actual code fix for issue #1027. This branch is now **test-only** — it adds static regression guards so a future refactor that drops the provisioning steps fails fast in CI, before a docker build runs. The original #1236 carried the code fix alongside tests; since upstream already has the fix from #1519, I rebased to upstream/main and kept only the missing regression coverage. ## What this adds `test/sandbox-provisioning.test.ts` — 8 static assertions on the image-build sources: **#1027 / #1519 (runtime-writable symlinks)** - `Dockerfile.base` creates the `exec-approvals.json` backing file in `.openclaw-data` - `Dockerfile.base` symlinks `.openclaw/exec-approvals.json` → `.openclaw-data/exec-approvals.json` - Same pair for `update-check.json` - Ordering guard: the data file is created before the symlink that points at it **#514 (root-owned read-only config)** - `Dockerfile` keeps `openclaw.json` at mode 0444 (agent cannot tamper with auth token / CORS) - `Dockerfile` keeps `.config-hash` at root:root 0444 (agent cannot forge integrity hash) - `Dockerfile` keeps `.openclaw/` at root:root 0755 (agent cannot replace symlinks) ## Why these tests matter - The existing `test/exec-approvals-path-regression.test.ts` only verifies the path-patching grep logic in `Dockerfile.base`, not the actual symlink/data file creation - The existing `test/e2e-gateway-isolation.sh` catches regressions at image-build time, but requires docker and a full build; static tests fail in seconds on every PR - #1519 was a 6-line code fix with zero test coverage. If a future cleanup of `Dockerfile.base` drops those six lines, the issue returns silently ## Test plan - [x] `npx vitest run test/sandbox-provisioning.test.ts` — 8/8 passing - [x] Rebased onto current `upstream/main` (`d9aced49`) - [x] No code changes; test-only PR Signed-off-by: Tony Luo <xialuo@nvidia.com> 🤖 Generated with [Claude Code](https://claude.com/claude-code) Signed-off-by: Tony Luo <xialuo@nvidia.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Aaron Erickson 🦞 <aerickson@nvidia.com>
, NVIDIA#514) (NVIDIA#1236) ## Summary Reboot of this PR after NVIDIA#1519 merged the actual code fix for issue NVIDIA#1027. This branch is now **test-only** — it adds static regression guards so a future refactor that drops the provisioning steps fails fast in CI, before a docker build runs. The original NVIDIA#1236 carried the code fix alongside tests; since upstream already has the fix from NVIDIA#1519, I rebased to upstream/main and kept only the missing regression coverage. ## What this adds `test/sandbox-provisioning.test.ts` — 8 static assertions on the image-build sources: **NVIDIA#1027 / NVIDIA#1519 (runtime-writable symlinks)** - `Dockerfile.base` creates the `exec-approvals.json` backing file in `.openclaw-data` - `Dockerfile.base` symlinks `.openclaw/exec-approvals.json` → `.openclaw-data/exec-approvals.json` - Same pair for `update-check.json` - Ordering guard: the data file is created before the symlink that points at it **NVIDIA#514 (root-owned read-only config)** - `Dockerfile` keeps `openclaw.json` at mode 0444 (agent cannot tamper with auth token / CORS) - `Dockerfile` keeps `.config-hash` at root:root 0444 (agent cannot forge integrity hash) - `Dockerfile` keeps `.openclaw/` at root:root 0755 (agent cannot replace symlinks) ## Why these tests matter - The existing `test/exec-approvals-path-regression.test.ts` only verifies the path-patching grep logic in `Dockerfile.base`, not the actual symlink/data file creation - The existing `test/e2e-gateway-isolation.sh` catches regressions at image-build time, but requires docker and a full build; static tests fail in seconds on every PR - NVIDIA#1519 was a 6-line code fix with zero test coverage. If a future cleanup of `Dockerfile.base` drops those six lines, the issue returns silently ## Test plan - [x] `npx vitest run test/sandbox-provisioning.test.ts` — 8/8 passing - [x] Rebased onto current `upstream/main` (`d9aced49`) - [x] No code changes; test-only PR Signed-off-by: Tony Luo <xialuo@nvidia.com> 🤖 Generated with [Claude Code](https://claude.com/claude-code) Signed-off-by: Tony Luo <xialuo@nvidia.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Aaron Erickson 🦞 <aerickson@nvidia.com>
Summary
exec-approvals.json,telegram/, andcredentials/from.openclaw/to.openclaw-data/Dockerfile.baseSecurity posture
Unchanged. The
.openclaw/directory remains root-owned (755), all symlinks are root-owned andchattr +ilocked at runtime bynemoclaw-start.sh. The agent can only write file contents through the symlinks to sandbox-owned.openclaw-data/.Fixes
exec-approvals.jsonEACCESSummary by CodeRabbit