Files
MeshSate/docs/01-诊断套件方案.md
zhaolei 16f134e328 feat: MeshSafe 文件摆渡安全卫士 v1.0 初始提交
核心功能:
- 6 层检测管线: L0 结构分析 → L1 ClamAV → L2 文档深度检测 → L3 关键词匹配 → L4 YARA → L5 PE/二进制
- FastAPI + PostgreSQL + Redis + Docker Compose 架构
- 客户端 HMAC-SHA256 签名鉴权 + 限流 + 幂等性
- 控制台 UI (登录/Dashboard/诊断中心/策略管理/任务列表/测试中心/产品介绍页)
- 三层诊断定时器 (error_scan 30s / db_aggregate 30s / container_check 5min)

并发与稳定性修复:
- 诊断定时器 Redis 时间槽位锁 (多 worker 同一槽位只有一个执行)
- JSONL 存储 fcntl 跨进程排他锁 + 容错解码 + 原子重写
- 移除 SQLite fallback, 统一 PostgreSQL
- API_CLIENTS 脱敏为环境变量, .env 已 .gitignore
2026-10-28 19:07:11 +08:00

24 KiB
Raw Permalink Blame History

MeshSafe Diagnostics Kit v1.0 方案

版本: v1.0
日期: 2026-10-28
状态: 待审核
作者: MeshSafe Team
优先级: 高(P0)


一、定位

目标:让 MeshSafe 运维人员在产品内部就能看到"系统健康状况",遇到问题能一键打包带出来给开发方分析。

边界:

  • ❌ 不联网、不依赖外部服务、不做 LLM、不自动上传
  • ✅ 所有分析跑在客户容器里(在线定时)
  • ✅ 导出诊断包(手动触发,U 盘带出)
  • ✅ Docker 能力自动检测 + 三级降级
  • ✅ 全部定时器周期 + 规则阈值配置化
  • ✅ 总开关可关闭整个诊断套件(只留手动导出)

二、核心设计决策

# 决策 理由
1 在线层:只跑 grep 模式匹配,不收集完整日志 光闸内网,日志可能带客户敏感信息;完整日志只在用户手动导出时打包
2 三层定时器(错误类 30s / DB 聚合 60s / 容器类 300s) 拦截日志要快出快分析;容器/配置类变化慢
3 页面请求时必跑轻量规则(DB 聚合 + 引擎健康 + 文件 stat) ~430ms 用户无感,确保页面显示的是最新数据
4 日志匹配 + docker inspect 跑后台定时器 涉及 subprocess + IO,耗时 ~2.5s 不阻塞前台
5 Docker 能力三档降级(full / partial / none) 客户环境可能挂不了 docker.sock;降级后页面明确告诉用户哪些规则没跑
6 总开关 enabled 有的客户不需要/不想跑诊断,一键关掉只留手动导出
7 全部定时器周期 + 规则阈值配置化 光闸环境差异大,硬编码不合适

三、功能清单

3.1 控制台新页面:诊断面板

# 模块 展示内容 数据源
A1 实时健康概览 4 张卡片:总扫描量、恶意拦截、错误比例(高就红)、引擎在线 DB ScanTask + scanner.health()
A2 引擎健康详情 每个引擎状态、ClamAV 病毒库日期、YARA 规则数、最近检测耗时 scanner.health() + 配置快照
A3 容器运行状态 5 个容器 status / restart_count / Docker 能力检测结果 docker inspect(或降级跳过)
A4 异常检测面板 规则引擎跑出来的问题列表(每条带严重度、消息、证据、检测时间戳) 实时计算 + Redis 缓存
A5 最近 20 条错误 从 DB 查 verdict='error' 的 ScanTask DB
A6 一键导出按钮 生成 diagnostics.tar.gz → 浏览器下载 后端打包

页面顶部信息栏:

