Skip to content
twinkling.top服务Eclipse MosquittoEN
板型any Pi, including a Zero 2 W
内核6.6.51+rpt-rpi-v8
空闲温度48 °C
内核架构arm64
参考机:Pi 4B 8GB / Bookworm 64-bit

服务卡片 · 自动化

Mosquitto:传感器下面的那条消息总线

二十五兆内存,却是所有「传感器形状」的东西所经过的那一环。Mosquitto 是 MQTT 的参考实现:传感器把读数发布到主题上,Home Assistant 订阅,谁都不需要知道谁的 IP。它同时也是机架上默认配置真正危险、必须在第一台设备接入前就换掉的那个服务。

eclipse-mosquitto:2.0.20 · 2026-10-12 ·

Eclipse Mosquitto rack plate: board stack and host ports
服务参数
容器镜像eclipse-mosquitto:2.0.20
宿主端口:1883/tcp, :9001/tcp
数据目录/srv/homelab/mosquitto/data
内存预留25 MB
CPU 上限0.10 vCPU (cpus: "0.10") — this is the cheapest service on the rack by a wide margin
更新节奏yearly. The protocol is stable and the 2.0 configuration file is not going to change again soon
ARM 兼容arm64 native; one of the few services here that has no meaningful architecture dependency at all
端口映射与暴露面
宿主容器proto暴露用途
:18831883tcp仅局域网MQTT for the sensors and the dashboards that read them
:90019001tcp仅局域网WebSocket listener for browser dashboards

部署步骤

  1. 用 mosquitto_passwd 生成密码文件,一设备一用户

    文件必须对容器用户可读。这里出权限错误的表现,是「所有设备突然都用错密码了」。

    run
    docker run --rm -it -v /srv/homelab/mosquitto/config:/cfg \
      eclipse-mosquitto:2.0.20 mosquitto_passwd -c -b /cfg/passwd kitchen-sensor 'LONG-SECRET'
    docker run --rm -it -v /srv/homelab/mosquitto/config:/cfg \
      eclipse-mosquitto:2.0.20 mosquitto_passwd -b /cfg/passwd homeassistant 'OTHER-SECRET'
  2. 在第一台设备接入之前就写好 ACL 文件

    等设备跑起来之后再补 ACL,意味着那台设备已经拥有过它不该有的权限。先写文件,再接设备。

    run
    cat > /srv/homelab/mosquitto/config/acl <<'EOF'
    user kitchen-sensor
    topic write home/kitchen/#
    topic read home/kitchen/cmd
    
    user homeassistant
    topic read #
    topic write home/#
    EOF
  3. 用一个没有凭据的客户端验证「拒绝」这条路

    接受匿名客户端的 broker,就是一个会泄露整栋房子状态的 broker。要显式测一次拒绝,而不是相信配置文件。

    run
    docker compose up -d mosquitto
    mosquitto_sub -h 192.168.10.20 -p 1883 -t '#' -C 1
    # 预期:Connection error: Connection Refused: not authorised.
    docker exec mosquitto tail -5 /mosquitto/log/mosquitto.log

默认监听是匿名的,这一点必须先改

Mosquitto 2.0 在 allow_anonymous 没有显式设置时,会拒绝用 1.x 那套宽松默认值启动,这是进步——但教程仍然从 allow_anonymous true 开始。在这个设置下,任何能连到 1883 端口的人都能读到所有主题,包括那些会暴露家里有没有人的主题。

这里的配置是明确的:不允许匿名客户端,有密码文件,还有一个 ACL 文件,给每台设备恰好它需要的那些主题。

shell
# /srv/homelab/mosquitto/config/mosquitto.conf
listener 1883 0.0.0.0
allow_anonymous false
password_file /mosquitto/config/passwd
acl_file /mosquitto/config/acl
persistence true
persistence_location /mosquitto/data/
log_dest file /mosquitto/log/mosquitto.log

ACL 才是拦住被攻陷传感器的东西

一个密码泄露的 ESP 传感器,不应该能订阅「家里现在有没有人」那个主题。主题级 ACL 由 broker 强制执行,配置起来不花任何代价。

