☰
Eclipse Mosquitto 插件开发实战指南:从动态安全到消息改写的内置插件生态解析
2026/9/26 8:27:27 网站建设 项目流程
  • 物联网
  • 消息队列
  • 后端

【免费下载链接】mosquitto

Eclipse Mosquitto - An open source MQTT broker

项目地址:https://gitcode.com/gh_mirrors/mosquit/mosquitto
点击查看免费下载

Eclipse Mosquitto 在 2.x 系列中提供了完整的插件体系,允许以共享库(.so/.dll)形式挂载认证授权、消息拦截、统计上报等能力,而无需改动 broker 内核。本文以仓库 plugins/README.md 为主线,结合plugins/目录下的全部源码与测试用例,系统讲解 Mosquitto 插件目录的组成、开发框架、编译加载方式,并逐一剖析动态安全、持久化、Sparkplug 感知三大生产级插件与 19 个示例插件的实现原理,帮助读者掌握"用插件扩展 Mosquitto"的完整实战路径。

插件目录总览与定位

plugins/目录是 Mosquitto 官方为 broker 扩展准备的"应用商店",其中:

  • dynamic-security/:一个功能完整的插件,通过$CONTROL主题实现认证与访问控制的动态配置(详见 plugins/dynamic-security/README.md);
  • persist-sqlite/:基于 SQLite 的持久化插件,提供比默认内存/文件持久化更强的存储能力;
  • sparkplug-aware/:面向 Sparkplug 规范(工业 MQTT 场景)的消息感知插件;
  • examples/:19 个教学级示例插件,覆盖插件 API 的绝大多数回调场景。

顶层构建入口 plugins/CMakeLists.txt 通过四个add_subdirectory将这四部分纳入 CMake 构建体系。插件编译为模块(MODULE)共享库,并链接common-options与mosquitto库;非 Windows 平台安装时会将运行时产物放入CMAKE_INSTALL_BINDIR、库文件放入CMAKE_INSTALL_LIBDIR。

每个示例插件目录都遵循统一结构:mosquitto_<功能>.c(源码)、Makefile(编译)、test.conf(运行配置)、test.sh(用仓库内已构建的../../../src/mosquitto -c test.conf -v启动 broker 进行验证,见 plugins/examples/message-timestamp/test.sh)。使用make(在 plugins/examples/Makefile 中会遍历所有子目录)或 CMake 均可一键构建全部插件。

Mosquitto 插件开发框架速览

所有插件都基于 include/mosquitto/broker_plugin.h 与 include/mosquitto/broker.h 中定义的事件回调 API。一个最简插件只包含三部分:

  1. 声明插件 API 版本:在源码顶部调用MOSQUITTO_PLUGIN_DECLARE_VERSION(5);,宏定义位于 include/mosquitto/broker_plugin.h。本仓库示例全部使用版本 5,因此要求 Mosquitto 2.0 及以上;wildcard-temp明确标注需要 2.1 及以上(因为用到了mosquitto_subscription_delete()等较新 API)。
  2. 实现mosquitto_plugin_init():接收插件标识符,调用mosquitto_plugin_set_info()(见 include/mosquitto/broker.h)设置插件名与版本,再用mosquitto_callback_register()注册感兴趣的事件回调。
  3. (可选)实现mosquitto_plugin_cleanup():用于释放插件自身资源;2.1 之后该函数可省略。

编译命令在所有示例源码头部注释中均有给出,形式统一为:

gcc -I<path to mosquitto-repo/include> -fPIC -shared mosquitto_xxx.c -o mosquitto_xxx.so

在 broker 配置中加载只需一行:

plugin /path/to/mosquitto_xxx.so

配置项写法可参考各示例的 test.conf。插件可用的事件覆盖认证、ACL 检查、连接/断开、消息进出、订阅/退订、时钟 tick 等生命周期,下面按功能类别逐一展开。

一、认证与访问控制类插件

动态安全(Dynamic Security):基于 $CONTROL 主题的完整安全方案

