ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Eclipse Mosquitto 认证插件机制全解析:从社区实践到官方插件架构

Eclipse Mosquitto 认证插件机制全解析:从社区实践到官方插件架构 后端消息队列消息路由【免费下载链接】mosquittoEclipse Mosquitto - An open source MQTT broker项目地址https://gitcode.com/gh_mirrors/mos/mosquitto点击查看免费下载本篇技术指南以 Mosquitto 官方博客于 2013 年发布的《Authentication plugins》一文为线索系统梳理 Mosquitto 认证Authentication与访问控制ACL插件的历史演进、社区经典实践与当前仓库中的官方插件架构。读者将掌握Mosquitto 插件接口v4/v5的编程模型、plugin/plugin_opt_*配置项的正确用法、密码文件与 Redis/数据库类认证插件的设计思路以及如何基于 include/mosquitto/broker_plugin.h 编写自己的认证插件。一、为什么需要认证插件2013 年的背景在 2013 年 7 月的官方博客文章《Authentication plugins》中作者 Roger Light 提到社区对 Mosquitto 的认证插件表现出了浓厚兴趣并已出现了一批第三方实现原文见 www/posts/2013/07/authentication-plugins.md。当时的动机非常朴素Mosquitto 自带的password_file用户名/密码明文校验与acl_file基于文件的主题访问控制无法满足真实业务场景——例如密码需要以哈希形式存储、用户数据存放在 PostgreSQL/Redis 等外部数据库中、ACL 需要按订阅模式动态判定等。而认证插件正是把如何验证用户和如何授权访问这两个安全决策从 broker 内部剥离出来交给可加载的共享库.so/.dll/.dylib去实现。二、原文提到的三个社区插件原博文介绍了当时涌现的三类认证插件它们分别代表了三种典型的存储后端方案1. 基于 MD5 哈希的认证mosquitto_auth_plugin_md5插件将用户名与密码的 MD5 哈希值作为认证依据密码不以明文形式存放。这类实现的典型场景是把哈希结果存放在普通文本文件或轻量存储中通过自定义校验逻辑完成登录判定从而避免password_file明文口令带来的泄露风险。2. 基于 PostgreSQL 的 MD5 认证mosquitto_auth_plugin_pg_md5将认证数据迁移到 PostgreSQL 数据库中密码仍以 MD5 哈希形式保存。该方案解决了插件 1 的扩展性问题当设备/用户规模增长时账号数据的增删改查、备份与权限管理都可以直接交给数据库完成broker 侧只负责在连接到来时把username/password交给插件去查库验证。3. 基于 Redis 与 PBKDF2 的认证 主题 ACLmosquitto-redis-auth这是原博文作者尤其欣赏的一个插件I particularly like the redis based plugin原因在于它有两个亮点使用 PBKDF2 派生密钥哈希相比 MD5PBKDF2 引入了盐值与迭代计算显著提高了口令被暴力破解的难度代表了更安全的密码存储实践超级用户superuser概念插件允许把部分用户标记为 superuser这类用户豁免所有 ACL 检查exempt from ACL checks适合作为管理端账号使用。从这些插件可以看出 Mosquitto 认证插件的设计定位身份认证authentication与授权authorization是两件独立的事——认证决定你是谁ACL 决定你能碰哪些主题。一个插件既可以只做认证也可以同时承担两者。三、认证插件在现代 Mosquitto 中的架构演进上述 2013 年的插件基于的是当时Mosquitto 1.x 时代的auth_plugin/auth_opt_*配置与 v4 认证接口。而在当前仓库中插件体系已经完成向通用 v5 接口的升级但 v4 认证接口仍保留兼容。3.1 两代插件接口在 include/mosquitto/broker_plugin.h 中头文件明确标注了两个版本常量/* The generic plugin interface starts at version 5 */ #define MOSQ_PLUGIN_VERSION 5 /* The old auth only interface stopped at version 4 */ #define MOSQ_AUTH_PLUGIN_VERSION 4v4认证专用接口只做认证与访问控制要求插件实现mosquitto_auth_plugin_version()、mosquitto_auth_plugin_init()、mosquitto_auth_security_init()、mosquitto_auth_unpwd_check()、mosquitto_auth_acl_check()等函数。历史第三方插件如上述三个社区项目多基于此接口编写v5通用插件接口自 Mosquitto 2.0 起可用覆盖认证、ACL、$CONTROL主题空间处理、消息检查与修改等更多事件。开发者只需实现三个函数mosquitto_plugin_version()、mosquitto_plugin_init()、mosquitto_plugin_cleanup()然后在mosquitto_plugin_init()中通过回调注册机制挂接不同事件。头文件注释建议If you are developing a new plugin, please use the v5 interface.声明 v5 版本支持的快捷方式是使用头文件提供的宏MOSQUITTO_PLUGIN_DECLARE_VERSION(5);该宏展开为完整的mosquitto_plugin_version()实现遍历 broker 传入的supported_versions数组若找到自己支持的版本号则返回该版本否则返回 -1 表示失败。3.2 事件驱动的回调模型v5 接口的核心是事件注册。在 include/mosquitto/broker.h 中定义了完整的mosquitto_plugin_event枚举与认证相关的核心事件包括事件触发时机MOSQ_EVT_BASIC_AUTH客户端连接时对用户名/密码/clientid 进行认证MOSQ_EVT_ACL_CHECK收到 publish/subscribe/unsubscribe 命令检查客户端是否被允许执行MOSQ_EVT_EXT_AUTH_START/MOSQ_EVT_EXT_AUTH_CONTINUEMQTT v5 客户端使用扩展认证多步认证时MOSQ_EVT_PSK_KEY客户端使用 TLS-PSK 连接broker 需要 PSK 信息MOSQ_EVT_RELOADbroker 收到配置重载信号MOSQ_EVT_CONNECT/MOSQ_EVT_DISCONNECT客户端成功连接 / 断开连接插件通过mosquitto_callback_register()注册回调通过mosquitto_callback_unregister()注销回调两者都需要在mosquitto_plugin_init()中保存的插件标识符mosquitto_plugin_id_t *。3.3 插件的裁决语义MOSQ_ERR_PLUGIN_DEFER多个插件可以同时加载broker 按配置顺序逐一咨询。头文件对 v4 接口的检查流程描述如下v5 事件回调的语义与此一致首先执行默认的password_file和/或acl_file检查如果未定义则视为延后deferred只要其中任一检查通过后续不再检查第一个插件执行检查只要它返回的不是MOSQ_ERR_PLUGIN_DEFER本次检查立即结束并采用该结果若返回MOSQ_ERR_PLUGIN_DEFER则轮到下一个插件若最后一个插件仍返回MOSQ_ERR_PLUGIN_DEFER访问将被拒绝。这就意味着一个插件既可以全权负责某项检查也可以只处理自己关心的部分、把其余请求放行给后续插件。认证相关的返回码还包括MOSQ_ERR_SUCCESS通过、MOSQ_ERR_AUTH认证失败、MOSQ_ERR_ACL_DENIEDACL 拒绝、MOSQ_ERR_UNKNOWN应用自定义错误。四、配置项从 auth_plugin 到 plugin / plugin_opt_*当前仓库的示例配置文件 mosquitto.conf 的External authentication and topic access plugin options一节完整描述了插件配置语义src/conf.c 中的配置解析逻辑conf.c的auth_opt_/plugin分支约 L1134–L1235则给出了底层实现。4.1 加载插件# 传统写法已弃用未来版本将移除 # auth_plugin path to plugin # 推荐写法 plugin path to plugin要点均摘自 mosquitto.conf 注释与 src/conf.c 实现plugin选项可多次指定以加载多个插件按配置文件中出现的顺序依次处理若plugin与password_file/acl_file同时配置插件检查优先于文件检查plugin是auth_plugin的替代名称auth_plugin已弃用deprecated受per_listener_settings影响为false时插件作用于所有 listener为true时仅作用于当前定义的 listener若开启per_listener_settings true但仍希望某个插件作用于全部 listener应使用global_plugin。当同时定义global_plugin与plugin时全局插件总是先被处理当per_listener_settings为false时二者行为一致plugin_load name path与plugin_use name用于为插件命名并按名引用例如配合 listener 级安全选项。4.2 向插件传递参数# 传统写法已弃用 # auth_opt_db_host localhost # auth_opt_db_port 5432 # 推荐写法plugin_opt_ 前缀后的部分会被原样传给插件 plugin_opt_db_host localhost plugin_opt_db_port 5432 plugin_opt_db_username mosquitto plugin_opt_db_password secret从 src/conf.c 的解析逻辑可以看到broker 截取plugin_opt_或auth_opt_前缀之后的内容组装成struct mosquitto_opt { char *key; char *value; }数组在调用mosquitto_plugin_init()时通过options/option_count参数交给插件。这正是 2013 年那批社区插件读取数据库连接参数的方式至今机制未变。4.3 其他相关选项auth_plugin_deny_special_chars布尔值用于控制插件参数中是否允许特殊字符在 src/conf.c 的解析分支中存在配合cur_plugin使用allow_anonymous匿名访问开关。注意 mosquitto.conf 中提示插件检查优先于密码文件因此即使启用了插件allow_anonymous的语义仍由安全选项层控制上述插件类配置项plugin、plugin_opt_*等在 broker 运行期间重载reload时不生效conf.c解析逻辑中对 reload 情况直接continue跳过。五、仓库内的官方参考插件可运行的实现范例当前仓库自带多个插件是理解认证插件到底怎么写的最佳教材。5.1 password-file完整的认证插件骨架plugins/password-file/plugin.c 是一个仅 95 行的最小认证插件完整演示了 v5 接口的四个关键步骤#define PLUGIN_NAME password-file MOSQUITTO_PLUGIN_DECLARE_VERSION(5); static mosquitto_plugin_id_t *mosq_pid NULL; int mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *options, int option_count) { struct password_file_data *data; ... data mosquitto_calloc(1, sizeof(struct password_file_data)); *user_data data; mosq_pid identifier; mosquitto_plugin_set_info(identifier, PLUGIN_NAME, NULL); rc handle_options(data, options, option_count); /* 解析 plugin_opt_* */ rc password_file__parse(data); /* 加载密码文件 */ rc mosquitto_callback_register(mosq_pid, MOSQ_EVT_BASIC_AUTH, password_file__check, NULL, data); rc mosquitto_callback_register(mosq_pid, MOSQ_EVT_RELOAD, password_file__reload, NULL, data); return MOSQ_ERR_SUCCESS; }可以提炼出所有 Mosquitto 认证插件的通用模板MOSQUITTO_PLUGIN_DECLARE_VERSION(5);声明接口版本mosquitto_plugin_init()保存identifier解析options即plugin_opt_*初始化数据用mosquitto_callback_register()注册MOSQ_EVT_BASIC_AUTH等事件回调mosquitto_plugin_cleanup()注销回调、释放内存。注意handle_options()中对未知选项的处理打印Error: Unknown option %s.并返回MOSQ_ERR_INVAL——这是防止配置拼写错误导致静默失效的最佳实践。5.2 acl-file只做授权不做认证plugins/acl-file/plugin.c 展示了只承担 ACL 职责的插件形态它注册的是MOSQ_EVT_ACL_CHECK与MOSQ_EVT_RELOAD完全不参与用户名/密码认证。这印证了 2013 年社区插件认证 主题 ACL 二合一的做法在现代 Mosquitto 中可以被拆分为独立插件、按需组合。5.3 dynamic-security基于 $CONTROL 主题的动态安全插件仓库中的 plugins/dynamic-security 是目前 Mosquitto 官方维护的最复杂的插件通过向$CONTROL/feature/v1主题发布 JSON 命令实现运行期动态安全配置无需重启 broker 即可完成以下操作详见 plugins/dynamic-security/README.md客户端ClientcreateClient/enableClient/disableClient/setClientPassword/setClientId组GroupcreateGroup/addGroupClient/setAnonymousGroup为匿名客户端指定组角色RolecreateRole/addRoleACL/removeRoleACLACL 类型包括subscribePattern、subscribeLiteral、publishClientSend、publishClientReceive等默认 ACLsetDefaultACLAccess可设置各 ACL 类型的默认行为默认 publishClientSend/subscribe 为 denypublishClientReceive/unsubscribe 为 allow。它把 2013 年 Redis 插件中超级用户豁免 ACL的思想推广成了完整的客户端—组—角色三级授权模型是理解插件能承载多复杂业务逻辑的绝佳案例。对应的命令行管理工具是 apps/mosquitto_ctrl例如mosquitto_ctrl dynsec createClient username password mosquitto_ctrl dynsec addRoleACL rolename subscribeLiteral topic/# deny六、如何编写自己的认证插件实操路径结合前文编写一个认证插件例如复刻 2013 年社区的 Redis/PBKDF2 方案的完整路径如下第 1 步引入头文件并声明版本#include mosquitto.h /* 或 mosquitto/broker_plugin.h */ #define PLUGIN_NAME my-auth MOSQUITTO_PLUGIN_DECLARE_VERSION(5);注意仓库根目录的 include/mosquitto_plugin.h 仅是兼容转发头#include mosquitto/broker_plugin.h真正的接口定义在 include/mosquitto/broker_plugin.h 与 include/mosquitto/broker.h。第 2 步实现 init / cleanup在mosquitto_plugin_init()中解析plugin_opt_*数据库地址、Redis 连接串、哈希算法参数等并在回调里通过struct mosquitto *client调用 broker 提供的访问器获取客户端信息例如mosquitto_client_address()、mosquitto_client_username()、mosquitto_client_protocol_version()见 include/mosquitto/broker.h 的 Client Functions 一节。第 3 步实现认证回调注册MOSQ_EVT_BASIC_AUTH在回调中从struct mosquitto_evt_basic_auth取出username/password查询后端存储Redis/PostgreSQL/MySQL……比对哈希MD5、PBKDF2 或 bcrypt按需返回MOSQ_ERR_SUCCESS——认证通过MOSQ_ERR_AUTH——认证失败MOSQ_ERR_PLUGIN_DEFER——本插件不处理交给下一个插件。若认证需要与外部服务异步通信还可以在回调中返回MOSQ_ERR_AUTH_DELAYED稍后调用mosquitto_complete_basic_auth(clientid, result)补交结果详见 include/mosquitto/broker.h 中mosquitto_complete_basic_auth的说明。第 4 步实现 ACL 回调可选注册MOSQ_EVT_ACL_CHECK根据access字段区分语义MOSQ_ACL_SUBSCRIBE是否允许订阅该主题串、MOSQ_ACL_READ消息是否可发送给该客户端、MOSQ_ACL_WRITE客户端是否可向该主题发布。若需要超级用户豁免 ACL只需在回调开头判断用户身份并直接返回MOSQ_ERR_SUCCESS——这正是原博文点赞的 Redis 插件核心设计用 v5 接口十几行代码即可复现。第 5 步编译与配置按头文件注释使用 gcc 编译为共享库gcc -Ipath to mosquitto_plugin.h -fPIC -shared plugin.c -o plugin.somacOS 下需追加-undefined dynamic_lookup。然后在 mosquitto.conf 中加载per_listener_settings false plugin /path/to/plugin.so plugin_opt_redis_host 127.0.0.1 plugin_opt_redis_port 6379 plugin_opt_redis_hash pbkdf2重启 broker 后观察日志确认插件加载成功即可。七、总结从 2013 年官方博客介绍的三个社区插件MD5 哈希、PostgreSQL MD5、RedisPBKDF2superuser到今天仓库中 plugins/password-file、plugins/acl-file、plugins/dynamic-security 等官方实现Mosquitto 认证插件体系的本质始终未变把身份认证与主题授权从 broker 内核中解耦出来交给可加载、可组合、可热替换的共享库。区别仅在于接口从 v4 演进到事件驱动的 v5配置从auth_plugin/auth_opt_*演进到plugin/plugin_opt_*。无论你是想复刻 2013 年的 Redis 认证方案、对接企业既有账号系统还是实现细粒度的动态 ACL都可以从本文给出的模板出发声明版本、实现 init/cleanup、注册MOSQ_EVT_BASIC_AUTH与MOSQ_EVT_ACL_CHECK回调、用plugin_opt_*注入后端连接参数。剩下的安全强度与业务灵活度完全由你在回调里决定。赞分享后端消息队列消息路由【免费下载链接】mosquittoEclipse Mosquitto - An open source MQTT broker项目地址https://gitcode.com/gh_mirrors/mos/mosquitto点击查看免费下载相关推荐Eclipse Mosquitto 密码文件认证插件mosquitto_password_file配置与实践指南Eclipse Mosquitto 密码文件认证插件mosquitto_password_file配置与实践指南 导读 本文讲解 Eclipse Mosqu后端消息队列消息路由Mosquitto 插件体系完全指南从 Dynamic Security 到 20 官方示例插件的实战解析Mosquitto 插件体系完全指南从 Dynamic Security 到 20 官方示例插件的实战解析 Eclipse Mosquitto 通过 plu后端消息队列消息路由Dio 插件生态全解析从官方插件到社区扩展的选型与实战指南Dio 插件生态全解析从官方插件到社区扩展的选型与实战指南 Dio 是 Dart / Flutter 生态中功能最完整的 HTTP 客户端之一其强大的可扩展网络后端WebSocket创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表