在 devcontainer 里用 Claude Code:容器内的权限与网络前置
你想让 Claude Code 少问几次权限、干脆跑无人值守,但它执行命令的地方就是你自己的开发机,删错了目录没有第二次机会。dev container 是官方文档给出的其中一条路径——把 Claude Code 装进容器里,命令在容器里执行,而项目文件通过 bind mount 出现在你本地仓库中,编辑结果照常落到工作区。
官方文档《Development containers》(code.claude.com/docs/en/devcontainer)把这件事拆成了「装 feature」和几个彼此独立的配置话题:跨重建保住登录态、下发组织策略、限制网络出口、免权限提示运行。下面按这个顺序过一遍,并把《Choose a sandbox environment》(code.claude.com/docs/en/sandbox-environments)里关于容器边界的说法一并对上。
前置条件:先确认你在不在这条路径上
编辑器得支持 Dev Containers 规范。 文档列出的是 VS Code、GitHub Codespaces、JetBrains IDE、Cursor 这类能连上容器的编辑器;同时明确写了「不支持 dev container 的编辑器,例如纯 Vim,不属于这套工作流」。这一句值得先读,省得配了半天发现编辑器根本连不进去。
需要 Docker。 沙箱对比页的表格里,dev container 那一行「Requires Docker」是 Yes,setup effort 标为 Medium;容器可以跑在本机,也可以跑在 GitHub Codespaces 这类云端宿主上。
Windows 侧要特别看一眼。 内置的 sandboxed Bash 工具在文档里写明「不支持原生 Windows」,选型表里给原生 Windows 宿主的建议是「用容器或虚拟机,或者在 WSL2 里跑 Bash sandbox」——也就是说 Windows 用户想要一层隔离,容器这条路是文档直接推荐的。命令面板快捷键文档也分开写了:Mac 上 Cmd+Shift+P,Windows 和 Linux 上 Ctrl+Shift+P。
权限方面,容器要跑无人值守就必须是非 root 用户,这一条放在第三节讲。至于 Claude Code 本身的最低版本要求、Docker 的最低版本要求,官方文档没有说明这一点——feature 装的是「最新版 Claude Code」,没有给版本下限。
步骤一:把 feature 加进 devcontainer.json
Claude Code 通过 Dev Container Feature 装进任意 dev container。文档给的最小配置是这个:
{
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"features": {
"ghcr.io/anthropics/devcontainer-features/claude-code:1.0": {}
}
}
存成仓库里的 .devcontainer/devcontainer.json,或者把 features 块加进你已有的文件。image 那行换成你项目的基础镜像;如果你现有配置用的是 Dockerfile,就把这行删掉。
这里有个容易读反的点,文档专门点了:结尾的 :1.0 这个版本标签钉的是 feature 的安装脚本,不是 Claude Code 的发行版本。 feature 装的永远是最新的 Claude Code,并且容器内默认开着自动更新。想钉住 CLI 版本得走另一条路,见步骤四。
还有个具体的失败信号:如果基础镜像不带 Node.js,feature 会自己装;万一装不上、构建停在 Failed to install Node.js and npm,文档要求在 features 块里、Claude Code 那条之上加 "ghcr.io/devcontainers/features/node:1": {},然后重建。
步骤二:重建并登录
在 VS Code 里打开命令面板执行 Dev Containers: Rebuild Container;其它工具走各自的重建动作(Codespaces 的重建、Dev Containers CLI 或你的 IDE 文档)。重建完在容器里开个终端跑 claude,按提示走认证。
认证提示看到什么,取决于你用的 provider:Anthropic 走浏览器登录(Claude 账号或 Anthropic Console 账号);Amazon Bedrock、Google Cloud’s Agent Platform、Microsoft Foundry 这三条则直接用云厂商凭据,没有浏览器提示。文档对后者有一条明确要求:凭据要通过 containerEnv、Codespaces secret 或者云上的 workload identity 传进容器,而不是把宿主机的凭据文件挂进去。
浏览器登录还有个已知的卡点:如果浏览器那边完成了、回调却没回到容器里,把浏览器上显示的 code 复制下来,粘到终端 Paste code here if prompted 那个提示处。文档说这在编辑器的端口转发没能路由 localhost 回调时会发生。
步骤三:让登录态活过重建
默认情况下容器的 home 目录在重建时被丢弃,于是每次重建都要重新登录一遍。要修这个,得先分清两个东西存在哪:
| 位置 | 存了什么 |
|---|---|
~/.claude 目录 | 认证 token、用户设置、会话历史 |
~/.claude.json 文件 | OAuth 账号、个人 MCP 服务器、按项目的信任状态 |
关键在第二行:~/.claude.json 是那个目录之外的一个单独文件,所以「只在 ~/.claude 上挂个卷」并不能让你保持登录。文档给的做法是挂命名卷的同时把 CLAUDE_CONFIG_DIR 指到同一个路径,让 Claude Code 把 .claude.json 写进卷里。以 remoteUser 为 node 的容器为例:
"mounts": [
"source=claude-code-config,target=/home/node/.claude,type=volume"
],
"containerEnv": {
"CLAUDE_CONFIG_DIR": "/home/node/.claude"
}
/home/node 换成你容器 remoteUser 的家目录——注意这是容器内的路径,跟你宿主机是 Windows 还是别的什么没有关系。另外文档提醒:如果你因为别的原因已经写了 containerEnv,把 CLAUDE_CONFIG_DIR 加进那个对象里,别再写第二个 containerEnv。
想让每个项目的状态互相隔离、而不是所有仓库共用一个卷,在卷名里带上 ${devcontainerId} 变量;官方的参考配置用的就是 source=claude-code-config-${devcontainerId}。
Codespaces 上的行为略有不同:~/.claude 在 stop / start 之间会保留,但重建时会被清掉,所以上面这套配置在 Codespaces 同样适用。要把认证带过多个 codespace,文档的做法是把 ANTHROPIC_API_KEY,或者 claude setup-token 生成的 CLAUDE_CODE_OAUTH_TOKEN,存成 Codespaces secret——Codespaces 会自动把 secret 暴露成容器内的环境变量。
步骤四:下发组织策略与钉版本
Claude Code 在 Linux 上读 /etc/claude-code/managed-settings.json,并且按设置层级里的最高优先级生效,也就是说这里的值会盖过工程师在 ~/.claude 或项目 .claude/ 里的设置。从 Dockerfile 拷进去:
RUN mkdir -p /etc/claude-code
COPY managed-settings.json /etc/claude-code/managed-settings.json
但文档紧接着自己泼了盆冷水(这属于文档自述):Dockerfile 就在仓库里,任何有写权限的人都能改掉或删掉这一步。要下发工程师改不动的策略,得走 server-managed settings 或者你们的 MDM。
要给容器里每个 Claude Code 会话都设环境变量,写进 devcontainer.json 的 containerEnv。文档给的例子是关掉非必要流量与错误上报,并阻止装完之后自动更新:
"containerEnv": {
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1",
"DISABLE_AUTOUPDATER": "1"
}
这里有个连带效果,文档写明了:CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC 同时会关掉 feature-flag 评估,而 Remote Control 以及其它依赖 feature-flag 拉取的功能都建立在这上面——所以容器里的会话用不了它们。配的时候没感觉,用的时候一头雾水,值得先记一笔。
至于钉版本:因为 feature 永远装最新版,要做可复现构建,文档的做法是不走 feature,改在 Dockerfile 里 npm install -g @anthropic-ai/claude-code@X.Y.Z,同时按上面设好 DISABLE_AUTOUPDATER。
MCP 也有一条对应说法:要让 MCP 服务器在容器里可用,就在仓库根的 .mcp.json 里按 project scope 定义,跟 dev container 配置一起进版本库;本地 stdio 服务器依赖的二进制在 Dockerfile 里装好,远端服务器的域名加进网络允许清单。
步骤五:限制出口,以及免提示运行
参考容器里带了一个 init-firewall.sh,作用是拦掉所有出站流量、只放行 Claude Code 与你的开发工具需要的域名。要在容器里跑防火墙需要额外权限,参考配置通过 runArgs 加了 NET_ADMIN 和 NET_RAW 两个 capability。文档同时说清了:这个脚本和这两个 capability 对 Claude Code 本身不是必需的,你完全可以不要它们,依赖自己的网络管控。具体放行哪些推理与认证域名,在《Network access requirements》那一页,不在本页。
免提示这块最需要慢读。因为容器以非 root 用户运行、且命令执行被限制在容器内,文档说你可以传 --dangerously-skip-permissions 做无人值守。CLI 在以 root 启动时会拒绝这个 flag,所以要确认 remoteUser 是非 root 账号。
把上述几块拼在一起的配置骨架大致长这样:
{
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"remoteUser": "node",
"features": {
"ghcr.io/anthropics/devcontainer-features/claude-code:1.0": {}
},
"mounts": [
"source=claude-code-config-${devcontainerId},target=/home/node/.claude,type=volume"
],
"containerEnv": {
"CLAUDE_CONFIG_DIR": "/home/node/.claude",
"DISABLE_AUTOUPDATER": "1"
}
}
以上为按官方文档中的参数语义组合的示例,未经实测,以官方文档与 --help 的实际输出为准。
边界:容器挡得住什么,挡不住什么
这一节比配置本身重要。文档页顶有一段 Warning,逐条对着看:
- dev container 提供了实质性的保护,但没有任何系统能免疫所有攻击;
- 在
--dangerously-skip-permissions下运行时,dev container 并不能阻止一个恶意项目把容器内可访问的任何东西外泄出去,包括存在~/.claude里的 Claude Code 凭据; - 只在你信任的仓库上使用 dev container,并留意 Claude 在做什么;
- 不要把宿主机的 secret(例如
~/.ssh或云凭据文件)挂进容器,优先用仓库范围内的、短期有效的 token。
沙箱对比页补了同方向的两句:任何允许网络出口的方案,仍然可能把 agent 读得到的数据带出去;任何把项目目录以可写方式挂进去的方案,仍然可能改掉那份代码。而且隔离不改变发给模型的内容——有没有沙箱,你的 prompt 和 Claude 读过的文件都会发给 Anthropic API 或你配置的 provider。
另外几条边界:
- 组织层面,dev container 不是强制边界。 沙箱对比页写得很直白:把示例 dev container 提交到各仓库只是一种「约定而非强制边界」,因为 Claude Code 并不要求必须跑在容器里;真要禁止容器外使用,得靠设备管理或软件白名单。
- 跳过权限提示不等于什么都不问。 传了
--dangerously-skip-permissions之后,文档列出仍然会提示的情形共五类:显式的 ask 规则、被组织设为ask的 connector 工具、标了requiresUserInteraction的 MCP 工具、针对/或家目录的删除、以及跨会话消息的安全保护。 - 想少些提示但不想关掉安全检查,文档给的替代是 auto mode——由一个分类器在动作执行前审查。但同一页也讲明分类器是「按动作的控制,不是隔离边界」。要干脆禁止工程师使用
--dangerously-skip-permissions,在 managed settings 里把permissions.disableBypassPermissionsMode设为"disable"。 - 想在容器里再叠一层内置 Bash sandbox 是可以的,但非特权容器需要 sandboxing 排查页里写的嵌套 sandbox 设置。
- 另一个能把 MCP 服务器与 hooks 一起圈进边界、又不需要 Docker 的选项是 sandbox runtime,但文档明确标注它是 beta research preview,配置格式可能随包演进而变化。
- 参考容器本身,文档说它是「一个可用的示例,而不是一个持续维护的基础镜像」,由
devcontainer.json、Dockerfile、init-firewall.sh三个文件组成。
怎么验证配对了
文档没有给一份「验收清单」,但把它写明的因果关系反过来用,能凑出几个可判定的检查点:
- feature 装上了没:重建后在容器终端跑
claude,能进到认证提示就说明 CLI 在容器里;构建阶段若出现Failed to install Node.js and npm,就是缺 Node feature 那条。 - 登录态持久化成了没:这是唯一一个必须靠「重建一次」才能验的项。执行一次 Dev Containers: Rebuild Container,如果重建后还要重新登录,先回去检查
CLAUDE_CONFIG_DIR是不是和挂载卷的target指到了同一路径——只挂~/.claude而没设这个变量,正是文档点名的失败模式。Codespaces 上注意区分:stop / start 不算,得 rebuild 才算数。 - 策略下发到位没:
/etc/claude-code/managed-settings.json在容器里存在,且内容是你 COPY 进去的那份。文档保证的是它在设置层级里优先级最高,至于「某个具体键有没有生效」的自检手段,官方文档没有说明这一点。 - 自动更新关了没:
DISABLE_AUTOUPDATER出现在容器环境变量里;若同时开了CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC,顺手确认没人指望在这个容器里用 Remote Control。 - 非 root 这一条:
remoteUser是非 root 账号。最容易被漏——直到你真的传--dangerously-skip-permissions,CLI 拒绝启动才发现。 - 网络出口:启用了参考容器那套防火墙的话,先确认
runArgs里有NET_ADMIN和NET_RAW;放行域名以《Network access requirements》页为准,别照抄第三方文章里的清单。
最后提醒一句方向性的:容器解决的是「命令在哪里执行」,它不解决「谁能改策略」(Dockerfile 就在仓库里)、也不解决「数据发给谁」(照发不误)。这三件事在文档里是三条独立的线。
本文依据 Claude Code 官方文档(code.claude.com/docs)于 2026-08-17 的公开内容整理。
该产品闭源,本文只复述官方文档写明的机制,不推断其内部实现;
我们没有对文中涉及的功能做过实测,因此不涉及界面外观、操作手感与运行速度的任何描述。
该产品迭代频繁,文中涉及的命令、配置项与默认值随版本变动,请以官方文档最新内容为准。
本文不涉及价格、额度与限流的具体数值,相关信息请以官方定价与用量说明页为准。
安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。