授权
在 EMQX 中,授权是指对 MQTT 客户端的发布和订阅操作进行权限控制。EMQX 的授权机制基本原理是:当客户端尝试发布或订阅时,EMQX 会根据特定流程或用户定义的查询语句,从数据源中获取该客户端的权限数据,将权限与要执行的操作进行匹配,根据匹配结果来允许或拒绝本次操作。
单条客户端权限数据由以下部分组成:
权限 | 客户端 | 操作 | 操作详情 |
---|---|---|---|
允许/拒绝 | 客户端 ID/用户名/IP 地址 | 发布/订阅/发布订阅 | 主题/QoS/保留消息 |
TIP
EMQX 5.1.1 版本开始引入了操作详情支持检查 QoS 和保留消息。
客户端权限列表需要提前存储到特定数据源(数据库、文件)中,更新对应的数据即可实现权限的运行时动态更新。
EMQX 已经默认配置一个基于文件的授权检查器,将基于 ACL 文件中存储的规则进行授权检查。您可直接使用。
工作原理
授权链
除配置单个授权检查器外,EMQX 支持用户通过配置多个授权检查器构成授权链,以实现更灵活的授权检查。EMQX 将按照授权链中配置的检查器顺序依次执行授权检查。如果在当前授权检查器中未检索到权限数据,将会切换至链上的下一个启用的授权检查器继续权限检查。
授权检查流程如下:
- 当前授权检查器执行时检索到了客户端的权限信息,匹配当前执行的操作与客户端的权限信息:
- 操作与权限匹配,根据权限允许或拒绝客户端的操作;
- 操作与权限不匹配,交由下一授权检查器继续检查。
- 当前授权检查器执行时没有检索到了客户端的权限信息,EMQX 将继续检查授权链中是否还有其他授权检查器:
- 如有,EMQX 将跳过当前授权检查器,并将请求交由下一授权检查器继续检查;
- 如当前授权检查器是链中最后一个授权检查器:根据未匹配时执行(
no_match
)配置决定检查结果(允许或拒绝)。
有关如何调整授权链中授权检查器顺序以及查看其运行统计指标,可查看 管理授权检查器 章节。
注意
为避免授权检查出错,必须记得在必要时停用或删除基于 ACL 文件的授权检查器,因为 ACL 文件末尾包含的特殊规则 {allow, all}
默认允许所有请求。
授权数据缓存
EMQX 支持对授权数据进行缓存,以便缓解由于大量客户端的订阅和发布请求对授权数据后端造成的访问压力。您可直接通过 EMQX Dashboard 的授权页面(访问控制 -> 授权 -> 设置)设置是否启用缓存。
启用缓存:设置是否启用授权数据缓存。
单客户端缓存条数:单个客户端允许的最大缓存条数,默认为 32 条。
缓存过期时间:缓存数据存活时间,默认为 1 分钟。
排除主题:提供主题列表,指定列表内的主题不会生成授权缓存。
未匹配时执行:所有授权检查器都未找到授权信息时,应执行的操作;可选值:allow(允许操作)、deny(拒绝操作),默认为 allow。
拒绝时执行:拒绝当前客户端的操作请求时,应执行的操作;可选值:ignore(忽略该操作请求)、disconnect(断开当前客户端的连接),默认为 ignore。
清除缓存:清除当前所有授权结果缓存。
您也可以通过配置文件配置授权数据缓存,请参考 配置文件。
TIP
启用授权数据缓存后,系统性能将受到一定的影响,建议及时根据系统表现调整缓存参数的设置。
占位符
EMQX 允许使用占位符动态构造授权数据查询语句、HTTP 请求,占位符会在授权检查器执行时替换为真实的客户端信息,以构造出与当前客户端匹配的查询语句或 HTTP 请求。
数据查询占位符
用于在查询权限数据时构造数据查询语句。以 MySQL 授权检查器为例,默认的查询 SQL 中使用了 ${username}
占位符:
SELECT action, permission, topic FROM mqtt_acl where username = ${username}
当用户名为 emqx_u
的客户端触发授权检查时,实际执行权限数据查询 SQL 将被替换为:
SELECT action, permission, topic FROM mqtt_acl where username = 'emqx_u'
EMQX 授权支持的数据查询占位符如下:
${username}
: 将在运行时被替换为用户名。用户名来自CONNECT
报文中的Username
字段。如果启用了peer_cert_as_username
,则会在连接时被证书中的字段或证书内容所覆盖。${clientid}
: 将在运行时被替换为客户端 ID。客户端 ID 一般由客户端在CONNECT
报文中显式指定,如果启用了use_username_as_clientid
或peer_cert_as_clientid
,则会在连接时被用户名、证书中的字段或证书内容所覆盖。${peerhost}
: 将在运行时被替换为客户端的 IP 地址。EMQX 支持 Proxy Protocol,即使 EMQX 部署在某些 TCP 代理或负载均衡器之后,用户也可以使用此占位符获得真实 IP 地址。${peername}
: 将在运行时被替换为客户端的 IP 地址和端口,格式为IP:PORT
。${cert_common_name}
: 将在运行时被替换为客户端 TLS 证书的通用名称(Common Name)。如果证书信息是从负载均衡器发送到 EMQX 的 TCP 端口,需要确保负载均衡器使用的是 Proxy Protocol v2。${cert_subject}
: 将在运行时被替换为客户端 TLS 证书的主题(Subject)。如果证书信息是从负载均衡器发送到 EMQX 的 TCP 端口,需要确保负载均衡器使用的是 Proxy Protocol v2。${client_attrs.NAME}
: 某个客户端属性。NAME
将在运行时根据预定义配置替换为属性名称。有客户端属性的详细信息,请参见 MQTT 客户端属性。${zone}
: 在运行时将替换为客户端的 Zone。${zone}
占位符可以直接用于授权模板中。有关 Zone 的详细配置信息,请参见 Zone 覆盖。
主题占位符
EMQX 还允许在主题中使用占位符,在匹配规则时将当前客户端信息等动态替换到主题中,支持的占位符如下:
${clientid}
${username}
${client_attrs.NAME}
: 某一个客户端属性,NAME
替换成mqtt.client_attrs_init
规则提取的某一个字段名称。
占位符只能用于替换主题的整个字段,例如 a/b/${username}/c/d
,但是不能用于替换字段的一部分,例如 a/b${username}c/d
。
为了避免占位符跟想要的主题冲突的问题,从 EMQX 5.4 开始,可以使用 ${$}
来对 $
进行转义。例如 t/${$}{username}
表示 t/${username}
本身,而不是将 username 替换进去之后的主题名称。
TIP
如果在查询语句中使用eq
语法,需要注意,eq
后面的主题不支持占位符替换,这个行为可能会在后续版本中改变。
eq
语法用于精确匹配一个订阅主题通配符,而不是匹配该通配符的其他主题。 例如 eq t/#
精确匹配 t/#
但不匹配 t/1
或 t/2
等等。
授权检查优先级
除了缓存与授权检查器之外,授权结果还可能受到认证阶段设置的超级用户角色与权限影响。
如果客户端在认证阶段设置了超级用户角色,则发布订阅操作不会再触发授权检查;如果设置了权限列表,则优先匹配客户端权限数据。三者匹配优先级如下:
超级用户 > 权限数据 > 授权检查
管理授权检查器
您可以在 Dashboard 访问控制 -> 授权 页面查看并管理授权检查器。
调整授权检查器顺序
如 授权链 所述,授权检查器按照配置顺序依次执行。
您可以通过列表中的 更多 操作,对授权检查器进行上移、下移、移到顶部、移到底部 等操作;也可以通过配置文件,改变其在 authorization.sources
配置项中的位置进行顺序调整。
授权检查器状态
您可以在 状态 列中查看每个授权检查器的连接状态,状态如下:
- 已连接:所有节点都成功连接到数据源;
- 已断开:部分或全部节点没有连接到数据源,此状态下请检查数据源是否可用,故障排除后手动重启( 禁用 并 启用 )授权检查器后可恢复;
- 连接中:部分或全部节点正在进行数据源的重连,请检查数据源是否可用,数据库或表是否存在,故障排除后会自动恢复,也可以通过手动重启( 禁用 并 启用 )授权检查器恢复。
你可以通过 是否启用 列查看并设置每个授权检查器的启用状态。
执行检查时,EMQX 将跳过连接状态异常(已断开 或 连接中)与处于 禁用 状态的授权检查器。
统计指标
您可以在授权检查器详情页中查看每个授权检查器的统计指标,指标如下:
- 允许:鉴权检查通过次数。
- 拒绝:鉴权检查拒绝次数。
- 不匹配:未查找到客户端权限数据次数。
- 忽略:鉴权查询被忽略的次数,原因是当授权源尝试对请求进行授权但遇到不适用或出现错误导致无法决定结果的情况。
- 当前速率(tps):当前触发检查执行速率。
您也可以通过 节点状态 查看每个节点上授权状态和执行情况。
如果您想查看全局的授权检查统计指标,请参考 指标 - 认证和授权。
授权数据存储
EMQX 授权支持与多种数据源集成,包括内置数据库、文件、MySQL、PostgreSQL、MongoDB 和 Redis。用户可以通过 REST API 或 Dashboard 管理权限数据。此外,EMQX 还支持用户通过 HTTP 对接自己开发的服务,借此实现更复杂的授权逻辑。
按照数据源来划分,EMQX 支持以下几种授权检查器,您可点击下方表格中的链接获取各授权检查器的配置方式:
数据存储 | 描述 |
---|---|
ACL 文件 | 通过文件存放授权信息 |
内置数据库 | 使用内置数据库存放授权数据 |
MySQL | 使用 MySQL 存放授权数据 |
PostgreSQL | 使用 PostgreSQL 存放授权数据 |
MongoDB | 使用 MongoDB 存放授权数据 |
Redis | 使用 Redis 存放授权数据 |
LDAP | 使用 LDAP 目录存放授权数据 |
HTTP 服务器 | 通过访问外部 HTTP 服务来获取授权信息 |
例如,MySQL 授权检查器的配置文件为:
{
type = mysql
database = "mqtt"
username = "root"
password = "public"
query = "SELECT permission, action, topic FROM mqtt_acl WHERE username = ${username}"
server = "127.0.0.1:3306"
}
配置方式
EMQX 提供了 3 种使用权限的配置方式,分别为:Dashboard、配置文件和 HTTP API。
Dashboard
在 Dashboard 的授权页面,您可直接完成相关授权检查器的配置,查看授权检查器状态,以及调整授权检查器在授权链中的位置。
配置文件
您也可通过配置文件 emqx.conf
中 authorization
相关字段进行授权的配置。
配置结构如下:
authorization {
sources = [
{ ... },
{ ... }
]
no_match = allow
deny_action = ignore
cache {
max_size = 32
excludes = ["t/1", "t/2"]
ttl = 1m
}
}
其中:
sources
(可选):带顺序的数组,用于配置授权检查器的数据源。有关各授权检查器的具体配置信息,请参考相应的配置文件文档。no_match
:如当前客户端操作无法匹配到任何规则,将基于此规则决定允许或拒绝操作;可选值:allow
、deny
;默认值:allow
。此配置也是黑名单/白名单模式的开关。deny_action
:如当前客户端的操作被拒绝,后续应执行的操作;可选值:ignore
、disconnect
;默认值:ignore
。ignore
: 丢弃当前操作,例如,如针对发布动作,该消息会被丢弃;如针对订阅操作,该请求将被拒绝。disconnect
: 丢弃当前操作,并将客户端连接断开。
cache
:授权缓存的相关配置,包括以下字段:cache.enable
: 是否为授权开启缓存;默认值:true
;注意:如果仅使用认证中提供权限信息进行权限检查,建议关闭缓存。cache.max_size
: 每个客户端允许缓存的最大授权结果数量 ;默认值32
;当缓存结果的数量超过上限时,老的记录将被删掉。cache.excludes
: 排除的主题列表,指定列表内的主题不会生成授权缓存;默认值:[]
。cache.ttl
:缓存的有效时间,默认值:1m
(一分钟)。
HTTP API
EMQX 为授权参数暴露如下 REST API 来支持进行运行时动态修改。
/api/v5/authorization/settings
: 查看、修改授权参数,例如no_match
,deny_action
和cache
/api/v5/authorization/sources
: 用于管理授权检查器/api/v5/authorization/cache
: 清除授权数据缓存/api/v5/authorization/sources/built_in_database
: 内置数据库数据管理
详细的请求方式与参数请参考 HTTP API。