2. 基于Mosquitto搭建矿山本地Broker

为什么矿山现场要自建 Broker

井下瓦斯、风速、水位传感器与地面监控客户端如果直接连公网云 Broker,会受井下环网抖动、出口带宽和断网影响。把 Mosquitto 部署在矿山本地服务器(如调度室工控机或边缘网关),让传感器与监控客户端走局域网,可显著降低时延并保证断网时本地仍能转发。本例在一台 Linux 本地服务器上完成 Mosquitto 的安装、监听端口、用户名密码认证与 ACL 授权,并用两个客户端验证“能连上、只能访问自己的主题”。

部署拓扑与访问控制关系

下图给出本例要落地的对象关系:井下传感器以设备账号接入,地面监控以监控账号接入,二者通过 ACL 被限制在各自的主题子树内,Broker 只开放一个内网监听端口。

井下环网(传感器侧) 瓦斯传感器 CH4-07 user: dev_ch4_07 风速传感器 WS-03 user: dev_ws_03 水位传感器 WL-02 user: dev_wl_02 发布 mine/{type}/{area}/{id}/data Mosquitto Broker 本地服务器 192.168.10.20 listener 1883(内网) allow_anonymous false password_file + acl_file 地面监控(订阅侧) 监控大屏客户端 user: mon_display 报警服务 user: mon_alarm 订阅 mine/+/+/+/data 禁止发布到 mine/# PUBLISH SUBSCRIBE ACL:设备仅可写自身主题 监控仅可读 mine/#

安装与基础配置

以 Debian/Ubuntu 系服务器为例,安装后 Mosquitto 默认只监听本机回环地址,必须显式配置监听地址才能被井下与地面客户端访问。

sudo apt update
sudo apt install -y mosquitto mosquitto-clients
sudo systemctl enable mosquitto

# 备份默认配置后编辑
sudo cp /etc/mosquitto/mosquitto.conf /etc/mosquitto/mosquitto.conf.bak
sudo tee /etc/mosquitto/conf.d/mine.conf > /dev/null <<'EOF'
# 矿山本地监听:仅绑定内网网卡,避免暴露到公网
listener 1883 192.168.10.20
allow_anonymous false
password_file /etc/mosquitto/passwd
acl_file /etc/mosquitto/aclfile
# 关闭持久化会话以外的多余日志噪声,保留连接与认证日志便于排障
log_type error
log_type warning
log_type notice
log_type information
connection_messages true
EOF
若服务器有多块网卡,listener 1883 192.168.10.20 中的 IP 必须替换为实际内网地址;写成 listener 1883 会监听所有网卡,存在被外部扫描的风险。

创建账号与 ACL 规则

为每类接入方建立独立账号,避免共用账号导致无法追溯。密码文件用 mosquitto_passwd 生成,ACL 文件按“用户 → 主题 → 读写权限”逐条声明。

# 创建密码文件并添加账号(-c 仅首次创建时使用,后续追加不要加 -c)
sudo mosquitto_passwd -c /etc/mosquitto/passwd dev_ch4_07
sudo mosquitto_passwd /etc/mosquitto/passwd dev_ws_03
sudo mosquitto_passwd /etc/mosquitto/passwd dev_wl_02
sudo mosquitto_passwd /etc/mosquitto/passwd mon_display
sudo mosquitto_passwd /etc/mosquitto/passwd mon_alarm

sudo tee /etc/mosquitto/aclfile > /dev/null <<'EOF'
# 设备账号:只能向自己的数据主题发布,不能订阅
user dev_ch4_07
topic write mine/ch4/area1/07/data

user dev_ws_03
topic write mine/ws/area1/03/data

user dev_wl_02
topic write mine/wl/area2/02/data

# 监控账号:只读全部矿山数据主题,禁止发布
user mon_display
topic read mine/#

user mon_alarm
topic read mine/#
EOF

sudo chown mosquitto:mosquitto /etc/mosquitto/passwd /etc/mosquitto/aclfile
sudo chmod 640 /etc/mosquitto/passwd /etc/mosquitto/aclfile
sudo systemctl restart mosquitto
sudo systemctl status mosquitto --no-pager
ACL 中 topic write 只授予发布权限,topic read 只授予订阅权限。若设备账号误写成 topic readwrite mine/#,该设备即可读取全矿数据,属于越权配置。

用两个客户端验证接入与隔离

验证分三步:监控端订阅、设备端发布、越权访问被拒绝。以下命令在局域网内另一台机器执行,-h 指向 Broker 内网地址。

# 终端 A:监控账号订阅全部数据主题
mosquitto_sub -h 192.168.10.20 -p 1883 \
  -u mon_display -P '监控账号密码' \
  -t 'mine/#' -v

# 终端 B:瓦斯设备账号发布一条数据
mosquitto_pub -h 192.168.10.20 -p 1883 \
  -u dev_ch4_07 -P '设备账号密码' \
  -t 'mine/ch4/area1/07/data' \
  -m '{"ch4":0.42,"ts":1730000000}'

# 终端 C:设备账号尝试发布到别人的主题,应被拒绝
mosquitto_pub -h 192.168.10.20 -p 1883 \
  -u dev_ch4_07 -P '设备账号密码' \
  -t 'mine/wl/area2/02/data' -m 'test'

# 终端 D:匿名连接,应被拒绝
mosquitto_pub -h 192.168.10.20 -p 1883 -t 'mine/test' -m 'x'

结果判读

验证项预期现象不符合时的排查方向
监控端订阅终端 A 收到 mine/ch4/area1/07/data {"ch4":0.42,...}检查 ACL 是否含 topic read mine/#、账号密码是否正确
设备端发布终端 B 无报错退出,终端 A 立即显示消息检查 topic write 主题是否与发布主题逐段一致
越权发布终端 C 连接成功但发布被拒,终端 A 收不到该消息若收到消息,说明 ACL 权限过宽,需收紧为精确主题
匿名连接终端 D 报 Connection Refused: not authorised确认 allow_anonymous false 已生效并重启服务

若终端 A 能连上却收不到任何消息,先看 Broker 日志 sudo journalctl -u mosquitto -n 50,重点确认是否有 Denied PUBLISH 或 not authorised 记录,再回到 ACL 文件核对主题层级是否与发布主题完全一致(ACL 不做前缀模糊匹配,mine/ch4/area1/07/data 与 mine/ch4/area1/07/data/ 视为不同主题)。

上线前检查清单

  • 监听地址绑定内网 IP,未使用 0.0.0.0 暴露到公网。
  • allow_anonymous false 已开启,匿名连接被拒。
  • 每个设备、每个监控服务使用独立账号,密码文件权限为 640 且属主为 mosquitto。
  • ACL 中设备账号只有 write,监控账号只有 read,无 readwrite mine/# 类宽泛规则。
  • 已用 mosquitto_sub/mosquitto_pub 完成“正常收发 + 越权被拒 + 匿名被拒”三项验证。