1. 为什么Redis默认不带密码,却总有人被扫
先说个真实经历。前几年我搭过一套内部用的Redis,没设密码。当时想得很简单:只在内网,访问的人就那几个,没必要折腾认证。结果第二天收到告警,CPU占用飙到100%,上去一看,Redis里被塞满了挖矿相关的Key,/var/spool/cron目录里多出了一个定时任务,进程列表里还有几条奇怪的bash在跑。事后复盘特别清楚——就是6379端口被扫到了,对方连进来之后通过Redis写了计划任务,整个过程不需要任何认证。
这个事给我的教训就是:Redis默认的状态并不是"安全可用",而是"信任网络环境"。官方默认配置里,protected-mode yes只会在bind 127.0.0.1且没有设置密码时生效,也就是只有本机能够访问。可一旦你改了bind、或者用Docker把端口映射出来、或者服务器在云上开了公网端口,这层保护就基本失效了。而requirepass默认是空的,什么都没有。
所以只要你装了Redis,第一件事就应该是考虑"怎么设置密码"。这不是什么进阶操作,而是跟安装配置同等基础的安全底线。下面这套方法,我按最常见的使用方式梳理一遍:配置文件改法、命令行改法、Windows和Docker环境的差别、客户端连接时的密码处理、主从复制的特殊场景,以及Redis 6之后更精细的ACL用户权限怎么用。
2. 两种最基本的设置方式:配置文件与命令行动态配置
给Redis设置密码,本质上就是设置requirepass参数。这个参数有两种改法:直接改redis.conf,或者用CONFIG SET命令动态修改。
2.1 改redis.conf:重启后依然生效
Linux下Redis的配置文件一般位于/etc/redis/redis.conf。打开之后找到这样一行:
# requirepass foobared默认是被注释掉的。把它改成你的密码即可:
requirepass YourStrongPassword改完以后重启Redis服务:
sudo systemctl restart redis这样可以验证密码是否生效:
redis-cli -a YourStrongPassword ping # 返回 PONG 就代表认证通过如果不加-a直接执行redis-cli ping,会看到这样的报错:
(error) NOAUTH Authentication required.看到这个报错说明认证已经生效,只是客户端还没带上密码。
有一点必须提醒:redis.conf里保存的是明文密码。自己本地测试怎么都行,但生产环境一定要把配置文件的权限收严,建议chmod 600,避免其他系统用户直接读到密码。
2.2 用CONFIG SET动态修改:无需重启
如果你不想重启Redis,或者只是想临时测试一下,可以在redis-cli里直接设置:
redis-cli CONFIG SET requirepass "YourStrongPassword" AUTH YourStrongPassword这里有一个很多人踩过的顺序问题:CONFIG SET requirepass执行成功后,当前连接本身不会断开,你依然可以继续操作。但下一次新建立的连接就必须带密码了。所以AUTH要在CONFIG SET之后执行,用来让当前这个会话继续获得认证状态。如果你先执行了CONFIG SET,再去敲其他命令,同样会报NOAUTH。
CONFIG SET只对运行中的实例生效,进程一重启就没了。要持久化,记得执行:
CONFIG REWRITE这个命令会把当前运行中的有效配置写回redis.conf,包括你刚设置的密码。所以我个人的习惯是:先用CONFIG SET在线把密码加上,确认无误后立刻CONFIG REWRITE,避免改配置文件后忘记重启导致没有生效,也避免生产环境直接乱动配置文件引发不必要的重启。
2.3 两种方式的对比
| 修改方式 | 生效时机 | 重启后是否保留 | 适用场景 |
|---|---|---|---|
| 修改redis.conf | 重启后生效 | 保留 | 新装Redis时的初始化配置 |
| CONFIG SET + CONFIG REWRITE | 立即生效 | 保留(执行REWRITE后) | 生产环境在线变更,不想重启 |
| CONFIG SET(不REWRITE) | 立即生效 | 不保留 | 临时调试、测试 |
实际操作中,我建议两条路都掌握。有时候你在内网临时拉个Redis做测试,没必要动配置文件,直接在线设置密码就够了。但要长期跑的服务,一定要保证密码落到配置文件里,否则哪天进程被重启,Redis又会变成无密码状态——这种事故我见过不止一次。
3. Windows、Linux和Docker环境下设置密码的实操差别
不同环境下配置文件的位置、启动方式甚至配置文件名都不一样。这部分最容易踩的坑不是密码本身,而是你改了配置,但服务加载的根本不是这个文件。
3.1 Linux下最标准的路子
Linux下以systemd方式运行的Redis,配置修改后重启即可。比较需要注意的一点是:先确认当前实例加载的配置文件路径,再动手改。用这个命令看:
redis-cli CONFIG GET dir这个命令返回的是Redis的工作目录,不是配置文件路径。想确认配置文件本身,通常看进程参数。
ps -ef | grep redis # 例如 /usr/bin/redis-server 127.0.0.1:6379这样能看到启动命令有没有显式指定配置文件。有些发行版会默认加载/etc/redis/redis.conf,有些老版本安装方式则是手动编译后启动时带一个6379.conf。改错文件是这类问题里最高发的错误。
3.2 Windows下部署Redis的特殊处理
Windows没有官方Redis版本,我一般用的是 tporadowski/redis 这个开源编译版本,或者微软Archive里的老版本。Windows版Redis的配置文件叫**redis.windows.conf**,不是redis.conf,这点很多新手直接懵。
启动时要显式指定配置文件:
redis-server.exe redis.windows.conf如果你不加配置文件直接运行redis-server.exe,它会按默认配置启动,你改的redis.windows.conf根本没被加载。设了密码不生效,大概率就是这个原因。
如果你把Redis注册成了Windows服务,还要检查服务的启动参数是否包含了配置文件路径。我在Windows服务管理器里见过很多次:服务命令行是D:\Redis\redis-server.exe,后头什么都没带,配什么密码都白搭。手动改成:
D:\Redis\redis-server.exe D:\Redis\redis.windows.conf然后重启服务。
Windows版Redis还有一个让人头疼的点:版本普遍偏老。如果你用的Redis 5.x,ACL功能就不存在,只能靠requirepass。所以Windows环境里别一上来就想着用ACL,先把最基本的密码加上才是正道。
3.3 Docker容器设置密码
Docker方式安装Redis,设置密码最直接的方式是在启动命令里传参:
docker run -d --name redis \ -p 6379:6379 \ redis:7.2 \ redis-server --requirepass YourStrongPassword注意,官方Redis镜像的默认启动命令是redis-server,所以当你传自定义参数时,要显式带上redis-server。如果写成docker run ... redis:7.2 --requirepass xxx,容器会尝试把--requirepass当作可执行文件执行,直接报错启动失败。
如果你用挂载配置文件的方式:
docker run -d --name redis \ -p 6379:6379 \ -v /myredis/conf/redis.conf:/usr/local/etc/redis/redis.conf \ redis:7.2 \ redis-server /usr/local/etc/redis/redis.conf那么在配置文件里写好requirepass即可。这里要提醒一个常见的误区:官方Redis镜像并没有REDIS_PASSWORD这个环境变量。很多第三方镜像(比如Bitnami的Redis镜像)才支持REDIS_PASSWORD环境变量。你把官方镜像配上REDIS_PASSWORD去启动,Redis根本不读这个变量,密码自然没生效。网上不少教程混着写,建议用之前先确认镜像来源。
3.4 改了密码却不生效的排查思路
如果你设置了密码,但无论怎么连都不要求认证,按这个顺序排查:
| 检查项 | 操作 |
|---|---|
| 当前实例加载了哪个配置文件 | ps -ef看启动参数 |
配置文件里有没有requirepass | grep requirepass /path/to/redis.conf |
| 配置里面是否被注释 | 去掉行首# |
| Docker容器挂载是否正确 | docker inspect看Mounts映射 |
| 进程重启过没有 | 修改配置文件后需重启或CONFIG REWRITE |
| 是否有启动脚本覆盖参数 | 检查systemd unit文件里的ExecStart追加参数 |
这套排查链路我实践下来能解决九成以上的"密码不生效"问题。
4. 设完密码之后:客户端、可视化工具和SDK怎么连
密码设置完成后,你的所有访问方式都要跟着改。这一节把最常见的几种连接方式挨个说一遍。
4.1 redis-cli的两种认证写法
第一种是启动时带密码:
redis-cli -a YourStrongPassword简单直接,但有个副作用:密码会出现在shell的进程列表里,如果你是在共享机器上执行,别人用ps -ef就能看到密码。更稳妥的做法是用环境变量:
export REDISCLI_AUTH=YourStrongPassword redis-cli这样既不用每次敲-a,也能避免密码直接暴露在进程参数里。这是Redis官方支持的一种方式,日常使用非常顺手。
第二种是进入交互模式后再认证:
redis-cli AUTH YourStrongPassword适合临时连接或者脚本里先连接再做认证的场景。需要特别注意的是,如果密码里包含@、$、#这类特殊字符,用-a参数直接传的话shell可能会做变量展开或截断,建议统一用环境变量方式,省心很多。
4.2 可视化客户端怎么填
现在使用频率比较高的是Another Redis Desktop Manager。连接配置很简单:地址填IP或域名,端口填6379,密码填你设置的requirepass值,测通即可。
如果你用老的Redis Desktop Manager,遇到连不上不要先怀疑密码。Redis 6.0之后ACL机制的引入,让旧版Redis Desktop Manager在部分场景下会出现"密码正确但认证失败"的问题。这个问题我处理过好几次,要么换新版,要么直接换Another Redis Desktop Manager。还有一个排查技巧:命令行先试通,再去填工具参数,这样能快速区分是Redis侧的问题还是客户端工具的问题。
4.3 常用语言SDK的密码配置
Python的redis-py:
import redis r = redis.Redis( host='localhost', port=6379, password='YourStrongPassword', decode_responses=True ) print(r.ping())Java的Jedis:
Jedis jedis = new Jedis("localhost", 6379); jedis.auth("YourStrongPassword"); System.out.println(jedis.ping()); jedis.close();Go的go-redis:
rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", Password: "YourStrongPassword", }) pong, err := rdb.Ping().Result() fmt.Println(pong, err)这些SDK的认证逻辑本质都一样:建立TCP连接后发送AUTH命令。如果密码错误,Python 的redis-py会抛出AuthenticationError,Java会收到NOAUTH相关的异常信息,Go的err非nil。错误信息本身并不统一,排查时不要死盯SDK异常,回到redis-cli验证是最快的路径。
4.4 认证对性能的影响
有人担心加了密码会拖慢Redis性能。实际上认证只在连接建立时发生一次,握手完成后后续命令走的是TCP长连接,完全不受影响。连接池模式下,一条连接认证一次,后续复用即可,基本可以忽略这个开销。
但连接池里有个小坑:如果某个连接因为密码错误被Redis拒了,有的连接池不会自动丢弃这个坏连接,会反复抛出认证异常。遇到这种情况,先把密码改对,然后重启应用或清空连接池。
5. 主从复制与哨兵模式下设置密码容易踩的坑
单机Redis设置密码很简单,一旦涉及主从复制、哨兵模式,坑就来了。最常见的问题是:主库设了密码,从库同步直接报错。
5.1 从库要配置masterauth
主库开启requirepass之后,从库去同步时会被要求认证,日志里会出现类似这样的信息:
Master induced authentication error解决方法是:在从库的配置文件里加上主库的密码:
masterauth YourStrongPassword这样从库向主库发起同步时就会自动携带认证信息。如果你用的是命令行方式,可以这样:
redis-cli -p 6380 CONFIG SET masterauth "YourStrongPassword" CONFIG REWRITE这里有一个很多人忽略的细节:主从节点最好都设置requirepass,并配置相同的masterauth。从库设置密码是为了防止有人直连从库读取数据,尤其当你把从库用于只读查询时,这个防护很重要。
5.2 哨兵模式的认证配置
如果用了Sentinel哨兵做高可用,哨兵本身也需要认证来连接主库和从库。在哨兵配置文件里要加这么一行:
sentinel auth-pass mymaster YourStrongPasswordmymaster是主库组的名字,YourStrongPassword是主从节点使用的密码。没有这一行,哨兵会发现不了主库宕机,或者在failover投票后无法完成切换。
我实际遇到过的故障场景是:主库和从库配置了密码,哨兵没配auth-pass,结果主库挂了之后哨兵迟迟不执行故障转移,整个服务在监控上看起来一切正常,但写操作全都失败。排查小半天才发现哨兵根本连不上主库,认证失败后就认为主库仍然存活。这个坑不踩一次是真的难以注意到。
5.3 集群模式下密码一致性
如果用的是Redis Cluster集群模式,所有节点的requirepass和masterauth必须保持一致。因为集群内节点之间会互相通信,如果某个节点的密码和其他节点不一致,它会被其他节点视为不健康节点,轻则警告,重则被踢出集群。我之前见过一个测试集群,其中一个节点被人手动改过密码,结果集群状态一直显示fail,找半天才发现是密码一致性导致的。
配置集群时我的建议是:通过统一配置管理工具下发,不要手工逐个改,否则漏一个节点就容易出这种隐性故障。
6. 从requirepass到ACL:更精细的用户权限控制
Redis 6.0引入了ACL(Access Control List),你可以把它理解成"Redis自己的用户系统"。相比requirepass这种全局限定一个密码的模式,ACL能做到不同用户不同权限、不同用户只能访问不同Key。
6.1 requirepass的局限
requirepass的本质是:只有一把钥匙,通了就能执行所有Redis命令。这在单人单项目的内网环境够用,但一涉及团队协作问题就来了:所有人都用同一个密码,你没法区分谁做了什么操作,也没法限制某个使用者只能读不能写。
ACL解决的就是这类问题。
6.2 用ACL创建受限用户
在redis-cli里执行:
AUTH YourStrongPassword ACL SETUSER devuser on >devpass123 ~* +@read这行命令创建了一个名叫devuser的用户:
on表示启用该用户>devpass123设置该用户的密码~*允许访问所有Key(~是Key模式的通配符)+@read只授权读类命令,比如GET、MGET、STRLEN等
再创建一个可读写的用户:
ACL SETUSER writeuser on >writepass456 ~* +@read +@write+@write授权写类命令,比如SET、DEL、INCR等。之后用devuser连接时,执行SET foo bar会被拒绝,报错:
NOPERM this user has no permissions to run the 'set' command这就是ACL的核心价值:密码从"全局一把钥匙"细化成了"每个用户各自的权限"。
ACL配置需要持久化,执行:
ACL SAVERedis会把ACL配置写入配置文件或单独的aclfile。具体写在哪个文件,由配置里的aclfile参数决定。如果你不执行ACL SAVE,重启后ACL配置会丢失,只保留配置文件里的requirepass。
6.3 requirepass和ACL的关系
看到这里你可能会有疑问:requirepass和ACL能不能混用?
其实requirepass本质上是在给ACL中的default用户设置密码。你设置了requirepass,等价于给default用户加了一个密码。当你再用ACL SETUSER修改default用户的密码时,效果和改requirepass是一样的。
生产环境我建议这样处理:要么只用requirepass守住入口,要么完全切换到ACL的default用户加自定义用户。两种机制别混着反复设置,不然你很难判断当前连接的认证状态到底由哪边控制。对于老项目,requirepass够用;对于新项目,尤其是有多团队复用Redis的场景,直接上ACL,后续再扩权限会从容很多。
另外说一句:如果你是Windows上的老版本Redis,ACL基本不用考虑,因为Redis 6.0才引入ACL,而Windows编译版普遍停在Redis 5.x。老老实实用requirepass就行。
7. 密码之外的加固组合:别让密码形同虚设
设置密码只是第一步。如果其他基础安全没做,密码再强也可能挡不住。
7.1 密码本身怎么选
Redis的密码不像网站登录密码需要频繁输入,所以完全没必要为了好记而设成123456或者redis。我见过真有生产环境把密码设成redis的,被扫进去是迟早的事。建议至少12位,大小写字母、数字、特殊字符混合。因为Redis客户端连接本来就是长连接,密码再长也就输一次,不存在体验问题。
还有一个安全细节:不要把密码提交到Git仓库。我见过有人在config目录里直接写了测试密码然后随项目一起提交,这种隐患比密码弱还麻烦。配置文件里涉及密码的,最好加到.gitignore或者用模板替换的方式管理。
7.2 我建议的生产环境加固组合
requirepass或ACL用户认证,确保所有访问方必须认证protected-mode yes,不要随手关掉bind限定访问源IP,比如只允许应用服务器IP访问- 云服务器安全组和Linux防火墙双重限制6379端口,只放行必要来源
- Redis运行账户使用独立低权限用户,不要用root运行
- 重命名高危命令,比如
CONFIG、FLUSHALL、SHUTDOWN,降低被入侵后的破坏半径
rename-command CONFIG "" rename-command FLUSHALL "" rename-command SHUTDOWN ""这条配置在旧版本Redis里很有用。如果使用ACL,可以更精细地直接不给相关用户授权这些命令,比rename更清晰。
7.3 常见故障排查速查表
| 故障现象 | 诊断思路 | 解决方案 |
|---|---|---|
连接时报NOAUTH Authentication required | 客户端没带密码或密码为空 | 客户端配置密码 |
执行ping也报NOAUTH | 当前会话未认证 | 执行AUTH 密码 |
| 密码正确但连接失败 | 客户端版本太老,不支持ACL | 换客户端或升级版本 |
| 主从同步中断 | 从库缺少masterauth | 配置masterauth |
| 哨兵不触发故障转移 | 哨兵缺少auth-pass配置 | 配置sentinel auth-pass |
| Docker启动后密码不生效 | 配置文件未加载或镜像不支持环境变量 | 显式传参或挂载配置文件 |
| 重启后密码丢失 | 用的是CONFIG SET但没CONFIG REWRITE | 执行CONFIG REWRITE |
最后再分享一个小习惯:每次修改完Redis认证配置后,我会用一条命令做体检,确认当前实例的认证状态符合预期:
redis-cli -a "$REDISCLI_AUTH" -h 127.0.0.1 --no-auth-warning INFO server | grep redis_version去掉密码后同样执行一遍,应该看到NOAUTH报错。这能快速确认"带密码能通、不带密码不通"这个最基本的预期,也方便后面排查客户端问题时有一个明确的对比基准。在生产环境里,我建议你在第一次部署Redis时就顺手把密码加上,比事后补救轻松得多——这句话是真的从被扫的教训里学来的。