22 KiB
22 KiB
安全性审核报告 (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 覆盖。
- 建议修复:
- Dockerfile 中 chpasswd 也改为环境变量引用或移除默认值
- 将
.env文件加入.gitignore,使用docker-compose --env-file加载 - 对已提交的密码执行
git filter-branch或 BFG 清理历史 - 若密码已泄露,立即轮换所有凭证
[严重] S-02: privileged: true 特权模式运行
- 位置:
docker-compose.yml:11—privileged: truedocker-compose-alpine.yml:7—privileged: true
- 描述: 容器以特权模式运行,拥有宿主机全部 capabilities(包括 SYS_ADMIN、NET_ADMIN 等),可访问所有设备文件(/dev/*),可修改内核参数,可逃逸到宿主机。
- 影响: 容器内任何代码执行均可直接控制宿主机操作系统。结合 root 用户 + 全权限 Claude 配置,AI 工具的任意命令执行等同于宿主机 root 权限。
- 建议修复:
- 评估是否真的需要特权模式。若仅需要 Docker-in-Docker,使用
docker.sock挂载替代 - 若必须使用特权模式,限制为最小必要 capabilities(如仅
SYS_PTRACE) - 考虑使用
--security-opt=no-new-privileges防止权限提升 - 在生产环境中绝对禁止特权模式
- 评估是否真的需要特权模式。若仅需要 Docker-in-Docker,使用
[严重] S-03: Claude Code settings.json 设置 allow: ["*"] — 无限制全权限 [不适用当前版本]
- 位置:
— 已归档到entrypoint-test.sh:41-48_archive/— 已归档到entrypoint-test.sh:51-58_archive/— 外部配置文件同样 allow: ["*"]config/test-claude-settings.json
- 描述:
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 环境变量的固有特性)。 - 建议修复:
- 使用 Docker secrets 或外部密钥管理服务进一步加固
- 限制密钥文件的权限为
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 yesDockerfile:34—chpasswd设置密码(默认 workpod123,可通过${ROOT_PASSWORD}覆盖)
- 描述: SSH 服务配置为允许 root 用户使用密码登录(而非仅密钥认证)。降级说明:
ROOT_PASSWORD现在可通过环境变量覆盖,默认值仍为workpod123但可自定义。端口 22 映射到宿主机 2222 端口,对外可达。 - 影响: 若用户未自定义
ROOT_PASSWORD,暴力破解攻击仍可在短时间内猜出默认密码。一旦成功,攻击者获得容器 root shell,在特权模式下进一步控制宿主机。 - 建议修复:
- 禁用密码登录:
PasswordAuthentication no,仅允许密钥认证 - 若必须保留密码登录,通过环境变量设置强密码(20+ 字符随机生成)
- 限制 SSH 来源 IP(防火墙规则)
- 考虑使用 fail2ban 防暴力破解
- 更改默认 SSH 端口(虽属隐匿安全但有一定价值)
- 禁用密码登录:
[高] H-03: ttyd 使用 Basic Auth 且密码强度不足 [已改进]
- 位置:
entrypoint.sh— ttyd 密码通过${TTYD_CREDENTIALS:-jc:1234567}环境变量配置
- 描述: ttyd 使用
-c参数启用 HTTP Basic Authentication。已改进:默认凭据改为jc:1234567通过环境变量TTYD_CREDENTIALS配置,不再硬编码在 entrypoint.sh 中。仍存在的风险:- 默认密码仍偏弱(7位数字/字母组合)
- Basic Auth 凭据以 Base64 编码传输(非加密),中间人可截获
- 未强制 HTTPS,密码明文传输
- ttyd
-c参数在 Safari 浏览器中存在兼容性问题(已知 Safari 不支持 ttyd 内置 Basic Auth) - Web 终端端口(7681)暴露,增加攻击面
- 影响: 默认密码可被暴力破解;未加密传输可被网络嗅探;浏览器兼容性问题可能迫使降级安全措施。
- 建议修复:
- 通过 TTYD_CREDENTIALS 环境变量设置强密码(16+ 字符随机字符串)
- 在前端加反向代理(nginx/caddy)终止 TLS
- 考虑使用 auth-proxy.js 作为认证层(已有实现),但需修复其自身安全问题
- 限制 ttyd 端口的网络访问范围
[高] H-04: 多实例共享网络 (workpod-network) 缺乏隔离
- 位置:
docker-compose-alpine.yml:23-24—name: workpod-network - 描述: 测试服多个 WorkPod 实例共享同一个 Docker 网络
workpod-network。这意味着:- 同一网络内的容器可以互相访问对方的开放端口
- 一个容器被攻陷后,可作为跳板攻击同网络其他容器
- 容器间通信不经过宿主机防火墙
- 影响: 横向移动风险——单点突破导致整个开发环境沦陷。
- 建议修复:
- 为每个实例创建独立网络
- 若需共享数据,使用专用数据卷而非网络共享
- 在容器内部使用 iptables/nftables 限制容器间流量
- 考虑 Docker network 的
internal选项禁止外部访问
[高] H-05: auth-proxy.js 存在多处安全隐患
- 位置:
auth-proxy.js全文(已从_archive/补充回主目录) - 描述: 认证代理脚本存在以下安全问题:
- 第8行:密码硬编码在源码中
{ wk: '1234567', admin: 'admin123' } - 第15行:Token 存储在内存 Set 中,无持久化,重启后所有 token 失效(可用性问题),但也意味着无法审计
- 第19行:Token 生成方式可预测(
wk_用户名_时间戳),格式固定且时间戳精度为毫秒,可被枚举猜测 - 第77-89行:Basic Auth 凭据仅做 Base64 解码比对,无速率限制,可被暴力破解
- 第103-105行:
/token和/ttyd/路径绕过 token 验证直接代理到默认工作区,形成认证旁路 - 第49行:代理请求时透传原始 headers(含 Authorization),可能导致凭据泄漏到后端
- 第8行:密码硬编码在源码中
- 影响: 认证层形同虚设,token 可被猜测/绕过,密码可被暴力破解,凭据可能泄漏到后端服务。
- 建议修复:
- Token 使用 crypto.randomBytes() 生成不可预测的随机 token
/token和/ttyd/端点也必须验证 token- 添加登录失败速率限制(如每 IP 每分钟最多5次)
- 过滤敏感 header(Authorization、Cookie)不转发到后端
- 密码使用环境变量或外部配置,不从源码读取
[高] H-06: /root 目录挂载到宿主机 (docker-compose-alpine.yml)
- 位置:
docker-compose-alpine.yml:20—./data/workpod-alpine/root:/root - 描述: 将容器的
/root目录 bind mount 到宿主机目录。这意味着:- 容器内的 root 用户 home 目录内容直接暴露在宿主机文件系统上
- 包含 .claude/settings.json(含全权限配置)、.bashrc(含 API 密钥)、ssh 密钥等敏感信息
- 宿主机上其他进程/用户可能有权读取这些文件
- 容器内对 /root 的修改直接影响宿主机
- 影响: 敏感配置和密钥在宿主机上的安全性取决于宿主机文件系统权限,增加了攻击面。
- 建议修复:
- 评估是否确实需要挂载 /root,若仅为持久化工具安装,考虑只挂载子目录
- 设置挂载目录的严格文件权限(chmod 700)
- 确保 /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 展开。虽然当前内容是期望的行为(需要展开环境变量),但如果将来有人在此处添加包含特殊字符的用户输入,可能导致注入。 - 影响: 当前风险较低(变量值来自受控的环境变量),但编码模式不规范,未来维护可能引入漏洞。
- 建议修复:
- 保持现状但添加注释说明此处有意使用未引用 heredoc 以展开变量
- 或者改用双引号定界符
"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过滤和数字匹配作为缓解措施,降低了但未消除风险。 - 建议修复:
- 在 read 后立即对 CHOICE 做输入验证
- 使用
[[ "$CHOICE" =~ ^[0-9]+$ ]]替代grep -qE做更严格的数字判断 - 使用数组索引代替 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 官方包源替换为阿里云镜像源。虽然这是常见的国内加速做法,但引入了第三方信任链:
- 包完整性依赖阿里云镜像的同步正确性
- 中间人攻击面扩大(DNS 劫持、镜像源被入侵)
- 生产环境构建应优先使用官方源或可信的企业镜像
- 影响: 若镜像源被污染,可能在构建阶段植入恶意软件到最终镜像中(供应链攻击)。
- 建议修复:
- 构建时使用
--no-cache并验证包签名 - 考虑使用官方源配合网络代理
- 至少在生产发布构建中使用官方源
- 构建时使用
[中] M-05: 容器缺少资源隔离和安全增强配置
- 位置:
docker-compose.yml、docker-compose-alpine.yml - 描述: 容器配置缺少以下安全加固措施:
- 未设置
read_only: true— 容器文件系统可写 - 未设置
security_opt: ["no-new-privileges:true"]— 进程可通过 setuid/setgid 提权 - 未设置
cap_drop: [ALL]+cap_add: [特定能力]— 保留全部 Linux capabilities - 未设置
user:— 默认以 root 运行 - 未设置
tmpfs: /tmp, /dev/shm— 临时目录使用容器默认挂载
- 未设置
- 影响: 容器逃逸向量增多,进程提权路径未被阻断。
- 建议修复:
- 添加
security_opt: ["no-new-privileges:true"] - 添加
cap_drop: [ALL]仅保留必要能力 - 考虑以非 root 用户运行主进程
- 对 /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 -tlnpdocker-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)。应确认是否有不必要的文件被复制。
- 影响: 攻击面增大(更多二进制文件 = 更多潜在漏洞),拉取/分发效率降低。
- 建议修复:
- 审查
/opt/node下是否包含不必要的文件(文档、测试等) - 考虑使用
COPY --from=builder /opt/node/bin /usr/local/bin仅复制必要文件 - 使用
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):
- 移除
privileged: true(除非有明确需求并提供理由) - 密码改为环境变量注入 + 强密码
- 移除
allow: ["*"]和--dangerously-skip-permissions - SSH 禁用密码登录或使用强密码
- 添加
.gitignore防止敏感文件提交