在 Redis 7.0 之前,想要确认一个 ACL 用户能否执行某条命令,通常只能切换身份实际执行一次,或者通过 ACL CAT、ACL GETUSER 手动比对命令类别。前者可能误改数据,后者在子命令、键模式、选择器等复杂场景下容易判断失误。ACL DRYRUN 的出现解决了这个痛点:它会像真实执行一样解析命令并完成权限校验,但不会真正执行写操作或返回数据。

这里的 dry-run 借鉴了软件工程中的演练概念,即走完流程但不产生副作用。对 Redis 来说,DRYRUN 会进行命令解析、命令重写、用户选择器匹配和键模式检查,最后只给出是否允许的结论。
ACL DRYRUN 的命令定位与返回值
从命令归属看,ACL DRYRUN 是 Redis 7.0 引入的 ACL 子命令,完整语法为 ACL DRYRUN username command [arg ...]。其中 username 必须是已经存在的 ACL 用户,command 可以是任意 Redis 命令,后面的参数按真实命令格式书写。该命令会返回一个简单字符串,如果权限检查通过,显示 OK;如果被拒绝,则返回以 This user has no permissions to ... 开头的错误信息,有些版本还会附带更具体的命令或键信息。
它不会连接或切换当前连接的用户身份,管理员可以继续以 default 或其他管理用户身份运行。正因为不需要实际执行,DRYRUN 不会产生数据变更,也不会因为命令带有写意图就影响数据库。这对敏感命令验证尤其重要,例如 FLUSHALL、CONFIG SET、SHUTDOWN 等。
返回值虽然通常是 OK 或拒绝信息,但在命令不存在、用户名不存在等输入错误场景下,Redis 会返回对应的错误。实际使用时应当把预检命令放在脚本中,根据返回值的开头是否为 OK 判断权限,而不是简单把输出当成布尔值。
# 检查 default 用户是否允许执行 SET ACL DRYRUN default SET cache:1 hello # 预期输出:OK # 检查一个受限用户是否允许执行 CONFIG GET ACL DRYRUN appuser CONFIG GET maxmemory # 预期输出:This user has no permissions to run the 'config|get' command
配合 ACL SETUSER 构建可验证的权限模型
仅有 DRYRUN 本身还不够,权限预检的准确性取决于用户配置是否完整。创建用户时通常使用 ACL SETUSER 指定密码、启用状态、键模式和命令规则。下面先创建一个名为 appuser 的用户,允许访问以 cache: 开头的键,开放所有命令类但禁用管理命令。
ACL SETUSER appuser on >apppass ~cache:* +@all -@admin ACL DRYRUN appuser SET cache:1 hello ACL DRYRUN appuser GET cache:1 ACL DRYRUN appuser CONFIG GET maxmemory ACL DRYRUN appuser GET user:1001
执行结果中,SET cache:1 hello 返回 OK,因为命令类允许且键符合 cache:* 模式;CONFIG GET 被 -@admin 禁止,返回命令权限拒绝;GET user:1001 则因键模式不匹配而拒绝。这样无需真正写入或读取,管理员就能一次性验证多条规则。
键模式检查是 ACL 预检中很容易被忽略的一环。很多管理员只配置命令类,却忘记 ~ 前缀的键约束。DRYRUN 会把命令名和键参数分开评估,因此如果键参数包含多个键,例如 MSET key1 value1 key2 value2,Redis 会检查每一个键是否都满足用户允许的模式,只要有一个不满足就会拒绝。这可以提前暴露出权限配置中的键范围盲区。
对于包含子命令的命令,例如 CONFIG GET、CLIENT LIST,DRYRUN 会按完整子命令进行匹配,而不是只检查父命令。用户在被授予 +CONFIG 但未授予 +CONFIG|GET 时,预检结果会明确拒绝。这种细粒度是 ACL CAT 无法直接体现的,这也是 DRYRUN 的实用价值。
命令重写与多选择器对预检结果的影响
Redis 内部存在命令重写机制,某些命令在执行前会被转换成其他命令。例如 GEOADD 会按一定条件重写为 ZADD,SORT_RO 可能被视为只读的 SORT。ACL DRYRUN 会在权限判断之前执行同样的重写流程,因此预检结果与真实执行时的 ACL 判定能够保持一致。
这意味着管理员在排查权限时,不能只盯着原始命令名。如果用户权限中包含 +ZADD 但禁止 +GEOADD,实际 GEOADD 可能会被重写为 ZADD,此时预检可能返回允许。相反,如果命令重写后命中受限类别,也会被拒绝。理解这一点可以避免误判,也能解释某些看起来不合理的权限结果。
Redis 7.0 还引入了 ACL selector 多选择器结构,一个用户可以拥有多个 selector,每个 selector 可以有不同的命令和键规则。DRYRUN 会按照 Redis 的选择器匹配逻辑评估命令,匹配过程与真实连接一致。因此在多选择器场景中,建议把 selector 顺序和内容纳入预检范围,尤其要注意默认选择器的存在。通过预检可以快速验证某个 selector 是否意外放行或拦截了命令。
另一个值得注意的细节是,DRYRUN 不会创建真实客户端上下文中的某些动态状态。比如真实客户端可能正处于事务、脚本或 Pub/Sub 状态,这些状态会影响命令是否允许执行。DRYRUN 也无法模拟客户端 IP 或 SSL 属性,除非这些信息只用于更上层的访问控制。因此它适合做权限规则本身的前置验证,不能替代完整的集成测试。
将 DRYRUN 融入权限审计与自动化测试
日常运维中,权限变更频繁,手动逐条测试效率很低。可以把需要验证的命令整理成清单,再通过脚本循环调用 ACL DRYRUN。例如准备一个包含用户、命令、预期结果的文本文件,用 redis-cli 从文件中读取并输出不符合预期的项。这样可以在每次修改 ACL 后快速回归。
#!/bin/bash
# 预期允许的命令
for cmd in "SET cache:1 hello" "GET cache:1" "DEL cache:1"; do
result=$(redis-cli ACL DRYRUN appuser $cmd)
if [ "$result" != "OK" ]; then
echo "FAIL: $cmd -> $result"
fi
done
# 预期拒绝的命令
for cmd in "CONFIG GET maxmemory" "SHUTDOWN NOSAVE"; do
result=$(redis-cli ACL DRYRUN appuser $cmd)
if [ "$result" = "OK" ]; then
echo "UNEXPECTED ALLOW: $cmd"
fi
done
这个脚本中,$result 的比较基于 Redis 返回简单字符串或错误字符串。注意 ACL DRYRUN 被拒绝时 redis-cli 不会以非零状态退出,因此要在脚本中判断返回值内容。对于需要更复杂判断的场景,可以使用 Redis 客户端库调用命令并检查返回类型。
权限审计方面,DRYRUN 也可以用来验证最小权限策略。先根据业务梳理出用户真正需要的命令和键范围,再用预检命令逐一确认允许列表之外的命令都被拒绝。通过把允许结果和拒绝结果同时纳入测试,可以防止权限收敛过度导致业务中断,也可以避免权限放宽后留下安全隐患。
总的来说,ACL DRYRUN 是 Redis 7.0 权限体系中的一个实用调试工具。它不产生副作用,却能高度还原真实权限判断,让管理员在修改 ACL 之前就能获得明确反馈。结合 ACL SETUSER、命令重写和自动化脚本,DRYRUN 能够显著降低 Redis 权限配置的试错成本。