Files
nanoclaw/docs/SECURITY.md
Gabi Simons 13ce4aaf67 feat: enhance container environment isolation via credential proxy (#798)
* feat: implement credential proxy for enhanced container environment isolation

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

* fix: address PR review — bind proxy to loopback, scope OAuth injection, add tests

- Bind credential proxy to 127.0.0.1 instead of 0.0.0.0 (security)
- OAuth mode: only inject Authorization on token exchange endpoint
- Add 5 integration tests for credential-proxy.ts
- Remove dangling comment
- Extract host gateway into container-runtime.ts abstraction
- Update Apple Container skill for credential proxy compatibility

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

* fix: scope OAuth token injection by header presence instead of path

Path-based matching missed auth probe requests the CLI sends before
the token exchange. Now the proxy replaces Authorization only when
the container actually sends one, leaving x-api-key-only requests
(post-exchange) untouched.

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

* fix: bind credential proxy to docker0 bridge IP on Linux

On bare-metal Linux Docker, containers reach the host via the bridge IP
(e.g. 172.17.0.1), not loopback. Detect the docker0 interface address
via os.networkInterfaces() and bind there instead of 0.0.0.0, so the
proxy is reachable by containers but not exposed to the LAN.

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

* fix: bind credential proxy to loopback on WSL

WSL uses Docker Desktop with the same VM routing as macOS, so
127.0.0.1 is correct and secure. Without this, the fallback to
0.0.0.0 was triggered because WSL has no docker0 interface.

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

* fix: detect WSL via /proc instead of env var

WSL_DISTRO_NAME isn't set under systemd. Use
/proc/sys/fs/binfmt_misc/WSLInterop which is always present on WSL.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 00:27:13 +02:00

6.3 KiB

NanoClaw Security Model

Trust Model

Entity Trust Level Rationale
Main group Trusted Private self-chat, admin control
Non-main groups Untrusted Other users may be malicious
Container agents Sandboxed Isolated execution environment
WhatsApp messages User input Potential prompt injection

Security Boundaries

1. Container Isolation (Primary Boundary)

Agents execute in containers (lightweight Linux VMs), providing:

  • Process isolation - Container processes cannot affect the host
  • Filesystem isolation - Only explicitly mounted directories are visible
  • Non-root execution - Runs as unprivileged node user (uid 1000)
  • Ephemeral containers - Fresh environment per invocation (--rm)

This is the primary security boundary. Rather than relying on application-level permission checks, the attack surface is limited by what's mounted.

2. Mount Security

External Allowlist - Mount permissions stored at ~/.config/nanoclaw/mount-allowlist.json, which is:

  • Outside project root
  • Never mounted into containers
  • Cannot be modified by agents

Default Blocked Patterns:

.ssh, .gnupg, .aws, .azure, .gcloud, .kube, .docker,
credentials, .env, .netrc, .npmrc, id_rsa, id_ed25519,
private_key, .secret

Protections:

  • Symlink resolution before validation (prevents traversal attacks)
  • Container path validation (rejects .. and absolute paths)
  • nonMainReadOnly option forces read-only for non-main groups

Read-Only Project Root:

The main group's project root is mounted read-only. Writable paths the agent needs (group folder, IPC, .claude/) are mounted separately. This prevents the agent from modifying host application code (src/, dist/, package.json, etc.) which would bypass the sandbox entirely on next restart.

3. Session Isolation

Each group has isolated Claude sessions at data/sessions/{group}/.claude/:

  • Groups cannot see other groups' conversation history
  • Session data includes full message history and file contents read
  • Prevents cross-group information disclosure

4. IPC Authorization

Messages and task operations are verified against group identity:

Operation Main Group Non-Main Group
Send message to own chat
Send message to other chats
Schedule task for self
Schedule task for others
View all tasks Own only
Manage other groups

5. Credential Isolation (Credential Proxy)

Real API credentials never enter containers. Instead, the host runs an HTTP credential proxy that injects authentication headers transparently.

How it works:

  1. Host starts a credential proxy on CREDENTIAL_PROXY_PORT (default: 3001)
  2. Containers receive ANTHROPIC_BASE_URL=http://host.docker.internal:<port> and ANTHROPIC_API_KEY=placeholder
  3. The SDK sends API requests to the proxy with the placeholder key
  4. The proxy strips placeholder auth, injects real credentials (x-api-key or Authorization: Bearer), and forwards to api.anthropic.com
  5. Agents cannot discover real credentials — not in environment, stdin, files, or /proc

NOT Mounted:

  • WhatsApp session (store/auth/) - host only
  • Mount allowlist - external, never mounted
  • Any credentials matching blocked patterns
  • .env is shadowed with /dev/null in the project root mount

Privilege Comparison

Capability Main Group Non-Main Group
Project root access /workspace/project (ro) None
Group folder /workspace/group (rw) /workspace/group (rw)
Global memory Implicit via project /workspace/global (ro)
Additional mounts Configurable Read-only unless allowed
Network access Unrestricted Unrestricted
MCP tools All All

Security Architecture Diagram

┌──────────────────────────────────────────────────────────────────┐
│                        UNTRUSTED ZONE                             │
│  WhatsApp Messages (potentially malicious)                        │
└────────────────────────────────┬─────────────────────────────────┘
                                 │
                                 ▼ Trigger check, input escaping
┌──────────────────────────────────────────────────────────────────┐
│                     HOST PROCESS (TRUSTED)                        │
│  • Message routing                                                │
│  • IPC authorization                                              │
│  • Mount validation (external allowlist)                          │
│  • Container lifecycle                                            │
│  • Credential proxy (injects auth headers)                       │
└────────────────────────────────┬─────────────────────────────────┘
                                 │
                                 ▼ Explicit mounts only, no secrets
┌──────────────────────────────────────────────────────────────────┐
│                CONTAINER (ISOLATED/SANDBOXED)                     │
│  • Agent execution                                                │
│  • Bash commands (sandboxed)                                      │
│  • File operations (limited to mounts)                            │
│  • API calls routed through credential proxy                     │
│  • No real credentials in environment or filesystem              │
└──────────────────────────────────────────────────────────────────┘