最后刷新: 10:29:15   🧩 Docker 能力: ✅ 完整(日志检测 + 容器状态)
                      或 ⚠️ 部分 / ℹ️ 未启用
                      或 ⛔ 诊断套件未启用

每条规则结果带时间戳(不同规则可不同,因来自不同运行通道):

🔴 R009  ClamAV 被 OOM Kill        (检测于 10:29:15)  ← 页面请求时跑
🟡 R002  ClamAV 病毒库 14 天未更新  (检测于 10:29:15)  ← 页面请求时跑
🟢 R004  所有引擎健康               (检测于 10:28:02)  ← 定时器跑

未启用 Docker 时:跳过 R009-R015,对应规则行显示 ℹ️ Docker 能力不可用,本条规则未运行。

诊断套件总开关关闭时:页面显示大横幅 ⛔ 诊断套件未启用,下面规则列表隐藏,只保留手动导出按钮。

3.2 规则清单(15 条)

页面请求必跑(轻量,~430ms,每次打开都算)

规则 类型 数据源 耗时
R001 error 比例 DB SQL ScanTask GROUP BY verdict ~20ms
R005 24h 无扫描 DB SQL ScanTask MAX(created_at) ~20ms
R002 ClamAV 病毒库日期 引擎协议 clamd SIGINFO ~100ms
R004 引擎健康 引擎协议 scanner.health() ~300ms
R003 YARA 规则 mtime 文件 stat os.path.getmtime <1ms

定时器组 A:错误/拦截类(30s 周期)

规则 依赖 异常信号 匹配模式
R009 ClamAV OOM/crash docker logs clamd clamd 被杀/崩 Killed / exit 137 / segfault / oom-killed
R010 Postgres FATAL docker logs postgres 连接池爆 FATAL / too many connections / PANIC
R011 Redis OOM docker logs redis 内存满 OOM command not allowed / out of memory
R012 磁盘满 5 个容器 docker logs 磁盘爆 No space left on device / ENOSPC
R013 Nginx upstream 失败 docker logs nginx api 挂了 Connection refused / upstream timed out
R014 API 连接异常 docker logs api 依赖断了 Redis connection refused / Postgres connection refused

定时器组 B:DB 聚合类(60s 周期)

规则 数据源
R006 suspicious > malicious ScanTask GROUP BY verdict
R007 reject 比例 ScanTask COUNT + verdict
R008 同 SHA256 重复 ScanTask GROUP BY sha256 HAVING COUNT > 1

定时器组 C:容器/引擎类(300s 周期)

规则 依赖
R015 容器重启次数 docker inspect × 5(任一 restart_count > 3)

3.3 每条规则输出格式

{
  "id": "R009",
  "severity": "high",           // high / medium / low / info
  "message": "ClamAV 被 OOM Kill(近 24h 重启 3 次)",
  "hint": "给 clamav 容器加内存限制 512m → 1g",
  "evidence": [
    "meshsafe-clamav 最近日志:Killed (exit 137)",
    "meshsafe-clamav restart_count = 3"
  ],
  "run_at": "2026-10-28T10:29:15+08:00",
  "run_by": "page_request",     // page_request / timer:error_scan / export
  "skipped": false,
  "skip_reason": null
}

3.4 一键导出

端点:GET /api/v1/telemetry/dump(返回文件下载)

导出包结构:

