Files
workpod/docs/04-审核/01-安全性审核.md

353 lines
22 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 安全性审核报告 (WorkPod Alpine)
> 审核日期:2026-04-07
> 审核范围:Dockerfile、entrypoint.sh、ttyd-session.sh、docker-compose.yml、auth-proxy.js、ISSUES.md
---
## 审核概要
| 严重等级 | 数量 |
|---------|------|
| 严重 (Critical) | 1 (S-02) |
| 高危 (High) | 3 (H-02, H-03, H-04, H-05, H-06) |
| 中危 (Medium) | 5 (S-01[降级], M-01~M-05) |
| 低危 (Low) | 5 (L-01~L-05) |
**总计:21 项安全问题(其中 S-01/S-03/S-04/H-01/M-06 已修复/改进/不适用)**
---
## 问题清单
### [中危] S-01: 硬编码密码明文存储在源码中 **[已修复]**
- **位置:**
- `Dockerfile:34``echo 'root:${ROOT_PASSWORD}' | chpasswd` (构建时仍保留默认值)
- `entrypoint.sh` — ttyd 密码通过 `${TTYD_CREDENTIALS:-jc:1234567}` 环境变量注入
- **描述:** ~~所有认证凭证均以明文硬编码在源码文件中~~**已修复**:密码现已通过环境变量 `${ROOT_PASSWORD}``${TTYD_CREDENTIALS:-jc:1234567}` 注入,`entrypoint.sh` 不再硬编码密码。但需注意 `Dockerfile:34``chpasswd` 仍保留默认值(构建时),运行时通过环境变量覆盖。
- **影响:** 攻击者获取源码不再直接获得运行时凭据。降级为 **[中危]**Dockerfile 构建时默认值仍存在,但运行时可被 env 覆盖。
- **建议修复:**
1. Dockerfile 中 chpasswd 也改为环境变量引用或移除默认值
2.`.env` 文件加入 `.gitignore`,使用 `docker-compose --env-file` 加载
3. 对已提交的密码执行 `git filter-branch` 或 BFG 清理历史
4. 若密码已泄露,立即轮换所有凭证
### [严重] S-02: privileged: true 特权模式运行
- **位置:**
- `docker-compose.yml:11``privileged: true`
- `docker-compose-alpine.yml:7``privileged: true`
- **描述:** 容器以特权模式运行,拥有宿主机全部 capabilities(包括 SYS_ADMIN、NET_ADMIN 等),可访问所有设备文件(/dev/*),可修改内核参数,可逃逸到宿主机。
- **影响:** 容器内任何代码执行均可直接控制宿主机操作系统。结合 root 用户 + 全权限 Claude 配置,AI 工具的任意命令执行等同于宿主机 root 权限。
- **建议修复:**
1. 评估是否真的需要特权模式。若仅需要 Docker-in-Docker,使用 `docker.sock` 挂载替代
2. 若必须使用特权模式,限制为最小必要 capabilities(如仅 `SYS_PTRACE`
3. 考虑使用 `--security-opt=no-new-privileges` 防止权限提升
4. 在生产环境中绝对禁止特权模式
### [严重] S-03: Claude Code settings.json 设置 allow: ["*"] — 无限制全权限 **[不适用当前版本]**
- **位置:**
- ~~`entrypoint-test.sh:41-48`~~ — 已归档到 `_archive/`
- ~~`entrypoint-test.sh:51-58`~~ — 已归档到 `_archive/`
- ~~`config/test-claude-settings.json`~~ — 外部配置文件同样 allow: ["*"]
- **描述:** ~~Claude Code 的 permissions 配置设置为 `allow: ["*"]`~~**不适用当前版本**`entrypoint-test.sh` 已归档到 `_archive/` 目录,当前主 `entrypoint.sh` 中无此 `allow: ["*"]` 配置。
- **影响:** 当前版本不受此问题影响。
- **建议修复:** 若后续重新启用类似配置,应遵循最小权限原则。
### [严重] S-04: API 密钥 (ANTHROPIC_AUTH_TOKEN) 通过环境变量暴露给所有进程 **[已修复]**
- **位置:**
- ~~`entrypoint-test.sh:67`~~ — ~~写入 .bashrc~~ → 已修复
- ~~`entrypoint-test.sh:68`~~ — ~~写入 .bashrc~~ → 已修复
- **描述:** ~~API 密钥写入 .bashrc 并全局暴露~~**已修复**:当前版本通过 `docker-compose.yml``environment` 直接传递 `ANTHROPIC_AUTH_TOKEN` 等环境变量,不再写入 `.bashrc`
- **影响:** 密钥不再持久化到 `.bashrc`,不再对所有登录用户自动暴露。但容器内子进程仍可通过 `/proc/*/environ` 读取(Docker 环境变量的固有特性)。
- **建议修复:**
1. 使用 Docker secrets 或外部密钥管理服务进一步加固
2. 限制密钥文件的权限为 `chmod 600`
---
### [高] H-01: developer 用户 NOPASSWD sudo 全权限 **[不适用当前版本]**
- **位置:** ~~`entrypoint-test.sh:34`~~ — 已归档到 `_archive/`
- **描述:** ~~developer 用户配置了无需密码的 sudo 全权限~~**不适用当前版本**`entrypoint-test.sh` 已归档到 `_archive/` 目录,当前主 `entrypoint.sh` 中无此配置。
- **影响:** 当前版本不受此问题影响。
### [高] H-02: SSH 允许 root 密码登录 + 弱密码
- **位置:**
- `Dockerfile:35``PermitRootLogin yes`
- `Dockerfile:34``chpasswd` 设置密码(默认 workpod123,可通过 `${ROOT_PASSWORD}` 覆盖)
- **描述:** SSH 服务配置为允许 root 用户使用密码登录(而非仅密钥认证)。**降级说明**:`ROOT_PASSWORD` 现在可通过环境变量覆盖,默认值仍为 `workpod123` 但可自定义。端口 22 映射到宿主机 2222 端口,对外可达。
- **影响:** 若用户未自定义 `ROOT_PASSWORD`,暴力破解攻击仍可在短时间内猜出默认密码。一旦成功,攻击者获得容器 root shell,在特权模式下进一步控制宿主机。
- **建议修复:**
1. 禁用密码登录:`PasswordAuthentication no`,仅允许密钥认证
2. 若必须保留密码登录,通过环境变量设置强密码(20+ 字符随机生成)
3. 限制 SSH 来源 IP(防火墙规则)
4. 考虑使用 fail2ban 防暴力破解
5. 更改默认 SSH 端口(虽属隐匿安全但有一定价值)
### [高] H-03: ttyd 使用 Basic Auth 且密码强度不足 **[已改进]**
- **位置:**
- `entrypoint.sh` — ttyd 密码通过 `${TTYD_CREDENTIALS:-jc:1234567}` 环境变量配置
- **描述:** ttyd 使用 `-c` 参数启用 HTTP Basic Authentication。**已改进**:默认凭据改为 `jc:1234567` 通过环境变量 `TTYD_CREDENTIALS` 配置,不再硬编码在 entrypoint.sh 中。仍存在的风险:
1. 默认密码仍偏弱(7位数字/字母组合)
2. Basic Auth 凭据以 Base64 编码传输(非加密),中间人可截获
3. 未强制 HTTPS,密码明文传输
4. ttyd `-c` 参数在 Safari 浏览器中存在兼容性问题(已知 Safari 不支持 ttyd 内置 Basic Auth
5. Web 终端端口(7681)暴露,增加攻击面
- **影响:** 默认密码可被暴力破解;未加密传输可被网络嗅探;浏览器兼容性问题可能迫使降级安全措施。
- **建议修复:**
1. 通过 TTYD_CREDENTIALS 环境变量设置强密码(16+ 字符随机字符串)
2. 在前端加反向代理(nginx/caddy)终止 TLS
3. 考虑使用 auth-proxy.js 作为认证层(已有实现),但需修复其自身安全问题
4. 限制 ttyd 端口的网络访问范围
### [高] H-04: 多实例共享网络 (workpod-network) 缺乏隔离
- **位置:** `docker-compose-alpine.yml:23-24``name: workpod-network`
- **描述:** 测试服多个 WorkPod 实例共享同一个 Docker 网络 `workpod-network`。这意味着:
1. 同一网络内的容器可以互相访问对方的开放端口
2. 一个容器被攻陷后,可作为跳板攻击同网络其他容器
3. 容器间通信不经过宿主机防火墙
- **影响:** 横向移动风险——单点突破导致整个开发环境沦陷。
- **建议修复:**
1. 为每个实例创建独立网络
2. 若需共享数据,使用专用数据卷而非网络共享
3. 在容器内部使用 iptables/nftables 限制容器间流量
4. 考虑 Docker network 的 `internal` 选项禁止外部访问
### [高] H-05: auth-proxy.js 存在多处安全隐患
- **位置:** `auth-proxy.js` 全文(已从 `_archive/` 补充回主目录)
- **描述:** 认证代理脚本存在以下安全问题:
1. **第8行**:密码硬编码在源码中 `{ wk: '1234567', admin: 'admin123' }`
2. **第15行**:Token 存储在内存 Set 中,无持久化,重启后所有 token 失效(可用性问题),但也意味着无法审计
3. **第19行**Token 生成方式可预测(`wk_用户名_时间戳`),格式固定且时间戳精度为毫秒,可被枚举猜测
4. **第77-89行**Basic Auth 凭据仅做 Base64 解码比对,无速率限制,可被暴力破解
5. **第103-105行**`/token``/ttyd/` 路径绕过 token 验证直接代理到默认工作区,形成认证旁路
6. **第49行**:代理请求时透传原始 headers(含 Authorization),可能导致凭据泄漏到后端
- **影响:** 认证层形同虚设,token 可被猜测/绕过,密码可被暴力破解,凭据可能泄漏到后端服务。
- **建议修复:**
1. Token 使用 crypto.randomBytes() 生成不可预测的随机 token
2. `/token``/ttyd/` 端点也必须验证 token
3. 添加登录失败速率限制(如每 IP 每分钟最多5次)
4. 过滤敏感 headerAuthorization、Cookie)不转发到后端
5. 密码使用环境变量或外部配置,不从源码读取
### [高] H-06: /root 目录挂载到宿主机 (docker-compose-alpine.yml)
- **位置:** `docker-compose-alpine.yml:20``./data/workpod-alpine/root:/root`
- **描述:** 将容器的 `/root` 目录 bind mount 到宿主机目录。这意味着:
1. 容器内的 root 用户 home 目录内容直接暴露在宿主机文件系统上
2. 包含 .claude/settings.json(含全权限配置)、.bashrc(含 API 密钥)、ssh 密钥等敏感信息
3. 宿主机上其他进程/用户可能有权读取这些文件
4. 容器内对 /root 的修改直接影响宿主机
- **影响:** 敏感配置和密钥在宿主机上的安全性取决于宿主机文件系统权限,增加了攻击面。
- **建议修复:**
1. 评估是否确实需要挂载 /root,若仅为持久化工具安装,考虑只挂载子目录
2. 设置挂载目录的严格文件权限(chmod 700)
3. 确保 /root 下的密钥文件权限为 600
---
### [中] M-01: heredoc 注入风险(部分已缓解)
- **位置:**
- `entrypoint-test.sh:41-48``cat > /root/.claude/settings.json << 'SETTINGS'` (已用引号包裹,安全)
- `entrypoint-test.sh:64-71``cat > /home/developer/.bashrc << BASHRC` **未用引号包裹,有风险**
- `entrypoint-test.sh:75-77``cat > /root/.bashrc << 'ROOTRC'` (已用引号包裹,安全)
- `entrypoint-test.sh:80-88``cat > /etc/profile.d/dev-tools.sh << 'PROFILE'` (已用引号包裹,安全)
- **描述:** `entrypoint-test.sh:64` 的 heredoc 定界符 `BASHRC` 未加引号,这意味着 heredoc 内容中的变量(`${ANTHROPIC_AUTH_TOKEN}``${ANTHROPIC_BASE_URL}` 等)会被 shell 展开。虽然当前内容是期望的行为(需要展开环境变量),但如果将来有人在此处添加包含特殊字符的用户输入,可能导致注入。
- **影响:** 当前风险较低(变量值来自受控的环境变量),但编码模式不规范,未来维护可能引入漏洞。
- **建议修复:**
1. 保持现状但添加注释说明此处有意使用未引用 heredoc 以展开变量
2. 或者改用双引号定界符 `"BASHRC"` 并显式使用 `${VAR}` 引用变量
### [中] M-02: ttyd-session.sh 命令注入风险(已部分缓解)
- **位置:** `ttyd-session.sh:9``SESSION_NAME=$(echo "$TTYD_QUERY_STRING" | tr '&' '\n' | grep '^session=' | head -1 | cut -d= -f2)`
- **描述:** 从 URL query string 提取 session 名称时,虽然第55行做了过滤(`tr -cd 'a-zA-Z0-9_\-'`),但在第37行的 `read` 命令中,用户交互输入的 `CHOICE` 变量在第44-46行被用于 `sed -n "${CHOICE}p"`,如果 CHOICE 包含恶意内容(如 `1; rm -rf /`),虽然 grep 数字检查提供了一定保护,但防御不够严谨。
- **影响:** 攻击者可能通过构造特殊的 session 名称或交互输入来注入命令。当前有 `tr -cd` 过滤和数字匹配作为缓解措施,降低了但未消除风险。
- **建议修复:**
1. 在 read 后立即对 CHOICE 做输入验证
2. 使用 `[[ "$CHOICE" =~ ^[0-9]+$ ]]` 替代 `grep -qE` 做更严格的数字判断
3. 使用数组索引代替 sed 行号引用
### [中] M-03: 缺少 .gitignore 导致敏感文件可能被提交
- **描述:** 项目根目录不存在 `.gitignore` 文件。以下敏感文件/目录可能被意外提交到 Git 仓库:
- `data/` — 可能包含工作区数据和挂载的 /root 内容
- `config/test-claude-settings.json` — 含全权限配置
- `packages/` — 可能包含二进制包
- `*.tar.gz``workpod-alpine-latest.tar.gz` 镜像导出文件
- `wk.1216.conf` — 可能含 nginx/服务器配置
- **影响:** 敏感数据(工作区代码、配置、镜像)泄露到 Git 仓库。
- **建议修复:** 创建 `.gitignore` 文件,至少排除:`data/``*.tar.gz``.env*``config/*.json`
### [中] M-04: Alpine 镜像使用第三方镜像源 (mirrors.aliyun.com)
- **位置:** `Dockerfile:6``Dockerfile:28``sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g'`
- **描述:** 构建过程中将 Alpine 官方包源替换为阿里云镜像源。虽然这是常见的国内加速做法,但引入了第三方信任链:
1. 包完整性依赖阿里云镜像的同步正确性
2. 中间人攻击面扩大(DNS 劫持、镜像源被入侵)
3. 生产环境构建应优先使用官方源或可信的企业镜像
- **影响:** 若镜像源被污染,可能在构建阶段植入恶意软件到最终镜像中(供应链攻击)。
- **建议修复:**
1. 构建时使用 `--no-cache` 并验证包签名
2. 考虑使用官方源配合网络代理
3. 至少在生产发布构建中使用官方源
### [中] M-05: 容器缺少资源隔离和安全增强配置
- **位置:** `docker-compose.yml``docker-compose-alpine.yml`
- **描述:** 容器配置缺少以下安全加固措施:
1. 未设置 `read_only: true` — 容器文件系统可写
2. 未设置 `security_opt: ["no-new-privileges:true"]` — 进程可通过 setuid/setgid 提权
3. 未设置 `cap_drop: [ALL]` + `cap_add: [特定能力]` — 保留全部 Linux capabilities
4. 未设置 `user:` — 默认以 root 运行
5. 未设置 `tmpfs: /tmp, /dev/shm` — 临时目录使用容器默认挂载
- **影响:** 容器逃逸向量增多,进程提权路径未被阻断。
- **建议修复:**
1. 添加 `security_opt: ["no-new-privileges:true"]`
2. 添加 `cap_drop: [ALL]` 仅保留必要能力
3. 考虑以非 root 用户运行主进程
4. 对 /tmp 等可写目录使用 tmpfs 并设置 `exec` 选项防止脚本执行
### [中] M-06: 日志和启动信息泄露敏感配置 **[已修复]**
- **位置:**
- ~~`entrypoint.sh:76-78`~~ — ~~打印连接方式和密码~~ → 已修复
- ~~`entrypoint-test.sh:124-126`~~ — ~~打印连接方式和密码~~ → 已归档
- **描述:** ~~启动时将连接地址和密码输出到 stdout/stderr~~**已修复**:当前 `entrypoint.sh` 不再打印密码。
- **影响:** `docker logs` 不再泄露密码。
- **建议修复:** 无需进一步操作。
---
### [低] L-01: HEALTHCHECK 命令不一致
- **位置:**
- `Dockerfile:49``netstat -tlnp`
- `docker-compose.yml:48``ss -tlnp | grep ...`
- **描述:** Dockerfile 中的 HEALTHCHECK 使用 `netstat`ISSUES.md 已记录 ss 不可用的问题),而 docker-compose.yml 中覆盖使用了 `ss` 命令。根据 ISSUES.md 记录,Alpine 中 `ss` 不可用(需要安装 iproute2),compose 中的 healthcheck 会因 `ss: not found` 而持续失败。
- **影响:** 健康检查不准确,可能导致编排系统错误判断容器状态。
- **建议修复:** 统一使用 `netstat -tlnp` 或安装 `iproute2` 后统一使用 `ss`
### [低] L-02: EXPOSE 指令声明过多端口
- **位置:** `Dockerfile:46``EXPOSE 22 7681`
- **描述:** Dockerfile 中声明暴露 22 (SSH) 和 7681 (ttyd) 端口。EXPOSE 主要是文档作用,但会给使用者错误的暗示。实际上 compose 文件映射了更多端口(2222-2226, 7681-7686)。
- **影响:** 信息误导,但不构成直接安全威胁。
- **建议修复:** 更新 EXPOSE 为实际使用的端口集合,或在注释中说明。
### [低] L-03: npm registry 使用第三方镜像源
- **位置:** `Dockerfile:17``npm config set registry https://registry.npmmirror.com`
- **描述:** npm 安装使用淘宝镜像源(npmmirror.com)。与 APK 镜像源类似,这引入了第三方供应链信任问题。
- **影响:** npm 包可能被篡改(供应链攻击),尤其 `@anthropic-ai/claude-code` 是核心依赖。
- **建议修复:** 生产构建使用官方 npm registry,或启用 npm 校验(`npm config set verify-signature true`)。
### [低] L-04: 镜像中包含不必要的构建工具痕迹
- **位置:** `Dockerfile:38``COPY --from=builder /opt/node /usr/local`
- **描述:** 多阶段构建是好的实践(builder 阶段的缓存和构建工具不会进入最终镜像),但最终镜像仍然较大(约 436MB)。应确认是否有不必要的文件被复制。
- **影响:** 攻击面增大(更多二进制文件 = 更多潜在漏洞),拉取/分发效率降低。
- **建议修复:**
1. 审查 `/opt/node` 下是否包含不必要的文件(文档、测试等)
2. 考虑使用 `COPY --from=builder /opt/node/bin /usr/local/bin` 仅复制必要文件
3. 使用 `docker scout` 分析镜像内容
### [低] L-05: Windows 伪影文件残留 **[风险降低]**
- **描述:** 项目目录中发现以下 Windows 伪影文件:
- `entrypoint.sh;C` — Windows 创建的 ADSAlternate Data Stream)伪影
- `ttyd-session.sh;C` — 同上
- **影响:** 这些文件可能是 Windows 资源管理器异常操作的产物,不影响 Linux 容器运行。**[风险降低]**entrypoint.sh 和 ttyd-session.sh 已改为 `COPY --chmod=755` 内置到镜像(4/4 修复),不再通过 bind mount 从 Windows 主机挂载,CRLF 换行符问题的影响已大幅减小。
- **建议修复:** 删除这些伪影文件,配置 `.gitattributes` 强制 LF 换行。
---
## 攻击路径分析
```
攻击者
├── [路径A: Web 终端]
│ ├── 1. 扫描发现 7681 端口开放的 ttyd
│ ├── 2. 暴力破解弱密码 (默认 jc:1234567,可通过 TTYD_CREDENTIALS 自定义)
│ ├── 3. 获得 shell → API 密钥不再在 .bashrc 中(已修复)
│ ├── 4. 使用 claude 执行命令(无 allow:["*"] 配置,需交互确认)
│ └── 5. privileged 容器 → 完全控制宿主机
├── [路径B: SSH]
│ ├── 1. 扫描发现 2222 端口开放的 SSH
│ ├── 2. 暴力破解 root 密码(默认 workpod123,可通过 ROOT_PASSWORD 自定义)
│ ├── 3. 直接获得 root shell
│ └── 4. privileged 容器 → 完全控制宿主机
└── [路径C: 认证代理]
├── 1. 发现 8080 端口的 auth-proxy
├── 2. 暴力破解 Basic Auth (wk:1234567)
├── 3. 利用 /token 端点的认证旁路
├── 4. 获取 token 后访问 ttyd
└── 5. 同路径 A 第 3-5 步
```
**关键发现:密码已外部化(环境变量注入),entrypoint-test.sh 已归档移除(无 allow:["*"]、无 NOPASSWD sudo、无 .bashrc 密钥写入)。当前主要风险仍为"弱默认密码 + 特权模式",自定义强密码可显著提升整体安全性。**
---
## 总结与优先级建议
### 立即处理 (P0 — 本周内)
| 编号 | 问题 | 状态 | 原因 |
|------|------|------|------|
| S-01 | 硬编码密码 | **[已修复]** / 降级[中危] | 密码已外部化,Dockerfile 默认值仍存 |
| S-02 | privileged: true | **待处理** | 最高风险的单一配置项 |
| S-03 | allow: ["*"] + skip-permissions | **[不适用]** | entrypoint-test.sh 已归档 |
| S-04 | API 密钥暴露 | **[已修复]** | 不再写入 .bashrc |
### 尽快处理 (P1 — 两周内)
| 编号 | 问题 | 状态 | 原因 |
|------|------|------|------|
| H-01 | NOPASSWD sudo | **[不适用]** | entrypoint-test.sh 已归档 |
| H-02 | SSH root 密码登录 | **[部分改进]** | ROOT_PASSWORD 可自定义,默认仍弱 |
| H-03 | ttyd 弱密码 + 明文传输 | **[已改进]** | TTYD_CREDENTIALS 可配置,默认仍偏弱 |
| H-05 | auth-proxy.js 安全缺陷 | **待处理** | 认证层可被绕过(已从 _archive 补回主目录) |
### 计划处理 (P2 — 一个月内)
| 编号 | 问题 | 原因 |
|------|------|------|
| H-04 | 共享网络隔离 | 横向移动风险 |
| H-06 | /root 目录挂载 | 敏感文件外露 |
| M-01 ~ M-06 | 各类中危问题 | 安全加固措施 |
| L-01 ~ L-05 | 低危问题 | 代码质量和 hygiene |
### 总体评价
WorkPod Alpine 是一个**开发环境工具**,其设计目标(便捷性、开箱即用)与安全性之间存在固有张力。当前配置明显偏向便利性:
**优势:**
- 采用多阶段构建减小镜像攻击面
- 使用 Alpine 基础镜像减少包数量
- 有信号处理和清理机制
- tmux session 管理支持多用户协作
**核心风险:**
- **特权模式 + root 运行 + 全权限 AI + 弱密码**的组合构成了"完美风暴"——每个单独看都是常见做法,但叠加在一起使整体安全基线极低
- 作为开发环境可以接受一定风险,但如果此容器部署在任何可从互联网访问的环境中,应当视为**已被攻陷**
**最低可行安全改进(MVP):**
1. 移除 `privileged: true`(除非有明确需求并提供理由)
2. 密码改为环境变量注入 + 强密码
3. 移除 `allow: ["*"]``--dangerously-skip-permissions`
4. SSH 禁用密码登录或使用强密码
5. 添加 `.gitignore` 防止敏感文件提交