dynamic-security/是本目录中唯一的"生产级"安全插件,实现了通过 MQTT 消息动态管理安全策略的机制:JSON 命令发布到$CONTROL/<feature>/v1形式的主题即可完成客户端、分组、角色与 ACL 的全生命周期管理,无需重启 broker 或编辑配置文件。

核心概念模型(详见 plugins/dynamic-security/README.md):

  • Clients(客户端):客户端连接时提供的用户名映射到 broker 上的一个 client 实例;多个物理连接可共用同一用户名,因而共享同一 broker 端 client 及其权限;
  • Groups(分组):broker client 可归属于零个或多个分组,用于批量授权;
  • Roles(角色):角色可绑定到 client 或 group,定义该主体允许做什么,例如可/不可发布或订阅的主题。

权限模型:ACL 共有四种类型——publishClientSend(客户端对外发布)、publishClientReceive(接收订阅消息)、subscribe(订阅)、unsubscribe(退订)。默认行为为:publishClientSend与subscribe默认拒绝,publishClientReceive与unsubscribe默认允许,可通过setDefaultACLAccess命令调整:

{ "commands":[ { "command": "setDefaultACLAccess", "acls":[ { "acltype": "publishClientSend", "allow": false }, { "acltype": "publishClientReceive", "allow": true }, { "acltype": "subscribe", "allow": false }, { "acltype": "unsubscribe", "allow": true } ] } ] }

对应命令行客户端(mosquitto_ctrl dynsec,源码见 apps/mosquitto_ctrl/dynsec.c):

mosquitto_ctrl dynsec setDefaultACLAccess subscribe deny mosquitto_ctrl dynsec getDefaultACLAccess

客户端管理命令(全部支持 JSON 与 mosquitto_ctrl 两种方式):

操作JSON 命令mosquitto_ctrl 示例
创建客户端createClient(可带clientid、textname、textdescription、groups、roles,组与角色须已存在)mosquitto_ctrl dynsec createClient username password
删除客户端deleteClientmosquitto_ctrl dynsec deleteClient username
启用客户端enableClientmosquitto_ctrl dynsec enableClient username
禁用客户端disableClient(阻止登录并踢掉当前同名连接)mosquitto_ctrl dynsec disableClient username
查询客户端getClientmosquitto_ctrl dynsec getClient username
列出客户端listClients(verbose、count=-1表示全部、offset分页)mosquitto_ctrl dynsec listClients 10 20
修改客户端modifyClient(可改clientid、password、textname、textdescription、roles、groups;暂不支持 mosquitto_ctrl)—
设置客户端 IDsetClientId(留空则清除)mosquitto_ctrl dynsec setClientId username clientId
设置密码setClientPasswordmosquitto_ctrl dynsec setClientPassword username password
添加/移除角色addClientRole(可带priority)、removeClientRolemosquitto_ctrl dynsec addClientRole username rolename/removeClientRole
加入/移出分组addGroupClient(可带priority)、removeGroupClientmosquitto_ctrl dynsec addGroupClient groupname username

分组管理命令:createGroup(可带roles)、deleteGroup、getGroup、listGroups(verbose/count/offset)、modifyGroup(可改textname、textdescription、roles、clients,暂不支持 mosquitto_ctrl)、addGroupRole/removeGroupRole、setAnonymousGroup(为匿名客户端指定分组)、getAnonymousGroup。对应的 mosquitto_ctrl 调用形如mosquitto_ctrl dynsec createGroup groupname、mosquitto_ctrl dynsec setAnonymousGroup groupname。

角色与 ACL 管理命令:createRole(可携带初始acls,如{ "acltype": "subscribePattern", "topic": "topic/#", "priority": -1, "allow": true })、getRole、listRoles、modifyRole(暂不支持 mosquitto_ctrl)、deleteRole、addRoleACL、removeRoleACL。ACL 类型支持subscribePattern(订阅模式匹配)与字面量形式(subscribeLiteral),命令示例:

mosquitto_ctrl dynsec createRole rolename mosquitto_ctrl dynsec addRoleACL rolename subscribeLiteral topic/# deny mosquitto_ctrl dynsec removeRoleACL rolename subscribeLiteral topic/#

