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

22 KiB
Raw Permalink Blame History

安全性审核报告 (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:34echo 'root:${ROOT_PASSWORD}' | chpasswd (构建时仍保留默认值)
    • entrypoint.sh — ttyd 密码通过 ${TTYD_CREDENTIALS:-jc:1234567} 环境变量注入
  • 描述: 所有认证凭证均以明文硬编码在源码文件中已修复:密码现已通过环境变量 ${ROOT_PASSWORD}${TTYD_CREDENTIALS:-jc:1234567} 注入,entrypoint.sh 不再硬编码密码。但需注意 Dockerfile:34chpasswd 仍保留默认值(构建时),运行时通过环境变量覆盖。
  • 影响: 攻击者获取源码不再直接获得运行时凭据。降级为 [中危]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:11privileged: true
    • docker-compose-alpine.yml:7privileged: 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.ymlenvironment 直接传递 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:35PermitRootLogin yes
    • Dockerfile:34chpasswd 设置密码(默认 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-24name: 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-48cat > /root/.claude/settings.json << 'SETTINGS' (已用引号包裹,安全)
    • entrypoint-test.sh:64-71cat > /home/developer/.bashrc << BASHRC 未用引号包裹,有风险
    • entrypoint-test.sh:75-77cat > /root/.bashrc << 'ROOTRC' (已用引号包裹,安全)
    • entrypoint-test.sh:80-88cat > /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:9SESSION_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.gzworkpod-alpine-latest.tar.gz 镜像导出文件
    • wk.1216.conf — 可能含 nginx/服务器配置
  • 影响: 敏感数据(工作区代码、配置、镜像)泄露到 Git 仓库。
  • 建议修复: 创建 .gitignore 文件,至少排除:data/*.tar.gz.env*config/*.json

[中] M-04: Alpine 镜像使用第三方镜像源 (mirrors.aliyun.com)

  • 位置: Dockerfile:6Dockerfile:28sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g'
  • 描述: 构建过程中将 Alpine 官方包源替换为阿里云镜像源。虽然这是常见的国内加速做法,但引入了第三方信任链:
    1. 包完整性依赖阿里云镜像的同步正确性
    2. 中间人攻击面扩大(DNS 劫持、镜像源被入侵)
    3. 生产环境构建应优先使用官方源或可信的企业镜像
  • 影响: 若镜像源被污染,可能在构建阶段植入恶意软件到最终镜像中(供应链攻击)。
  • 建议修复:
    1. 构建时使用 --no-cache 并验证包签名
    2. 考虑使用官方源配合网络代理
    3. 至少在生产发布构建中使用官方源

[中] M-05: 容器缺少资源隔离和安全增强配置

  • 位置: docker-compose.ymldocker-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:49netstat -tlnp
    • docker-compose.yml:48ss -tlnp | grep ...
  • 描述: Dockerfile 中的 HEALTHCHECK 使用 netstatISSUES.md 已记录 ss 不可用的问题),而 docker-compose.yml 中覆盖使用了 ss 命令。根据 ISSUES.md 记录,Alpine 中 ss 不可用(需要安装 iproute2),compose 中的 healthcheck 会因 ss: not found 而持续失败。
  • 影响: 健康检查不准确,可能导致编排系统错误判断容器状态。
  • 建议修复: 统一使用 netstat -tlnp 或安装 iproute2 后统一使用 ss

[低] L-02: EXPOSE 指令声明过多端口

  • 位置: Dockerfile:46EXPOSE 22 7681
  • 描述: Dockerfile 中声明暴露 22 (SSH) 和 7681 (ttyd) 端口。EXPOSE 主要是文档作用,但会给使用者错误的暗示。实际上 compose 文件映射了更多端口(2222-2226, 7681-7686)。
  • 影响: 信息误导,但不构成直接安全威胁。
  • 建议修复: 更新 EXPOSE 为实际使用的端口集合,或在注释中说明。

[低] L-03: npm registry 使用第三方镜像源

  • 位置: Dockerfile:17npm 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:38COPY --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 防止敏感文件提交