bigywater.tech
Redis · 124

Redis ACL Channel

ACL 除了限制命令和 key,还限制频道(&pattern)。发布订阅出了名的没有认证之外的隔离,所以这一层要看清:哪些命令受它管、模式订阅怎么匹配、撤销权限时已经订阅的连接怎么办。全部在本机实例上实测。

resetchannels
acl-pubsub-default 的默认值:新用户没有任何频道权限,连 PUBLISH 也不行
字面相同
PSUBSCRIBE news:a* 在只有 &news:* 时被拒;模式必须与授权的模式字面一致
EOF
撤销频道权限后,正在订阅的连接被服务端断开

默认值:没有频道权限

实测 · 本机执行输出
> CONFIG GET acl-pubsub-default
1) "acl-pubsub-default"
2) "resetchannels"
实测 · 本机执行输出
> ACL SETUSER u1 on nopass ~* +@all
"OK"
   user u1 on nopass sanitize-payload ~* resetchannels +@all
> AUTH u1 x
"OK"
> PUBLISH news:1 hi
(error) NOPERM No permissions to access a channel
> SUBSCRIBE news:1
(error) NOPERM No permissions to access a channel

命令权限(+@all)和频道权限(&…)是两套独立的东西。只给命令,不给频道,PUBLISH 和 SUBSCRIBE 都会被拒。ACL LIST 里看得见 resetchannels(即没有任何频道)。

频道模式如何匹配

实测 · 本机执行输出
> ACL SETUSER u2 on nopass ~* +@all resetchannels &news:*
"OK"
   user u2 on nopass sanitize-payload ~* resetchannels &news:* +@all
  DRYRUN u2 PUBLISH news:1 hi          → "OK"
  DRYRUN u2 PUBLISH other hi           → "User u2 has no permissions to access the 'other' channel"
  DRYRUN u2 SUBSCRIBE news:1           → "OK"
  DRYRUN u2 SUBSCRIBE news:*           → "OK"
  DRYRUN u2 SUBSCRIBE other            → "User u2 has no permissions to access the 'other' channel"
  DRYRUN u2 PSUBSCRIBE news:*          → "OK"
  DRYRUN u2 PSUBSCRIBE news:a*         → "User u2 has no permissions to access the 'news:a*' channel"
  DRYRUN u2 PSUBSCRIBE *               → "User u2 has no permissions to access the '*' channel"
  DRYRUN u2 SSUBSCRIBE news:1          → "OK"

DRYRUN 的判定见上表。要点:授权的是一个 glob 模式 news:*,普通订阅 news:1 命中它;而 PSUBSCRIBE 本身带模式,必须与已授权的模式完全相同——news:a* 虽然范围更小也被拒。SUBSCRIBE news:* 是把 news:* 当作一个普通频道名,能被 &news:* 匹配,所以放行。(实测得到的行为;背后的匹配实现本文没有翻源码。)

撤销权限时,已订阅的连接怎么办

实测 · 本机执行输出
> AUTH u2 x
"OK"
  订阅: 1) "subscribe"
2) "news:1"
3) (integer) 1
  管理员发布一条: 1 个订阅者收到 -> 1) "message"
2) "news:1"
3) "before"
> ACL SETUSER u2 resetchannels
"OK"
  撤销之后订阅连接:服务端已断开连接(EOF)
  之后发布,订阅者数: 0
  PUBSUB CHANNELS: (empty array)

管理员 resetchannels 之后,服务端直接把这条订阅连接断开(客户端读到 EOF),不会留一个「还在订阅但没权限」的连接。所以客户端库要做好订阅连接被踢后重连(并重新鉴权,会被拒)的处理。

allchannels 与键空间通知

实测 · 本机执行输出
> ACL SETUSER u3 on nopass ~* +@all allchannels
"OK"
   user u3 on nopass sanitize-payload ~* &* +@all
> CONFIG SET acl-pubsub-default allchannels
"OK"
> ACL SETUSER u4 on nopass ~* +@all
"OK"
   user u4 on nopass sanitize-payload ~* &* +@all
> CONFIG SET acl-pubsub-default resetchannels
"OK"
实测 · 本机执行输出
> CONFIG SET notify-keyspace-events KEA
"OK"
> ACL SETUSER u5 on nopass ~* +@all resetchannels &news:*
"OK"
  DRYRUN u5 PSUBSCRIBE __keyspace@0__:* → "User u5 has no permissions to access the '__keyspace@0__:*' channel"

想让新用户默认能用发布订阅,可以把 acl-pubsub-default 改成 allchannels——影响之后新建的用户。键空间通知的频道(__keyspace@0__:*)也归 ACL 管:没有授权的用户订阅不了,这是个天然的「谁能看到 key 变化」的开关。

分步演示:频道权限

ACL Channel
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Redis 8.10.2 独立实例(本机回环端口,save 关闭),redis-cli 风格输出由自写的 RESP 客户端打印;计时用 time.perf_counter,数字每次运行会有小幅抖动。判定用 ACL DRYRUN,撤销实验用真实订阅连接
    • 没有验证:Cluster 下 SSUBSCRIBE 的槽位与 ACL 的交互、模块命令、ACL 频道权限对 Lua 内 redis.call(PUBLISH) 的作用、版本差异(acl-pubsub-default 的默认值在 Redis 7 之前不同,未测)
    京ICP备2026067502号-1京公网安备11010502063487号