Skip to content

ADR-0005:边缘遥测走 MQTT,Agent 侧不持有 Broker 凭证 ​

  • 状态:已采纳
  • 日期:2026-09-15
  • 决策者:毕设作者
  • 相关文档:docs/07-STM32边缘终端设计.md、docs/04-MCP安全网关设计.md、docs/adr/0002-mcp-gateway-as-sole-egress.md

背景 ​

边缘终端需要把温度与振动数据送到服务端。可选方案包括 MQTT、HTTP 上报、CoAP、自定义 TCP 协议、甚至串口转接。同时必须回答一个安全问题:Agent 能不能自己订阅 MQTT 主题拿数据?

从工程便利角度看,让 Agent 直接订阅 iot/+/telemetry 是最省事的——一个 mqtt.connect 就拿到全部设备数据。但这会直接推翻 ADR-0001 与 ADR-0002 建立的边界:

  • Agent 一旦持有 Broker 凭证,就能订阅全部主题,包括其他设备、其他产线的数据;
  • Broker 侧无法做"按调用上下文裁剪字段";
  • Agent 的取数行为不再经过审计落库,审计出现盲区;
  • 提示注入可以诱导 Agent 把订阅范围扩大到与任务无关的设备。

决策 ​

分工明确,两条通道物理分离:

通道两端协议承载内容
上行数据通道边缘终端 ↔ MQTT Broker ↔ 网关MQTT温度、振动遥测;终端状态;指令 ACK
工具调用通道Agent ↔ 网关MCPmqtt.read_sensor 等工具调用

Agent 不持有 MQTT Broker 的任何凭证,不部署 MQTT 客户端库,环境变量中不存在 Broker 地址与账号。Agent 取遥测数据的唯一方式是调用网关的 mqtt.read_sensor 工具,由网关:

  1. 从 Broker 订阅/查询得到原始数据(或直接读 business-db.sensor_data 的已落库数据);
  2. 按参数校验设备 ID 是否在 Agent 的授权设备范围内;
  3. 按字段白名单裁剪为 {temp, vibration, time};
  4. 落审计;
  5. 返回。

理由 ​

  1. 订阅是"全量能力",工具是"按次授权"。MQTT 主题通配符 + / # 天然是"一次订阅、长期全量",与最小权限原则冲突;MCP 工具天然是"一次调用、一次授权、一次审计"。
  2. Broker 无法表达业务语义。Broker 不知道"这个 Agent 当前在处理 3 号产线的故障",网关知道。裁剪与授权必须在有业务上下文的地方做。
  3. 终端的信任对象收敛为网关一个。终端只需配置一套 Broker 凭证(或一机一密),不需要为 Agent 单独开权限,固件侧的安全设计大幅简化。
  4. 审计完整性。所有 Agent 取数行为在 audit_log 中有且只有一条来源,答辩时可以用一条查询证明"Agent 从未绕过网关取过数"。

备选方案与拒绝理由 ​

方案为什么没选
Agent 直接订阅 MQTT 主题违反 ADR-0002 唯一出口不变量;无法字段裁剪;审计盲区;主题通配符导致越权范围不可控。
Agent 直连 business-db.sensor_data违反 ADR-0001;Agent 会顺带获得同库其他表的访问能力。
终端 HTTP 直连业务接口服务工业现场网络抖动与功耗场景下,HTTP 长轮询/短连接开销高于 MQTT;且失去 Broker 的 QoS 与遗嘱消息能力。
网关把 MQTT 数据实时推给 Agent(订阅式)原型阶段会引入推流背压与上下文膨胀问题;且"推"绕过了"一次调用一次审计"。落地期若需要,可作为 mqtt.watch_sensor 工具另行设计并保留审计语义。

后果 ​

正面

  • 终端侧只需信任一个网关,固件安全设计简化为一机一密 + TLS。
  • Agent 的取数范围可被逐次限制(设备 ID、时间窗口、采样点数)。
  • 审计链条完整:终端 → Broker → 网关 → business-db 每一跳都有日志。

负面 / 代价

  • 网关成为 MQTT 的唯一订阅方,需要承担主题订阅管理、连接保活与消息积压处理。
  • 实时性要求高的场景(如高频振动波形分析)经网关转发会有延迟;原型阶段用"网关先落库、Agent 按窗口读取"的方式规避,落地期需评估。
  • 遥测数据存在"落库一份 + 网关缓存一份"的双份问题,需要明确以 business-db 为权威来源。

原型期的具体取值 ​

  • Broker:Mosquitto,仅监听内网/本机,启用 password_file 与 ACL。
  • 主题:iot/{deviceId}/telemetry、iot/{deviceId}/status、iot/{deviceId}/cmd。
  • QoS:遥测用 QoS 1(至少一次,容忍重复,靠 seq 去重);指令用 QoS 1 + ACK。
  • 网关订阅 iot/+/telemetry,以服务账号身份;该账号不出现在 Agent 侧任何配置中。