我在豆子碎片项目中开始使用MQTT,比如文章上传后我使用了MQTT来通知审核的消息。这样就要求不能让张三接收到属于李四的消息。当然还有其它场景使用MQTT,比如ESP设备连接MQTT。比如用户上传文章后,我的ESP设备的LED就会闪烁,蜂鸣器也会响。我服务器上部署的MQTT Broker是mosquitto。在 Mosquitto 实例上配置身份验证非常重要,这样未经授权的客户端就无法连接。在 Mosquitto 2.0 及更高版本中,你必须明确选择身份验证选项,然后客户端才能连接。早期版本中,默认设置是允许客户端无需身份验证即可连接。

Mosquitto身份验证有三种选择:密码文件、身份验证插件和匿名访问。可以单独使用,也可以使用三个选项的组合。在 Mosquitto2.0 及更高版本中,可以通过配置文件中将 per_listener_settings 设置为 true,让不同的侦听器使用不同的身份验证方法。除了身份验证,还应该考虑访问权限控制,以确定哪些客户端可以访问哪些主题。我们来看下不同选项的配置。

密码文件设置

密码文件是一种将用户名和密码存储在单个文件中的简单机制。适合于有相对较少的静态用户。请注意,修改了密码文件后,需要重新加载 mosquitto。

创建密码文件,可以使用mosquitto_passwd工具,命令如下:

mosquitto_passwd -c <password file> <username>

请注意,使用-c 标志将覆盖已存在的文件,如果向已存在文件添加,请去掉-c 标志。

要开始使用密码文件,需要配置 broker,在配置文件中,添加如下项:

password_file <path to the configuration file>

密码文件必须能够被运行 mosquitto 的用户读取,也就是必须具备读取它的权限。

身份验证插件设置

如果需要更多的控制来认证用户,则需要使用认证插件。具体的特性依赖于使用的认证插件。

可使用的插件有:

  • mosquitto-go-auth ,它提供了各种后端来存储用户数据,例如 mysql,postgresql,jwt 或 redis 等。
  • Dynamic security,动态安全插件,仅适用于 2.0 及更高版本,可提供原创管理的灵活的代理客户端、组和角色。

配置身份验证插件依赖你的 Mosquitto 的版本。版本 1.6.x 和以前的版本,使用auth_plugin选项。这个选项在版本 2.0 也被支持。

listener 1883
auth_plugin <path to plugin>

版本 2.0 及更高,使用plugin选项。

listener 1883
plugin <path to plugin>

我使用了这个选项,因为这个可以对接数据库,我可以方便的在数据库中维护权限,这样我不需要像密码文件那样,修改一个权限还需要重新启动Mosquitto。这对于对接其他系统或扩展业务非常方便。

匿名访问设置

配置匿名访问,请使用allow_anonymous选项。

listener 1883
allow_anonymous true

这种模式一般在开发和测试时使用,生产模式不推荐。可以对同一代理同时进行匿名和身份验证访问两种模式,使用动态安全插件允许为匿名用户分配与经过身份验证的用户不同的权限。下面我将根据在实际应用中碰到的场景进行说明和解释。比如限制采集器只能发布数据到自己的路径,且不能订阅别人的数据。

应用场景:基于 ClientID 的细粒度 ACL 权限控制

不要只开启全局密码,要增加一个 acl_file。假设你有两类设备:采集器(Sensor)和控制端(Admin)。
我们可以这样配置,具体操作步骤:
在 mosquitto.conf 中指定:

acl_file /etc/mosquitto/aclfile

在 /etc/mosquitto/aclfile 中写入:

# 
pattern readwrite devices/%c/telemetry
pattern read devices/%c/commands

# 限制管理后台可以查看所有数据
user admin
topic readwrite devices/#

注:%c 会自动匹配连接时的 ClientID,实现了一套配置管理成千上万个设备。

在实际布署 ESP8266/ESP32 接入 Mosquitto 时,务必注意以下两点:

  • 连接超时:开启认证后,握手报文变大,若网络环境极差(如粮仓深处),需适当调大客户端的 setKeepAlive 时间。
  • 明文风险:若不配合 TLS/SSL,密码在内网是明文传输的。在工业现场,建议至少配合内网隔离或简单的 username 混淆。

这是我在实际工作中的碰到的案例,还有更多的场景因为一些原因不便分享和记录。