# WechatOnCloud 安全加固指南

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

---

## 一、Docker Socket 代理

### 1.1 威胁面分析

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

```yaml
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-proxy](https://github.com/Tecnativa/docker-socket-proxy)**（[Tecnativa](https://github.com/Tecnativa)）：

```
面板  ──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 使用方法

```bash
# 启动（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）：

```bash
# 代理容器日志中能看到面板的 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` 都可能拉取到不同内容

**Digest（`sha256:...`）是镜像内容的加密哈希，固定后任何修改都会导致 digest 不匹配，拉取失败。**

### 2.2 固定方法

**第一步：查看当前镜像的 digest**

```bash
# 拉取后查看本地 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` 中固定版本**

```bash
# .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 示例**

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

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

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

### 2.3 更新流程建议

固定 digest 后，更新时需要三步：

```bash
# 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`。

---

## 参考

- [Tecnativa/docker-socket-proxy](https://github.com/Tecnativa/docker-socket-proxy) — Docker Socket 代理实现
- [Docker Engine API](https://docs.docker.com/engine/api/) — 端点权限参考
- [Docker Content Trust](https://docs.docker.com/engine/security/trust/) — Docker 官方镜像签名方案（进阶）
