Fix/shadow env in container (#646)

* fix: shadow .env file in container to prevent agents from reading secrets

The main agent's container mounts the project root read-only, which
inadvertently exposed the .env file containing API keys. Mount /dev/null
over /workspace/project/.env to shadow it — secrets are already passed
via stdin and never need to be read from disk inside the container.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: adapt .env shadowing and runtime for Apple Container

Apple Container (VirtioFS) only supports directory mounts, not file
mounts. The previous /dev/null host-side mount over .env crashes with
VZErrorDomain "A directory sharing device configuration is invalid".

- Dockerfile: entrypoint now shadows .env via mount --bind inside the
  container, then drops privileges via setpriv to the host UID/GID
- container-runner: main containers skip --user and pass RUN_UID/RUN_GID
  env vars so entrypoint starts as root for mount --bind
- container-runtime: switch to Apple Container CLI (container), fix
  cleanupOrphans to use container list --format json
- Skill: add Dockerfile and container-runner.ts to
  convert-to-apple-container skill (v1.1.0)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: revert src to Docker runtime, keep Apple Container in skill only

The source files should remain Docker-compatible. The Apple Container
adaptations live in the convert-to-apple-container skill and are applied
on demand.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
gavrielc
2026-03-02 13:28:28 +02:00
committed by GitHub
parent 770231687a
commit bae8538695
7 changed files with 850 additions and 3 deletions

View File

@@ -13,11 +13,13 @@ This skill switches NanoClaw's container runtime from Docker to Apple Container
- Startup check: `docker info``container system status` (with auto-start)
- Orphan detection: `docker ps --filter``container ls --format json`
- Build script default: `docker``container`
- Dockerfile entrypoint: `.env` shadowing via `mount --bind` inside the container (Apple Container only supports directory mounts, not file mounts like Docker's `/dev/null` overlay)
- Container runner: main-group containers start as root for `mount --bind`, then drop privileges via `setpriv`
**What stays the same:**
- Dockerfile (shared by both runtimes)
- Container runner code (`src/container-runner.ts`)
- Mount security/allowlist validation
- All exported interfaces and IPC protocol
- Non-main container behavior (still uses `--user` flag)
- All other functionality
## Prerequisites
@@ -72,11 +74,15 @@ npx tsx scripts/apply-skill.ts .claude/skills/convert-to-apple-container
This deterministically:
- Replaces `src/container-runtime.ts` with the Apple Container implementation
- Replaces `src/container-runtime.test.ts` with Apple Container-specific tests
- Updates `src/container-runner.ts` with .env shadow mount fix and privilege dropping
- Updates `container/Dockerfile` with entrypoint that shadows .env via `mount --bind`
- Updates `container/build.sh` to default to `container` runtime
- Records the application in `.nanoclaw/state.yaml`
If the apply reports merge conflicts, read the intent files:
- `modify/src/container-runtime.ts.intent.md` — what changed and invariants
- `modify/src/container-runner.ts.intent.md` — .env shadow and privilege drop changes
- `modify/container/Dockerfile.intent.md` — entrypoint changes for .env shadowing
- `modify/container/build.sh.intent.md` — what changed for build script
### Validate code changes
@@ -172,4 +178,6 @@ Check directory permissions on the host. The container runs as uid 1000.
|------|----------------|
| `src/container-runtime.ts` | Full replacement — Docker → Apple Container API |
| `src/container-runtime.test.ts` | Full replacement — tests for Apple Container behavior |
| `src/container-runner.ts` | .env shadow mount removed, main containers start as root with privilege drop |
| `container/Dockerfile` | Entrypoint: `mount --bind` for .env shadowing, `setpriv` privilege drop |
| `container/build.sh` | Default runtime: `docker``container` |