给每条命令写一遍 +get +set 太累,ACL 提供了命令分类:+@read、-@dangerous。这篇把每个分类里到底有多少命令、几种常见配置对常用命令的放行/拒绝表、规则顺序造成的差别都实测出来,用 ACL DRYRUN 让服务端自己回答,不用真连。
分类: 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 用户 命令… 让服务端按该用户的规则判定,不执行命令。看表要注意几件事:
ro(-@all +@read)不能 PING、INFO——它们不属于 @read,给只读账号别忘了单独放行 +ping 和需要的 +info。nodanger(+@all -@dangerous)能 EVAL(脚本可以做很多事,对它的边界见第 130 篇),但 INFO、CLIENT LIST、MONITOR、DEBUG 都在 @dangerous 里,被一并拦下——监控账号要单独给 +info。SUBSCRIBE、PUBLISH 被拒则是另一回事:没有频道权限(见第 124 篇),加上 &* 就放行了(见第六节)。empty)就是全拒。> 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 时这两类都要考虑。