1. 从一次线上误删事故说起为什么Redis6的ACL值得单独拿出来讲几年前我还在负责一个中小型电商系统的缓存层维护当时整个团队对Redis的使用方式非常“粗放”——所有服务共用同一个默认用户密码就是配置文件里那一行requirepass谁拿到密码谁就是超级管理员能执行任何命令。某天凌晨一个刚入职的同事在排查一个数据不一致问题时本来只想清理某个业务前缀下的几个key结果手一抖敲成了FLUSHALL整个实例的缓存瞬间被清空。虽然缓存可以重建但那一刻数据库被打穿接口响应时间从几十毫秒飙到几秒订单服务直接告警。事后复盘大家最痛的点不是“人犯错”而是Redis根本没有给我们任何细粒度的权限约束手段——那个同事的账号理论上就不该拥有FLUSHALL这种危险命令的执行权限。这件事之后我一直在关注Redis在权限管理上的演进。在Redis6之前官方的安全手段基本只有两样requirepass设置一个全局密码以及通过rename-command把危险命令改名或禁用。前者是“一把钥匙开所有门”后者则是“把门焊死”运维起来极其别扭——你没法针对不同业务分配不同权限也没法在不重启的情况下动态调整。Redis6引入的ACLAccess Control List访问控制列表彻底改变了这个局面它把“用户”这个概念真正带进了Redis让每个连接可以绑定到不同的用户每个用户拥有独立的密码、独立的命令权限、独立的key访问范围。这篇文章我想聊的就是Redis6 ACL这套安全策略到底怎么用、背后的设计逻辑是什么、实际落地时有哪些坑。它适合已经在用Redis、并且开始关心“谁能操作什么”的运维和开发同学也适合那些还在用单密码裸奔、想升级权限体系但不知道从哪下手的朋友。我会从整体设计思路讲到具体命令再到生产环境的实操配置和排查技巧尽量把每个“为什么这么设计”讲透让你看完能直接在自己的环境里落地一套可用的权限方案。2. ACL整体设计与核心概念拆解2.1 为什么是“用户规则”而不是继续加密码要理解ACL的设计先得理解Redis之前的权限模型有多单薄。在Redis6之前一个实例只有一个密码所有客户端连上来都是同一个身份权限完全一致。这种模型在单团队、单业务的小规模场景下勉强够用但一旦实例被多个业务线共享问题就来了A业务只需要读写自己的缓存key却因为共用密码而拥有了删除任意key、甚至执行CONFIG SET改配置的能力。这本质上是一个权限过度授予的问题。Redis6的ACL选择用“用户user”作为权限的载体而不是继续在密码上做文章原因很直接密码只能回答“你能不能进来”回答不了“你进来之后能干什么”。用户这个概念天然可以承载多维度的权限描述——密码、命令白名单/黑名单、key模式、频道模式。这样一来权限的最小粒度就从“整个实例”细化到了“某个用户能碰哪些key、能跑哪些命令”。我在实际项目里最直观的感受是以前给一个新业务开缓存权限只能新建实例或者共用密码现在直接ACL SETUSER建一个用户把它的key前缀和命令范围卡死几分钟就能搞定而且互不干扰。2.2 ACL的四个核心维度ACL对用户的权限描述可以拆成四个维度理解了这四个维度后面所有命令都是它们的组合。第一个维度是认证方式也就是这个用户怎么证明自己是自己。可以是明文密码password也可以是密码的SHA256哈希#hash还可以配置多个密码。第二个维度是命令权限控制这个用户能执行哪些命令可以精确到子命令比如只允许GET不允许SET或者只允许CLIENT LIST不允许CLIENT KILL。第三个维度是key权限通过模式匹配限定用户能访问哪些key比如只允许操作order:*开头的key。第四个维度是频道权限针对发布订阅场景限定用户能订阅或发布哪些频道。这四个维度组合起来就形成了一个用户的完整权限画像。我习惯把它类比成公司门禁系统认证方式是门禁卡命令权限是你能进哪些楼层key权限是你能开哪些房间的门频道权限是你能收听哪些广播。这个类比虽然不严谨但能帮新手快速建立直觉。2.3 默认用户与ACL的兼容性设计Redis6在引入ACL的同时必须保证对老版本的兼容否则升级会变成灾难。它的做法是保留一个名为default的默认用户所有没有显式认证的客户端都自动以default用户身份运行。在默认配置下default用户拥有所有命令、所有key、所有频道的权限并且不需要密码——这跟Redis5的行为完全一致。也就是说你升级到Redis6之后如果什么都不配置业务代码一行不用改照常运行。这个兼容性设计非常关键它意味着ACL的落地可以是渐进的你可以先让default用户继续裸奔同时新建几个受限用户给新业务用等验证稳定后再逐步收紧default的权限最后把它彻底禁用。我在迁移一个老系统时就是这么干的前后花了两周时间业务方完全无感知。这里要提醒一句default用户虽然方便但生产环境最终一定要处理它——要么给它设一个强密码要么直接ACL SETUSER default off禁用它否则ACL形同虚设。3. ACL核心命令与实操要点详解3.1 用户管理命令从创建到删除的完整链路ACL的用户管理命令以ACL为前缀最常用的是ACL SETUSER它既能创建用户也能修改用户。这个命令的语法是ACL SETUSER username [规则...]规则可以一次给多个也可以分多次追加。我个人的习惯是创建用户时一次性把规则写全这样配置意图清晰也方便后续用ACL LIST导出备份。创建用户的基本流程是这样的先ACL SETUSER app_order on OrderPass123 ~order:* read write -dangerous这条命令做了几件事——开启用户、设置密码、限定只能访问order:开头的key、允许读写类命令、禁止危险命令。然后可以用ACL WHOAMI确认当前连接的身份用ACL LIST查看所有用户的完整规则用ACL GETUSER app_order查看单个用户的详情。删除用户用ACL DELUSER username注意default用户不能被删除只能被禁用。这里有个细节值得展开ACL SETUSER的规则是增量叠加的不是覆盖。也就是说如果你先执行了ACL SETUSER app_order read再执行ACL SETUSER app_order write最终这个用户同时拥有读写权限。如果你想重置某个用户的规则得先用ACL SETUSER app_order reset清空再重新配置。我踩过一次坑以为再次SETUSER会覆盖旧规则结果旧权限一直残留排查了半天才发现是增量叠加的机制。3.2 命令权限的精细控制从命令类别到子命令命令权限是ACL里最灵活也最容易配错的部分。Redis把命令分成了若干类别用符号引用比如read、write、admin、dangerous、fast、slow等。你可以用read允许整个读类别也可以用-dangerous禁止所有危险命令。类别的好处是省事坏处是粒度粗——比如write里既有安全的SET也有能清库的FLUSHDB。所以生产环境我一般会采用“类别打底单命令微调”的策略。先用read write给一个基础范围再用-FLUSHALL -FLUSHDB -KEYS -CONFIG把明确的危险命令摘掉。这里要注意命令的大小写不敏感get和GET效果一样但为了可读性建议统一大写。另外子命令的控制用|分隔比如config|get只允许查询配置-config|set禁止修改配置。这个特性在需要给监控系统开只读配置权限时特别有用。还有一个容易被忽略的点命令权限的判定顺序。当用户同时被允许和禁止某个命令时Redis的规则是“后配置的生效”。比如你先all再-GET那GET就是被禁止的反过来先-GET再allGET又会被允许。这个顺序依赖在多人协作维护ACL时很容易出问题我的建议是永远把禁止规则放在允许规则之后并且用ACL GETUSER确认最终生效的权限不要凭记忆。3.3 key权限的模式匹配通配符的边界与陷阱key权限用~符号声明后面跟模式。~order:*表示只能访问order:开头的key~*表示所有key~后面什么都不跟则表示禁止访问任何key。模式匹配用的是glob风格的通配符*匹配任意字符?匹配单个字符[abc]匹配字符集。这套语法跟Redis的KEYS命令一致上手不难但有几个边界要注意。第一模式匹配是针对完整key名的不是前缀匹配。~order:*能匹配order:123但匹配不了myorder:123因为后者不以order:开头。第二多个key权限是“或”的关系~order:* ~user:*表示两类key都能访问。第三key权限对命令的约束是全局的也就是说即使用户有GET命令权限如果目标key不在它的key模式范围内命令依然会被拒绝报错信息是NOPERM。这个报错在实际排查时很有用看到NOPERM基本就能定位到是key权限或命令权限的问题。我在配置key权限时有个经验尽量用业务前缀冒号的模式比如~cache:order:*而不是宽泛的~*order*。后者看起来方便实际上会把user:order_history这种不相关的key也放进来权限边界就模糊了。另外如果业务key的命名本身不规范比如有的用下划线有的用冒号那ACL配置会非常痛苦这时候应该先推动key命名规范再上ACL。3.4 频道权限与发布订阅的权限隔离频道权限用符号声明针对的是发布订阅Pub/Sub场景。news:*表示只能订阅或发布news:开头的频道。这个维度在纯缓存场景下用得少但在用Redis做消息总线的系统里就很重要了。比如一个系统里既有订单消息频道又有用户消息频道你肯定不希望订单服务能往用户频道里发消息。频道权限的配置逻辑和key权限类似也是模式匹配、多个“或”关系。需要注意的是频道权限同时约束PUBLISH和SUBSCRIBE你不能只允许订阅不允许发布或者反过来除非用命令权限单独控制PUBLISH和SUBSCRIBE命令。实际项目里我一般会把频道权限和命令权限配合使用用order:*限定频道范围再用subscribe publish允许这两个命令这样权限边界就很清晰了。4. 生产环境ACL落地的完整实操流程4.1 配置文件持久化让ACL规则不丢用ACL SETUSER在运行时创建的用户默认只存在于内存里重启就没了。这在生产环境是致命的所以必须把ACL规则持久化。Redis提供了两种方式一种是在redis.conf里用user指令声明另一种是用ACL SAVE把当前规则写入ACL文件再在配置里用aclfile指定这个文件。我推荐用aclfile的方式原因是它支持运行时ACL SAVE和ACL LOAD运维起来更灵活。具体做法是在redis.conf里加一行aclfile /etc/redis/users.acl然后执行ACL SAVE当前所有用户规则就会写入这个文件。之后每次修改用户记得再ACL SAVE一次。这里有个坑如果aclfile指定的文件不存在Redis启动会报错所以第一次部署时要先手动创建空文件或者先执行一次ACL SAVE。还有一种情况是配置文件和aclfile同时存在user定义这时候aclfile的优先级更高配置文件里的user会被忽略。这个行为在排查“为什么我改了配置文件没生效”时经常是元凶记住就行。4.2 分阶段迁移从单密码到多用户的平滑过渡直接给一个正在运行的生产实例上ACL风险很高因为一旦配错业务可能瞬间不可用。我的做法是分四个阶段推进。第一阶段保持default用户不变新建受限用户给新业务用老业务继续走default。这个阶段的目标是验证ACL规则本身没问题同时让团队熟悉命令。第二阶段给default用户设置密码但保留全部权限然后逐步把老业务的连接配置改成带密码。第三阶段收紧default用户的权限比如去掉dangerous观察是否有业务报错。第四阶段禁用default用户所有业务都用自己的专属用户。每个阶段之间至少观察一天重点看Redis的慢日志和业务侧的NOPERM报错。我在第二阶段遇到过一个问题某个老服务的连接池配置里密码是写死的改密码后没同步更新导致连接全部失败。所以改default密码前一定要先梳理清楚有哪些客户端在用别漏了。4.3 一个完整的用户配置示例与验证假设我们要给订单服务配一个用户需求是只能访问order:和cart:开头的key允许读写命令禁止所有危险命令密码用强密码。配置命令如下ACL SETUSER app_order on Str0ngPss2024 ~order:* ~cart:* read write -dangerous -FLUSHALL -FLUSHDB -KEYS -CONFIG ACL SAVE配完之后必须验证验证分三步。第一步用ACL GETUSER app_order看规则是否符合预期重点确认keys和commands两栏。第二步用redis-cli --user app_order --pass Str0ngPss2024连上去执行SET order:1 test应该成功执行SET other:1 test应该报NOPERM执行FLUSHALL也应该报NOPERM。第三步用ACL LOG查看被拒绝的命令记录这个命令会列出最近所有权限拒绝事件是排查权限问题的利器。注意ACL LOG默认只保留最近的128条记录高并发场景下可能很快被冲掉排查时要及时查看或者用ACL LOG 数量调整保留条数。5. 常见问题与排查技巧实录5.1 NOPERM报错的定位思路NOPERM是ACL最常见的报错格式一般是NOPERM this user has no permissions to run the xxx command或者NOPERM this user has no permissions to access one of the keys used as arguments。看到这个报错第一步是用ACL WHOAMI确认当前连接用的是哪个用户很多时候问题出在客户端根本没认证成功还在用default或者匿名身份。第二步用ACL GETUSER 用户名看这个用户的命令权限和key权限对照报错里的命令名和key名基本就能定位。我整理了一个排查速查表覆盖了几种典型场景报错信息关键词可能原因排查动作no permissions to run the command命令权限不足检查ACL GETUSER的commands栏确认命令或类别是否被允许no permissions to access one of the keyskey权限不足检查keys栏的模式是否覆盖目标key注意前缀和通配符no permissions to access one of the channels频道权限不足检查channels栏确认发布订阅的频道在范围内WRONGPASS密码错误或用户未开启检查用户是否on密码是否正确注意和#的区别连接直接被拒绝用户被禁用或不存在用ACL LIST确认用户存在且状态为on5.2 密码配置的两种写法与选择ACL支持明文密码和哈希密码两种写法。明文用前缀比如mypassword哈希用#前缀后面跟密码的SHA256值。明文的好处是直观配置时不容易出错坏处是ACL LIST和ACL GETUSER会把密码明文显示出来任何能执行这些命令的人都能看到密码。哈希的好处是即使规则泄露攻击者也无法直接得到明文密码坏处是配置时得先算哈希稍微麻烦一点。我的建议是生产环境用哈希尤其是多人能访问Redis的团队。算哈希可以用echo -n mypassword | sha256sum把结果前面加#填进去。这里有个细节echo -n的-n不能省否则会把换行符也算进哈希导致密码对不上。我见过有人因为这个细节排查了一下午。5.3 ACL规则冲突与优先级问题前面提过命令权限是“后配置生效”但实际场景里还有更复杂的冲突。比如一个用户同时属于read和-GET那GET到底能不能用答案是看配置顺序后配的-GET生效所以GET被禁止。但如果先-GET再readGET又会被read重新允许。这种顺序依赖在规则多的时候非常容易出错。我的应对策略是规则分层先配类别级别的允许read write再配类别级别的禁止-dangerous最后配单命令级别的微调-FLUSHALL config|get。这样从粗到细顺序固定不容易乱。另外每次修改后用ACL GETUSER确认最终结果不要凭记忆推断。如果团队人多建议把ACL规则纳入版本管理每次变更走review避免有人随手改了一条规则把权限放大。5.4 性能影响与监控要点很多人关心ACL会不会拖慢Redis。实测下来ACL的权限检查是在命令执行前做的一次规则匹配开销很小在常规QPS下基本感知不到。但如果一个用户的key权限模式特别多、特别复杂每次命令都要遍历所有模式做匹配理论上会有额外开销。我的经验是单个用户的key模式控制在10个以内超过这个数量就该考虑是不是key命名设计有问题或者该拆实例了。监控方面除了常规的Redis指标建议关注ACL LOG里的拒绝事件数量。如果某个用户的拒绝事件突然飙升要么是业务代码改了命令用法要么是有人在试探权限都值得警惕。可以用ACL LOG 数量配合定时任务把拒绝日志采集出来接入告警系统。另外INFO里的acl相关字段也能反映一些状态虽然信息不多但聊胜于无。6. 我在实际落地中总结的几条经验ACL这套东西刚上手时命令不少但真正用起来核心就是SETUSER、GETUSER、LIST、SAVE、LOG这几个。我最大的体会是ACL的价值不在于技术多复杂而在于它逼着团队去梳理“谁该有什么权限”。以前共用密码时没人说得清某个服务到底需要哪些命令上了ACL之后每个用户的规则就是一份权限文档反而让架构更清晰了。另一个经验是别追求一步到位。我见过有团队想一次性把所有用户配好、把default禁掉结果业务大面积报错回滚都来不及。ACL的迁移一定是渐进的先兼容、再收紧、最后禁用每一步都留观察期。还有ACL SAVE这个动作一定要养成习惯改完就存别等重启时才发现规则丢了。最后分享一个小技巧如果你不确定某个用户该配哪些命令可以先用all让它跑一段时间然后用ACL LOG统计它实际用到的命令再据此收敛权限。这个“先放后收”的思路在权限治理里很实用比拍脑袋配规则靠谱得多。