从源码结构看,该插件由 acl.c(ACL 评估)、auth.c(认证)、clients.c / groups.c / roles.c(三类主体管理)、control.c($CONTROL命令分发)、config.c(配置文件加载)等模块组成,并有test.sh与配套的 test.conf 做端到端验证,broker 测试套件中14-dynsec-*.py(见 test/broker)覆盖了客户端、分组、角色、ACL、禁用、匿名组等大量场景。

按 IP 认证(auth-by-ip)

mosquitto_auth_by_ip.c 演示基于 IP 地址的认证回调:插件直接根据客户端来源 IP 决定是否放行。README 明确提示:这类简单的访问控制不如基于密码的认证可靠(IP 可伪造、可被 NAT 掩盖),更适合作为二次约束或演示用途。

环境变量认证(auth-by-env)

mosquitto_auth_by_env.c 从环境变量中读取用户名/密码进行校验,演示插件如何利用mosquitto_opt配置参数与外部数据源对接。它是理解"插件如何获得 broker 配置参数"的最小范例。

延迟认证(delayed-auth)

mosquitto_delayed_auth.c 演示异步/延迟认证的正确姿势:当插件需要把认证请求发给外部服务器(如 HTTP 鉴权服务)时,不能在回调里同步等待响应,否则会阻塞 broker 主线程。插件可以自行派生线程处理认证请求,但最终必须在 Mosquitto 主线程中调用mosquitto_complete_basic_auth()提交结果——该函数声明于 include/mosquitto/broker.h,注释明确要求该调用发生在主线程。这是避免阻塞式鉴权拖垮整个 broker 的关键模式。

拒绝协议版本(deny-protocol-version)

mosquitto_deny_protocol_version.c 演示如何拒绝指定 MQTT 协议版本的客户端连接,可用于统一升级到 MQTT 5、禁用过旧协议等安全合规场景。

二、连接生命周期与统计类插件

连接状态(connection-state)

mosquitto_connection_state.c 演示MOSQ_EVT_CONNECT/MOSQ_EVT_DISCONNECT事件的使用:对每个客户端,在连接时向$SYS/broker/connection/client/<client id>/state发布载荷"1",断开时发布"0",即把在线状态本身变成一条可订阅的 MQTT 消息。实现要点:

  • 用mosquitto_client_id(ed->client)取客户端 ID,拼装主题时检查snprintf返回值防止 client id 过长;
  • 通过mosquitto_broker_publish_copy()(见 include/mosquitto/broker.h)让 broker 代为发布消息;
  • 断开消息附带MQTT_PROP_MESSAGE_EXPIRY_INTERVAL = 86400(1 天)的 MQTT v5 消息过期属性,避免"离线状态"永远驻留。

客户端生命周期统计(client-lifetime-stats)

mosquitto_client_lifetime_stats.c 统计会话存活时长分布:用 uthash 哈希表记录每个客户端的连接时间,断开时计算time(NULL) - connect并归入对应时间桶。时间桶共 28 档:0, 1, 2, 5, 10, 20, 50, 100, 200, 500, 1k, 2k, 5k, 10k, 20k, 50k, 100k, 200k, 500k, 1M, 2M, 5M, 10M, 20M, 50M, 100M, 200M, 500M秒(源码中lifetime_strs与lifetime_values两数组一一对应)。MOSQ_EVT_TICK回调每 10 秒把有变化的计数发布到$SYS/broker/client/lifetimes/<bucket>。

载荷大小统计(payload-size-stats)

mosquitto_payload_size_stats.c 与生命周期统计对称,统计发布消息的载荷字节数分布,桶划分同为 28 档(0, 1, 2, 5, ..., 500M字节),周期性发布到$SYS/broker/publish/sizes/<bucket>。此类统计对评估流量构成、定位超大消息很有价值。

发布时打印 IP(print-ip-on-publish)

mosquitto_print_ip_on_publish.c 在客户端向特定主题发布消息时,把该客户端的client ID 与 IP 地址打印到 broker 日志,用于审计"谁在向敏感主题发数据"。

