开头就直接进入主题,别绕弯子。Redis设置密码这件事本身不大,但你要是没搞明白它的认证机制,一旦踩到“改完密码连不上主从了”、“容器环境变量配了没生效”、“redis-cli -a被同事看到”这类坑,真的会被折腾得够呛。
这篇文章我就把Redis设密码的三种常用场景——配置文件、Docker容器、命令行——完整拆开来讲。每种都会说清楚“为什么这么做”、“操作里有哪些隐藏细节”、“坑在哪里”。适合三类人看:刚部署完Redis不知道怎么写密码保护的生产环境维护者、用Docker跑Redis但没有系统化梳理过参数配置的朋友、以及面试前想搞懂requirepass和ACL区别的开发者。
1. 先搞清楚:Redis的密码认证到底是怎么回事
1.1 不设密码的真实风险
很多人觉得“Redis嘛,我内网用的,设什么密码”。我可以负责任地告诉你,这个想法相当危险。Redis默认监听在6379端口,如果服务器有公网IP或者云安全组规则没收紧,任何人都可以扫到你的端口,然后直接redis-cli -h 你的IP ping一下,如果返回PONG,恭喜你,你的Redis等于裸奔。
更严重的是,Redis支持很多危险命令。攻击者不需要密码就能执行FLUSHALL清空你所有数据,或者通过CONFIG SET dir、CONFIG SET dbfilename结合SAVE把恶意数据写到服务器上,甚至可能配合其他漏洞直接拿系统权限。所以设密码这步,不是“可选优化”,而是“基础安全配置”。
1.2 requirepass与ACL:两代认证机制的区别
Redis的密码认证经历过两个阶段。Redis 6以前,大家用的都是requirepass指令,思路特别简单:服务器端设置一个全局密码,客户端连进来之后必须发送AUTH <密码>,认证通过才能执行操作。这就好比整栋楼只有一个大门钥匙,谁拿着都能进所有房间。
Redis 6以后引入了ACL(Access Control List,访问控制列表),你不再只能设一个全局密码,而是可以给不同用户分配不同权限,比如某个用户只能读不能写,某个用户只能访问指定key,还能限制命令类别。“用户密码权限”三个维度解耦,比单一requirepass灵活得多,生产环境也更推荐。
不过这不意味着requirepass就没用了。小项目、内网工具、快速部署场景,requirepass依然是最高效的选择。而且ACL的默认用户(default user)在配置上跟requirepass有对应关系——你设置了requirepass,其实就是在给default用户设密码。理解这一点,后面看配置文件的逻辑就顺了。
2. 配置文件方式:最经典也最容易踩坑的场景
2.1 找到并修改redis.conf
这里先停一下,很多人第一步就卡住了。Redis安装方式不同,配置文件位置也不一样。源码编译安装,配置一般在/usr/local/redis/redis.conf这种路径,或者解压目录下。apt/yum安装,通常在/etc/redis/redis.conf。Docker挂载方式更灵活,你可以把宿主机任意路径的配置文件映射进去。
提示:不确定配置在哪,先执行
redis-server --version看版本,再执行redis-cli CONFIG GET dir查看工作目录,不过更直接的办法是找启动脚本或者systemd服务文件里ExecStart指定的配置路径,那里写的一定是实际加载的配置。
找到配置文件后,搜索requirepass,默认情况下这一行是注释掉的。在Redis 6.x版本里你会看到类似这样的内容:
# requirepass foobared去掉前面的#,把foobared改成你自己的强密码,比如:
requirepass YourStrongPassword_2024然后保存退出,重启Redis。为什么注释样例叫foobared?其实这就是官方文档里的占位符,真不是让你用这个当密码。
2.2 requirepass与masterauth:主从同步场景必须一起设置
配置文件方式最容易栽的坑就在这里。如果你只是单机Redis,requirepass就够了。但如果你搭了主从复制,主库设置了requirepass,从库同步主库数据时就需要一种机制来认证主库。这个机制就是masterauth。
从库的配置文件里必须这样设置:
masterauth YourStrongPassword_2024注意,masterauth的值必须跟主库的requirepass一致。我当时第一次搭主从就吃了这个亏:主库设了密码,从库replicaof也配了,结果日志里疯狂刷MASTER <-> REPLICA sync started失败,报错信息是-NOAUTH Authentication required。当时一下就明白了——从库在向主库发起复制请求时,它自己也是一个客户端,一样要被认证。
还有一点,不能把主从的认证逻辑跟客户端认证混淆。主库的requirepass是让所有客户端“证明自己是谁”,从库的masterauth是“我用什么身份去连接主库”。你可以把主从关系类比成员工刷门禁卡,masterauth就是员工自己的卡,而requirepass是大楼的门锁。
2.3 配置完必须做的事:重启与验证
修改完配置文件后,有两种方式让配置生效:
一是通过redis-server /path/to/redis.conf带配置路径启动。如果Redis已经在运行,你需要先关掉旧进程。我一般习惯用redis-cli shutdown,这种方式干净安全,不会像直接kill -9那样可能导致数据未落盘。
二是不重启,在线动态修改(这块后面讲命令行方式时详细展开)。但在生产环境,我非常不建议长期依赖在线改配置而不更新配置文件,因为Redis重启后如果加载的还是旧配置文件,修改就直接丢失了,这种“临时生效”带来的假安全感反而坑人。
验证方式也很简单:
redis-cli ping # 如果返回 (error) NOAUTH Authentication required, 说明密码已生效 redis-cli -a YourStrongPassword_2024 ping # 返回 PONG, 说明认证通过这里有个细节:redis-cli -a后面直接跟密码,命令行会被shell历史记录保存下来。你敲完这条命令,执行history就会发现密码明文躺在里面。安全起见,生产环境建议用redis-cli进入交互模式后执行AUTH YourStrongPassword_2024,这样不会留在shell历史里。
3. Docker容器方式:三种姿势与一个经典误区
3.1 先搞清楚容器里Redis是怎么加载配置的
Docker跑Redis,最容易让人困惑的地方是:容器里其实也有一份默认配置文件。官方镜像redis的默认工作目录是/data,配置文件默认路径是/usr/local/etc/redis/redis.conf。但默认情况下,官方镜像启动时如果没指定配置文件,Redis是“无配置裸启动”的,也就是说所有配置都走默认值。
所以容器场景下设置密码,本质上有三条路:
- 启动时通过命令行参数直接传
--requirepass - 通过环境变量间接传参(部分镜像支持)
- 挂载宿主机上写好的配置文件并指定加载
三种方式各有适用场景,下面逐一拆解。
3.2 方式一:docker run命令行直接传参
这是最快最直观的方式:
docker run -d \ --name redis-test \ -p 6379:6379 \ redis:7.0 \ redis-server --requirepass MyDockerPassword注意redis-server后面跟的参数,是给容器内Redis进程的启动参数,不是docker run的参数。很多新手会把--requirepass写在docker run的参数位置,然后发现完全不生效,就是因为传错了地方。
这种方式的优点是快速、适合临时测试。缺点也很明显:密码直接出现在docker ps或者docker inspect能看到的环境变量/启动命令里,如果周围人有服务器权限,密码等于泄露了。另外,如果你需要配置很多参数,命令行会变得又长又乱。
3.3 方式二:环境变量方式
官方Redis镜像本身没有直接提供“REDIS_PASSWORD”这种环境变量来自动设置密码,但社区里有些镜像(比如bitnami/redis)是支持这种方式的。用bitnami镜像时,你可以这样:
docker run -d \ --name redis-test \ -e REDIS_PASSWORD=MyBitnamiPassword \ -p 6379:6379 \ bitnami/redis:latest这里必须提醒一句:官方镜像不会响应REDIS_PASSWORD这个环境变量。很多人之前被其他软件的习惯带偏了,以为设个环境变量就行,结果容器起来了,密码根本没生效。我在文章里专门提这个,是因为这个坑在社区里出现频率太高了。
如果你坚持用官方镜像,又想集中管理配置,最稳妥的办法还是第三种——挂载配置文件。
3.4 方式三:挂载自定义配置文件
这是生产环境最推荐的方式,因为配置文件可以纳入版本管理,所有参数一目了然。
假设你宿主机上有一个/data/redis/redis.conf,里面已经写好了:
requirepass DockerFilePassword appendonly yes启动时挂载进去:
docker run -d \ --name redis-prod \ -p 6379:6379 \ -v /data/redis/redis.conf:/usr/local/etc/redis/redis.conf \ redis:7.0 \ redis-server /usr/local/etc/redis/redis.conf这里有个很关键的细节:-v把宿主机配置文件挂载到容器内路径之后,必须在启动命令里显式指定这个配置文件的路径,否则Redis还是不会加载它。因为官方镜像默认不会主动去读/usr/local/etc/redis/redis.conf,你得告诉它“用这个文件启动”。
挂载方式的好处在哪?你在宿主机上改配置文件,然后重启容器就能生效,不用进容器里折腾vi。而且docker inspect里看不到密码明文,相对安全。
3.5 容器内验证密码是否生效
容器启动后,验证方式比宿主机上要多一步:
# 进入容器 docker exec -it redis-prod redis-cli # 在交互模式下直接输入 AUTH DockerFilePassword # 返回 OK或者一条命令验证:
docker exec -it redis-prod redis-cli -a DockerFilePassword ping # 返回 PONG注意:容器内的
redis-cli默认连接的是127.0.0.1:6379,在容器内验证没问题。但如果你在宿主机上执行redis-cli,连接的是宿主机的6379端口,前提是你做了-p 6379:6379端口映射,否则连不进去。这个区别虽然基础,但容易让人误解“密码到底设上去没有”。
4. 命令行方式:临时修改与持久化的正确姿势
4.1 用CONFIG SET在线修改
Redis允许在运行状态下通过命令动态修改配置,无需重启:
redis-cli -a 旧密码 CONFIG SET requirepass 新密码如果现在没设密码,直接:
redis-cli CONFIG SET requirepass MyNewPassword这种方式在什么场景下用?比如你在排查问题,怀疑密码策略导致某客户端频繁重连,想临时换个弱密码验证一下;或者你只是想在角色切换前快速改密。CONFIG SET是热加载的,改了立刻生效,所有已连接且未认证的客户端会被要求重新认证。
但记住一个核心点:CONFIG SET只改内存中的配置,不改配置文件。如果Redis重启,它还是会加载旧配置,密码又变回原样。
4.2 CONFIG REWRITE:把在线修改持久化
有没有办法既在线改,又让配置落盘?有,Redis提供了CONFIG REWRITE命令:
redis-cli -a 新密码 CONFIG REWRITE这条命令会把当前运行配置重写到配置文件里。它做的事其实是在保留原配置注释结构的基础上,把当前生效的参数合并写回文件。如果你之前用CONFIG SET改了一堆参数,执行一次CONFIG REWRITE就能全部固化下来。
具体使用顺序建议这样:
# 1. 登录并设置新密码 redis-cli CONFIG SET requirepass MyNewPassword # 2. 用新密码登录并持久化配置 redis-cli -a MyNewPassword CONFIG REWRITE这里有个细节,CONFIG REWRITE之后配置文件里会自动出现一行requirepass MyNewPassword,而且它比手动编辑配置文件的好处是不容易改坏格式。当然,前提是你的Redis进程启动时确实加载了一个可写的配置文件。如果启动时没指定配置文件(比如纯默认启动),执行CONFIG REWRITE会提示The server is running without a config file,这时候它就无能为力了。
4.3 redis-cli -a 的泄露风险与替代方案
命令行场景还有个绕不开的话题:redis-cli -a虽然方便,但真的不妥。
一方面,shell history会记录明文密码。尤其在生产环境,别人通过history或者~/.bash_history文件就能看到。另一方面,如果你在写脚本时用-a传密码,脚本文件本身的权限控制不好也会泄露。
替代方案有两个:
一是用REDISCLI_AUTH环境变量替代-a参数。这是redis-cli内置支持的环境变量,设置后会自动作为默认认证密码传给每次连接:
export REDISCLI_AUTH=MyNewPassword redis-cli ping # 返回 PONG好处是连接命令简洁,而且ps进程列表里不会出现明文密码(因为密码是运行时从环境变量里读取的,不过环境变量本身也有泄露风险,使用时注意控制shell会话权限)。
二是用交互模式手动AUTH:
redis-cli 127.0.0.1:6379> AUTH MyNewPassword OK 127.0.0.1:6379> PING PONG这种方式适合临时操作,不会在shellhistory里留下密码痕迹。我个人在排查生产问题时,只要不是脚本化操作,基本都用交互模式。
5. 常见问题与排查技巧实录
5.1 设置密码后提示NOAUTH Authentication required
这个报错其实是个好消息,说明密码验证已经开始工作了。它出现在两种情况:
一种是你没带密码就执行命令。比如:
redis-cli ping (error) NOAUTH Authentication required.解决方案就是先AUTH再操作。
另一种情况比较隐蔽:主从复制里,从库连接主库时由于masterauth没配或配错,也会报这个错。排查时先看从库日志,关键字是MASTER <-> REPLICA sync started,后面跟着认证失败信息。
我一般用三步排查这个问题:
- 确认主库
CONFIG GET requirepass能看到密码。 - 确认从库
CONFIG GET masterauth配置了同样的密码。 - 如果两边都正确,检查从库执行
CONFIG GET masterauth和CONFIG GET requirepass是否被重启后重置了(比如从库依赖的命令行启动参数覆盖了配置文件的masterauth)。
5.2 Redis Desktop Manager连接不上
经常有朋友说“Redis Desktop Manager连不上,用命令行能连”。多数情况不是密码问题,而是RDM默认用TCP连接到目标IP的6379端口,如果Redis只绑定了127.0.0.1,外部客户端自然连不上。
检查以下几项:
- Redis进程里
CONFIG GET bind返回的IP是什么,如果是127.0.0.1,改用0.0.0.0或具体内网IP。 - 云服务器安全组是否放行6379端口。
- Redis的
protected-mode是否开启。默认情况下protected-mode是yes,它要求只能来自回环地址的连接才能无密码访问,外部IP必须密码认证。
在RDM里填密码的位置,对应的是requirepass,也就是默认用户的密码。如果版本较新还支持配置ACL用户,那要单独指定用户名。
5.3 修改密码后主从同步失败
这种情况多半是只改了主库的requirepass,忘了同步改从库的masterauth。Redis主从复制,从库要模拟一个客户端连主库,它的身份验证靠的就是masterauth。主库密码换新,从库还拿旧密码认证,自然被拒绝。
解决方式:
# 在主库上改密码 redis-cli -a 旧密码 CONFIG SET requirepass 新密码 # 在从库上同步修改masterauth redis-cli -a 旧密码 CONFIG SET masterauth 新密码 # 两边都执行CONFIG REWRITE,确保重启后不丢失另外,Redis 7.x版本里还可以用CONFIG SET replicaof重新指定主节点,但核心还是认证信息要对得上。
5.4 日志中的WARNING提示“Current password is empty”
Redis在开启某些安全相关特性时会输出警告,比如当protected-mode设为yes时,如果没设密码,日志会提示Warning: no config file specified, using the default config.等相关信息。这类警告本身不致命,但它提醒你:当前Redis处在“仅内网访问、无密码”的状态,如果网络隔离做得不够好,还是趁早加上密码。
5.5 忘记Redis密码怎么办
忘记密码是每个运维都可能遇到的事。这时候不用慌,有两个方案:
一是如果Redis启动时没有加载配置文件,你可以直接停掉Redis,然后临时用--requirepass ""起一个空密码实例,把数据导出来,再用正常配置启动。但这个方法在容器里比较麻烦,不建议在生产环境乱搞。
二是从配置文件里找回,因为CONFIG REWRITE之后密码会明文写入配置文件。但如果配置文件是只读权限,你可能需要root权限才能读。
最惨的情况是启动时没有配置文件,也没做过CONFIG REWRITE,那内存里的密码理论上无法直接读取。这时候只能重启Redis并设置新密码。由于Redis在关闭时若开启了appendonly,数据会以AOF形式保存在磁盘上,即使重启换密码,数据也不会丢。不过重启前最好先确认这个节点是否为主节点,如果为主节点,涉及主从切换或客户端重连,尽量选业务低峰期操作。
5.6 生产环境加密码的几个实操建议
最后补充几个我踩过坑后总结出来的经验:
第一,密码不要写在命令行里,不要写在docker run的启动命令里(除非是临时测试)。时间久了你会发现,最简单安全的办法还是配置文件统一管理。
第二,密码强度要有底线,但别复杂到自己都记不住。很多团队选择用固定格式,比如“项目代号+年份+随机字符”,既方便记忆又不容易被猜到。网上大量扫描器还停留在跑默认密码foobared和简单字典阶段,用个中等强度的随机密码就足够挡掉大部分扫描。
第三,改了密码之后,第一时间检查所有调用了Redis的服务。比如Java Spring Boot项目里的redis.password配置、Python的redis.Redis(password=...)、Node.js的ioredis初始化参数等。经常有人密码在主库改好了,业务侧没同步,服务连不上才开始排查,白白浪费半天时间。
第四,如果是Redis 6以上版本且是多团队共享实例,认真考虑用ACL按团队分用户。你可以给A团队一个只读只读自己key前缀的用户,给B团队一个能执行特定命令集但不能FLUSHALL的用户,比一个全局requirepass给所有人要好管控得多。当然,这属于密码之上更细粒度的权限设计,等你有需求再来折腾不迟。
Redis设置密码这个动作,看起来就是一行配置的事,但真正落到不同部署环境、不同拓扑结构里,细节还挺多。我自己在实际操作中的体会是:不管用哪种方式,先在测试环境完整跑一遍“修改密码、验证登录、检查主从、确认业务侧恢复”的闭环,再上生产,能帮你避开大部分“改完密码服务全挂”的尴尬局面。