diagnostics-20261028-103045.tar.gz
├── rules_report.json          ← 导出这一刻跑一遍规则引擎的完整结果(带证据 + 时间戳)
├── containers/
│   ├── meshsafe-api.log       ← docker logs --tail 2000(导出时抓完整)
│   ├── meshsafe-api.meta.json ← restart_count / started_at / 镜像 tag
│   ├── meshsafe-clamav.log
│   ├── meshsafe-clamav.meta.json
│   ├── meshsafe-postgres.log
│   ├── meshsafe-postgres.meta.json
│   ├── meshsafe-redis.log
│   ├── meshsafe-redis.meta.json
│   ├── meshsafe-nginx.log
│   └── meshsafe-nginx.meta.json
├── scan_tasks.json            ← 最近 500 条 ScanTask(脱敏)
├── scan_findings.json         ← 对应 ScanFinding(rule name / severity / engine)
├── engine_health.json         ← scanner.health() 快照
├── policy_snapshot.json       ← 策略版本号 + YARA 规则文件列表(**不含规则原文**)
├── docker_detect.json         ← 启动时 Docker 能力检测结果
├── compose_state.json         ← docker compose ps 输出(容器运行状态表)
└── meta.json                  ← 打包时间 / 生成人 / MeshSafe 版本 / 规则版本

数据脱敏策略:

字段 处理方式
原始文件名 保留扩展名 + SHA256 前 8 位
文件路径 截断为相对路径
用户名 u***n
IP 10.0.1.*(保留最后一段)
策略 YAML 默认只传版本号 + 规则列表;加开关可选全量

3.5 Docker 能力检测

启动时自动运行(@app.on_event("startup")),三档降级:

检测结果 条件 影响 页面显示
full docker.sock 挂载 + docker CLI 可用 + docker logs/inspect meshsafe-api 成功 R009-R015 全部正常运行 ✅ Docker 能力完整
partial docker CLI 有但 socket 权限不够 R009-R015 可能部分失败 ⚠️ Docker 访问受限,部分容器日志规则未运行
none socket 未挂 / CLI 未装 R009-R015 自动跳过 ℹ️ 未启用 Docker 日志检测

检测结果写 docker_detect.json(导出包),同时存 Redis 供页面查询。


四、定时器运行架构

4.1 三层定时器 + 页面必跑

MeshSafe API 进程
│
├── FastAPI app
│   └── startup event
│       ├── ① DIAG_ENABLED? → false 直接 return(全部定时器不启动)
│       ├── ② Docker 能力检测 → 存内存 dict + Redis
│       ├── ③ asyncio.create_task(_timer_loop("error_scan",  30s,  R001/R005/R009-R014))
│       ├── ④ asyncio.create_task(_timer_loop("db_aggregate", 60s,  R006/R007/R008))
│       └── ⑤ asyncio.create_task(_timer_loop("container",  300s, R002/R003/R004/R015))
│
├── 页面请求 GET /console/diagnostics
│   ├── 同步跑 R001/R002/R003/R004/R005(~430ms,用户无感)
│   ├── 读 Redis 缓存拿 R006-R015
│   └── 合并 + 渲染(每条带时间戳)
│
└── 手动导出 GET /api/v1/telemetry/dump
    ├── 阻塞跑完整规则引擎(含实时 + 日志 + 容器)
    ├── 抓 docker logs --tail 2000(全容器)
    ├── 打包 tar.gz
    └── 用后台线程跑,超时 60 秒

4.2 单次运行耗时预期

通道 规则 耗时 周期 并发安全
页面必跑 R001/R002/R003/R004/R005 ~430ms 每次页面加载 同步 await,不阻塞 worker
定时器 A R001/R005/R009-R014 ~2.5s 30s asyncio.create_subprocess_exec
定时器 B R006/R007/R008 ~60ms 60s 单次 SQL 查询
定时器 C R002/R003/R004/R015 ~1.5s 300s clamd TCP + docker inspect × 5
导出 全部 15 条 + docker logs 全量 ~5-8s 用户手动 后台线程,60s 超时

4.3 Redis 缓存结构

ms:diag:latest          ← Hash: { rules: [...], generated_at: 1730406555, docker_status: "full", enabled: true }
ms:diag:cache:R001      ← String: { "result": {...}, "ts": 1730406555, "run_by": "timer:error_scan" }
ms:diag:cache:R009      ← 同上
...
ms:diag:docker_status   ← String: "full" / "partial" / "none"
ms:diag:enabled         ← String: "true" / "false"

