WechatOnCloud/doc/安全加固.md
Kris e35f1d64cf feat: 初始化项目,添加完整的微信云部署方案与面板实现
包含以下核心内容:
1.  完整的Docker容器化部署方案,支持多架构amd64/arm64
2.  自研Web管理面板,包含登录认证、实例管理、权限控制
3.  微信实例容器镜像,内置中文字体与输入法修复
4.  飞牛OS应用打包适配
5.  完整的文档与运维指南
2026-07-05 03:50:30 +08:00

6.3 KiB
Raw Permalink Blame History

WechatOnCloud 安全加固指南

本文介绍两项可选的安全加固措施:Docker Socket 代理镜像 Digest 固定。两者均为自愿启用,不影响默认部署行为。


一、Docker Socket 代理

1.1 威胁面分析

默认配置中,面板容器直接挂载了宿主的 /var/run/docker.sock

volumes:
  - /var/run/docker.sock:/var/run/docker.sock

对任意能访问这个 socket 的进程而言,这等同于宿主 root 权限

  • 可以创建任意容器并挂载宿主任意目录(如 //etc/home
  • 可以以 --privileged 模式启动容器逃逸到宿主
  • 可以停止、删除宿主上所有现有容器
  • 可以通过 exec 向任意已运行容器注入命令

攻击路径示例:面板存在 Web 漏洞(如 RCE、SSRF、依赖链投毒→ 攻击者控制面板进程 → 通过裸 socket 调用 POST /containers/create(挂载 / 并运行 chroot shell→ 获得宿主 root Shell。

关键点:这不是面板代码本身是否安全的问题,而是一旦面板被攻陷,攻击者能利用 socket 做什么的问题。最小化 socket 暴露面是纵深防御的标准做法。

1.2 缓解方案docker-socket-proxy

docker-compose.secure.yml overlay 在面板与宿主 socket 之间插入一个 docker-socket-proxyTecnativa

面板  ──TCP 2375──▶  docker-socket-proxy  ──Unix socket──▶  /var/run/docker.sock
        (允许列表过滤)               (只读挂载)

代理仅透传面板实际需要的端点,拒绝其余所有 Docker API 调用:

权限 状态 原因
CONTAINERS 开启 容器生命周期 + 日志 + 统计 + 文件存取
EXEC 开启 面板通过 exec 触发应用安装、文件管理、中文输入、设备 ID 重置
IMAGES 开启 实例镜像拉取与检查
VOLUMES 开启 孤儿卷列举与清理
POST 开启 所有写操作create/start/exec-start/pull均需 POST
INFO 开启 诊断包展示 Docker 版本与资源概况
BUILD / COMMIT / SWARM / SERVICES / SECRETS / PLUGINS / NODES / NETWORKS / ... 关闭 面板不需要,一律拒绝

同时overlay 将面板对裸 socket 的直接挂载替换为 /dev/null,从文件系统层面阻断直接访问。

1.3 残余风险

代理按端点过滤,而非按请求体内容过滤。这意味着:

  • 代理无法阻止面板通过 POST /containers/create 创建带 --privileged 或任意目录挂载的容器
  • EXEC=1 意味着可向 woc-wx-* 容器注入任意命令(这些容器本身已是不可信的用户进程,风险相对可接受;但若代理本身被绕过则影响扩大)
  • 代理容器自身持有只读 socket若代理镜像被供应链攻击保护失效

因此,本方案将攻击者在面板被攻陷后能做的事情从"获得宿主 root"降低到"有限制地操作 Docker 资源",但并非完全消除风险。

1.4 使用方法

# 启动overlay 叠加在默认 compose 之上)
docker compose -f docker-compose.yml -f docker-compose.secure.yml up -d

# 更新
docker compose -f docker-compose.yml -f docker-compose.secure.yml pull
docker compose -f docker-compose.yml -f docker-compose.secure.yml up -d

# 停止
docker compose -f docker-compose.yml -f docker-compose.secure.yml down

验证代理是否生效(面板应通过代理访问 Docker而非裸 socket

# 代理容器日志中能看到面板的 API 请求
docker logs woc-socket-proxy --follow

# 确认面板容器内 /var/run/docker.sock 已被替换为 /dev/null非 socket 文件)
docker exec woc-panel file /var/run/docker.sock
# 预期输出:/var/run/docker.sock: character special (1/3)

二、镜像 Digest 固定

2.1 为什么要固定 Digest

使用 :latest 或具体版本标签(如 :v1.2.3)时,存在以下风险:

  • 标签是可变的:镜像仓库维护者可以将同一个标签重新指向不同的镜像层
  • 供应链攻击:若 GHCR/Docker Hub 账号被盗,攻击者可以推送携带恶意代码的同名镜像
  • 浮动标签latest 每次 docker pull 都可能拉取到不同内容

Digestsha256:...)是镜像内容的加密哈希,固定后任何修改都会导致 digest 不匹配,拉取失败。

2.2 固定方法

第一步:查看当前镜像的 digest

# 拉取后查看本地 digest
docker pull ghcr.io/gloridust/woc-panel:latest
docker inspect ghcr.io/gloridust/woc-panel:latest \
  --format '{{index .RepoDigests 0}}'

# 或从仓库直接查询(无需拉取)
docker buildx imagetools inspect ghcr.io/gloridust/woc-panel:latest \
  | grep Digest

第二步:在 .env 中固定版本

# .env
WOC_VERSION=latest   # 改为具体 digest

# 推荐写法(在 docker-compose.yml 里 image 字段支持 @digest 语法)
# WOC_PANEL_IMAGE=ghcr.io/gloridust/woc-panel@sha256:abcdef1234...
# WOC_WECHAT_IMAGE=ghcr.io/gloridust/wechat-on-cloud@sha256:abcdef5678...

第三步:固定 digest 的 compose 示例

services:
  panel:
    image: ghcr.io/gloridust/woc-panel@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

或在 docker-compose.yml 同目录维护一个 digests.env

# digests.env加入 .gitignore每次发版后手动更新
WOC_PANEL_DIGEST=sha256:aaaa...
WOC_WECHAT_DIGEST=sha256:bbbb...

2.3 更新流程建议

固定 digest 后,更新时需要三步:

# 1. 查看新版 digest
docker buildx imagetools inspect ghcr.io/gloridust/woc-panel:latest | grep Digest

# 2. 更新 digests.env 或 docker-compose.yml 中的 digest 值

# 3. 验证并拉起
docker compose pull   # 拉取时 docker 会验证 digest
docker compose up -d

提示:可以在 GitHub 上 Watch 本仓库的 Release在收到新版通知后再更新 digest避免被动跟随 latest


参考