启动redis acl 默认acl订阅失败

问题描述

使用 jwt+redis acl 认证,acl_nomatch=allow

环境信息

  • EMQ X 版本:4.1.0
  • 操作系统及版本:centos 7.6
  • 其他

配置文件及日志

acl.conf

## Value: true | false
allow_anonymous = false

## Allow or deny if no ACL rules matched.
##
## Value: allow | deny
acl_nomatch = allow

## Default ACL File.
##
## Value: File Name
acl_file = etc/acl.conf

emqx_auth_jwt.conf

auth.jwt.secret = emqxsecret

## From where the JWT string can be got
##
## Value: username | password
## Default: password
auth.jwt.from = password

## RSA or ECDSA public key file.
##
## Value: File
## auth.jwt.pubkey = etc/certs/jwt_public_key.pem

## Enable to verify claims fields
##
## Value: on | off
auth.jwt.verify_claims = off

emqx_auth_redis.conf 
关闭了redis auth认证
## Authentication query command.
##
## Value: Redis cmd
##
## Variables:
##  - %u: username
##  - %c: clientid
##  - %C: common name of client TLS cert
##  - %d: subject of client TLS cert
##
## Examples:
##  - HGET mqtt_user:%u password
##  - HMGET mqtt_user:%u password
##  - HMGET mqtt_user:%u password salt
##auth.redis.auth_cmd = HMGET mqtt_user:%u password

## ACL query command.
##
## Value: Redis cmd
##
## Variables:
##  - %u: username
##  - %c: clientid
auth.redis.acl_cmd = HGETALL mqtt_acl:%c

redis acl 规则

127.0.0.1:6379> HGETALL mqtt_acl:gtja
1) "test/gtja"
2) "1"

用 clientid 为 gtja 去订阅 只能订阅 “test/gtja” 这个 topic,其他匹配失败的 topic 均不能订阅

Redis ACL 是白名单模式,中添加的所有规则都是 允许 规则,可以搭配 etc/emqx.confacl_nomatch = deny 使用。

ACL 匹配不到就是 ignore,按照你的配置应该是能够订阅的。

当redis 中为空时,两个topic均能订阅上,此时应该是走到了default配置

127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> 