TTL:单条规则缓存 6 分钟;latest 整个包 10 分钟。

4.4 定时器异常保护

async def _timer_loop(name, interval, rules, docker_status):
    """通用定时器循环。"""
    while True:
        try:
            await asyncio.sleep(interval)
            results = await _run_rule_group(rules, docker_status)
            await _write_redis(results, name)
        except asyncio.CancelledError:
            logger.info(f"定时器 {name} 被取消")
            break
        except Exception as e:
            logger.exception(f"定时器 {name} 异常,{interval}s 后重试: {e}")
            # 继续 while True,下次 sleep 后重试

五、后端接口清单

方法 路径 说明 响应
GET /console/diagnostics 诊断面板 HTML 页面(含 DIAG_ENABLED=false 横幅)
GET /api/v1/telemetry/summary 实时面板数据(页面调用) JSON
GET /api/v1/telemetry/engine-health 引擎健康快照 JSON
GET /api/v1/telemetry/errors?limit=20 最近错误列表 JSON
GET /api/v1/telemetry/docker-status Docker 能力检测结果 JSON
GET /api/v1/telemetry/rules 所有规则最新结果(带时间戳 + 运行来源) JSON
GET /api/v1/telemetry/dump 一键导出 tar.gz 文件下载
GET /api/v1/telemetry/config 当前配置快照(周期、阈值、Docker 模式) JSON

六、后端代码结构

app/
├── diagnostic/                        ← 新增目录
│   ├── __init__.py
│   ├── config.py                       ← 读取 diagnostics.yaml + 环境变量覆盖
│   ├── engine.py                       ← DiagnosticEngine(跑规则集)
│   ├── rules_db.py                     ← DB 聚合类规则实现(R001/R005-R008)
│   ├── rules_engine.py                 ← 引擎健康类规则实现(R002-R004)
│   ├── rules_log.py                    ← 日志匹配类规则实现(R009-R014)
│   ├── rules_container.py              ← 容器状态类规则实现(R015)
│   ├── docker_probe.py                 ← 启动时 Docker 能力检测
│   ├── collector.py                    ← DB + scanner.health() 原始数据拉取
│   ├── dumper.py                       ← 打包 tar.gz + 脱敏
│   └── scheduler.py                    ← 三层定时器循环
├── console.py
│   └── 新增 1 个页面路由 + 7 个 API 路由
├── main.py
│   └── startup event 加 DIAG_ENABLED 检查 + scheduler 启动
├── config/
│   └── diagnostics.yaml                ← 新增配置文件
├── templates/
│   ├── diagnostics.html               ← 新增诊断面板模板
│   └── _nav.html                       ← nav_active='diagnostics' 入口
└── Dockerfile
    └── 加 `RUN apt-get install docker.io-cli`

七、配置文件完整内容

文件:app/config/diagnostics.yaml

# ===============================================================
# MeshSafe Diagnostics Kit v1.0 配置
# ===============================================================
# 环境变量可覆盖:
#   MESHSAFE_DIAG_ENABLED=true|false
#   MESHSAFE_DIAG_DOCKER=auto|true|false
#   MESHSAFE_DIAG_ERROR_INTERVAL=30
#   MESHSAFE_DIAG_CONTAINER_INTERVAL=300
#   MESHSAFE_DIAG_DB_INTERVAL=60
# ===============================================================