插件事件统计(plugin-event-stats)

mosquitto_plugin_event_stats.c 统计插件系统各类事件被触发的次数,是理解事件驱动模型、排查"回调是否被调用"的实用调试工具。

三、消息处理类插件(消息进入后、转发前)

此类插件均注册MOSQ_EVT_MESSAGE_IN回调,在消息被 broker 接收之后、转发给订阅者之前介入,是 Mosquitto 插件体系中最具扩展力的能力之一。

附加 MQTT v5 属性(add-properties)

mosquitto_add_properties.c 演示为入站消息追加 MQTT v5 用户属性(user-property),并展示如何获取客户端信息。它通过mosquitto_property_add_string_pair()依次写入三个属性:

  • $timestamp:broker 接收消息时的 Unix 毫秒时间戳(clock_gettime(CLOCK_REALTIME)获取);
  • $clientid:发布者客户端 ID(mosquitto_client_id());
  • $client_username:发布者认证用户名(mosquitto_client_username())。

注意:修改的是struct mosquitto_evt_message中的ed->properties,MQTT v5 客户端订阅时即可读到这些附加属性。

消息时间戳(message-timestamp)

mosquitto_message_timestamp.c 与 add-properties 思路一致但更聚焦:为每条消息追加键为timestamp的 user-property,值为ISO-8601 格式(%Y-%m-%dT%H:%M:%SZ,UTC)的接收时间。README 指出,这让 MQTT v5 客户端能判断一条**保留消息(retained message)**有多"老"——这是保留消息场景下"消息何时到达 broker"的唯一可靠信息来源。

载荷修改(payload-modification)

mosquitto_payload_modification.c 演示修改消息载荷:在每条载荷前拼接"hello "前缀。实现要点(也是所有载荷改写插件的通用范式):

  1. 计算新长度ed->payloadlen + strlen("hello ") + 1;
  2. 用mosquitto_calloc()(而非裸malloc)分配内存,让 broker 能跟踪插件内存占用;
  3. 构造新载荷后将ed->payload/ed->payloadlen指向新内容;
  4. 绝不能 free 原始载荷,broker 会负责释放。

README 特意给出强烈警告:必须百分百确认载荷格式正确后再修改——本插件在任何非纯文本消息上都会破坏内容(二进制、压缩、序列化数据都会"中毒")。这正是"能力越大责任越大"的典型案例。

主题修改(topic-modification)

mosquitto_topic_modification.c 演示改写消息主题:先用mosquitto_topic_matches_sub("device/+/data/uplink", ed->topic, &result)(函数声明见 include/mosquitto/libcommon_topic.h)判断主题是否匹配模式,命中则截掉末尾的/uplink。效果是:设备发布到device/0001/data/uplink,订阅者实际收到的是device/0001/data——可用于在 broker 侧完成"上行数据归一化/降噪",设备端无需改动。

强制保留(force-retain)与载荷禁用(payload-ban)

mosquitto_force_retain.c 演示把某些主题的消息强制设为保留消息(retain),适合"状态型数据必须留存"的场景;mosquitto_payload_ban.c 演示按载荷内容拒绝消息,可做简单的敏感内容拦截。

四、订阅与 ACL 策略类插件

通配符限时访问(wildcard-temp)

mosquitto_wildcard_temp.c 是一个限制#通配符订阅的巧妙示例:默认拒绝对#主题过滤器的订阅(避免客户端探测活跃主题、防止带宽滥用),但以用户名wildcard登录的客户端首次订阅#会被放行 20 秒(ACCESS_PERIOD),到期后订阅被静默移除。实现上组合了四个事件:

  • MOSQ_EVT_CONNECT:识别wildcard用户并登记(用 uthash 维护客户端表);
  • MOSQ_EVT_ACL_CHECK:对#订阅返回MOSQ_ERR_SUCCESS(放行一次)或MOSQ_ERR_ACL_DENIED,其余场景返回MOSQ_ERR_PLUGIN_IGNORE交给后续规则;
  • MOSQ_EVT_TICK:通过mosquitto_subscription_delete(client->id, "#")到期移除订阅,并通过ed->next_s = 1声明最多 1 秒后再回调一次;
  • MOSQ_EVT_DISCONNECT:清理登记信息(utlist 双向链表 + uthash 双结构维护活跃订阅)。

