Repository navigation
Expand file tree
/
Copy pathdevcontainer.json
More file actions
175 lines (157 loc) · 8.32 KB
/
Copy pathdevcontainer.json
File metadata and controls
175 lines (157 loc) · 8.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
// =============================================================================
// AI Warden - Dev Container specification (Cursor / VS Code)
// -----------------------------------------------------------------------------
// HOW TO USE
// 1. Build the warden images once, from the AI Warden repo:
// make build (or ./scripts/warden-cli.sh build)
// 2. Start the egress filter:
// make up
// 3. Copy this file into the project you want to work on:
// mkdir -p /path/to/your-project/.devcontainer
// cp devcontainer/devcontainer.json /path/to/your-project/.devcontainer/
// 4. In Cursor or VS Code: "Reopen in Container".
//
// WHAT YOU GET
// The editor's terminal, its extensions and any agent you launch from it all
// run inside the same hardened sandbox that `warden-cli.sh run` produces:
// uid 1001, every capability dropped, no privilege escalation, only this
// project folder mounted, and no route to the internet except the allowlist
// proxy. Cursor's own agent, Claude Code, Aider and Codex are all contained
// by the same rules.
//
// This file is JSONC - comments are part of the format and are intentional.
// =============================================================================
{
"name": "AI Warden Sandbox",
// ---------------------------------------------------------------------------
// Use the prebuilt warden image. Building from core/Dockerfile here would only
// work inside the AI Warden repo itself; referencing the image by tag lets
// this file be dropped into any project unchanged.
//
// If you would rather build per project, replace "image" with:
// "build": { "dockerfile": "../core/Dockerfile", "context": ".." }
// ---------------------------------------------------------------------------
"image": "ai-warden/agent:latest",
// ---------------------------------------------------------------------------
// THE ISOLATION CONTRACT
// These flags are the whole point of AI Warden. Removing any one of them
// silently downgrades the sandbox, and the entrypoint will refuse to start
// (exit 78) rather than run an agent under a weakened posture.
// ---------------------------------------------------------------------------
"runArgs": [
"--cap-drop=ALL",
"--security-opt", "no-new-privileges:true",
// Internal network: declared `internal: true`, so there is no gateway
// route out. The only reachable destination is warden-egress-proxy:3128.
"--network=warden_internal",
"--hostname=warden-sandbox",
"--user=1001:1001",
// Resource ceilings: a runaway agent cannot exhaust the host.
"--memory=4g",
"--memory-swap=4g",
"--cpus=2",
"--pids-limit=512",
"--ulimit", "nofile=8192:8192",
// Writable scratch for the canary tripwire's state, owned by ai_user.
"--tmpfs", "/run/warden:rw,nosuid,size=16m,uid=1001,gid=1001",
// Canary vault. A Docker Desktop bind mount is 9p/virtiofs, where
// inotify_add_watch() succeeds and no event is ever delivered, so canaries
// in the workspace itself cannot be enforced there. This named volume lives
// on real ext4 inside the VM. initializeCommand creates it and hands it to
// uid 1001 before the container starts.
"-v", "warden_devcontainer_vault:/workspace/.secrets",
"--label", "ai.warden.role=agent-sandbox"
],
// ---------------------------------------------------------------------------
// Mount ONLY the opened project. Nothing else from the host is visible:
// no $HOME, no ~/.ssh, no ~/.aws, no Docker socket, no parent directory.
// ---------------------------------------------------------------------------
"workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=cached",
"workspaceFolder": "/workspace",
// Deliberately empty. Do NOT add SSH agent forwarding, a Docker socket bind,
// or a ~/.aws mount here - each one hands the agent exactly what this sandbox
// exists to withhold. (The canary vault is a warden-managed volume declared
// in runArgs above, not host data.)
"mounts": [],
"remoteUser": "ai_user",
"containerUser": "ai_user",
"updateRemoteUserUID": false,
// Keep the image ENTRYPOINT (warden-entrypoint) in charge: it seeds the
// canary tokens, arms the tripwire, verifies the launch posture and fails
// closed if the egress proxy is unreachable.
"overrideCommand": true,
"shutdownAction": "stopContainer",
// ---------------------------------------------------------------------------
// Egress: every client library inside the container is pointed at the proxy.
// ---------------------------------------------------------------------------
"containerEnv": {
"WARDEN_WORKSPACE": "/workspace",
"WARDEN_STRICT": "1",
"WARDEN_REQUIRE_PROXY": "1",
"WARDEN_CANARY_ACTION": "kill",
"HTTP_PROXY": "http://warden-egress-proxy:3128",
"HTTPS_PROXY": "http://warden-egress-proxy:3128",
"http_proxy": "http://warden-egress-proxy:3128",
"https_proxy": "http://warden-egress-proxy:3128",
"NO_PROXY": "localhost,127.0.0.1,::1,warden-egress-proxy",
"no_proxy": "localhost,127.0.0.1,::1,warden-egress-proxy"
},
// API keys are taken from YOUR shell at attach time. They are never written
// into the image, never committed, and disappear with the container.
"remoteEnv": {
"ANTHROPIC_API_KEY": "${localEnv:ANTHROPIC_API_KEY}",
"OPENAI_API_KEY": "${localEnv:OPENAI_API_KEY}",
"PERPLEXITY_API_KEY": "${localEnv:PERPLEXITY_API_KEY}",
"GEMINI_API_KEY": "${localEnv:GEMINI_API_KEY}",
"GITHUB_TOKEN": "${localEnv:GITHUB_TOKEN}"
},
// ---------------------------------------------------------------------------
// Check the warden network and proxy before the container is created. Runs on
// the HOST. It deliberately does NOT create the network itself: compose is
// what creates warden_internal, and a network made by hand lacks the
// com.docker.compose.network label, after which compose refuses to adopt it.
// ---------------------------------------------------------------------------
"initializeCommand": "docker volume create --label ai.warden.role=canary-vault warden_devcontainer_vault >/dev/null; docker network inspect warden_internal >/dev/null 2>&1 || echo 'AI Warden: warden_internal is missing - run \"make up\" in the AI Warden repo first'; docker ps --filter name=warden-egress-proxy --filter status=running --format '{{.Names}}' | grep -q warden-egress-proxy || echo 'AI Warden: egress proxy is not running - start it with \"make up\"; the sandbox will refuse to start without it'",
// Print the effective security posture into the dev container log, so a
// weakened configuration is visible immediately rather than discovered later.
"postAttachCommand": "printf '\\nAI Warden posture\\n user : %s (uid %s)\\n CapBnd : %s\\n NoNewPrivs: %s\\n proxy : %s\\n\\n' \"$(id -un)\" \"$(id -u)\" \"$(awk '/^CapBnd:/{print $2}' /proc/self/status)\" \"$(awk '/^NoNewPrivs:/{print $2}' /proc/self/status)\" \"${HTTPS_PROXY:-none}\"",
"customizations": {
"vscode": {
"extensions": [
"ms-python.python",
"dbaeumer.vscode-eslint",
"timonwong.shellcheck"
],
"settings": {
"terminal.integrated.defaultProfile.linux": "bash",
"terminal.integrated.profiles.linux": {
"bash": { "path": "/bin/bash", "args": ["-l"] }
},
// Keep the editor's own indexer away from the honeypot tokens. Reading
// one of them terminates the container, and a background file watcher
// has no business opening a file called .env.vault.
"files.exclude": {
"**/.secrets.canary": true,
"**/secrets.json": true,
"**/.env.vault": true
},
"files.watcherExclude": {
"**/.secrets.canary": true,
"**/secrets.json": true,
"**/.env.vault": true,
"**/node_modules/**": true
},
"search.exclude": {
"**/.secrets.canary": true,
"**/secrets.json": true,
"**/.env.vault": true
}
}
}
},
// No dev container Features: each one runs an install script at build time
// with network access, which is exactly the kind of unreviewed code this
// sandbox exists to keep out. Add tooling to core/Dockerfile instead, where
// it is reviewed once and pinned.
"features": {}
}