2021-05-24 10:01:32.512 [debug] 10.176.118.154:49851 [MQTT] RECV <<16,235,1,0,4,77,81,84,84,4,194,0,100,0,4,103,116,106,97,0,4,121,100,112,116,0,211,101,121,74,104,98,71,99,105,79,105,74,73,85,122,73,49,78,105,73,115,73,110,82,53,99,67,73,54,73,107,112,88,86,67,74,57,46,101,121,74,107,89,88,82,104,73,106,112,55,73,109,70,49,100,71,104,118,99,105,73,54,73,110,100,112,100,110,100,112,100,105,73,115,73,110,78,112,100,71,85,105,79,105,74,111,100,72,82,119,99,122,111,118,76,51,100,112,100,110,100,112,100,105,53,106,98,50,48,105,102,83,119,105,90,88,104,119,73,106,111,120,78,84,103,121,77,106,85,49,77,122,89,119,78,106,81,121,77,68,65,119,77,67,119,105,97,87,70,48,73,106,111,120,78,84,103,121,77,106,85,49,77,122,89,119,102,81,46,70,100,121,65,120,50,102,89,97,104,109,54,104,51,103,52,55,109,56,56,116,116,121,73,78,122,112,116,122,75,121,95,115,112,101,105,109,121,85,99,109,97,52>>
2021-05-24 10:01:32.513 [debug] 10.176.118.154:49851 [MQTT] RECV CONNECT(Q0, R0, D0, ClientId=gtja, ProtoName=MQTT, ProtoVsn=4, CleanStart=true, KeepAlive=100, Username=ydpt, Password=******)
2021-05-24 10:01:32.514 [debug] <<"gtja">>@10.176.118.154:49851 [MQTT] SEND CONNACK(Q0, R0, D0, AckFlags=0, ReasonCode=0)
2021-05-24 10:01:32.518 [debug] <<"gtja">>@10.176.118.154:49851 [MQTT] RECV <<130,9,0,1,0,4,116,101,115,116,1,130,14,0,2,0,9,116,101,115,116,47,103,116,106,97,1>>
2021-05-24 10:01:32.518 [debug] <<"gtja">>@10.176.118.154:49851 [MQTT] RECV SUBSCRIBE(Q1, R0, D0, PacketId=1, TopicFilters=[{<<"test">>,#{nl => 0,qos => 1,rap => 0,rh => 0}}])
2021-05-24 10:01:32.520 [debug] <<"gtja">>@10.176.118.154:49851 [MQTT] RECV SUBSCRIBE(Q1, R0, D0, PacketId=2, TopicFilters=[{<<"test/gtja">>,#{nl => 0,qos => 1,rap => 0,rh => 0}}])
2021-05-24 10:01:32.522 [debug] <<"gtja">>@10.176.118.154:49851 [MQTT] SEND SUBACK(Q0, R0, D0, PacketId=1, ReasonCodes=[1])
2021-05-24 10:01:32.522 [debug] <<"gtja">>@10.176.118.154:49851 [MQTT] SEND SUBACK(Q0, R0, D0, PacketId=2, ReasonCodes=[1])

当redis中增加ACL规则时,只能订阅匹配的topic,"test"这个topic就订阅不上了

127.0.0.1:6379> HSET mqtt_acl:gtja test/gtja 1
(integer) 1
127.0.0.1:6379> HGETALL mqtt_acl:gtja
1) "test/gtja"
2) "1"
127.0.0.1:6379> 

2021-05-24 10:05:43.955 [debug] 10.176.118.154:49880 [MQTT] RECV <<16,235,1,0,4,77,81,84,84,4,194,0,100,0,4,103,116,106,97,0,4,121,100,112,116,0,211,101,121,74,104,98,71,99,105,79,105,74,73,85,122,73,49,78,105,73,115,73,110,82,53,99,67,73,54,73,107,112,88,86,67,74,57,46,101,121,74,107,89,88,82,104,73,106,112,55,73,109,70,49,100,71,104,118,99,105,73,54,73,110,100,112,100,110,100,112,100,105,73,115,73,110,78,112,100,71,85,105,79,105,74,111,100,72,82,119,99,122,111,118,76,51,100,112,100,110,100,112,100,105,53,106,98,50,48,105,102,83,119,105,90,88,104,119,73,106,111,120,78,84,103,121,77,106,85,49,77,122,89,119,78,106,81,121,77,68,65,119,77,67,119,105,97,87,70,48,73,106,111,120,78,84,103,121,77,106,85,49,77,122,89,119,102,81,46,70,100,121,65,120,50,102,89,97,104,109,54,104,51,103,52,55,109,56,56,116,116,121,73,78,122,112,116,122,75,121,95,115,112,101,105,109,121,85,99,109,97,52>>
2021-05-24 10:05:43.956 [debug] 10.176.118.154:49880 [MQTT] RECV CONNECT(Q0, R0, D0, ClientId=gtja, ProtoName=MQTT, ProtoVsn=4, CleanStart=true, KeepAlive=100, Username=ydpt, Password=******)
2021-05-24 10:05:43.958 [debug] <<"gtja">>@10.176.118.154:49880 [MQTT] SEND CONNACK(Q0, R0, D0, AckFlags=0, ReasonCode=0)
2021-05-24 10:05:43.961 [debug] <<"gtja">>@10.176.118.154:49880 [MQTT] RECV <<130,9,0,1,0,4,116,101,115,116,1>>
2021-05-24 10:05:43.961 [debug] <<"gtja">>@10.176.118.154:49880 [MQTT] RECV SUBSCRIBE(Q1, R0, D0, PacketId=1, TopicFilters=[{<<"test">>,#{nl => 0,qos => 1,rap => 0,rh => 0}}])
2021-05-24 10:05:43.963 [debug] <<"gtja">>@10.176.118.154:49880 [MQTT] SEND SUBACK(Q0, R0, D0, PacketId=1, ReasonCodes=[128])
2021-05-24 10:05:43.963 [debug] <<"gtja">>@10.176.118.154:49880 [MQTT] RECV <<130,14,0,2,0,9,116,101,115,116,47,103,116,106,97,1>>
2021-05-24 10:05:43.964 [debug] <<"gtja">>@10.176.118.154:49880 [MQTT] RECV SUBSCRIBE(Q1, R0, D0, PacketId=2, TopicFilters=[{<<"test/gtja">>,#{nl => 0,qos => 1,rap => 0,rh => 0}}])
2021-05-24 10:05:43.965 [debug] <<"gtja">>@10.176.118.154:49880 [MQTT] SEND SUBACK(Q0, R0, D0, PacketId=2, ReasonCodes=[1])

启动的modules和plugins如下:

[root@publish data]# cat loaded_plugins 
emqx_management.
emqx_recon.
emqx_rule_engine.
emqx_dashboard.
emqx_retainer.
emqx_auth_jwt.
{emqx_auth_redis,true}.
[root@publish data]# cat loaded_modules 
{emqx_mod_delayed,false}.
{emqx_mod_topic_metrics,false}.
{emqx_mod_subscription,false}.
{emqx_mod_acl_internal,true}.
{emqx_mod_rewrite,false}.
{emqx_mod_presence,true}.

很抱歉给您造成了困扰,Redis 的 ACL 是以白名单的方式工作的,只有命中了规则,发布或订阅操作才能被允许,所以您需要再添加一条 HSET mqtt_acl:gtja test 1 才能让客户端 gtja 订阅主题 test

也许您会发现没有在 Redis 中为这个客户端添加任何规则时,它可以发布订阅任何主题,这是实现和设计上的一些问题,我们正准备在 5.0 中解决它。


那是这个文档里的描述有问题了,acl_nomatch = deny | allow 起不了任何作用了

acl_nomatch 只有在 ACL 链上所有的插件都返回 nomatch 时才会触发生效。

那按这样来说的话redis ACL最后还是会走到default了,但现在acl_nomatch=allow是没有走到acl.conf这个配置

你前面的这个场景里订阅 test 主题时 Redis 插件返回的是 deny,所以不会到最终的 acl_nomatch。

比如启了2个ACL插件redis+mysql,acl_nomatch = allow :
一、redis中没有test的匹配规则,mysql中允许订阅test,这时候客户端能订阅上test吗?此时redis是返回deny还是nomatch呢?
二、redis、mysql中都没有test的匹配规则,此时客户端能否走到default配置订阅上test,如果不能,什么情况下才会走到default配置