Files
wecom_it_smart_desk/docs/06-安全审计/01-审计报告/Dockerfile优化与镜像审计.md
T
Simon facc04aa65 chore: docs 结构整改 + compose 双目录对齐(合并重建提交)
本提交为 .git 对象库损坏后的重建提交,内容等价于原先三个本地提交
(5e2fd4c2 / 57a53c98 / 5d7e1873)的累积结果,未做任何额外改动。

一、docs 结构整改(整改 #14)
根因:重构时新结构为 untracked 文件,执行 git stash(未带 -u)未纳入,
随后 git reset 拉回 HEAD 旧 tracked 树,导致旧树复活、新旧两棵目录
树并存于 docs/,共 791 文件、双分类体系冲突。

修复动作:
- b2 同名异主题文件改名迁移保全 9 个
- C 类 39 个孤立文件按主题正确归类
- A/B1 类 222 个重复文件删除(新结构已有内容副本)
- 9 个旧独有空目录删除
- 270 处内部引用按 verified 映射改写
- 整改记录 #14 登记于 04-运维文档/部署运维

结果:docs 791 → 569 文件,顶层仅规范 8 类 + 治理文件,单树恢复。
残留:约 20 处指向从未存在文件的陈旧死链,归入独立文档卫生任务。

二、compose 双目录对齐(消除踩坑 A)
- docker-compose.yml:nginx 前端挂载全部由根目录 frontend-*/dist
  改为 src/frontend-*/dist(h5 / agent / admin / terminal)
- docker-compose.dev.yml:dev 服务 build context 与卷同步改 src/
- 效果:本地 docker compose up 不再把根目录 stale dist 挂回,
  与线上一致,分叉隐患消除(已 docker compose config 校验通过)

防复发铁律:
- 重构须提交;仓库修复须 git stash -u 或先 commit
- 新结构须 git add 并提交,避免再次 untracked 复活
- H5 改动只动 src/frontend-h5/,禁改根目录遗留 frontend-*/
2026-08-07 22:31:32 +08:00

376 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Dockerfile 优化 + 镜像审计报告
**审计日期**: 2026-06-15
**审计人**: Claude
**关联**: [[风险跟踪表]] / [[SOP-001-Gitea部署]]
---
## 📌 1. 现状盘点
| 镜像 | Dockerfile | 基础 | 估计大小 | 多阶段 |
|---|---|---|---|---|
| backend | `backend/Dockerfile` | `python:3.12-slim` | ~250 MB | ✅ |
| frontend-agent | `frontend-agent/Dockerfile` | `nginx:1.27-alpine` | ~50 MB | ✅ |
| frontend-h5 | `frontend-h5/Dockerfile` | `nginx:1.27-alpine` | ~50 MB | ✅ |
| postgres | (用官方) | `postgres:16-alpine` | ~80 MB | — |
| redis | (用官方) | `redis:7-alpine` | ~30 MB | — |
| nginx | (用官方) | `nginx:1.27-alpine` | ~40 MB | — |
**总估计镜像大小**:`~500 MB`(4 业务 + 2 数据库)
---
## 📌 2. backend Dockerfile 审计
### 2.1 当前实现
```dockerfile
FROM python:3.12-slim AS builder
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc libpq-dev libjpeg-dev zlib1g-dev curl
COPY requirements.txt .
RUN pip install --no-cache-dir --timeout 120 --retries 5 \
-i https://pypi.tuna.tsinghua.edu.cn/simple/ \
--trusted-host pypi.tuna.tsinghua.edu.cn \
-r requirements.txt
FROM python:3.12-slim
RUN apt-get update && apt-get install -y --no-install-recommends libpq5 curl
COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
COPY --from=builder /usr/local/bin /usr/local/bin
COPY . .
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
```
### 2.2 问题清单
| # | 问题 | 严重度 | 优化 |
|---|---|---|---|
| B-1 | ⚠️ **装 curl** — 但 P1-3 已改 healthcheck 用 Python urllib | 🟡 | 删 curl(节省 1MB) |
| B-2 | ⚠️ **不用非 root 用户** | 🟠 中 | 加 `USER appuser` |
| B-3 | ⚠️ **没 HEALTHCHECK** — 交给 docker-compose | 🟡 | Dockerfile 也加 |
| B-4 | ⚠️ **COPY . . 太宽** — 含 .git / tests / docs | 🟡 | 加 .dockerignore |
| B-5 | 🟢 pip 装到 venv(更隔离) | 🟢 | 已用 site-packages |
| B-6 | ⚠️ **没用 BuildKit cache mount** | 🟡 | 加 `--mount=type=cache` |
| B-7 | ⚠️ **PyPI 用清华源** — 公司内网可,但生产建议官方 | 🟡 | 评估 |
### 2.3 优化版
```dockerfile
# syntax=docker/dockerfile:1.7
FROM python:3.12-slim AS builder
# 创建非 root 用户
RUN groupadd -r appuser && useradd -r -g appuser appuser
WORKDIR /app
# 系统依赖(只装构建期需要的)
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && \
apt-get install -y --no-install-recommends \
gcc libpq-dev libjpeg-dev zlib1g-dev && \
rm -rf /var/lib/apt/lists/*
# 依赖(用 cache mount + BuildKit)
COPY requirements.txt .
RUN --mount=type=cache,target=/root/.cache/pip \
pip install --no-cache-dir --user \
--timeout 120 --retries 5 \
-i https://pypi.tuna.tsinghua.edu.cn/simple/ \
--trusted-host pypi.tuna.tsinghua.edu.cn \
-r requirements.txt
# 运行镜像
FROM python:3.12-slim
# 复制非 root 用户
COPY --from=builder /etc/passwd /etc/passwd
COPY --from=builder /etc/group /etc/group
# 运行时依赖(只 libpq5,**不装 curl**)
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && \
apt-get install -y --no-install-recommends libpq5 && \
rm -rf /var/lib/apt/lists/*
WORKDIR /app
# 复制构建好的 Python 包
COPY --from=builder /root/.local /home/appuser/.local
COPY --chown=appuser:appuser . .
# 切非 root 用户
USER appuser
ENV PATH=/home/appuser/.local/bin:$PATH
ENV PYTHONUNBUFFERED=1
EXPOSE 8000
# 内置 healthcheck(不依赖 curl)
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health').read()"
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
```
**预期收益**:
- 镜像小 ~10 MB(删 curl)
- 安全(非 root)
- 加速 rebuild(BuildKit cache)
- 内置 healthcheck(无需依赖 compose)
---
## 📌 3. frontend Dockerfile 审计(agent + h5 同)
### 3.1 当前实现
```dockerfile
FROM node:20-slim AS builder
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm install
COPY . .
RUN npm run build
FROM nginx:1.27-alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
```
### 3.2 问题清单
| # | 问题 | 严重度 | 优化 |
|---|---|---|---|
| F-1 | ⚠️ **不用非 root**(nginx 默认 root) | 🟠 中 | 自定义 nginx.conf 改 user |
| F-2 | ⚠️ **没 nginx.conf** — 用默认 | 🟡 | 复制 custom nginx.conf |
| F-3 | ⚠️ **没 .dockerignore** | 🟡 | 加 |
| F-4 | ⚠️ **没 layer cache 优化** | 🟡 | BuildKit cache mount |
| F-5 | ⚠️ **不用 alpine node** | 🟡 | 改 `node:20-alpine` |
### 3.3 优化版
```dockerfile
# syntax=docker/dockerfile:1.7
FROM node:20-alpine AS builder
WORKDIR /app
# 装 pnpm(快 2-3 倍,磁盘省 50%)
RUN corepack enable && corepack prepare pnpm@9 --activate
# 依赖
COPY package.json pnpm-lock.yaml* ./
RUN --mount=type=cache,target=/root/.local/share/pnpm/store \
pnpm install --frozen-lockfile
# 源码 + 构建
COPY . .
RUN pnpm run build
# 运行镜像
FROM nginx:1.27-alpine
# 自定义 nginx.conf(非 root + 反代配置)
COPY nginx.conf /etc/nginx/nginx.conf
# 从 builder 复制 dist
COPY --from=builder --chown=nginx:nginx /app/dist /usr/share/nginx/html
# nginx alpine 默认是 nginx user
USER nginx
EXPOSE 80
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD wget -q --spider http://localhost/ || exit 1
CMD ["nginx", "-g", "daemon off;"]
```
**预期收益**:
- 用 pnpm 代替 npm(快 2-3 倍)
- alpine node 镜像小 ~150 MB
- 自定义 nginx.conf + 非 root
- 内置 healthcheck
---
## 📌 4. .dockerignore 建议
**根目录** `.dockerignore`:
```
# Git
.git/
.gitignore
.gitattributes
.git-blame-ignore-revs
# 文档
docs/
*.md
!backend/README.md
# 测试
tests/
**/test_*.py
**/*_test.py
**/*.test.ts
**/*.spec.ts
coverage/
.coverage
htmlcov/
.pytest_cache/
# 开发工具
.vscode/
.idea/
*.swp
.DS_Store
Thumbs.db
# 构建产物(各端 dist)
frontend-*/dist/
frontend-*/node_modules/
# 部署包 / 备份
deploy-*.tar
deploy-*.tar.gz
*.log
*.log.err
build_logs/
# Python
__pycache__/
*.py[cod]
*$py.class
.venv/
venv/
*.egg-info/
# 环境变量(敏感)
.env
.env.*
!.env.example
# Docker
Dockerfile
.dockerignore
docker-compose*.yml
```
**每个前端** `frontend-X/.dockerignore`:
```
node_modules/
dist/
.env
.env.*
```
---
## 📌 5. 镜像大小优化(整体)
| 优化项 | 节省 | 风险 |
|---|---|---|
| backend 删 curl | 1 MB | 无 |
| 前端换 `node:20-alpine` | ~150 MB × 2 | 无 |
| 前端用 pnpm | ~50 MB × 2 | 无 |
| 加 .dockerignore | ~30% build 体积 | 无 |
| 跑 `docker system prune` | 100-500 MB | 无 |
| **总节省** | **~400 MB** | — |
---
## 📌 6. 安全加固
### 6.1 当前问题
| # | 问题 | 严重度 |
|---|---|---|
| S-1 | 全部容器跑 root | 🟠 中 |
| S-2 | 没 secret 扫描(防 docker build 时 COPY 进 secret) | 🟡 |
| S-3 | 没镜像漏洞扫描(Trivy) | 🟡 |
### 6.2 修复
1. **所有 Dockerfile 加 `USER` 指令**(已写优化版)
2. **加 Trivy 扫描到 CI**:
```yaml
# .gitea/workflows/security.yml
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: 'wecom-it-desk-backend:latest'
format: 'table'
exit-code: '1'
ignore-unfixed: true
```
3. **加 secret 扫描**:
- `.gitleaks.toml` 配 gitleaks
- pre-commit hook 跑 gitleaks
---
## 📌 7. 构建性能
| 优化 | 加速 | 实现 |
|---|---|---|
| BuildKit cache mount | 3-5x | `RUN --mount=type=cache,target=...` |
| 多阶段 | 减少最终大小 | 已用 |
| 依赖层缓存 | 2-3x | `COPY requirements.txt` 先于 `COPY .` |
| 并行构建 | 2-3x | `docker buildx build` |
| 镜像 registry 缓存 | 1.5-2x | 推 Gitea Container Registry |
---
## 📌 8. 实施路径
### 8.1 立即(本次跑批)
- [x] 审计报告写完(本文件)
- [ ] 加根目录 `.dockerignore`
- [ ] 加每个前端 `.dockerignore`
### 8.2 下周
- [ ] backend Dockerfile 优化版(删 curl + 非 root + healthcheck)
- [ ] frontend Dockerfile 优化版(alpine + pnpm + 非 root)
- [ ] 跑 `docker build` 验证大小
### 8.3 季度
- [ ] 加 Trivy 扫描到 CI
- [ ] 加 Gitea Container Registry
- [ ] 多架构构建(amd64 + arm64)
---
## 📌 9. 风险与缓解
| 风险 | 等级 | 缓解 |
|---|---|---|
| 优化版 Dockerfile 漏改回归 | 🟡 中 | CI 跑 `docker build` 测试 |
| alpine 镜像 musl libc 兼容性 | 🟡 中 | 验证 Python wheels |
| pnpm lockfile 跟 npm 差异 | 🟢 低 | 用 `pnpm import` 转 |
| 非 root 用户文件权限 | 🟡 中 | `chown` 显式指定 |
---
## 📌 10. 关联文档
- [[风险跟踪表]] M-11(数据库密码弱) / 部署相关
- [[SOP-001-Gitea部署]] - Gitea 部署参考
- [[Gitea部署指南]] - 部署文档
---
*本审计是 2026-06-15 Claude 满载跑批产出*