你有没有发现一件很可怕的事:AI Agent 越聪明,你越容易变蠢。
因为它会不断问你:
“我要执行 npm install,可以吗?”
“我要跑测试,可以吗?”
“我要调试错误,可以吗?”
“我要 curl 一个地址,可以吗?”
你一开始还认真看。 到第十次,你已经不看了——手比脑子快,直接点Yes。
这不是你懒。 这是人类的大脑被系统训练出来的审批疲劳(approval fatigue)。
而审批疲劳,就是安全的天敌。
所以真正的解决方案不是让 AI “问得更礼貌”,而是——让它根本没必要问。
把它关进一个盒子里。 一个它想作死也作不出去的盒子。
今天我就带你搭一个:给 AI Agent 用的 Docker 安全沙盒 + 网络代理防火墙。
它能做到:
允许读写你的项目目录
允许联网
但联网只能访问你允许的域名
容器系统本身只读,删不掉 /bin
就算 Agent 想偷你的 .env,也发不出去
你不需要再不断弹窗批准。 你只需要相信“笼子”,而不是相信“机器人”。
一、整体架构:别给 Agent 自由,给它规则
我们要的不是一个普通容器。 我们要的是一个“可控环境”。
整个系统有两道防线:
1)文件系统监狱(Docker)
容器内部看到的是一个只读的操作系统。
它能写的地方只有:
你挂载进去的项目目录(比如 /workspace)
临时目录 /tmp(用 tmpfs,容器死了就清空)
它想碰你电脑里的东西? 对不起,根本看不见。
SSH Key?看不到
你别的目录里的 .env?碰不到
系统配置文件?动不了
一句话:它只能在你允许的文件夹里“折腾”。
2)网络门卫(Node.js Proxy)
更狠的来了。
我们不直接给容器开“全网通”。 我们让它所有网络请求都必须经过一个代理。
这个代理做的事很简单:
请求域名在白名单 → 放行
不在白名单 → 直接掐断
这就像机场安检: 你可以飞,但你不能带炸弹。
二、第一部分:写一个网络“门卫”(Proxy)
Docker 自带网络控制,但它是按 IP 控的。 问题是你真正想管的是域名,比如:
registry.npmjs.org
github.com
所以我们自己写个极简 Node.js 代理。
创建 proxy.js
新建文件proxy.js:
const http = require('http'); const net = require('net'); const url = require('url'); // 1. The Policy const ALLOWED_DOMAINS = [ 'registry.npmjs.org', // For installing packages 'github.com', // For cloning repos 'api.openai.com' // If the agent needs to talk to the LLM from inside ]; const PORT = 8888; const server = http.createServer((req, res) => { // Handle standard HTTP requests (less common now, but good to have) const parsed = url.parse(req.url); if (!isAllowed(parsed.hostname)) { res.writeHead(403); res.end('Blocked by Sandbox Proxy'); console.log(`[BLOCKED] HTTP request to ${parsed.hostname}`); return; } // ... (HTTP forwarding logic would go here, omitting for brevity/HTTPS focus) }); // 2. Handle HTTPS (The CONNECT method) server.on('connect', (req, clientSocket, head) => { const { port, hostname } = url.parse(`//${req.url}`, false, true); if (!isAllowed(hostname)) { console.log(`[BLOCKED] CONNECT request to ${hostname}`); clientSocket.write('HTTP/1.1 403 Forbidden\r\n\r\n'); clientSocket.end(); return; } console.log(`[ALLOWED] ${hostname}`); // 3. The Pipe const serverSocket = net.connect(port || 443, hostname, () => { clientSocket.write('HTTP/1.1 200 Connection Established\r\n\r\n'); serverSocket.write(head); serverSocket.pipe(clientSocket); clientSocket.pipe(serverSocket); }); serverSocket.on('error', (err) => { console.error(`[ERROR] Connection to ${hostname} failed:`, err.message); clientSocket.end(); }); }); function isAllowed(hostname) { // Simple substring match to allow subdomains (e.g., sub.github.com) return ALLOWED_DOMAINS.some(d => hostname === d || hostname.endsWith(`.${d}`)); } server.listen(PORT, () => { console.log(`🛡️ Sandbox Proxy running on port ${PORT}`); });启动代理
终端运行:
node proxy.js这玩意儿就是你的“网络防火墙”。
从此以后,容器里的 AI 想联网? 先过我这一关。
三、第二部分:把 Agent 关进 Docker “牢房”
现在开始搭“监狱”。
我们的目标配置是:
根目录只读(--read-only)
/tmp 用 tmpfs 临时存储
容器里用普通用户运行,别用 root
强制走 HTTP_PROXY / HTTPS_PROXY
只挂载当前目录,不给它全盘权限
概念 Docker 命令(先看效果)
docker run \ --rm \ --user $(id -u):$(id -g) \ --read-only \ --tmpfs /tmp \ --network host \ -v $(pwd):/workspace \ -w /workspace \ -e HTTP_PROXY=http://localhost:8888 \ -e HTTPS_PROXY=http://localhost:8888 \ node:18-slim \ npm install看到这里你可能会皱眉:为什么用 --network host?
原因很简单: 在 Linux 上,host 模式最省事,容器可以直接访问 localhost:8888。
如果你是 macOS 或 Windows: host 网络模式不一样,你得改成 bridge,然后代理地址写:
host.docker.internal:8888四、第三部分:写一个“沙盒执行器”(Runner)
我们要做的,是让 AI Agent 不再直接 exec()。 它只允许调用这个 runner。
你可以理解为:
AI 想执行命令? 可以。 但只能通过“狱警”执行。
创建 sandbox-runner.js
新建sandbox-runner.js:
const { spawn } = require('child_process'); const os = require('os'); const commandToRun = process.argv[2] || 'echo "No command provided"'; const isMac = os.platform() === 'darwin'; // Configure network for Mac vs Linux const networkFlag = isMac ? [] : ['--network', 'host']; const proxyHost = isMac ? 'host.docker.internal' : 'localhost'; const proxyUrl = `http://${proxyHost}:8888`; const dockerArgs = [ 'run', '--rm', // Cleanup after run '--read-only', // Immutable OS '--tmpfs', '/tmp', // Writable temp space '--tmpfs', '/run', // Needed for some tools '-v', `${process.cwd()}:/workspace`, // Mount current dir ONLY '-w', '/workspace', // Set working dir '-e', `HTTP_PROXY=${proxyUrl}`, // Force traffic to proxy '-e', `HTTPS_PROXY=${proxyUrl}`, '-e', 'NPM_CONFIG_REGISTRY=http://registry.npmjs.org/', // Force non-SSL for easier proxying, or allow HTTPS registry ...networkFlag, 'node:18-slim', // Image to use '/bin/sh', '-c', commandToRun ]; console.log(`📦 Sandboxing: "${commandToRun}"`); const child = spawn('docker', dockerArgs, { stdio: 'inherit' }); child.on('close', (code) => { console.log(`\nEnd of sandbox session. Exit code: ${code}`); });现在你的 Agent 执行任何命令,只需要调用:
node sandbox-runner.js "npm install"就行。
五、开始测试:让它试试“越狱”
先开代理:
node proxy.js然后新开一个终端。
1)测试禁止访问:Google
node sandbox-runner.js "curl -I https://google.com"结果:直接失败。 代理会输出:
[BLOCKED] CONNECT request to google.com爽不爽? 这才叫控制权。
2)测试允许访问:GitHub
node sandbox-runner.js "curl -I https://github.com"结果成功。
你会看到:
HTTP/1.1 200 Connection Established3)测试删除系统文件
node sandbox-runner.js "rm -rf /bin"结果是:
rm: cannot remove '/bin': Read-only file system容器系统是只读的,它连“自残”都做不到。
六、为什么这套东西意义巨大?
因为它改变了安全模型。
以前的安全是什么?
以前的安全靠的是:流程。
AI 每执行一步,你都要看一眼。
理论上你应该认真审查。 但现实是——你迟早会累。
累了之后你就会进入一种状态:
只要不是 rm -rf /,我就点允许。 只要它说“为了修复 bug”,我就点允许。
然后有一天,它顺手把你的.env上传到了某个地址。 你连发现都来不及。
现在的安全是什么?
现在的安全靠的是:架构。
你不需要一直盯着 AI。 你只需要盯着“规则”。
AI 想把.env发到 pastebin?
对不起:
pastebin.com 不在白名单
网络请求被代理掐断
它上传不了
你甚至可以不在电脑前。 它也出不了事。
这就叫:信任盒子,不信任机器人。
七、局限性
这套方案很强,但不是“绝对安全”。
它能挡住 99% 的现实事故,但不是核弹级防御。
1)Docker Socket 是绝对禁区
永远别挂载:
/var/run/docker.sock一旦挂载进去,Agent 就能自己启动一个特权容器。 然后越狱成功。
一句话:你等于把监狱钥匙递给犯人。
2)代理规则靠环境变量,有绕开的可能
我们靠的是:
HTTP_PROXY
HTTPS_PROXY
如果 Agent 运行某些恶意二进制,完全可以无视这些变量,直接连 IP。
企业环境里通常会用防火墙阻断非代理流量。 本地开发环境里,这套方案主要防:
误操作泄露
供应链攻击(npm install 拉恶意包)
Agent “顺手”上传敏感文件
3)性能开销
每次执行命令都要起一个容器,肯定有成本。
但对于“对话式写代码”来说,这点开销可以忽略。 如果你要极限跑测试循环,那可以把容器常驻。
八、最终结论:别让 Agent 变成你最大的安全漏洞
AI Agent 越来越强。 强到你给它权限,它真的敢乱来。
你要做的不是让它更听话, 而是让它在可控的范围里“撒野”。
你应该给它一个游乐场,而不是把整个家交给它。
你只需要三样东西:
proxy.js:网络门卫
docker read-only sandbox:文件系统牢房
sandbox-runner.js:统一执行入口
总共加起来也就几十行代码。
但它能帮你避免某个深夜里:
你点了第 11 次“允许” 然后第二天醒来发现密钥全没了。
如果你愿意,我还可以继续帮你把这篇文章再升级成更“咪蒙风”的版本,比如:
更强的情绪钩子
更像朋友圈爆款的节奏
更多反问句、短句、狠句
开头直接抓人那种
全栈AI·探索:涵盖动效、React Hooks、Vue 技巧、LLM 应用、Python 脚本等专栏,案例驱动实战学习,点击二维码了解更多详情。
最后:
CSS终极指南
Vue 设计模式实战指南
20个前端开发者必备的响应式布局
深入React:从基础到最佳实践完整攻略
python 技巧精讲
React Hook 深入浅出
CSS技巧与案例详解
vue2与vue3技巧合集