Docker Desktop 已内置 docker agent 命令,支持 OpenAI/Anthropic/Gemini 等多厂商模型与任意 MCP server,以 YAML 配置驱动 AI Agent。
如果你最近更新了 Docker Desktop——4.63 或更高版本——你的机器上已经安装好一个新命令:docker agent。无需配置,无需阅读任何公告。仓库对它的描述是一种"用声明式 YAML 配置来构建、运行和分享 AI Agent"的方式,它对接 OpenAI、Anthropic、Gemini、Bedrock、Mistral、xAI 以及 Docker 自有的模型运行器,支持"任何 MCP server(本地、远程或基于 Docker 的)",并且是预装的。Docker 声称他们自己就在用这套工具来构建 Docker。这意味着数百万台开发机正悄无声息地通过一次次自动更新获得了一个 Agent 运行时。
我花了一个晚上研究这个仓库和文档,而不是写另一篇框架评论,因为这个项目恰好落在我一直关注的那个断层上:Agent 能读取什么和它被允许做什么之间的鸿沟。两年前这个问题困扰着我——我为自己的编码 Agent 写了一个安全带钩子,进行了攻击测试,最后帖子停在唯一有效的防护栏:操作系统。Docker 刚刚推出了这个防护栏的产品化版本。工程做得确实很好。默认设置才是故事的关键。
这个模型源自 Claude Code 并转化为 YAML:Agent 作为配置而非代码。toolsets 块声明 Agent 获得了哪些能力——type: filesystem、type: shell、type: mcp(可从 Docker 的 MCP 目录中连接任何东西,文档说"数百个 MCP server",通过 ref: 行附加)。有权限页面、密钥指南、OpenTelemetry 追踪。README 报告了匿名遥测和 Apache-2.0 许可证,积累了一万多条 commit。
从积极策略的角度来看,toolset 块的方向是对的——这与我的安全带第二版经历相同的思路 inversion:停止列举禁止事项,改为声明允许事项,让未列出的内容默认失败。如果工具表面是一个可以在 pull request 中 diff 审查的 YAML 文件,那这样的运行时是可以在 PR 中 review 的。该赞扬的地方就赞扬。
沙箱模式的文档描述的东西超出了我那套钩子脚本的能力范围。启用它之后,Agent 运行在一个 Docker Sandboxes VM 内部——不是原始容器,而是一个围绕专用沙箱 CLI 的编排器。挂载表是明确的:工作目录可读写;Agent 配置和 kit 目录只读;其他一切不可见——"其他主机文件对 Agent 是不可见的"。这个 VM 有自己的 $HOME。
网络层面的设计是我本来会提出来、但没料到已经实现的部分:默认拒绝的出口代理,每次运行有一个覆盖模型网关和包仓库的允许列表,仅通过声明的主机(runtime.network_allowlist)或持久化的 docker agent sandbox allow 来扩展。默认拒绝出口是真正能困住提示词注入 Agent 的控制手段,因为无论模型决定了什么,泄密路径都会在代理层被截断。云端模式意味着沙箱环境,并拒绝上传主机 API 密钥。
这就是那堵墙。它比我拼凑出来的任何东西设计得更好,我希望它默认开启。
--sandbox 默认为 false。
读一下文档自己的措辞:每次运行时启用(docker agent run --sandbox)、 baked 进 YAML(runtime: sandbox: true)、或附加到别名上。每条路径都是可选择的。开箱即用,docker agent run agent.yaml 在主机上运行——type: shell 和 type: filesystem 在笔记本上的含义一如既往:你的用户账户就是爆炸半径。带有只读挂载和默认拒绝代理的沙箱是存在的,有文档记录,建造良好——躺在手套箱里。
默认设置即策略。每一个把安全模式藏在 flag 后面发货的主流框架,都赌的是用户会读文档,而这个赌注的记录就是整个"我的 Agent 删了数据库"帖子类型。dev.to 上已经有了一篇关于相邻工具链的——一个 Agent 通过 MCP 工具权限删除了机器上的所有容器——而 Docker 的 MCP 目录使得通过一键、YAML 声明的方式附加那种能力成为可能。便利不是漏洞。便利决定了漏洞是否会被触发。
即使沙箱开启,也有三扇门没有关上,文档对其中至少一扇是坦诚的:
工具供应链即上下文供应链。隔离contain了代码做什么;工具投毒攻击的是模型读取的内容。一个完美 VM 内部的 MCP server 仍然返回带有指令的工具描述——模型将它们作为上下文消费,没有挂载表可以过滤文本。目录的一键附加是我安全带帖子中 blocklist 问题的新版本,只是往上一层:信任决策从 shell 命令移到了工具定义,而大多数对 agent YAML 的审查止步于 toolset 名称。
密钥重删是尽力而为的——用 Docker 自己的话说。文档指出自动-kit 重删可能让混淆的 token 漏网。加上"以前的沙箱永远不会自动删除",这意味着 token 可以持久化在停止的沙箱状态中——文档提醒你删除需要 sbx rm --force,而且停止的沙箱可能仍然产生存储费用。敏感工作需要云端模式的规则——不上传主机密钥——或者干脆什么都不做。
可变 tag 破坏可重现性,所以文档说要用 digest 固定远程 kits 和镜像。正确的建议,但没有人会默认这样做。
还有一层完全缺失,不只是配置松散:文档随附 OpenTelemetry 追踪,回答的是"发生了什么"——到你控制的收集器,以一种你无法签名的格式。Docker 的威胁模型中没有任何人包括"运营者事后编辑记录"这一项,这对他们是合理的,但对任何运行接触生产的 Agent 的人来说则是错的。追踪是一个调试故事。审计需要的是你能证明从未被编辑过的记录。这一层没有随附在这里——或者任何主流平台。
# 安全带保持开启——通往沙箱运行的每条路径,按安全性排序:
docker agent run --sandbox agent.yaml # 每次运行,明确指定
# baked in,这样复制粘贴 YAML 时也能保持:
# runtime:
# sandbox: true
# 附加到你的团队实际输入的别名上:
docker agent alias add safe-coder myorg/coder --sandbox
# 固定 Agent 会拉取的所有内容:
# 用 digest 固定远程 kits/镜像(文档自己的建议)
# 清理永远不会自动清理的状态:
docker agent sandbox ls
sbx rm --force <name> # 停止的沙箱会持久化
加上两条 YAML 无法表达的规则:永远不要让你未审查过的默认配置中同时出现 type: shell 和 type: filesystem,以及像读代码一样读 MCP server 的工具定义——因为它们现在就是代码。
这是源码阅读,不是测试。我写这篇文章时没有运行 docker-agent;每个关于沙箱的声明都来自 README 和文档,这意味着我是在假设文档的准确性。我自己在钩子中发现的四个绕过,正是在沙箱文档和实际行为之间的差距里找到的,所以把我的评估当作"设计是对的",而不是"墙站住了"。
而且这个批评有保质期。一万多条 commit 和预装分发意味着 Docker 可以在一个版本中翻转沙箱默认值——这将让这篇文章的标题在最好的方式下过时。我真心希望到周二我是错的。
你的 Docker Desktop 是否已经自带 docker agent 了?你是在阅读本文之前还是之后启用的沙箱——如果你两种方式都跑过,沙箱阻止了主机运行会做的什么事?安全带讨论在评论区继续。