该设计初衷(源码注释)是允许公共服务器(如 test.mosquitto.org)上的主题发现行为,同时把带宽消耗限制在可控窗口内。

订阅 QoS 限制(limit-subscription-qos)

mosquitto_limit_subscription_qos.c 演示在订阅时限制允许的最大 QoS 等级,可用于资源受限场景下强制降级。

主题监狱(topic-jail)

mosquitto_topic_jail.c 演示把客户端"关进"指定主题前缀,越界订阅/发布一律拒绝,是多租户隔离的经典模式。

客户端属性(client-properties)

mosquitto_client_properties.c 集中演示检索客户端信息的各类 API(client id、用户名等,部分函数在 add-properties 中也有使用),可作为编写认证/审计插件时查阅 API 用法的参考。

五、生产级扩展:SQLite 持久化与 Sparkplug 感知

persist-sqlite:可插拔持久化后端

persist-sqlite/ 是一个完整实现mosquitto_plugin_persist系列接口的插件,将客户端会话、订阅、保留消息、消息队列等持久化数据落到 SQLite 数据库(见 init.c、clients.c、retain_msgs.c、subscriptions.c 等模块),并配套test.conf/test.sh可独立验证。相比默认持久化,SQLite 后端适合需要外部可查、跨进程访问持久化数据的部署。

sparkplug-aware:面向工业 MQTT 的语义插件

sparkplug-aware/ 针对 Sparkplug(工业物联网数据规范)提供消息感知处理,包含 plugin.c(插件入口)、on_message.c(消息处理)与 plugin_global.h,并附有自己的 README.md。该插件可作为在 Sparkplug 拓扑上叠加自定义策略(如名称空间校验、命令下发控制)的起点。

六、如何快速上手验证

  1. 构建:仓库根目录make(或make binary)会按 plugins/examples/Makefile 的DIRS列表逐一编译所有示例插件;CMake 构建则由 plugins/CMakeLists.txt 统一驱动;
  2. 试运行:进入任一示例目录执行sh test.sh,脚本会用仓库内已编译的../../../src/mosquitto -c test.conf -v启动 broker 并加载对应插件(例如 message-timestamp 会为所有消息附加 ISO-8601 时间戳属性);
  3. 定制:以示例为模板,用gcc -I<include 目录> -fPIC -shared xxx.c -o xxx.so编译自己的插件,在mosquitto.conf中用plugin /path/to/xxx.so加载,必要时配合plugin_opt_前缀传递插件自定义参数;
  4. 深入:插件 API 的权威说明在 include/mosquitto/broker.h 与 include/mosquitto/broker_plugin.h,动态安全的完整命令参考在 plugins/dynamic-security/README.md,官方集成测试见 test/broker 下的09-plugin-*.py、14-dynsec-*.py等脚本。

结语

plugins/目录是理解 Mosquitto 扩展机制的"活教材":动态安全插件展示了如何用$CONTROL主题把安全管理变成可编程的运行时操作;message-timestamp、topic-modification 等示例则揭示了MOSQ_EVT_MESSAGE_IN背后"接收后、转发前"的拦截时机;wildcard-temp 组合四个事件演示了有状态策略的完整写法。以此为起点,读者完全可以在不改动 broker 内核的前提下,为自己的部署构建认证、审计、统计、消息治理等专属能力。

  • 物联网
  • 消息队列
  • 后端

【免费下载链接】mosquitto

Eclipse Mosquitto - An open source MQTT broker

项目地址:https://gitcode.com/gh_mirrors/mosquit/mosquitto
点击查看免费下载

相关推荐

上一篇:Zotero PDF Translate插件版本兼容性深度解析:3种技术架构演进路径与迁移决策框架
下一篇:终极免费视频加速神器:Video Speed Controller 完整指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询