你有没有发现一件很可怕的事: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 Established

3)测试删除系统文件

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技巧合集

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