说实话给 Redis 设置密码这件事是我见过的最容易被低估的运维操作。很多人觉得不就是在配置文件里加一行 requirepass 吗有什么好讲的。可我在排查过的生产事故里至少有一半的 Redis 被入侵案例都源于“觉得加了密码就行”或者“觉得反正内网没事”的侥幸心理。今天这篇就围绕 Redis 设置密码把配置文件、动态改密、主从复制、Docker、Windows、ACL 这些场景下的正确做法和常见坑都捋一遍希望给正在部署 Redis或者准备完善 Redis 安全配置的同学一点参考。Redis 本身默认是裸奔的设置密码只是安全的第一步但这一步有没有走对直接决定了你的服务器会不会变成别人的挖矿矿场。1. 为什么Redis密码这件事被低估了1.1 没有密码的Redis有多危险我见过不少刚学 Redis 的同学下载完 Redis 后在本地跑一跑觉得“反正是自己电脑配密码没必要”。这个想法在本地开发环境问题不大但一旦 Redis 被部署到云服务器、公司测试机、或者任何绑定了非本地网卡的机器上情况就完全不同了。Redis 有一个很危险的特性如果以 root 权限启动且没有配置密码攻击者一旦能通过网络访问到你的 6379 端口就可以利用 Redis 写文件的机制把 SSH 公钥写到服务器的authorized_keys文件里或者往定时任务目录里写入恶意脚本。这已经不是“数据被删”的程度了而是直接拿到服务器权限。网上流传的很多 Redis 入侵案例攻击流程都是一条命令扫全网Open 的 6379 端口全部尝试一遍先执行CONFIG SET dir /root/.ssh/再执行CONFIG SET dbfilename authorized_keys然后用SET写入公钥。整个过程不需要任何认证一条脚本就能完成。所以设置密码真正要防的不是“有人偷看我的缓存数据”而是“有人通过 Redis 直接拿下我的整台服务器”。这个威胁级别是完全不同的。1.2 密码、bind与protected-mode三者的关系很多人不知道Redis 在默认配置下其实是有一定“保护”的。默认的bind配置是127.0.0.1也就是只允许本机访问再加上protected-mode yes如果配置完全处于默认状态Redis 会拒绝来自非本机的连接。这也是为什么有些新手把 Redis 装好后云服务器从外部怎么都连不上原因就在这里。保护是一层一层叠的bind 127.0.0.1只监听本机回环地址。protected-mode yes当 Redis 没有任何显式的 bind 配置或密码配置时会拒绝非本机 IP 的连接。requirepass所有连接都必须先通过 AUTH 认证才能执行命令。问题在于很多部署教程为了让外部能够访问 Redis会建议把 bind 改成0.0.0.0或者把 protected-mode 关闭却忘了同时设置密码。这就等于把前面两道保护门都拆了最后一道门也没上锁。我见过的最常见的组合是bind 0.0.0.0protected-mode no 没有 requirepass。这种配置暴露在公网上存活时间不会超过几分钟脚本扫描器会在极短时间内发现这个端口并展开攻击。正确的逻辑应该是如果你确实需要让 Redis 被非本机访问那么密码就是最后的底线。bind 可以放宽但密码必须设而且不能用弱密码。2. 最基础的改密方式redis.conf配好再重启2.1 先找准你的redis.conf设置密码最传统的方式就是修改 redis.conf 配置文件。但实际操作里第一步绊倒不少人的反而是“找不到配置文件”。如果你是用包管理器装的 Redis比如在 Ubuntu 上通过 apt 安装配置文件一般位于/etc/redis/redis.conf。如果是手动编译安装配置文件通常在解压目录下比如/opt/redis-7.0.12/redis.conf。Windows 下官方其实不提供原生二进制你下载到的 Windows 版 Redis 解压后可能根本没有 redis.conf只有一个redis.windows.conf或者干脆什么都没有这个我在后面的 Windows 章节单独讲。如果你实在找不到可以用redis-server --version先确认版本再尝试在 Redis 安装目录里找。或者直接启动时用redis-server /全路径/redis.conf来指定这样最稳妥因为这就相当于告诉 Redis“我的配置文件就是这份你加载它”。2.2 requirepass的正确写法与引号陷阱找到 redis.conf 后搜索requirepass关键词。老版本的配置文件里有一条被注释掉的示例# requirepass foobared把这行注释去掉改成你自己的密码即可requirepass my-strong-password这里我要重点强调一个很多人踩过的坑Redis 的配置文件不会自动解析引号。如果你写成requirepass 123456那么实际的密码会包含双引号也就是说密码是123456而不是123456。你连接的时候必须输入包括引号在内的完整字符串一旦忘记就永远提示密码错误。这一点和很多编程语言的配置文件行为不一样非常反直觉。所以正确做法是直接写裸密码不要在密码两侧加引号。如果密码里面包含空格、#、这类特殊字符也直接写就行#在 requirepass 这一行不会被视为注释的开始整行除了开头的指令外都是密码。但我个人不建议用太复杂的特殊字符因为后面涉及 URL 连接串、命令行参数时非常容易踩转义坑后面会细说。2.3 重启后的验证流程AUTH命令与NOAUTH报错改完配置后需要重启 Redis 才能生效。重启分两种方式如果你是通过 systemd 管理的执行systemctl restart redis如果你是手动启动的进程先redis-cli shutdown关掉再用指定配置文件启动。启动完成后可以用redis-cli验证密码是否生效redis-cli 127.0.0.1:6379 keys * (error) NOAUTH Authentication required.看到NOAUTH Authentication required.说明密码已经生效Redis 在未认证状态下拒绝执行命令。然后执行认证127.0.0.1:6379 auth my-strong-password OK 127.0.0.1:6379 keys * (empty array)AUTH 返回 OK就可以正常操作了。这里有一个小细节值得注意PING命令在未认证状态下也可以得到PONG响应。很多人在排查“密码有没有生效”的时候习惯先用redis-cli ping测试结果发现返回 PONG就误以为密码没生效或者连接没问题。实际上PING是被 Redis 特殊放行的命令真正的判断标准应该是执行KEYS、GET、SET这类业务命令时是否提示 NOAUTH。这个细节在自动化监控脚本里尤其重要很多健康检查探活脚本如果只发 PING就测不出密码认证是否真实生效。3. 生产环境不重启改密CONFIG SET与REWRITE的组合拳3.1 动态改密的命令与刷新逻辑生产环境的 Redis 通常承载着大量线上流量为了改一个密码重启服务代价有点大。Redis 提供了运行时动态修改配置的能力核心是CONFIG SET命令。先通过命令行登录redis-cli -a 旧密码然后执行CONFIG SET requirepass 新密码执行成功后新密码会立即生效。此时旧密码失效当前这个已经认证过的连接不会因为改密而断开但新发起的连接必须使用新密码才能通过认证。这里有一个运维上很容易忽略的点动态改密之后依赖旧密码的所有客户端都会开始报认证失败。尤其是线上有多个应用服务共用同一个 Redis 的时候你在命令行敲下CONFIG SET的那一刻所有没有及时更新的客户端就已经开始报错了。所以生产环境改密前一定要先梳理清楚到底有多少客户端在连接最好在低峰期操作。3.2 CONFIG REWRITE持久化为什么配置文件里没有requirepass也会被追加CONFIG SET只修改运行时配置不会同步写回 redis.conf。也就是说如果此时 Redis 重启密码会恢复到旧配置甚至可能恢复成没有密码的状态。要让新密码在重启后依然生效必须再执行一条命令CONFIG REWRITE这条命令会把当前运行时的配置重写到 redis.conf 文件。如果原配置文件里根本没有 requirepass 这一行REWRITE 也会自动把它追加进去。这是一个很省心的设计不需要你手动去编辑文件。但注意REWRITE 有它的边界条件如果启动 Redis 时没有指定配置文件而是通过纯命令行参数启动的那么执行CONFIG REWRITE会返回错误因为 Redis 不知道要把配置写回哪里。这种情况下你只能手动维护配置文件或者干脆把CONFIG SET 配置文件修改两步都做了。我个人的习惯是即使通过CONFIG SET动态改了密码也会接着修改磁盘上的 redis.conf保证两者一致并且顺手把配置文件的权限收紧到只有 redis 用户可读。因为密码是以明文形式存在配置文件里的这个文件如果权限不当等于把密码贴在门上。3.3 改密后老连接不会掉线一个容易被忽略的安全细节Redis 的认证状态是连接级别的。一个连接只要通过了 AUTH除非这个连接被主动关闭或者调用了CLIENT KILL否则服务端不会因为全局密码被修改而主动踢掉这个已经认证过的连接。这个特性的正面意义是生产改密期间已建立的连接不会瞬间中断影响面能被控制。但也意味着如果你是因为安全事件而紧急改密——比如发现密码泄露——那么光改密码是不够的攻击者已经建立的连接依然可以继续操作数据。正确做法是改完密码后再执行CLIENT KILL -a 客户端的地址或者更粗暴一点把所有连接全部杀掉CLIENT KILL TYPE normal这样所有普通连接都会被强制断开客户端必须用新密码重新连接。这在紧急救援场景下非常关键。4. 不同部署形态下的密码配置Windows、Docker与主从复制4.1 Windows上跑Redis配置文件从哪来Windows 不是 Redis 官方支持的主战场官方推荐是在 Linux 上部署。但 Windows 开发者本地调试确实有需求于是社区里流行的是微软维护过的旧版 Redis for Windows或者后来的一些社区移植版本。这些 Windows 版 Redis 的解压包里往往只有redis-server.exe和redis-cli.exe不一定带 redis.conf。这种情况下有两种办法可以设置密码第一种在 Redis 安装目录下手动创建一个 redis.conf内容至少包含requirepass my-password port 6379 bind 127.0.0.1然后用指定配置文件的模式启动redis-server.exe C:\redis\redis.conf第二种直接通过命令行参数指定不需要配置文件redis-server.exe --requirepass my-password --port 6379命令行方式的好处是简单直接坏处是密码会出现在进程命令行里用tasklist或者任务管理器、Process Explorer 之类工具查看时能直接看到。这显然不适合生产环境本地临时调试倒还能接受。我在本地 Windows 上给 Redis 设置密码时更倾向于第一种方式因为后续改密只需要改文件再重启不用记一长串启动参数。Windows 上的 redis.conf 如果没有现成的模板直接从 Linux 版的示例配置里把 requirepass 相关段落抄过来就够用了。4.2 Docker部署Redis时挂载配置与命令行参数Docker 部署 Redis 是目前最常见的场景之一。官方 Redis 镜像本身没有直接提供类似于 MySQL 的MYSQL_ROOT_PASSWORD那种环境变量来设置密码。有些第三方镜像会自定义环境变量但官方镜像不会这是很多从 MySQL Docker 迁移过来的同学最容易踩的坑。官方镜像推荐的方式有两种。第一种用 docker run 的 command 参数直接指定docker run -d --name my-redis \ -p 6379:6379 \ redis:7.2 \ redis-server --requirepass my-password注意镜像名后面跟的那一串redis-server --requirepass my-password会覆盖镜像的默认启动命令。第二种把宿主机上的 redis.conf 挂载到容器内然后以指定配置文件启动docker run -d --name my-redis \ -p 6379:6379 \ -v /data/redis/redis.conf:/usr/local/etc/redis/redis.conf \ redis:7.2 \ redis-server /usr/local/etc/redis/redis.conf宿主机上的/data/redis/redis.conf里写好requirepass my-password这样容器启动时就会加载。配置文件挂载的方式更符合运维规范因为密码和参数不会直接裸露在 docker run 的命令行里也不会被 docker inspect 看到完整命令时直接暴露。如果是 docker-compose可以这样写services: redis: image: redis:7.2 container_name: my-redis ports: - 6379:6379 command: [redis-server, --requirepass, my-password]或者services: redis: image: redis:7.2 container_name: my-redis ports: - 6379:6379 volumes: - /data/redis/redis.conf:/usr/local/etc/redis/redis.conf command: [redis-server, /usr/local/etc/redis/redis.conf]我比较推荐 docker-compose 配置卷挂载的方式因为后面如果要加maxmemory、持久化策略等更多配置直接在 redis.conf 里改就行不需要频繁修改 compose 文件。4.3 主从复制必须同步设置的masterauth与sentinel auth-passRedis 主从复制场景下密码设置比单机要复杂一个维度因为存在两条认证链路客户端访问 Redis 时需要requirepass。从节点连接主节点做复制时也需要密码。很多人在主节点设置了密码从节点的replicaof也配置好了但从节点一直处于无法同步的状态。看从节点日志会发现反复出现类似MASTER - REPLICA sync started之后连接失败或者一直握手不成功的日志。原因就是从节点没有配置主节点的认证密码。Redis 从节点连接主节点时需要使用masterauth参数masterauth my-password在主节点上设置的requirepass my-password只影响普通客户端连接从节点作为特殊客户端必须通过masterauth提供密码才能通过主节点的认证。如果部署了 Redis Sentinel还需要在 sentinel.conf 里为每个监控的主节点名称配置认证信息sentinel auth-pass mymaster my-password这个配置的作用是让 Sentinel 能够向主节点和从节点发起命令检查它们的状态。如果 Sentinel 没有配置正确的密码它会一直报告sdown也就是主观下线但实际上节点是活着的。从节点自身要不要也设置requirepass如果业务方可能会直接读从节点或者 Sentinel 需要访问从节点那么从节点也应该设置requirepass。但要注意主节点和从节点之间的复制不会因为从节点设置了requirepass而受影响因为复制连接走的是内部机制不受普通客户端的认证限制。5. 密码之上的一层Redis 6.0的ACL用户权限体系5.1 为什么需要ACL从“一把钥匙”到“一套权限”requirepass模式下所有人拿到的都是同一个密码也自然拥有全部权限。这在多业务线共用 Redis 的团队里会产生一个问题A 业务的开发人员拿到密码后可以顺手执行FLUSHALL、KEYS *、CONFIG SET这些高危命令不小心就把整个 Redis 干掉了。Redis 6.0 正式引入了 ACLAccess Control List它把“密码认证”和“权限控制”拆成了两个维度。简单理解就是requirepass是一把万能钥匙能开所有门ACL 是一套门禁系统不同用户拿着不同钥匙只能进自己该进的门比如只能访问某些 key 前缀只能执行某些命令。到了 Redis 7.xACL 已经非常成熟新部署的实例我更建议直接思考 ACL 方案而不是继续沿用传统的全局 requirepass。5.2 常用ACL命令与配置文件写法先看一个最常用的 ACL 命令给 default 用户设置密码。Redis 6.0 之后requirepass本质上是在修改默认用户 default 的密码。你也可以用 ACL 命令显式来做这件事ACL SETUSER default on my-new-password ~* all拆解一下这条命令的含义default指定用户名。on启用该用户off表示禁用。my-new-password为该用户设置密码。~*允许访问所有 key。all允许执行所有命令。等价地在 redis.conf 里可以写成user default on my-new-password ~* all这样写和requirepass my-new-password的效果等价。但如果你同时配了requirepass和user default on xxx配置文件语法会冲突Redis 甚至可能直接拒绝启动。因为requirepass本身就是改 default 用户密码的快捷方式两者不能同时写两个不同的密码。再举一个创建独立用户的例子。比如我要创建一个只允许读写cache:*前缀 key、只允许执行GET、SET、TTL等命令的用户供应用服务使用ACL SETUSER appuser on app-password-9527 ~cache:* get set ttl exists expire如果这个用户连CONFIG、KEYS、FLUSHALL这类高危命令都用不了那么即使密码泄露到别人手里破坏力也极其有限。其他常用命令ACL LIST查看所有用户的权限配置。ACL WHOAMI查看当前连接属于哪个用户。ACL GETUSER appuser查看 appuser 的详细权限。ACL DELUSER appuser删除一个用户。ACL LOG查看被拒绝执行命令的日志排查应用为什么突然权限不够。5.3 迁移到ACL时要小心的几个问题从requirepass迁移到 ACL有几种情况容易踩坑。第一老版本客户端可能不兼容带用户名的 AUTH 语法。Redis 6.0 起AUTH 命令支持两种写法AUTH password AUTH username password旧版客户端如果只支持单参数 AUTH遇到默认用户以外的多用户时可能认证不成功。好在 Redis 6.0 默认还是 RESP2 协议多数客户端走AUTH username password也没问题但如果你用的是非常古老的客户端库迁移前最好先确认版本兼容性。第二default 用户默认权限不要轻易改成off。Redis 内部有一些后台任务和复制链路比如从节点的复制、持久化相关操作需要特殊的内部权限。你把 default 用户整个禁用掉可能会导致意想不到的问题。更稳妥的做法是保留 default 用户但设置一个强密码同时用ACL SETUSER创建专用用户给应用使用。第三ACL 规则与持久化的关系。通过ACL SETUSER修改的用户配置同样需要通过CONFIG REWRITE写入 redis.conf 才能在重启后保留。如果你懒得写命令也可以直接把 user 规则行写在 redis.conf 里启动时就会加载。6. 改密之后的连带影响客户端、工具与运维习惯6.1 redis-cli连接时怎么避免密码泄露命令行里直接带密码是很多同学常用的方式redis-cli -h 127.0.0.1 -p 6379 -a my-password但这会有一个明显的问题密码会出现在 shell 的历史记录里也会出现在系统进程列表中任何一个能执行ps aux的用户都能看到。Redis 自己也给过警告提示在命令行使用密码是不安全的。推荐的方式有两种。第一种使用环境变量export REDISCLI_AUTHmy-password redis-cli -h 127.0.0.1 -p 6379这样密码不会出现在ps的输出里。第二种使用--askpass选项交互式输入密码redis-cli -h 127.0.0.1 -p 6379 --askpass它会提示你输入密码不会留下命令行痕迹也不会写进 shell history。对经常手动排查问题的同学来说这个选项非常实用。我在脚本里通常都是用环境变量的方式因为在自动化环境里交互式输入不方便。6.2 可视化客户端与Spring Boot里的密码配置图形化客户端方面用得比较多的有 Redis Desktop ManagerRDM和它的开源替代 Another Redis Desktop Manager还有官方出品的 RedisInsight。它们连接时的逻辑都类似填上 Host、PortAuth 或者 Password 一栏填密码连接测试能过就行。这里有一个实际经验如果你在 redis.conf 里设置了密码后用 RDM 连接时提示认证失败先检查密码两端有没有意外的空格或引号。因为前面说过redis.conf 不解析引号很多人在配置文件里写了requirepass abc然后在图形界面里填的是abc当然连不上要填abc才行。这个细节排查起来非常隐蔽肉眼很难发现。Java Spring Boot 项目里连接 Redis 时配置如下spring: redis: host: 127.0.0.1 port: 6379 password: my-password如果你的密码包含等特殊字符使用 Spring Data Redis 的 URL 方式连接时还需要对密码做 URL 编码否则redis://:passwordhost:port这种格式会把误认为主机名分隔符。所以密码设计尽量避开 URL 保留字符能省掉很多麻烦。6.3 密码轮换的运维清单最后聊一下密码轮换。很多公司要求定期改 Redis 密码但改密如果只执行一条CONFIG SET往往会在几分钟内引发一堆线上告警。我建议按下面这个顺序操作先梳理依赖 Redis 的客户端清单包括应用服务、工具脚本、监控系统、数据同步任务。在非业务高峰窗口先修改配置文件里的 requirepass 或 ACL user 规则。通过CONFIG SET动态修改密码或者直接重启 Redis 使配置生效。立即更新所有客户端的连接密码。如果是主从架构先改从节点配置再改主节点按从到主的顺序逐个推进。这里的原因是从节点配置masterauth错误时影响的只是复制链路而主节点改密错误会直接影响线上读写。改完后用redis-cli --askpass验证新密码用旧密码验证是否确实失效。检查 Redis 日志重点看有没有WRONGPASS invalid username-password pair之类的报错这能帮你找到漏改密码的客户端。我在实际运维中最深刻的体会是设置 Redis 密码从来不是改一个参数那么简单它牵扯到客户端、监控、复制、权限模型一整条链路。但正因为如此把密码这件事做规范了整个 Redis 实例的稳定性也就踏实了一大半。接下来你在操作时可以先用本地环境把配置文件、ACL、可视化客户端整套流程走一遍再慢慢推广到生产。它们之间踩过的坑基本都是相通的。