ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

EMQX 客户端属性初始化:为 `mqtt.client_attrs_init` 表达式新增 `cert_common_name` 与 `cert_subject` 证书变量别名

2026/9/24 2:30:18 拓冰建站 浏览量
EMQX 客户端属性初始化:为 `mqtt.client_attrs_init` 表达式新增 `cert_common_name` 与 `cert_subject` 证书变量别名 后端物联网消息队列通信【免费下载链接】emqxThe most scalable and reliable MQTT broker for AI, IoT, IIoT and connected vehicles项目地址https://gitcode.com/gh_mirrors/em/emqx点击查看免费下载mqtt.client_attrs_init是 EMQX 在客户端连接阶段用表达式初始化客户端属性Client Attributes的机制这些属性随后可用于鉴权 ACL、规则引擎等场景。本变更见 changes/ee/fix-16865.en.md为其表达式引入了两个证书相关的变量别名cert_common_name等价于原有cn与cert_subject等价于原有dn使 TLS 客户端证书信息在属性初始化表达式中可读性更强、语义更明确。读完本文你将掌握client_attrs_init的配置结构、证书变量的取值来源以及如何在配置与 ACL 规则中实际使用这两个新别名。变更概述一次清晰的别名增强本次变更的内容非常聚焦原文只有一句话Addedcert_common_nameandcert_subjectaliases formqtt.client_attrs_initexpressions, alongside the existingcnanddnvariables.即在mqtt.client_attrs_init表达式中新增cert_common_name、cert_subject两个变量作为已有变量cn、dn的别名二者可以互换使用。别名引入后配置语义不再依赖两个含义模糊的缩写而是直接表达“证书通用名”与“证书主题”降低了配置的可读性与理解成本。client_attrs_init是什么连接期属性初始化机制在 EMQX 中客户端属性client attributes是挂在clientinfo上的一组键值数据可被后续鉴权、ACL、消息路由等模块引用。mqtt.client_attrs_init提供了一种声明式手段在客户端建立连接CONNECT 处理阶段时按配置的表达式列表计算属性并写入客户端信息。其配置结构定义在 emqx_schema.erlfields(client_attrs_init) - [ {expression, emqx_variform:sc(#{desc ?DESC(client_attrs_init_expression)})}, {set_as_attr, sc(binary(), #{ desc ?DESC(client_attrs_init_set_as_attr), validator fun restricted_string/1 })} ];也就是说client_attrs_init是一个配置项列表列表中的每一项包含两个字段字段类型说明expressionvariform 表达式计算属性值的表达式可引用连接阶段的各类变量并调用字符串处理函数set_as_attrbinary计算结果要写入的客户端属性名受限字符串配置挂载在mqtt配置根之下按 zone 生效emqx_schema.erl中将其声明为数组见 emqx_schema.erl。一个典型的 HOCON 配置如下mqtt { client_attrs_init [ { expression cert_common_name set_as_attr tls_cn }, { expression cert_subject set_as_attr tls_subject } ] }执行链路表达式在连接阶段如何被渲染client_attrs_init的实际执行发生在 MQTT 连接处理流程中核心逻辑位于 emqx_channel.erl。1. 读取配置并按 zone 生效get_client_attrs_init_config(Zone) - get_mqtt_conf(Zone, client_attrs_init, []).2. 组装渲染上下文并计算属性maybe_set_client_initial_attrs(ConnPkt, #{zone : Zone} ClientInfo, ConnInfo) - case get_client_attrs_init_config(Zone) of [] - {ok, ClientInfo}; Inits - UserProperty get_user_property_as_map(ConnPkt), Password get_connect_password(ConnPkt), RenderCtx client_attrs_init_render_ctx( ClientInfo#{user_property UserProperty, password Password}, ConnInfo ), Attrs0 maps:get(client_attrs, ClientInfo, #{}), Attrs1 initialize_client_attrs(Inits, RenderCtx), {ok, ClientInfo#{client_attrs maps:merge(Attrs0, Attrs1)}} end.从源码可以看出渲染上下文RenderCtx除了clientinfo中的username、clientid、user_property、password等基础变量外还会叠加来自ConnInfo的证书信息。3. 别名注入cert_common_name与cert_subject的来源本次变更的核心实现就在这里见 emqx_channel.erlclient_attrs_init_render_ctx(Ctx, ConnInfo) - client_attrs_init_render_ctx_cn(add_cert_san_render_ctx(Ctx, ConnInfo)). client_attrs_init_render_ctx_cn(#{cn : CN} Ctx) - client_attrs_init_render_ctx_dn(Ctx#{cert_common_name CN}); client_attrs_init_render_ctx_cn(Ctx) - client_attrs_init_render_ctx_dn(Ctx). client_attrs_init_render_ctx_dn(#{dn : DN} Ctx) - Ctx#{cert_subject DN}; client_attrs_init_render_ctx_dn(Ctx) - Ctx.逻辑非常清晰只要渲染上下文中存在cn就同步注入cert_common_name CN只要存在dn就同步注入cert_subject DN。因此新旧两组变量完全等价表达式里用哪一个都能取到相同的值。4. 底层数据来源证书的 CN 与 DN 从哪来cn与dn本身来自对端peerTLS 证书的解析见 emqx_channel.erlset_peercert_infos(PeerCert, ClientInfo, Zone) - DN esockd_peercert:subject(PeerCert), CN esockd_peercert:common_name(PeerCert), ok validate_peercert_string(dn, DN, PeerCert), ok validate_peercert_string(cn, CN, PeerCert), ... ClientInfo#{username Username, clientid ClientId, dn DN, cn CN}.cn即别名cert_common_name取对端证书的 Common NameCNdn即别名cert_subject取对端证书的完整 Subject Distinguished NameDN如CNclient, OEMQX。代码还通过validate_peercert_string/3对这两个字段做字节级校验防止异常字符进入后续渲染流程校验失败会触发{shutdown, peercert_field_invalid}拒绝连接说明证书字段是可以被安全用于模板渲染和后续鉴权逻辑的。另外渲染上下文还会额外注入cert_san证书 Subject Alternative Name按dns/ip/email/uri分组见 emqx_channel.erl可用于按 SAN 信息初始化属性。变量清单client_attrs_init表达式中可用的证书相关变量结合占位符头文件 emqx_placeholder.hrl 与上述执行逻辑证书相关变量整理如下变量含义说明cn证书 Common Name原有变量cert_common_name证书 Common Name本次新增的cn别名dn证书完整 Subject DN原有变量cert_subject证书完整 Subject DN本次新增的dn别名cert_san.dns/cert_san.ip/cert_san.email/cert_san.uri证书 SAN 各类型值列表形式头文件同时声明了宏与二进制占位符定义例如?PH_CERT_CN_NAME对应${cert_common_name}、?PH_CERT_SUBJECT对应${cert_subject}见 emqx_placeholder.hrl说明这两个名字不仅是client_attrs_init的局部约定而是整个占位符体系中的一等公民其他支持占位符的模块同样可以引用。实战把证书 CN 提炼为客户端属性并用于 ACL场景一将证书信息写入客户端属性假设需要把 TLS 客户端的证书 CN 与完整 Subject 提取为属性tls_cn、tls_subject配置如下mqtt { client_attrs_init [ { expression cert_common_name set_as_attr tls_cn }, { expression cert_subject set_as_attr tls_subject } ] }连接建立后这两个属性会出现在客户端的client_attrs中可通过 Dashboard 或emqx_cm通道信息查看。场景二在 ACL 规则中按证书 CN 授权客户端属性初始化后即可被鉴权模块引用。在基于文件的 ACL 配置acl.conf中占位符${client_attrs.NAME}会被渲染为对应属性值例如{allow, all, publish, [${client_attrs.tls_cn}/#]}acl.conf的注释还单独列出了${cert_common_name}占位符可直接在规则中引用证书 CN 而无需先写入属性{allow, all, publish, [${cert_common_name}/#]}需要留意的是注释中明确指出当引用的值不存在时占位符不会被渲染为空字符串例如客户端没有group属性时${client_attrs.group}/#不会退化为/#——这一安全行为同样适用于证书变量未启用 TLS 或客户端未携带证书时cert_common_name/cert_subject不可用。测试验证新别名与旧变量行为一致新增别名有对应的自动化测试用例覆盖见 emqx_client_SUITE.erlt_certcn_as_alias/1、t_certdn_as_alias/1验证原有cn、dnt_cert_common_name_as_alias/1、t_cert_subject_as_alias/1验证本次新增的cert_common_name、cert_subject。四组用例共用同一套断言逻辑test_cert_extraction_as_alias(Which)其核心步骤是{ok, Compiled} emqx_variform:compile(substr( atom_to_list(Which) ,0,2)), emqx_config:put_zone_conf(default, [mqtt, client_attrs_init], [ #{expression Compiled, set_as_attr alias} ]), %% 通过 mTLS 连接 8883 端口 {ok, Client} emqtt:start_link([ {clientid, ClientId}, {port, 8883}, {ssl, true}, {ssl_opts, SslConf} ]), {ok, _} emqtt:connect(Client), ?assertMatch( #{clientinfo : #{client_attrs : #{alias : _, _}}}, emqx_cm:get_chan_info(ClientId) )用例以 mTLS 客户端连接 8883 端口对cn、dn、cert_common_name、cert_subject分别执行substr(x, 0, 2)表达式断言最终写入的属性值为两个字符——四种变量取值行为完全一致从测试层面印证了别名的等价性。使用建议与注意事项新旧变量可混用建议新配置优先使用长名cert_common_name与cert_subject语义自明在团队协作与后续维护中更不易产生歧义旧配置中的cn/dn无需迁移完全兼容。仅在 TLS/mTLS 连接下有意义两个证书变量依赖对端证书非 TLS 监听器或未携带证书的客户端不会提供这些值表达式引用时需考虑缺失场景。属性名受restricted_string校验约束set_as_attr指定的属性名必须满足受限字符串规则配置非法时会在启动阶段即被拒绝。表达式的表达能力expression采用 variform 语法支持substr等字符串函数可对 CN/DN 做截取、拼接等预处理后再落为属性满足更细粒度的派生需求。本次变更虽小却补齐了client_attrs_init证书变量的语义化命名配合属性初始化与 ACL 占位符渲染为基于 TLS 客户端证书的细粒度接入控制提供了更清晰的配置表达。赞分享后端物联网消息队列通信【免费下载链接】emqxThe most scalable and reliable MQTT broker for AI, IoT, IIoT and connected vehicles项目地址https://gitcode.com/gh_mirrors/em/emqx点击查看免费下载相关推荐EMQX client_attrs_init 支持 password 变量用 JWT 密码初始化客户端属性EMQX client_attrs_init 支持 password 变量用 JWT 密码初始化客户端属性 导读 本篇文章围绕 EMQX 开源仓库中的变更记录后端物联网消息队列通信EMQX 从 TLS 客户端证书提取 Subject Alternative NameSAN到 MQTT 客户端属性实战指南EMQX 从 TLS 客户端证书提取 Subject Alternative NameSAN到 MQTT 客户端属性实战指南 本文介绍 EMQX 5.x/6后端物联网消息队列通信Slang 初始化表达式与初始化列表表达式语言规范、一致性测试与源码验证实战Slang 初始化表达式与初始化列表表达式语言规范、一致性测试与源码验证实战 导读 本文聚焦 Slang 着色语言中两个紧密关联的表达式特性—— 初始化表达式编译器图形学编程语言创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考