diagnostics:
  # ---------- 总开关 ----------
  # true  = 启动定时器、检测 Docker、运行规则引擎
  # false = 只保留手动导出功能,所有自动诊断关闭
  enabled: true

  # ---------- Docker 日志/容器状态能力 ----------
  # auto  = 启动时自动检测 docker.sock 挂载
  # true  = 强制启用,连不上启动报错
  # false = 强制关闭
  docker:
    enabled: auto

  # ---------- 后台定时器周期(秒,0 = 不跑)----------
  timers:
    error_scan_interval: 30        # 错误/拦截类日志(R001/R005/R009-R014)
    container_check_interval: 300  # 容器/引擎类(R002/R003/R004/R015)
    db_aggregate_interval: 60      # DB 聚合类(R006/R007/R008)

  # ---------- 日志行数控制 ----------
  docker_log_tail: 500             # 定时器 grep 最近多少行
  export_log_tail: 2000            # 导出时完整抓多少行

  # ---------- 规则阈值 ----------
  rules:
    error_ratio_threshold: 0.05          # R001: error > 5% 告警
    clamav_db_stale_days: 7              # R002: 病毒库 > 7 天告警
    reject_ratio_threshold: 0.20         # R007: reject > 20% 告警
    suspicious_exceeds_malicious: true    # R006 开关

八、页面设计

8.1 诊断面板布局

┌────────────────────────────────────────────────────────────────────┐
│ Nav: 概览 | 策略配置 | 任务历史 | 测试靶场 | 🔧 诊断 ← active       │
│ 最后刷新: 10:29:15  🧩 Docker: ✅ 完整                               │
├────────────────────────────────────────────────────────────────────┤
│                                                                    │
│ ┌─ 实时健康概览 ────────────────────────────────────────────────┐ │
│ │ 扫描总量  恶意拦截  可疑待核  错误比例  引擎在线                │ │
│ │  12,472    117      47      10.0% 🔴   5/5 ✅                 │ │
│ └────────────────────────────────────────────────────────────────┘ │
│                                                                    │
│ ┌─ ⚠️ 异常检测(本地规则引擎)───────────────────────────────────┐ │
│ │ 🔴 R001  错误比例偏高(10.0% > 阈值 5%)         (10:29:15)   │ │
│ │    近 100 次扫描 12 次 error                                   │ │
│ │    💡 建议检查 ClamAV / Redis / Postgres 引擎                  │ │
│ │                                                                │ │
│ │ 🔴 R009  ClamAV 被 OOM Kill(重启 3 次)          (10:29:15)   │ │
│ │    证据: meshsafe-clamav stderr: Killed (exit 137)             │ │
│ │    💡 建议给 clamav 容器加内存 512m → 1g                        │ │
│ │                                                                │ │
│ │ 🟡 R002  ClamAV 病毒库 14 天未更新                 (10:29:15)   │ │
│ │    💡 在线更新失败 → U 盘离线导入                               │ │
│ │                                                                │ │
│ │ 🟢 所有其他规则通过                                             │ │
│ └────────────────────────────────────────────────────────────────┘ │
│                                                                    │
│ ┌─ 🧩 容器运行状态 ──────────────────────────────────────────────┐ │
│ │ API      ✅  healthy    restart: 0   启动: 2h 前               │ │
│ │ Postgres ✅  healthy    restart: 0                             │ │
│ │ Redis    ✅  healthy    restart: 0                             │ │
│ │ ClamAV   ✅  running    restart: 3  ⚠️ 近 24h 不稳定           │ │
│ │ Nginx    ✅  healthy    restart: 0                             │ │
│ └────────────────────────────────────────────────────────────────┘ │
│                                                                    │
│ ┌─ 🧪 引擎状态 ─────────────────────────────────────────────────┐ │
│ │ 🦠 ClamAV    ✅ 病毒库: 2026-10-14 (14天前)                     │ │
│ │ 🎯 YARA      ✅ 规则: 205 条                                    │ │
│ │ 📄 Document  ✅                                                 │ │
│ │ 🔑 Keywords  ✅                                                 │ │
│ │ ⚙️ Binary    ✅                                                 │ │
│ └────────────────────────────────────────────────────────────────┘ │
│                                                                    │
│ ┌─ ❌ 最近 20 条错误 ────────────────────────────────────────────┐ │
│ │ [table: 时间 | 文件名 | 错误原因 | 引擎 | 分数]                │ │
│ └────────────────────────────────────────────────────────────────┘ │
│                                                                    │
│                                          [📦 导出诊断包] 按钮      │
└────────────────────────────────────────────────────────────────────┘