这里的模式是一设备一用户,用设备名命名,只有对自己那棵子树的写权限,以及只对控制自己的那条命令主题的读权限。

shell
# /srv/homelab/mosquitto/config/acl
user kitchen-sensor
topic write home/kitchen/#
topic read home/kitchen/cmd

user homeassistant
topic read #
topic write home/#

WebSocket 监听只为一个理由存在

浏览器开不了原始的 TCP MQTT 连接,所以 9001 上的 WebSocket 监听是给那台直接订阅主题的壁挂仪表盘用的。家里其它一切都用 1883 上的普通 MQTT。

WebSocket 监听的名声不好,是因为浏览器客户端最容易泄露凭据。仪表盘持有的是一个只读账号,而这是唯一重要的缓解措施。

compose 文件

整份文件放在 /srv/homelab/mosquitto/compose.yaml。标签固定版本号,不用 latest——树莓派上回滚比升级麻烦得多。

compose.yaml
services:
  mosquitto:
    image: eclipse-mosquitto:2.0.20
    container_name: mosquitto
    restart: unless-stopped
    ports:
      - "1883:1883/tcp"
      - "9001:9001/tcp"
    environment:
      TZ: Asia/Shanghai
    volumes:
      - /srv/homelab/mosquitto/config:/mosquitto/config
      - /srv/homelab/mosquitto/data:/mosquitto/data
      - /srv/homelab/mosquitto/log:/mosquitto/log
    networks: [rack]

networks:
  rack:
    external: true

加固清单

  • 关闭匿名访问,每个客户端都用用户名密码认证
  • 按客户端配置 ACL:ESP 传感器只能发布到自己的主题分支,订阅不了别人的
  • 监听绑定在局域网上、不做转发;远程传感器通过 WireGuard 连进来
  • 局域网监听上配置了 TLS 并使用本地信任的证书,即使在家里,凭据也不会明文传输

备份方案

broker 的持久化数据库里存着排队消息和保留值;丢了它意味着传感器会在一个上报周期内重新发布状态,而保留主题会空到下一次读数为止。真正值得复制的只有配置和密码文件,两个都很小。

上线后验证

  • 不带凭据的客户端被拒绝,且拒绝记录出现在 broker 日志里
  • 厨房传感器能发布到自己主题上,尝试读取「有人在家」主题时被拒绝
  • 往某个主题发布的保留消息,会立刻投递给新订阅者
  • broker 用同一个密码文件重启后,所有设备无需重配即可重连

踩过的坑

  • 密码文件要用 mosquitto_passwd 生成,不能手写;而且文件必须对容器用户可读。这里出权限错误的表现,是「所有设备突然都用错密码了」。
  • 保留消息会跨 broker 重启存在,并投递给每一个新订阅者。来自一台已被移除的传感器的过期保留读数,会永远留在仪表盘上,直到有人往那个主题发一个空载荷把它清掉。
  • 持久化数据库会随着离线客户端的排队消息增长。一台以 QoS 1 订阅的设备离线后,broker 会为它把一切都排起队;给每客户端设置最大排队消息数能避免慢性膨胀。
  • 配置错误的客户端发出的一个通配订阅,就能拉走整棵主题树。拦住它的地方是 ACL 文件,不是客户端。

硬件与选型问答

只有 Zigbee 设备的话还需要 MQTT 吗?
不一定。Home Assistant 能直接和 Zigbee 协调器通信,加一个没人往里发布消息的 broker,就是白白多一个要维护的服务。当家里有基于 ESP 的传感器或者原生讲 MQTT 的 DIY 设备时,它才值这个位置——而大多数有意思的项目最后都会走到那里。
为什么它的内存占用比这里其它一切都低这么多?
因为 broker 就是一张路由表加一堆订阅,而不是一个应用。二十五兆主要是进程本身的开销,而一个房子里的消息量微不足道。它被放进这台机架,恰恰因为它几乎不花钱,却能省掉大量点对点配置。
远程传感器要不要把 broker 暴露到公网?
不要。远程传感器应该待在 WireGuard 隧道里,亲戚家车库里的那一个就是这么连的。公开暴露 1883 只会招来 MQTT broker 常见的那种暴力破解流量,而隧道能提供的能力它一样提供不了。