Redis 6.0 ACL权限控制实战:从粗放密码到精细化安全隔离

Redis 6.0 ACL权限控制实战:从粗放密码到精细化安全隔离 1. 先搞清楚一件事Redis 6.0 的 ACL 到底解决了什么用过 Redis 的人都知道在 6.0 之前Redis 的权限控制其实约等于没有。你打开redis.conf能找到的安全相关配置无非就是一个requirepass设置一个全局密码。所有连上来的客户端只要拿着这同一个密码就能执行 Redis 里所有的命令——FLUSHALL、CONFIG SET、SHUTDOWN、KEYS *想干嘛就干嘛。这在生产环境里是非常吓人的一件事情。想象一下你给业务团队开了一个 Redis 实例结果因为某个人误操作执行了FLUSHALL整个缓存瞬间清空这口锅谁背都背不动。Redis 6.0 推出的 ACLAccess Control List访问控制列表就是来解决这个问题的。它允许你针对不同的用户、不同的连接去精细地控制“能执行哪些命令”“能访问哪些 key”甚至还能限制用户连接的客户端地址。说白了就是从以前“一把钥匙开一把锁”的粗放模式进化成了“一人一卡刷哪儿管哪儿”的精细化权限管理模式。我个人的观点是ACL 的引入是 Redis 从“缓存工具”走向“数据基础设施”的关键一步。在 6.0 之前很多公司根本不敢把 Redis 直接暴露给多个业务方共用因为没法隔离风险。有了 ACL你完全可以开一个实例给订单服务、用户服务、消息队列各分配一个独立的账号各自只能访问自己的 key 前缀只能执行自己业务需要的命令。这个变化对于架构层面的安全隔离来说意义是非常大的。这篇文章我就结合自己的实际使用经验把 ACL 从概念、命令、配置到实战踩坑完整地过一遍。内容偏实操看完你直接能在自己的环境里配一套像样的权限体系出来。2. ACL 的核心概念拆解用户、规则、权限别搞混了ACL 这个东西听起来高大上但它的模型其实非常简单你可以把它类比成公司门禁系统。首先Redis 里会有多个用户User。默认有一个超级管理员用户叫default默认情况下他是最高权限什么都能干谁连接 Redis 如果没有指定用户名用的就是这个default用户。其次每个用户底下会挂一堆规则Rule。这些规则定义了这个人能做什么、不能做什么。比如规则可以规定该用户能不能执行CONFIG命令能不能访问order:*这组 key密码是什么等等。最后当客户端连接 Redis 的时候需要提供用户名 密码来认证。认证通过后这个连接的所有操作都会受到该用户规则的限制。一旦越权Redis 会直接返回错误。让我用一句好记的话来总结用户是身份的载体规则是权限的描述命令和 key 是被控制的对象。这里有个地方要特别注意在 Redis 6.0 中即使你的redis.conf里配了requirepassACL 系统仍然是生效的。实际上requirepass本质上是在给default用户设置密码。如果你同时配置了 ACL 文件和requirepass可能会产生预期外的行为后面我会专门说一下这个坑。理解了这些基础概念我们接下来看规则语法。ACL 规则语法分两种大方向一种是on/off控制用户是否启用另一种是命令/-命令控制命令权限还有~keypattern控制键空间访问以及密码设置密码。下面这张表把常用的规则语法整理了一下建议收藏规则语法含义示例on/off启用 / 禁用用户onpassword设置明文密码123456#hash设置 SHA256 哈希密码#5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8nopass允许无密码登录nopasscommand允许执行某个命令get-command禁止执行某个命令-flushallcategory允许某个命令类别如readread-category禁止某个命令类别-adminallcommands/all允许所有命令allcommandsnocommands/-all禁止所有命令nocommands~pattern允许访问匹配该 pattern 的 key~order:*%R~pattern只允许读特定 pattern 的 key%R~user:*%W~pattern只允许写特定 pattern 的 key%W~user:*allkeys/~*允许访问所有 keyallkeysresetkeys清除所有 key 权限resetkeysreset重置用户到默认状态reset有一个概念容易被新手忽略category命令类别。Redis 把几百个命令分成了若干组比如read读类命令、write写类命令、admin管理类命令、keyspace键空间类、dangerous危险类等。你不需要一条一条去加直接按类别授权效率高很多。可以用ACL CAT命令查看所有分类后面会讲。3. 一批开箱即用的 ACL 命令建议全部掌握ACL 的命令并不复杂我来按功能分类过一遍每一个我都会配上实际返回结果方便你对照着看。3.1 查询类ACL WHOAMI、ACL LIST、ACL CAT、ACL GETUSER这三个是排查问题最常用的。ACL WHOAMI查看当前连接的用户名。这个最简单也最实用。有时候你配了半天权限连上去不知道自己是哪个用户跑一下就知道了。127.0.0.1:6379 ACL WHOAMI defaultACL LIST列出所有用户及其权限规则。请记住这里显示的规则是标准化的、脱敏后的格式密码不会明文展示而是显示#hash或者占位符。127.0.0.1:6379 ACL LIST 1) user default on nopass ~* * all看到没这行内容的含义是用户default状态为on启用nopass无密码~*可以访问所有 key*可以访问所有 Pub/Sub 频道all允许所有命令。ACL CAT查看命令分类。不带参数是列出所有分类带分类名是列出该分类下包含的所有命令。127.0.0.1:6379 ACL CAT 1) keyspace 2) read 3) write 4) set 5) sortedset 6) list 7) hash 8) string 9) bitmap 10) hyperloglog 11) geo 12) stream 13) pubsub 14) admin 15) fast 16) slow 17) blocking 18) dangerous 19) connection 20) transaction 21) scripting再看某个分类下具体有哪些命令127.0.0.1:6379 ACL CAT dangerous 1) flushall 2) flushdb 3) keys 4) shutdown 5) debug 6) config 7) replicaof 8) monitor 9) sort 10) scan 11) migrate 12) restore 13) swapdb 14) pfdebug 15) replconf 16) client 17) lastsave这告诉我们像flushall、config、keys、shutdown这种命令全都被归到了dangerous分类里。如果你的用户业务逻辑上完全不需要这些命令一定要在规则里显式禁用-dangerous。我见过太多事故就是因为业务账号能执行KEYS *在数据量大的时候直接把 Redis 卡死。ACL GETUSER username查看某个具体用户的全部信息。这个比ACL LIST更详细会把规则拆开给你看。127.0.0.1:6379 ACL GETUSER default 1) flags 2) 1) on 3) passwords 4) (empty array) 5) commands 6) all 7) keys 8) ~* 9) channels 10) * 11) selectors 12) (empty array)注意这几个字段flags是用户状态passwords是密码哈希列表nopass的会显示空数组commands是命令权限keys是键权限channels是 Pub/Sub 频道权限。这些信息在排障时非常有用。3.2 用户管理ACL SETUSER、ACL DELUSER这是 ACL 的核心操作命令。它的特点是多次调用是叠加生效的不是覆盖。也就是说你先ACL SETUSER john get再ACL SETUSER john ~user:*两次规则会叠加。创建用户并设置密码127.0.0.1:6379 ACL SETUSER alice on alice123 ~user:* read write -flushall -flushdb -config -keys OK这个命令的含义是创建或更新用户alice启用状态密码设为alice123允许访问所有以user:开头的 key允许所有读类和写类命令同时明确禁止flushall、flushdb、config、keys这几个危险命令。然后我们看看这个用户现在的配置127.0.0.1:6379 ACL GETUSER alice 1) flags 2) 1) on 3) passwords 4) 1) 2b5b0e2c0f2ef0e2fed4d73a6f0cf7c3e0d56ede68d0d851d1ae6f0aabb9d5fa 5) commands 6) read write -flushall -flushdb -config -keys 7) keys 8) ~user:* 9) channels 10) *关于密码这里我重点提醒一句ACL SETUSER之后如果这个用户之前存在且它原来有密码你设置新密码后旧密码还能不能用答案是可以的。ACL SETUSER user newpass是追加一个密码而不是替换。如果你确定要清除旧密码得用newpass配上oldpass才能移除。这一点非常容易踩坑我用一个例子演示# 给 alice 追加一个新密码 127.0.0.1:6379 ACL SETUSER alice newpass321 OK # 查看, 现在 alice 有两个密码 127.0.0.1:6379 ACL GETUSER alice ... passwords 1) 2b5b0e2c0f2ef0e2fed4d73a6f0cf7c3e0d56ede68d0d851d1ae6f0aabb9d5fa # 这是 alice123 的哈希 2) 2420d16c41f4b5aaefde46af0d4e232d3cd82036802e146a4eb1ed18bc3e6ed5 # 这是 newpass321 的哈希想要彻底把alice123这个老密码干掉127.0.0.1:6379 ACL SETUSER alice alice123 OK这样passwords里就只剩newpass321对应的哈希了。所以如果你有轮换密码的需求和配套用先加后减就不会导致密码清空时用户突然失联。删除用户127.0.0.1:6379 ACL DELUSER alice (integer) 1返回1表示删除成功返回0表示该用户不存在。还有一个非常实用的技巧你可以用ACL SETUSER来临时禁用账号不需要删除。# 禁用 alice 127.0.0.1:6379 ACL SETUSER alice off OK # 此时 alice 无论密码对不对都连不上 Redis这个方案比删除好因为配置都还在恢复时只需要ACL SETUSER alice on即可。3.3 其他辅助命令ACL GENPASS、ACL SAVE、ACL LOAD、ACL LOGACL GENPASS快速生成一个强密码。不带参数时默认生成 64 字节的十六进制随机串你也可以指定长度。127.0.0.1:6379 ACL GENPASS 2d9ff276b10e6a2d1740a6fbc5d2d20f5f584e9337aa6d8b0a6ebfa1d0f668abACL SAVE把当前内存中的 ACL 规则持久化到aclfile指定的文件里。这个命令在重启不丢配置的场景下非常关键。127.0.0.1:6379 ACL SAVE OK前提是你得在redis.conf里配置了aclfile /etc/redis/users.acl。如果没配会报错(error) ERR This Redis instance is not configured to use an ACL file. You may want to specify users via the ACL SETUSER command and then issue a CONFIG REWRITE (assuming you have a Redis configuration file set) in order to store users in the Redis configuration.ACL LOAD从aclfile加载 ACL 规则覆盖当前的规则配置。当你改了文件但不想重启 Redis 时用这个命令。ACL LOG查看 ACL 相关的拒绝日志。这玩意儿在排查“为什么我的操作被拒绝”时太好用了它会告诉你是什么命令、哪个用户、访问了哪个 key、被哪条规则拦下的。后面排障章节我再细讲。4. 实战从零配置一套可落地的 ACL 权限体系理论说再多不如跑一遍。这一节我会从真实场景出发演示一套完整的 ACL 配置流程。场景设定是一个电商项目Redis 里有三个业务方要接入订单微服务、用户微服务、运营后台。要求是订单微服务只能读写order:*的 key禁止所有管理命令。用户微服务只能读写user:*的 key禁止所有管理命令。运营后台可以读所有 key但只能写report:*的 key并且不允许删库、不能执行KEYS、FLUSHALL、CONFIG这些危险命令。默认default用户只允许本机访问其他客户端一律要求显式账号密码登录。4.1 设计用户与规则映射先把需求翻译成规则业务用户名允许访问 key命令权限密码订单微服务srv_order~order:*read write -dangerous随机生成用户微服务srv_user~user:*read write -dangerous随机生成运营后台ops~*读所有key%W~report:*read -dangerous set随机生成管理员default~*all改为强密码需要说明的是ops用户这里我用到了 Redis 6.0 加入的键权限选择器key permission selector。用%R~pattern和%W~pattern可以分别限定读和写的 key 范围。~*是允许读所有 key%W~report:*是只允许写report:*。注意规则顺序先写读权限再写写权限语义更清晰。4.2 逐条执行 ACL 配置先给default用户设置一个强密码。这里要非常小心一旦执行了下面的命令当前连接如果验证过密码就没事如果没设置过密码的旧客户端就会全部掉线。127.0.0.1:6379 ACL SETUSER default on Default_Admin_2024_Str0ng! OK创建订单微服务账号。我习惯把密码存到环境变量或密钥管理平台这里为了演示就直接写了127.0.0.1:6379 ACL SETUSER srv_order on Ord3n_$rv_Pa55 ~order:* read write -dangerous OK这里解释一下规则顺序on启用设密码~order:*限定 keyread write放开读写命令最后-dangerous把危险命令一票否决。Redis 处理 ACL 规则的顺序很重要后面的-dangerous会覆盖前面的某条具体命令权限。如果有冲突以后面的规则为准。创建用户微服务账号127.0.0.1:6379 ACL SETUSER srv_user on Us3r_$rv_Pa55 ~user:* read write -dangerous OK创建运营后台账号。这里注意我用到了%R和%W选择器127.0.0.1:6379 ACL SETUSER ops on Op5_Pa55_Adm1n %R~* %W~report:* read set -dangerous OK解释一下set是允许 SET 命令因为默认read并不包含SET。%R~*允许读任何 key%W~report:*只允许写report:*前缀的 key。检查一下各用户配置127.0.0.1:6379 ACL GETUSER ops 1) flags 2) 1) on 3) passwords 4) 1) 1f9a1c4b9bbd606e6f374ac7bfb7739c1dfa12dcea09bc5d4a41b7e5cc1af397 5) commands 6) read set -dangerous 7) keys 8) ~* 9) channels 10) * 11) selectors 12) 1) 1) key 2) %W~report:*注意selectors里多了一条%W~report:*这说明写入权限被单独隔离出来了。4.3 验证配置效果单独开一个 redis-cli 连接用srv_order登录$ redis-cli --user srv_order --pass Ord3n_$rv_Pa55 127.0.0.1:6379 AUTH srv_order Ord3n_$rv_Pa55 OK 127.0.0.1:6379 SET order:10001 paid OK 127.0.0.1:6379 GET order:10001 paid # 尝试访问非授权 key被拒绝 127.0.0.1:6379 GET user:10001 (error) NOPERM No permissions to access a key # 尝试执行危险命令被拒绝 127.0.0.1:6379 FLUSHALL (error) NOPERM this user has no permissions to run the flushall command再看看运营后台账号的表现$ redis-cli --user ops --pass Op5_Pa55_Adm1n 127.0.0.1:6379 GET order:10001 paid # 写非 report 前缀的 key被拒绝 127.0.0.1:6379 SET order:10001 refunded (error) NOPERM No permissions to access a key # 写 report 前缀的 key成功 127.0.0.1:6379 SET report:20240601 total_orders 1000 OK完美符合我们的预期。这套配置验证通过后就可以放心交付给各个业务方使用了。4.4 配置文件与持久化方案ACL 配置有两种持久化方式我强烈推荐你用第二种。方案一写在 redis.conf 里面直接在配置文件底部加user default on Default_Admin_2024_Str0ng! ~* * all user srv_order on Ord3n_$rv_Pa55 ~order:* read write -dangerous user srv_user on Us3r_$rv_Pa55 ~user:* read write -dangerous user ops on Op5_Pa55_Adm1n %R~* %W~report:* read set -dangerous然后重启 Redis 或执行CONFIG REWRITE让配置生效并写回文件。方案二使用独立的 aclfile强烈推荐在redis.conf中指定aclfile /etc/redis/users.acl然后在运行时用ACL SAVE将内存中的规则一次性写入文件。这样做的最大好处是规则文件与主配置分离权限变更不需要动 redis.conf也不容易引入语法错误把主配置搞崩。我经历过一次在 redis.conf 里写错一个 ACL 规则导致整个 Redis 起不来的事故之后就再也不用方案一了。另外要提醒的是CONFIG REWRITE不会重写aclfile指向的文件。如果同时配置了aclfile修改 ACL 后想持久化必须执行ACL SAVE。反过来如果只写在 redis.conf 里用CONFIG REWRITE也会把user指令写进去。这两条路不要混着走不然容易出现配置和实际生效状态不一致的情况。5. 高频踩坑与排查技巧实录这块内容是真正要让你的运维生活变得更轻松的部分。ACL 上线初期我前前后后踩了不下十个坑这里挑几个最典型的分享出来。5.1 坑一requirepass和 ACL 混用导致 default 用户行为诡异有些老项目的redis.conf里还留着requirepass foobared然后你又在同一个文件里配置了user default on newpassword ...。这两个配置同时存在时requirepass foobared会被翻译成user default on foobared ...。结果就是default用户拥有两个密码一个是foobared一个是newpassword两个都能登录。解决方案也很明确要么彻底移除requirepass只依赖 ACL要么只保留requirepass别再用 ACL 去设置default密码。我建议直接移除requirepass全面切换到 ACL一个体系管到底脑子不累。5.2 坑二ACL SETUSER加密码是“追加”不是“覆盖”这个前面已经提到了再强调一次。很多人以为执行ACL SETUSER alice newpass后旧密码自动失效实际上不会。这会导致一种很尴尬的情况离职人员的账号虽然你改了密码但他原来的密码依然能登录。排查起来极具迷惑性因为ACL GETUSER里会列出所有密码哈希。我的检查习惯是每次改密码后都执行一次ACL GETUSER user确认passwords字段里只剩预期的哈希条目。如果有多余的用oldpass把它删掉。5.3 坑三忘记授权 Pub/Sub 频道权限Redis 6.0 的 ACL 默认*是允许所有频道的但如果你显式写了规则比如ACL SETUSER alice on pass ~* all -pubsub然后 alice 订阅频道SUBSCRIBE news时报错(error) NOPERM No permissions to access a channel这是因为 Pub/Sub 频道的权限是独立的由pattern控制。如果你希望某个用户只能订阅特定频道可以这样配ACL SETUSER alice on pass ~* news:* all -pubsub subscribe psubscribe unsubscribe punsubscribe不过这里要注意SUBSCRIBE、PSUBSCRIBE这些命令虽然属于pubsub分类但默认情况下是不受-pubsub影响的否则可能会误伤正常的订阅场景。我实际测试下来6.0 的-pubsub主要禁用的是PUBLISH订阅命令需要单独用频道规则去限制。所以当你发现订阅时报 channel 权限错误不要怀疑是命令被禁了去查一下规则。5.4 坑四ACL LOG级别不够看不到想看的日志ACL LOG默认只记录最近 10 条事件而且日志在 Redis 重启后会清空。排查问题时如果需要更长时间窗口的数据可以调大日志长度127.0.0.1:6379 CONFIG SET acllog-max-len 128 OK这个配置是动态生效的不需要重启。ACL LOG也支持按用户过滤比如只看srv_order被拒绝的记录127.0.0.1:6379 ACL LOG 128 srv_order5.5 常见故障速查表现象可能原因解决方式AUTH认证失败用户名不存在 / 密码错误 / 用户被offACL GETUSER user确认状态与密码ACL SETUSER user on执行命令提示NOPERM this user has no permissions to run the xxx command命令未在规则中显式允许xxx添加命令或直接read、write访问 key 提示NOPERM No permissions to access a keykey 规则没覆盖检查~pattern是否匹配如需区分读写用%R~、%W~订阅频道提示NOPERM No permissions to access a channel频道规则pattern未配置添加频道前缀*重启后 ACL 规则丢失未持久化配置aclfile执行ACL SAVE用长时间运行的客户端突然掉线且无法重连密码被修改或用户被off检查ACL GETUSER更新客户端配置default用户意外能访问所有 keydefault默认就是 allkeys用ACL SETUSER default resetkeys ~业务前缀:*收紧权限5.6 排障时必须有的操作习惯这里我掏心窝子分享几个日常习惯改完 ACL 先看ACL GETUSER再测连接。连续执行ACL GETUSER看清楚commands、keys、channels三个字段确认和预期一致再切流量。生产环境变更前先备份当前 ACL 配置。用ACL LIST把输出保存到一个文件万一改崩了可以直接ACL SETUSER一条条恢复或者直接ACL LOAD加载备份的 aclfile。危险命令不仅要禁还要在监控里盯。就算禁用FLUSHALL也别对运维安全掉以轻心最好在 Redis 的审计日志或云监控里配置告警一旦出现FLUSHALL、CONFIG SET等危险命令就触发通知。给每个服务单独的账号禁止共享账号。如果两个业务共用一个账号出问题的时候你根本没法定位是谁干的。ACL 的价值之一就是审计账号隔离是审计的前提。6. 权限设计的几条经验省得你走弯路最后说几条宏观层面的设计建议是我在多个项目里沉淀下来的。第一遵循最小权限原则。这句话听着像废话但执行起来很难。我给你一个具体可操作的标准“这个用户如果完全不执行某条命令、完全访问不到某个 key业务能不能正常跑如果能就把他这条权限删掉。” 很多团队上线时图省事直接all等出了事故再来补救成本完全不一样。Redis 的命令分类已经帮你做好了基础过滤至少要把dangerous和admin这两个分类关掉。第二key 的命名规范比权限规则更值得投入。ACL 的~pattern强烈依赖 key 的命名规律。如果你的业务 key 五花八门没有统一前缀那你写 ACL 规则会非常痛苦。反过来如果所有业务 key 都有清晰的前缀order:*、user:*、session:*规则写起来一目了然安全性和可维护性都大幅提升。顺便说一句这也是 Redis 最佳实践中一直强调的点。第三ACL 是权限控制但不能替代网络隔离。我见过一些团队配置了 ACL 之后就把 Redis 直接暴露在公网上觉得“反正没密码进不来”。这是非常危险的认知。ACL 在 Redis 内部做权限控制但网络层该做的防火墙、安全组、私有网络隔离一点都不能少。应用层安全永远是最后一道防线而不是第一道。第四定期审查 ACL 配置。我建议每个季度过一遍ACL LIST看看有没有长期不用的账号有没有密码已经泄露但还没轮换的账号。线上环境账号多了之后很容易出现僵尸账号这是安全隐患。审查的时候可以对比应用部署清单逐一确认每个账号是否还在用。7. 一点个人体会用了一个多月的 ACL我最大的感受是Redis 6.0 引入 ACL 并不是为了炫技而是把企业级缓存/存储该有的安全底线给补齐了。以前要在一个 Redis 实例上做多业务隔离只能靠多实例 网络策略成本高、管理麻烦。现在一个实例就能做到相对清晰的权限切分架构简化的同时安全性反而提升了。我个人在实际操作中比较推荐“先小范围验证再全量推广”的策略。可以先把某一个非核心业务的账号切到 ACL 上观察一段时间确认没有误伤生产请求再把其他业务全部切换过来。毕竟权限配置这东西写错了不像代码报错那么明显它往往是“看似正常但某个功能悄悄失灵”的状态排查起来特别费劲。只有在验证充分、规则明确的情况下批量切换才比较稳妥。