8.2 总开关关闭时

┌────────────────────────────────────────────────────────────────────┐
│  ⛔ 诊断套件未启用                                                  │
│  diagnostics.yaml: diagnostics.enabled = false                     │
│                                                                    │
│  诊断套件提供实时健康检测、容器运行状态监控、一键诊断包导出。         │
│  关闭后仅保留手动导出功能,所有自动规则引擎将停止运行。               │
│                                                                    │
│  启用方式:                                                         │
│    1. 修改 app/config/diagnostics.yaml: enabled → true             │
│    2. 或设置环境变量:MESHSAFE_DIAG_ENABLED=true                   │
│    3. 重启 api 容器                                                 │
│                                                                    │
│  [📦 手动导出诊断包(离线收集)]                                    │
└────────────────────────────────────────────────────────────────────┘

九、实施计划

Phase 1(3-4 天)— 核心框架
├── app/diagnostic/ 目录结构 + 配置读取
├── DiagnosticEngine 框架 + 规则基类
├── DB 聚合类规则(R001/R005-R008)
├── 引擎协议类规则(R002/R003/R004)
└── Docker 能力检测

Phase 2(2-3 天)— 定时器 + 页面
├── 三层定时器 + Redis 缓存
├── 页面必跑规则通道
├── 诊断面板 HTML 模板(含 Docker 状态 + 总开关横幅)
├── 异常列表 + 时间戳显示
└── docker-compose.yml api 服务加 docker.sock 挂载

Phase 3(2 天)— 日志匹配 + 导出
├── 日志匹配类规则(R009-R014)
├── docker inspect 类规则(R015)
├── 一键导出 tar.gz + 脱敏
├── Dockerfile 加 docker CLI
└── 导出包结构完成

Phase 4(1 天)— 测试 + 验证
├── docker build + 重启 + 功能验证
├── 模拟无 Docker 环境(不挂 socket)→ 验证降级
├── 关闭总开关 → 验证横幅 + 手动导出仍可用
└── 全接口自测

十、后续演进(不在 v1.0)

版本 功能
v1.1 导出包可选"含完整策略 YAML";规则阈值页面可配(不用改 yaml)
v1.1 导出包可选"上传到指定 S3 桶"(如果客户有内网 S3)
v2.0 云端收集服务(MeshSafe Pulse):多客户聚合 + dashboard + 规则引擎云端版
v2.0 LLM 辅助分析:把 diagnostics.tar.gz 喂给 LLM 生成结构化报告
v3.0 AI 辅助修复闭环(仅在你自己的 sandbox 里做,不碰客户环境)

附录 A:Dockerfile 改动

# ===== 新增:装 docker CLI =====
# api 容器需要 docker CLI 才能通过 docker.sock 访问宿主机 Docker
RUN apt-get update && apt-get install -y --no-install-recommends \
    docker.io-cli \
    && rm -rf /var/lib/apt/lists/*

附录 B:docker-compose.yml 改动

api:
  volumes:
    # ... 原有 volume ...
    - /var/run/docker.sock:/var/run/docker.sock:ro   # ← 新增:只读挂载

附录 C:快速降级指引

问题 处理
启动报 ModuleNotFoundError: docker 忘装 docker CLI,跑 apt-get install docker.io-cli
启动后页面显示 ℹ️ Docker 能力未启用 没挂 docker.sock;compose api volumes 加一行 + rebuild
想完全关掉诊断 diagnostics.enabled: false 或环境变量
定时器吃 CPU 三个定时器周期都可以调大;或设为 0 不跑某个
导出包太大 export_log_tail 调小;或只导出 scan_tasks.json

文档结束 — 待审核 → 确认后开始编码