# 安全性审核报告 (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. 过滤敏感 header(Authorization、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 创建的 ADS(Alternate 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` 防止敏感文件提交