内容
# 受控自主性:为开发团队设置安全的 AI 编码代理
**一份关于使用 DevContainers 沙盒 Claude Code 的实用指南,提供标准化的环境、范围权限和可复制的配置,用于团队工作流程。**
---
## 目录
- [快速开始](#快速开始) -- 5 分钟上手
- [前提条件](#前提条件)
- [本指南为何存在](#本指南为何存在)
- [架构:广泛读取,狭窄写入](#架构:广泛读取,狭窄写入)
- [为什么在仓库内](#为什么在仓库内)
- [项目结构](#项目结构)
- [逐步设置](#逐步设置) -- Dockerfile、容器配置、防火墙、MCP 服务器、权限、CLAUDE.md、技能、.gitignore、验证
- [范围混淆:用户 vs 项目 vs 本地](#范围混淆:用户-vs-项目-vs-本地)
- [安全模型](#安全模型) -- 威胁模型、强化细节、SSH/git 推送、已知限制
- [前后对比](#前后对比)
- [CI/CD 身份验证](#cicd-身份验证)
- [秘籍:如何添加内容](#秘籍:如何添加内容)
- [关键要点](#关键要点)
- [进一步阅读](#进一步阅读)
- [参考文献](#参考文献)
---
## 快速开始
对于那些希望先快速上手然后再阅读的人:
```bash
# 1. 在您的仓库中创建 devcontainer
mkdir -p .devcontainer
# 添加 Dockerfile、devcontainer.json、init-firewall.sh(参见逐步设置)
# 2. 添加 Claude Code 配置
mkdir -p .claude/skills .claude/rules
# 添加 .claude/settings.json(权限)
# 添加 .mcp.json(MCP 服务器)
# 添加 CLAUDE.md(项目说明)
# 3. 在 VS Code 中打开
code .
# Cmd+Shift+P -> "Dev Containers: Reopen in Container"
# 4. 身份验证 MCP 服务器
# 输入 /mcp,选择 Figma,点击身份验证
# 输入 /mcp,选择 Supabase,点击身份验证
# 5. 开始编码
claude --dangerously-skip-permissions
```
或者,将 Claude Code 添加到任何现有的 devcontainer 中,无需自定义 Dockerfile:
```jsonc
{
"features": {
"ghcr.io/anthropics/devcontainer-features/claude-code:1.0": {}
}
}
```
本指南的其余部分解释了每个部分存在的原因和如何自定义它。
---
## 前提条件
- [Docker Desktop](https://www.docker.com/products/docker-desktop/)(或 Linux 上的 Docker Engine)
- [VS Code](https://code.visualstudio.com/),带有 [Dev Containers 扩展](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
- Claude Code 账户([Anthropic API 密钥](https://console.anthropic.com/) 或 Claude Pro/Team 订阅)
---
## 本指南为何存在
AI 编码代理是生产力倍增器。它们可以重构、生成测试、连接样板代码并处理占据大量工程小时的机械工作。但它们提供的每个功能都需要相应的权限:文件系统访问、命令执行、网络调用、环境变量访问。对于个人开发者来说,在个人项目上进行此操作很简单。对于一个团队来说,这个过程需要架构。
默认设置有两种故障模式:
1. **过于开放** -- 代理以与开发人员相同的访问权限运行。它可以写入任何地方、执行任意命令并访问任何网络端点。
2. **过于严格** -- 每个命令都需要手动批准。工作量抵消了生产力增益。开发人员因摩擦而禁用保护。
将 AI 代理设置视为基础设施问题(而非个人偏好)会改变结果。本指南介绍如何为开发团队设置标准化和沙盒化的 AI 编码代理环境。我们使用 Claude Code 和 DevContainers 作为参考实现,但原理广泛适用:隔离执行环境、强制执行默认拒绝的网络策略、将文件系统访问范围限定在项目边界内,并对整个配置进行版本控制,以便每个工程师都能获得相同的防护措施。
---
## 架构:广泛读取,狭窄写入
Claude Code 应该具有 **最大上下文**(读取)与 **最小爆炸半径**(写入)。
```
输入(只读) 输出(读写)
=============== =================
+------------------++
| MCP:Figma |---+
| (设计令牌) | |
+------------------+ |
|
+------------------+ |
| MCP:Supabase |---+
| (read_only=true) | |
+------------------+ | +------------------------+
+---->| | +------------------+
+------------------+ | | Claude Code |---->| /workspace |
| MCP:Context7 |---+ | (容器内) | | (您的项目) |
| (库文档) | | | | +------------------+
+------------------+ +---->| | 仅可写入
| +------------------------+ 持久路径
+------------------+ | |
| MCP:shadcn |---+ |
| (组件注册) | | |
+------------------+ |
+------------------+
| MCP:Playwright |--+
| (浏览器测试)|
+------------------+
```
这在四个层次上得到强制执行:
| 层 | 功能 | 强制执行者 |
|-------|-------------|-------------|
| **1. 容器隔离** | 与主机分离文件系统和进程空间 | Docker |
| **2. 网络防火墙** | 默认拒绝出站;仅允许列出的域名 | iptables(`init-firewall.sh`) |
| **3. 文件系统挂载** | 项目目录 = 读写;其他目录不可见 | Docker 绑定挂载 |
| **4. Claude Code 权限** | 工具级允许/拒绝规则 + 操作系统沙盒 | `.claude/settings.json` |
没有单个层是足够的。它们共同构成纵深防御。
---
## 为什么在仓库内
devcontainer 配置(`.devcontainer/`)**位于您的仓库内**。这不是个人偏好,而是适用于团队的唯一方法。
| 因素 | 在仓库内 | 在仓库外 |
|--------|------------|--------------|
| **团队入职** | 克隆 + “在容器中重新打开” -- 完成 | 每个开发人员都设置自己的环境 |
| **一致性** | 版本控制;每个人都运行相同的东西 | 在不同机器上静默分发 |
| **GitHub Codespaces** | 需要 -- Codespaces 从仓库读取 `.devcontainer/` | 不支持 |
| **CI/CD 重用** | 相同的容器定义在 CI 中运行 | CI 无法访问开发本地配置 |
| **代码审查** | 容器更改通过 PR 审查 | 无法看到更改内容 |
| **配置演化** | 添加依赖?在同一个 PR 中更新 Dockerfile | 希望每个人都记得在本地更新 |
有一个合理的理由可以放到外面:您不拥有仓库(开源贡献、供应商代码)。其他情况都应提交。
### 个人偏好怎么办?
Shell 主题、编辑器键绑定、字体大小 -- 这些不是 devcontainer 问题。使用 [dotfiles 仓库](https://code.visualstudio.com/docs/devcontainers/containers#_personalizing-with-dotfile-repositories) 和 VS Code 设置同步。devcontainer 定义了 **团队标准**;您的 dotfiles 定义了 **您的品味**。
---
## 项目结构
以下是带有 Claude Code devcontainer 的 Next.js 项目的完整文件树:
```
your-nextjs-app/
.devcontainer/
devcontainer.json # 容器配置
Dockerfile # 镜像定义 + CLI
init-firewall.sh # 网络白名单
.claude/
settings.json # 权限规则(提交,团队共享)
settings.local.json # 个人覆盖(不提交 -- 参见 .gitignore)
skills/
generate-page/
SKILL.md # 自定义斜杠命令:/generate-page
agents/
frontend-reviewer.md # 自定义子代理
rules/
code-style.md # 始终加载的说明
api-patterns.md # 加载时触及 src/api/**
.mcp.json # MCP 服务器(提交,团队共享)
CLAUDE.md # Claude 项目说明
src/ # 您的 Next.js 应用
package.json
next.config.ts
...
```
---
## 逐步设置
### 步骤 1:Dockerfile
在此安装您堆栈的 CLI。这是您回答“如何添加 CLI?”的地方。
```dockerfile
# .devcontainer/Dockerfile
FROM node:20
# 系统工具
RUN apt-get update && apt-get install -y --no-install-recommends \
git sudo zsh fzf jq vim curl unzip gnupg2 gh \
iptables ipset iproute2 dnsutils aggregate \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
# 工作区
RUN mkdir -p /workspace /home/node/.claude \
&& chown -R node:node /workspace /home/node/.claude
# Shell 历史记录持久性
RUN mkdir /commandhistory && touch /commandhistory/.bash_history \
&& chown -R node /commandhistory
ENV DEVCONTAINER=true
WORKDIR /workspace
USER node
ENV NPM_CONFIG_PREFIX=/usr/local/share/npm-global
ENV PATH=$PATH:/usr/local/share/npm-global/bin
ENV SHELL=/bin/zsh
# ============================================
# 在此处添加您的 CLI
# ============================================
# Claude Code
ARG CLAUDE_CODE_VERSION=latest
RUN npm install -g @anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}
# Supabase CLI(如果使用 Convex,请跳过)
RUN npm install -g supabase
# Convex CLI(如果使用 Supabase,请跳过)
RUN npm install -g convex
# pnpm(如果您的项目使用它)
RUN npm install -g pnpm
# shadcn 不需要全局安装 -- 使用:pnpm dlx shadcn@latest
# Playwright 不需要全局安装 -- 通过 MCP 或 npx 使用
# ============================================
# 防火墙设置
# ============================================
COPY init-firewall.sh /usr/local/bin/
USER root
RUN chmod +x /usr/local/bin/init-firewall.sh \
&& echo "node ALL=(root) NOPASSWD: /usr/local/bin/init-firewall.sh" \
> /etc/sudoers.d/node-firewall \
&& chmod 0440 /etc/sudoers.d/node-firewall
USER node
```
**如何添加 CLI**:在标记的节中添加 `RUN npm install -g <package>` 行。重建容器即可。
### 步骤 2:容器配置
```jsonc
// .devcontainer/devcontainer.json
{
"name": "Next.js + Claude Code",
"build": {
"dockerfile": "Dockerfile",
"args": {
"CLAUDE_CODE_VERSION": "latest"
}
},
"runArgs": [
"--cap-drop=ALL",
"--cap-add=NET_ADMIN",
"--cap-add=NET_RAW",
"--security-opt=no-new-privileges:true"
],
// YOUR PROJECT:唯一可写的持久路径
"workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=delegated",
"workspaceFolder": "/workspace",
"mounts": [
// Claude 状态 -- 在容器重建后存活
"source=claude-config-${devcontainerId},target=/home/node/.claude,type=volume",
// Shell 历史记录 -- 在容器重建后存活
"source=claude-history-${devcontainerId},target=/commandhistory,type=volume",
// Git 身份 -- 来自主机的只读
"source=${localEnv:HOME}/.gitconfig,target=/home/node/.gitconfig,type=bind,readonly"
],
"remoteUser": "node",
"postStartCommand": "sudo /usr/local/bin/init-firewall.sh",
"waitFor": "postStartCommand",
"postCreateCommand": "npm install",
"forwardPorts": [3000],
"portsAttributes": {
"3000": { "label": "Next.js Dev", "onAutoForward": "notify" }
},
"containerEnv": {
"NODE_OPTIONS": "--max-old-space-size=4096",
"CLAUDE_CONFIG_DIR": "/home/node/.claude",
"NEXT_TELEMETRY_DISABLED": "1",
// 中和 VS Code IPC 套接字,代理可以利用它们逃离沙盒
"VSCODE_IPC_HOOK_CLI": "",
"VSCODE_GIT_IPC_HANDLE": "",
"GIT_ASKPASS": ""
},
// 从主机环境变量中传递秘密(永远不要硬编码)
"remoteEnv": {
"SUPABASE_URL": "${localEnv:SUPABASE_URL}",
"SUPABASE_ANON_KEY": "${localEnv:SUPABASE_ANON_KEY}"
},
"customizations": {
"vscode": {
"extensions": [
"anthropic.claude-code",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"bradlc.vscode-tailwindcss",
"eamodio.gitlens"
],
"settings": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"terminal.integrated.defaultProfile.linux": "zsh"
}
}
}
}
```
| 挂载 | 类型 | 为什么 |
|-------|------|-----|
| `/workspace` | **绑定,读写** | 您的项目。Claude 写入的唯一位置。主机上立即可见。 |
| `~/.gitconfig` | **绑定,只读** | Claude 使用您的身份提交,但不能修改您的 git 配置。 |
| `~/.claude` | **命名卷** | 在重建容器之间保持 Claude 状态。 不在您的主机上。 |
| `/commandhistory` | **命名卷** | Shell 历史记录在重启后存活。 可丢弃。 |
| `~/.ssh` | **不挂载** | 故意排除 SSH 密钥。代理无法使用 SSH 身份验证 `git push` 到需要 SSH 身份验证的远程仓库。如果您的工作流程需要推送,请以只读方式挂载密钥,并在 `.claude/settings.json` 中添加 `Bash(git push *)` 的拒绝规则,以使决策明确。 |
| 其他一切 | **不挂载** | 容器根本无法看到您的主机 home、其他仓库或系统文件。 |
### 步骤 3:防火墙
**重要**: 您在后续步骤中添加的每个 MCP 服务器或外部服务都需要在此处将其域名列入白名单。如果 MCP 服务器无法连接,请首先检查此文件。
自定义您堆栈的域名白名单:
```bash
#!/bin/bash
# .devcontainer/init-firewall.sh
set -euo pipefail
# 保留 Docker DNS
DOCKER_DNS_RULES=$(iptables-save 2>/dev/null | grep "127.0.0.11" || true)
# 清除现有规则
iptables -F
iptables -X
ipset destroy allowed-domains 2>/dev/null || true
# 恢复 Docker DNS
if [ -n "$DOCKER_DNS_RULES" ]; then
echo "$DOCKER_DNS_RULES" | iptables-restore --noflush
fi
# 允许基本操作
iptables -A OUTPUT -o lo -j ACCEPT
iptables -A OUTPUT -p udp --dport 53 -j ACCEPT
iptables -A OUTPUT -p tcp --dport 22 -j ACCEPT
iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# 创建允许的域名 IP 集
ipset create allowed-domains hash:net
# ============================================
# 在此处列出您的域名
# ============================================
whitelist() {
for ip in $(dig +short "$1" | grep -E '^[0-9]' || true) {
ipset add allowed-domains "$ip/32" 2>/dev/null || true
done}
# Claude Code (必需)
whitelist api.anthropic.com
whitelist statsig.anthropic.com
whitelist sentry.io
# 包注册表
whitelist registry.npmjs.org
whitelist registry.yarnpkg.com
# GitHub
for cidr in $(curl -s https://api.github.com/meta | jq -r '.web[], .api[], .git[]' 2>/dev/null); do
ipset add allowed-domains "$cidr" 2>/dev/null || true
done
# Supabase(如果使用 Convex,请跳过)
whitelist api.supabase.com
whitelist mcp.supabase.com
# 添加您的项目的 Supabase URL:
# whitelist <your-project>.supabase.co
# Convex(如果使用 Supabase,请跳过)
whitelist api.convex.dev
# Figma MCP
whitelist mcp.figma.com
whitelist api.figma.com
# VS Code 扩展
whitelist marketplace.visualstudio.com
whitelist vscode.blob.core.windows.net
# Vercel(如果部署)
# whitelist api.vercel.com
# whitelist vscode.blob.core.windows.net
# Vercel(如果部署)
# whitelist api.vercel.com
# whitelist vercel.com
# Context7(库文档)
whitelist mcp.context7.com
# ============================================
# 允许主机网络(Docker主机访问)
HOST_IP=$(ip route | grep default | awk '{print $3}')
ipset add allowed-domains "$HOST_IP/32" 2>/dev/null || true
# 默认拒绝
iptables -P OUTPUT DROP
iptables -A OUTPUT -m set --match-set allowed-domains dst -j ACCEPT
iptables -A OUTPUT -j REJECT --reject-with icmp-admin-prohibited
echo "防火墙初始化。已加载允许的域名。"
```
### 步骤 4:MCP 服务器
MCP 服务器为 Claude Code 提供只读访问外部系统。在项目根目录定义它们,以便整个团队共享。
```jsonc
// .mcp.json (提交到仓库)
{
"mcpServers": {
"figma": {
"type": "http",
"url": "https://mcp.figma.com/mcp"
},
"supabase": {
"type": "http",
"url": "https://mcp.supabase.com/mcp?read_only=true"
},
"shadcn": {
"command": "npx",
"args": ["shadcn@latest", "mcp"]
},
"context7": {
"command": "npx",
"args": ["-y", "@upstash/context7-mcp"]
},
"playwright": {
"command": "npx",
"args": ["@playwright/mcp@latest", "--headless"]
}
}
}
```
打开容器后,在 Claude Code 中运行 `/mcp` 以通过 OAuth 认证 HTTP 服务器(Figma,Supabase)。
**如何添加 MCP 服务器**:两种方式。
通过 CLI:
```bash
# 添加到项目范围(写入 .mcp.json,提交到 git)
claude mcp add --transport http figma --scope project https://mcp.figma.com/mcp
# 添加 stdio 服务器到项目范围
claude mcp add --transport stdio shadcn --scope project -- npx shadcn@latest mcp
# 添加标准输入/输出服务器到用户范围(仅限您,跨所有项目)
claude mcp add --transport http notion --scope user https://mcp.notion.com/mcp
# 添加到本地范围(仅限您,仅限此项目 -- 默认)
claude mcp add --transport http my-debug-server https://localhost:8080/mcp
```
或者,直接编辑 `.mcp.json`(适用于项目范围)。
#### Next.js 堆栈的 MCP 服务器
| 服务器 | 传输方式 | 访问权限 | 功能描述 |
|--------|-----------|--------|-------------|
| **Figma** | HTTP | 读写 | 拉取设计令牌、框架、布局数据。推送生成的 UI 回画布。 |
| **Supabase** | HTTP | 只读,带 `?read_only=true` 参数 | 查询表、检查模式、读取迁移。添加 `?features=database,docs` 参数以限制范围。 |
| **Convex** | stdio | 读写(生产环境默认禁用) | 查询数据、检查函数、读取日志。使用 `--disable-tools data,run` 命令进一步限制。 |
| **shadcn** | stdio | 只读 | 查询组件注册表、获取安装命令、读取组件文档。 |
| **Context7** | stdio/HTTP | 只读 | 获取任意库(Next.js、React、Tailwind 等)的最新文档。 |
| **Playwright** | stdio | 读写 | 用于测试的无头浏览器自动化。使用 `--allowed-hosts` 命令限制导航。 |
| **Notion** | HTTP | 读写 | 读取项目规格、PRD、会议笔记。 |
| **GitHub** | HTTP | 读写 | PR、问题、代码搜索。 |
| **Sentry** | HTTP | 只读 | 错误监控、堆栈跟踪。 |
### 步骤 5:Claude 代码权限
此文件与步骤 4 中的 `.mcp.json` 文件配合使用。下面的 `mcp__<server-name>__*` 规则引用了您在 `.mcp.json` 中定义的服务器名称(例如,`mcp__figma__*` 匹配来自 `"figma"` 服务器的所有工具)。
```jsonc
// .claude/settings.json(提交到仓库,与团队共享)
{
"permissions": {
"allow": [
"Read",
"Bash(git *)",
"Bash(npm *)",
"Bash(pnpm *)",
"Bash(npx *)",
"Bash(node *)",
"Bash(jq *)",
"Bash(supabase *)",
"Bash(convex *)",
"mcp__figma__*",
"mcp__supabase__*",
"mcp__shadcn__*",
"mcp__context7__*",
"mcp__playwright__*"
],
"deny": [
"Bash(rm -rf *)",
"Bash(curl * | *)",
"Bash(wget *)",
"Read(./.env)",
"Read(./.env.*)"
]
}
}
```
### 步骤 6:项目说明(CLAUDE.md)
```markdown
<!-- CLAUDE.md 在项目根目录(已提交) -->
# 我的 Next.js 应用
## 技术栈
- Next.js 15(应用路由器)
- TypeScript
- Tailwind CSS + shadcn/ui
- Supabase(身份验证、数据库、存储)
- 部署在 Vercel 上
## 规范
- 使用 `pnpm` 进行包管理
- 组件位于 `src/components/ui/`(shadcn)或 `src/components/`(自定义)
- 服务器操作位于 `src/app/_actions/`
- 使用 `pnpm dlx shadcn@latest add <component>` 添加 shadcn 组件
- 运行 `pnpm dev` 在端口 3000 启动开发服务器
- 运行 `pnpm test` 运行测试
## 禁止事项
- 修改 `.env.local` -- 凭证在仓库外管理
- 直接推送到 main 分支 -- 始终创建功能分支
```
### 步骤 7:自定义技能
技能是团队可以调用的斜杠命令,位于 `.claude/skills/`。
```markdown
<!-- .claude/skills/generate-page/SKILL.md -->
---
name: generate-page
description: 为 Next.js 页面生成新的页面,包含加载、错误和布局文件
argument-hint: <路由路径>
allowed-tools: Read, Write, Edit, Bash, Glob, Grep
---
在路由 $ARGUMENTS 处生成新的 Next.js 应用路由器页面:
步骤:
1. 检查路由是否已在 src/app/ 中存在
2. 创建 page.tsx、loading.tsx 和 error.tsx 文件
3. 使用 @src/app/layout.tsx 中的现有布局模式
4. 为 UI 使用 shadcn 组件 -- 使用 shadcn MCP 检查可用组件
5. 如果页面需要数据,请在 src/app/_actions/ 中创建服务器操作
6. 运行 `pnpm lint` 以验证无错误
```
在 Claude Code 中使用:`/generate-page dashboard/settings`
**如何添加技能**:在 `.claude/skills/<name>/` 目录中创建一个文件夹,并包含一个 `SKILL.md` 文件即可。该技能将立即显示为 `/<name>` 在 Claude Code 中。
### 步骤 8:更新 .gitignore
将以下行添加到您的 `.gitignore` 中,以防止个人配置进入版本控制:
```gitignore
# Claude Code 本地设置(个人覆盖,不共享)
.claude/settings.local.json
```
### 步骤 9:验证您的设置
在打开容器(“在容器中重新打开”)后,运行以下检查:
```bash
# 1. 确认您在容器内
echo $DEVCONTAINER # 应该打印 "true"
# 2. 确认 Claude Code 已安装
claude --version
# 3. 确认防火墙已激活(应该失败)
curl -s --max-time 3 https://example.com && echo "FAIL: 防火墙未激活" || echo "OK: 防火墙阻止"
# 4. 确认白名单域名可用(应该成功)
curl -s --max-time 3 https://api.anthropic.com > /dev/null && echo "OK: Anthropic API 可访问" || echo "FAIL: 检查防火墙"
# 5. 确认 MCP 服务器已连接
# 在 Claude Code 中输入:/mcp
# 所有服务器应显示“connected” 或提示身份验证
# 6. 确认写入隔离
touch /tmp/test-write && echo "OK: /tmp 可写入" || echo "FAIL"
touch /workspace/test-write && rm /workspace/test-write && echo "OK: /workspace 可写入" || echo "FAIL"
```
如果步骤 3 未失败(即 `example.com` 可访问),则防火墙未初始化。请检查 `postStartCommand` 是否成功运行以及容器是否具有 `NET_ADMIN` 能力。
---
## 范围混淆:用户 vs 项目 vs 本地
当您添加 MCP 服务器、权限规则、钩子或技能时,Claude Code 会询问 **哪个范围**。以下是决策树:
```
是否应允许整个团队使用?
├─ 是 → 项目范围(.claude/settings.json 或 .mcp.json)
│ 提交到 git。团队成员克隆后即可使用。
│
└─ 否 → 它是适用于所有项目还是仅限此项目?
├─ 所有项目 → 用户范围(~/.claude/settings.json)
│ 个人设置。永不提交。
│
└─ 仅限此项目 → 本地范围(.claude/settings.local.json)
个人设置 + gitignore。
```
### 东西都存放在磁盘上的位置
| 事物 | 用户(您,所有项目) | 项目(团队,此仓库) | 本地(您,此仓库) |
|-------|-------------------------|--------------------------|----------------------|
| **设置** | `~/.claude/settings.json` | `.claude/settings.json` | `.claude/settings.local.json` |
| **MCP 服务器** | `~/.claude.json` | `.mcp.json` | `~/.claude.json`(每个项目的路径) |
| **技能** | `~/.claude/skills/<name>/SKILL.md` | `.claude/skills/<name>/SKILL.md` | -- |
| **代理** | `~/.claude/agents/<name>.md` | `.claude/agents/<name>.md` | -- |
| **规则** | `~/.claude/rules/*.md` | `.claude/rules/*.md` | -- |
| **说明** | `~/.claude/CLAUDE.md` | `./CLAUDE.md` | -- |
| **钩子** | 在 `~/.claude/settings.json` 中 | 在 `.claude/settings.json` 中 | 在 `.claude/settings.local.json` 中 |
### 经验法则
| 您要添加的内容 | 最佳范围 | 为什么 |
|-------------------|-----------|-----|
| Figma MCP、Supabase MCP、shadcn MCP | **项目** | 团队成员需要相同的数据源 |
| Notion MCP(您的个人工作区) | **用户** | 您的 Notion,不是团队的 |
| 本地运行的调试 MCP,监听 localhost:8080 | **本地** | 临时,仅在您的机器上 |
| `Bash(pnpm *)` 权限 | **项目** | 团队标准 |
| `Bash(my-personal-script *)` 权限 | **本地** | 仅限您 |
| `/generate-page` 技能 | **项目** | 团队工作流程 |
| `/my-snippets` 技能 | **用户** | 个人生产力 |
| 代码样式规则 | **项目**(`.claude/rules/`) | 一致性 |
| “我更喜欢简洁的回应” | **用户**(`~/.claude/CLAUDE.md`) | 个人偏好 |
### 优先级
设置按以下顺序评估。第一个匹配项获胜。拒绝始终优于允许。
```
1. 已管理(IT/管理员,无法覆盖)
2. 命令行标志
3. 本地(.claude/settings.local.json)
4. 项目(.claude/settings.json)
5. 用户(~/.claude/settings.json)
```
---
## 安全模型
上一节中描述的四层在步骤 1-5 中配置。每一层都阻止了什么以及它不阻止什么:
| 层 | 阻止 | 不阻止 |
|-------|-------|---------------|
| **容器隔离** | 主机文件系统访问、进程干扰、系统级损坏 | 容器内可访问的任何内容 |
| **网络防火墙** | 渗出到未知域、任意 `curl`/`wget` | 到白名单域的流量(数据可能发送到允许的端点) |
| **文件系统挂载** | 读取/写入主机主目录、SSH 密钥、其他仓库 | `/workspace`(您的项目)内的读取/写入 |
| **Claude Code 权限** | 特定破坏性命令(`rm -rf`)、读取 `.env` 文件 | 未被拒绝规则覆盖的命令 |
使用所有四层,您可以运行 `claude --dangerously-skip-permissions` 以消除权限提示。容器 **就是** 沙盒。这是无人值守和批量操作的预期工作流。
**Anthropic 警告**:DevContainers 不会阻止恶意项目从容器中渗出任何可访问的内容,包括 Claude Code 凭证。仅与受信任的仓库一起使用。
### 硬化细节
步骤 2 中的 `devcontainer.json` 包含三个强化措施,超出了 [Anthropic 参考实现](https://github.com/anthropics/claude-code/tree/main/.devcontainer):
| 措施 | 它做什么 | 为什么很重要 |
|---------|-------------|----------------|
| `--cap-drop=ALL` + 选择性 `--cap-add` | 删除所有 Linux 功能,然后仅添加 `NET_ADMIN` 和 `NET_RAW`(iptables 需要)。 | 没有它,容器保留默认功能,如 `CHOWN`、`DAC_OVERRIDE`、`FOWNER`、`SETUID` 等。 - 任何一个都可能被利用来提升权限。 |
| `--security-opt=no-new-privileges:true` | 防止容器内的进程通过 `setuid` 二进制文件或功能升级获得额外权限。 | 即使攻击者将 setuid 二进制文件放入 `/workspace`,也无法升级到 root。 |
| `VSCODE_IPC_HOOK_CLI=""`、`VSCODE_GIT_IPC_HANDLE=""`、`GIT_ASKPASS=""` | 中和容器内暴露的 VS Code IPC 套接字。 | 具有 shell 访问权限的代理可以使用这些套接字在主机 VS Code 进程中执行命令 - 有效地逃离沙盒。清除它们会关闭此攻击向量。 致谢: [Daniel Demmel](https://www.danieldemmel.me/blog/coding-agents-in-secured-vscode-dev-containers)。 |
### SSH 密钥和 git 推送
SSH 密钥(`~/.ssh`)被故意 **不挂载** 到容器中。这意味着:
- 代理无法 `git push` 到需要 SSH 身份验证的远程仓库。
- `git commit` 工作正常(使用 `.gitconfig` 进行身份验证)。
- 如果您的工作流程需要代理推送(例如,用于自动创建 PR),请只读挂载密钥并添加显式拒绝规则:
```jsonc
// 在 .claude/settings.json 中 - 仅允许通过批准的模式推送
"deny": ["Bash(git push --force *)"]
```
最安全的模式是让代理提交本地,然后让开发人员(或 CI)处理推送。
### 防火墙可靠性
`init-firewall.sh` 脚本在容器启动时解析域名为 IP 地址。两个注意事项:
1. **IP 轮换**:云服务轮换 IP 地址。如果 MCP 服务器在重启容器后停止工作,请重建容器(`Ctrl+Shift+P` > “重建容器”)以重新解析 IP 地址。
2. **验证**: [Anthropic 参考实现](https://github.com/anthropics/claude-code/blob/main/.devcontainer/init-firewall.sh) 包括一个验证步骤,该步骤在初始化后测试阻止的域和允许的域。将此添加到您的脚本以用于生产使用。
### 已知限制
| 限制 | 详情 |
|-----------|--------|
| **容器中的 MCP OAuth** | 一些具有浏览器 OAuth 的 MCP 服务器(例如,Linear)可能无法在 devcontainer 中完成身份验证。变通方法:在主机上验证,然后通过 `remoteEnv` 传递令牌。 |
| **域名前置** | 防火墙按 IP 过滤,而不是按 SNI/主机名。白名单 IP 服务器提供多个域名可用于访问意外的服务。这是基于 IP 过滤的已知限制。 |
| **共享内核** | DevContainers 共享主机内核(与 VM 或 [Docker Sandbox](https://docs.docker.com/security/sandbox/) 不同)。为了与不受信任的代码实现最大隔离,请考虑基于超visor 的解决方案。 |
| **Bash 拒绝规则很脆弱** | `Bash(rm -rf *)` 可以通过重写命令(例如,`find . -delete`)绕过。拒绝规则是速度限制器,而不是墙。容器边界是真正的强制层。 |
---
## 之前和之后
| 问题 | 之前(裸金属) | 之后(安全 devcontainer) |
|---------|--------------------|-----------------------------|
| **文件系统访问** | Claude 在您的用户可以访问的任何地方读取/写入 | 仅写入到 `/workspace`。主机主目录、SSH 密钥、其他仓库不可见 |
| **网络访问** | 无限制。可以 `curl` 任何东西 | 默认拒绝。仅 Anthropic API、npm、GitHub、Figma、Supabase 可访问 |
| **权限疲劳** | 批准每个命令,或在裸金属上 `--dangerously-skip-permissions`(有风险) | 安全地使用 `--dangerously-skip-permissions`。容器是沙盒 |
| **团队一致性 | “在我的机器上运行。” 不同的 Node、pnpm、CLI 版本 | `.devcontainer/` 已提交。克隆 + 重新打开 = 对所有人一致 |
| **入职** | 安装 Node、pnpm、supabase CLI、配置身份验证、安装扩展... | 打开 VS Code。“在容器中重新打开”。完成 |
| **MCP 服务器** | 每个开发人员配置自己的 | `.mcp.json` 提交。所有人使用相同的数据源 |
| **技能和工作流程** | 知识存在于某人的脑海中 | `.claude/skills/` 提交。 `/generate-page` 对所有人可用 |
| **凭证隔离** | `~/.env`、`~/.aws`、`~/.ssh` 都可访问 | 仅显式传递的环境变量可见 |
| **破坏半径** | `rm -rf /` 销毁您的实际文件 | `rm -rf /` 销毁容器。30 秒重建 |
| **范围混淆** | Claude 询问“用户或项目?”并且您猜对机或项目?”并且你猜对了 |
| **CI/CD 奇偶性** | 开发和 CI 在不同的环境中运行 | CI 使用相同的 Dockerfile |
| **可审计性** | 无法得知 Claude 做了什么 | Git 历史记录、容器日志、钩子可以记录每个工具调用 |
---
## 快速入门:如何添加东西
| 我想添加... | 如何做 | 存放在哪里 |
|-------------------|-----|--------------|
| CLI(supabase、convex、pnpm) | 在 Dockerfile 中运行 `npm install -g <pkg>`。重建容器。 | `.devcontainer/Dockerfile` |
| 不需要全局安装的 CLI(shadcn) | 只需在运行时使用 `npx` 或 `pnpm dlx` 即可。无需安装。 | -- |
| 团队的 MCP 服务器 | `claude mcp add --scope project --transport http <name> <url>` | `.mcp.json` |
| 仅限我使用的 MCP 服务器 | `claude mcp add --transport http <name> <url>`(默认 = 本地范围) | `~/.claude.json` |
| 权限规则适用于团队 | 编辑 `.claude/settings.json` > `permissions.allow` 或 `permissions.deny` | `.claude/settings.json` |
| 自定义斜杠命令 | 创建 `.claude/skills/<name>/SKILL.md` | `.claude/skills/` |
| 个人斜杠命令 | 创建 `~/.claude/skills/<name>/SKILL.md` | `~/.claude/skills/` |
| 项目说明 | 编辑项目根目录的 `CLAUDE.md` | `./CLAUDE.md` |
| 个人说明 | 编辑 `~/.claude/CLAUDE.md` | `~/.claude/CLAUDE.md` |
| 代码样式规则 | 创建 `.claude/rules/<topic>.md` | `.claude/rules/` |
| 钩子(例如,保存时 lint) | 添加到 `.claude/settings.json` > `hooks` | `.claude/settings.json` |
| 白名单域名 | 添加到 `init-firewall.sh` > `whitelist <domain>`。重建容器。 | `.devcontainer/init-firewall.sh` |
| VS Code 扩展 | 添加到 `devcontainer.json` > `customizations.vscode.extensions` | `.devcontainer/devcontainer.json` |
| 转发端口(例如,Supabase Studio 的 54321 端口) | 添加到 `devcontainer.json` > `forwardPorts` | `.devcontainer/devcontainer.json` |
| 环境变量来自主机 | 添加到 `devcontainer.json` > `remoteEnv` 作为 `${localEnv:VAR_NAME}` | `.devcontainer/devcontainer.json` |
---
## 更多阅读材料
本指南主要关注团队工作流程和 Claude Code 生态系统。为了更深入地了解特定领域,请参阅以下资源:
| 主题 | 资源 | 为什么阅读 |
|-------|----------|-------------|
| **低级别容器强化** | [Daniel Demmel: 在安全的 VS Code 开发容器中编写代理](https://www.danieldemmel.me/blog/coding-agents-in-secured-vscode-dev-containers) | IPC 套接字强化、Docker 套接字代理、权限提升预防。该领域最深入的安全处理。与本指南互补。 |
| **安全审计工具** | [Trail of Bits: claude-code-devcontainer](https://github.com/trailofbits/claude-code-devcontainer) | 生产级 CLI (`devc`)、会话分析、无头 OAuth、多客户端部署模式。由安全公司构建。 |
| **虚拟化级别隔离** | [Docker 沙箱](https://docs.docker.com/security/sandbox/) | 单独的内核,比开发容器更强大。适用于审查不受信任的代码或需要 VM 级隔离而无需 VM 开销的团队。 |
| **AI 代理威胁建模** | [NVIDIA: 用于隔离代理工作流的实用安全指南](https://developer.nvidia.com/blog/practical-security-guidance-for-sandboxing-agentic-workflows-and-managing-execution-risk/) | 代理隔离的推理框架。涵盖“三重致命威胁”(LLM + 代码执行 + 不受信任的内容)。 |
| **安全运行代理(教程)** | [Andrea Bizzotto: 如何在 DevContainer 中安全运行 AI 代理](https://codewithandrea.com/articles/run-ai-agents-inside-devcontainer/) | 易于理解的介绍。对“从监督者到架构师”的良好框架。 |
| **MCP 安全** | [CoSAI: MCP 安全实用指南](https://www.coalitionforsecureai.org/securing-the-ai-agent-revolution-a-practical-guide-to-mcp-security/) | OAuth 风险、混淆委托问题、通过 MCP 元数据发现的 SSRF。 |
---
## 参考资料
**Claude Code 文档**
- [开发容器](https://code.claude.com/docs/en/devcontainer) -- [安全](https://code.claude.com/docs/en/security) -- [隔离](https://code.claude.com/docs/en/sandboxing) -- [权限](https://code.claude.com/docs/en/permissions) -- [设置](https://code.claude.com/docs/en/settings) -- [MCP](https://code.claude.com/docs/en/mcp) -- [内存和 CLAUDE.md](https://code.claude.com/docs/en/memory) -- [自定义命令](https://code.claude.com/docs/en/commands)
**Anthropic 参考实现**
- [参考开发容器](https://github.com/anthropics/claude-code/tree/main/.devcontainer) -- [开发容器功能](https://github.com/anthropics/devcontainer-features)
**MCP 服务器**
- [Figma](https://developers.figma.com/docs/figma-mcp-server/) -- [Supabase](https://supabase.com/docs/guides/getting-started/mcp) -- [Convex](https://docs.convex.dev/ai/convex-mcp-server) -- [shadcn](https://ui.shadcn.com/docs/mcp) -- [Playwright](https://github.com/microsoft/playwright-mcp) -- [Context7](https://github.com/upstash/context7) -- [MCP 协议](https://modelcontextprotocol.io/introduction)
**开发容器和 Docker**
- [VS Code 开发容器](https://code.visualstudio.com/docs/devcontainers/containers) -- [开发容器规范](https://containers.dev/) -- [Docker 绑定挂载](https://docs.docker.com/engine/storage/bind-mounts/) -- [微软工程手册](https://microsoft.github.io/code-with-engineering-playbook/developer-experience/devcontainers-getting-started/)
**安全参考**
- [Trail of Bits 强化开发容器](https://github.com/trailofbits/claude-code-devcontainer) -- [Daniel Demmel: 容器强化](https://www.danieldemmel.me/blog/coding-agents-in-secured-vscode-dev-containers) -- [Docker 沙箱](https://docs.docker.com/security/sandbox/) -- [NVIDIA: 隔离代理工作流](https://developer.nvidia.com/blog/practical-security-guidance-for-sandboxing-agentic-workflows-and-managing-execution-risk/)
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器