bigywater.tech
Redis · 123

Redis ACL Category

给每条命令写一遍 +get +set 太累,ACL 提供了命令分类:+@read、-@dangerous。这篇把每个分类里到底有多少命令、几种常见配置对常用命令的放行/拒绝表、规则顺序造成的差别都实测出来,用 ACL DRYRUN 让服务端自己回答,不用真连。

449 / 22
命令总数 / 类别数(8.10.2,COMMAND LIST 与 ACL CAT)
@dangerous 88
危险命令的数量,INFO、CLIENT LIST、MONITOR、DEBUG 都在里面
顺序
+@all -@dangerous 与 -@dangerous +@all 结果相反

分类与数量

实测 · 本机执行输出
  分类: keyspace read write set sortedset list hash string array bitmap hyperloglog geo stream pubsub admin fast slow blocking dangerous connection transaction scripting
  COMMAND LIST 总数(不含子命令): 449
  @keyspace       35
  @read          105
  @write         135
  @set            21
  @sortedset      37
  @list           26
  @hash           33
  @string         26
  @array          18
  @bitmap          7
  @hyperloglog     5
  @geo            10
  @stream         28
  @pubsub         13
  @admin          77
  @fast          129
  @slow          307
  @blocking       13
  @dangerous      88
  @connection     38
  @transaction     5
  @scripting      21
  @dangerous 的前 20 条: failover slowlog|reset slowlog|get slowlog|len role hotkeys|stop hotkeys|reset hotkeys|start hotkeys|get replconf save debug bgrewriteaof sort_ro client|kill client|pause client|list client|no-evict client|unpause client|unblock

分类不是互斥的:一条命令可以同时是 @write、@slow、@dangerous。上面的数字是 8.10.2 的,别的版本不同(本文没有验证)。

几种常见配置的判定表

实测 · 本机执行输出
> ACL SETUSER ro on nopass ~* -@all +@read
"OK"
> ACL SETUSER nodanger on nopass ~* +@all -@dangerous
"OK"
> ACL SETUSER appuser on nopass ~app:* -@all +get +set +del
"OK"
> ACL SETUSER empty on nopass
"OK"
  ro: user ro on nopass sanitize-payload ~* resetchannels -@all +@read
  nodanger: user nodanger on nopass sanitize-payload ~* resetchannels +@all -@dangerous
  appuser: user appuser on nopass sanitize-payload ~app:* resetchannels -@all +get +set +del
  empty: user empty on nopass sanitize-payload resetchannels -@all
  命令                              ro  nodanger   appuser     empty
  GET app:1                       允许        允许        允许        拒绝
  SET app:1 v                     拒绝        允许        允许        拒绝
  DEL app:1                       拒绝        允许        允许        拒绝
  GET x                           允许        允许        拒绝        拒绝
  SCAN 0                          允许        允许        拒绝        拒绝
  KEYS *                          允许        拒绝        拒绝        拒绝
  INFO                            拒绝        拒绝        拒绝        拒绝
  PING                            拒绝        允许        拒绝        拒绝
  EVAL return 1 0                 拒绝        允许        拒绝        拒绝
  CONFIG GET dir                  拒绝        拒绝        拒绝        拒绝
  CONFIG SET dir /tmp             拒绝        拒绝        拒绝        拒绝
  FLUSHALL                        拒绝        拒绝        拒绝        拒绝
  SUBSCRIBE ch                    拒绝        拒绝        拒绝        拒绝
  PUBLISH ch m                    拒绝        拒绝        拒绝        拒绝
  SAVE                            拒绝        拒绝        拒绝        拒绝
  SHUTDOWN NOSAVE                 拒绝        拒绝        拒绝        拒绝
  MONITOR                         拒绝        拒绝        拒绝        拒绝
  CLIENT LIST                     拒绝        拒绝        拒绝        拒绝
  DEBUG SLEEP 0                   拒绝        拒绝        拒绝        拒绝
  ACL DRYRUN 的拒绝原因示例:
> ACL DRYRUN ro SET k v
"User ro has no permissions to run the 'set' command"
> ACL DRYRUN appuser GET x
"User appuser has no permissions to access the 'x' key"
> ACL DRYRUN nodanger FLUSHALL
"User nodanger has no permissions to run the 'flushall' command"

ACL DRYRUN 用户 命令… 让服务端按该用户的规则判定,不执行命令。看表要注意几件事:

规则顺序

实测 · 本机执行输出
> ACL SETUSER o1 on nopass ~* +@all -@dangerous
"OK"
> ACL SETUSER o2 on nopass ~* -@dangerous +@all
"OK"
  o1: user o1 on nopass sanitize-payload ~* resetchannels +@all -@dangerous
    FLUSHALL → User o1 has no permissions to run the 'flushall' command
  o2: user o2 on nopass sanitize-payload ~* resetchannels +@all
    FLUSHALL → OK

两条规则同一组、顺序相反,FLUSHALL 一个被拒一个放行。ACL LIST 输出的规则也保留了顺序。写 ACL 时把「先给大范围、再收窄」作为习惯,并且写完用 ACL DRYRUN 验一遍。

子命令与选择器

实测 · 本机执行输出
> ACL SETUSER cfg on nopass ~* -@all +config|get +ping
"OK"
   user cfg on nopass sanitize-payload ~* resetchannels -@all +config|get +ping
> AUTH cfg x
"OK"
> CONFIG GET maxmemory
1) "maxmemory"
2) "0"
> CONFIG SET maxmemory 1mb
(error) NOPERM User cfg has no permissions to run the 'config|set' command
> CONFIG REWRITE
(error) NOPERM User cfg has no permissions to run the 'config|rewrite' command
实测 · 本机执行输出
> ACL SETUSER sel on nopass ~cache:* +@all -@dangerous "(~reports:* +get)"
"OK"
   user sel on nopass sanitize-payload ~cache:* resetchannels +@all -@dangerous (~reports:* resetchannels -@all +get)
  DRYRUN sel SET cache:1 v → "OK"
  DRYRUN sel GET reports:1 → "OK"
  DRYRUN sel SET reports:1 v → "User sel has no permissions to access the 'reports:1' key"
  DRYRUN sel GET other:1 → "User sel has no permissions to access the 'other:1' key"

+config|get 只放行 CONFIG GET。选择器 (~reports:* +get) 是一组独立的「key + 命令」权限,和根权限是「或」的关系:命中任一组就放行。

两个容易误会的地方

实测 · 本机执行输出
  INFO / CLIENT LIST / MONITOR / DEBUG 在 @dangerous 里吗: {'info': True, 'client|list': True, 'monitor': True, 'debug': True}
  PING 在 @read 里吗: False ;在 @fast 里吗: True
  DRYRUN nodanger SUBSCRIBE ch → "User nodanger has no permissions to access the 'ch' channel"
  DRYRUN nodanger INFO → "User nodanger has no permissions to run the 'info' command"
> ACL SETUSER nodanger2 on nopass ~* &* +@all -@dangerous
"OK"
  DRYRUN nodanger2(加了 &*) SUBSCRIBE ch → "OK"
  DRYRUN nodanger2(加了 &*) PUBLISH ch m → "OK"

拦掉 INFO 的是 @dangerous,拦掉 SUBSCRIBE / PUBLISH 的是频道权限。搭配 +@all -@dangerous 时这两类都要考虑。

分步演示:同一批命令,不同配置

ACL Category
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Redis 8.10.2 独立实例(本机回环端口,save 关闭),redis-cli 风格输出由自写的 RESP 客户端打印;计时用 time.perf_counter,数字每次运行会有小幅抖动。判定全部来自 ACL DRYRUN
    • 没有验证:其它版本的分类内容、自定义模块命令的分类、+@all 之后新增命令的行为、ACL DRYRUN 与真实连接执行结果的逐条一致性(这里只用 DRYRUN;第 122 篇里有少量真实连接的对照)
    京ICP备2026067502号-1京公网安备11010502063487号