ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Docker容器中Redis密码设置与安全加固完整指南

Docker容器中Redis密码设置与安全加固完整指南 最近帮同事排查一个 Redis 连接报错发现一个很典型的情况他直接用 Docker 启动了 Redis 容器端口映射一开密码完全没设就这么跑在服务器上。我问他怎么不设密码他说“反正就我自己的服务在连应该没事吧”。这个想法在 Docker 环境下真的特别危险尤其是你的服务器有公网 IP、或者 Docker 端口直接映射出去的时候Redis 默认配置等于大门敞开扫描工具十几秒就能扫到。今天这篇就把 Docker 里给 Redis 设置密码这件事彻底讲透。不绕弯子直接从最简单的启动参数方式说起再到生产环境更推荐的配置文件方式最后把主从复制场景下的密码配置和常见排查问题一起带过。不管你用的是 Docker Desktop 还是 Linux 服务器上的 Docker这套思路都是通用的。1. 先搞清楚一件事为什么 Docker 里的 Redis 更需要密码1.1 默认状态下的 Redis等于家门没锁Redis 官方镜像拉下来直接运行默认配置下 requirepass 是空的也就是说任何客户端只要能连到 6379 端口不需要任何认证就能执行全部命令。你可能会想“那我把端口映射只开在本地或者只在公司内网用问题不大吧”这里先给一个结论只要 Redis 的端口暴露到了容器外部无论内网还是公网都有风险。内网不等于安全很多企业的内网里跑着扫描器或者被攻破了一台机器后攻击者第一件事就是扫内网里的 6379、3306 这种端口。Redis 默认无密码扫到就是直接进FLUSHALL 清空数据、写入定时任务、挖矿脚本这些都不是危言耸听。1.2 Docker 端口映射让风险成倍放大Docker 部署相比传统本机安装多了一层端口映射的概念。映射到宿主机后Redis 对宿主机所在的整个网络都是可见的。举例来说你在云服务器上用 docker run -p 6379:6379 启动 Redis如果云服务商的安全组策略没有限制 6379 端口的来源 IP那这台 Redis 就等于对公网开放。SecurityTrails 等安全团队曾经统计过互联网上暴露的 Redis 实例超过 20 万个其中大量是未认证的。攻击者拿到 Redis 权限后手段太多了写 crontab 反弹 shell、修改 authorized_keys、加密数据勒索都有现成工具。所以我的观点很直接Docker 里跑 Redis密码不是可选项是必选项。1.3 两种部署方式下的密码设置差异在宿主机上直接安装 Redis改配置文件后 restart 就行。但在 Docker 里容器是临时性的rm 掉再 run 一个配置就没了。如果只靠挂载配置文件文件没挂好启动后 Redis 就又回到无密码状态。很多人踩的坑就在这里。另外还要注意一个关键点Redis 官方 Docker 镜像不支持通过环境变量直接设置密码。什么意思MySQL 的官方镜像有 MYSQL_ROOT_PASSWORD你用 docker run -e MYSQL_ROOT_PASSWORDxxx 就自动设置好了。但 Redis 官方镜像没有 REDIS_PASSWORD 这个环境变量你必须自己想办法把 requirepass 传给 redis-server。这就是后面几种方式存在的原因。2. Docker 中设置 Redis 密码的三条路线2.1 最快的方式docker run 启动参数直接指定如果你只是想快速起一个带密码的 Redis 实例做测试最简单的方法是在镜像名后面通过 redis-server 命令传参数。docker run -d \ --name redis-test \ -p 6379:6379 \ redis:7.0 \ redis-server --requirepass MyStrongPass123注意参数的位置。--requirepass 必须写在 redis:7.0 镜像名后面因为它会被作为 redis-server 的启动参数传入容器。如果你写到 docker run 后面Docker 会直接报错因为 docker run 不认识这个参数。启动之后验证一下密码是否生效# 不输入密码直接执行命令 docker exec -it redis-test redis-cli keys * (error) NOAUTH Authentication required. # 带上密码执行 docker exec -it redis-test redis-cli -a MyStrongPass123 keys * Warning: Using a password with -a or -u option on the command line interface may not be safe. (empty array)命令能跑通但 Redis 会给你一个警告提示在命令行里明文写密码不安全。这个警告不是报错可以忽略但你心里得有数如果服务器上有其他用户能看到进程列表或 shell 历史密码就泄露了。使用这种方式时有几个细节值得注意密码建议用单引号包住。如果你的密码里有 $、!、 这类符号不加引号会被 Bash 解释比如 $ 开头会被当成变量展开! 在交互式 shell 里可能触发历史展开。单引号能避免这些意外。密码不要有空格。在命令行里传带空格的密码要处理额外的转义问题redis-server 解析配置时也容易出歧义。干脆在生成密码时就避开空格。建议显式开启 AOF 持久化。上面的命令没有挂载数据卷也没有开启 appendonly容器一删数据全没。如果只是测试那无所谓如果想保留数据需要加上 -v 挂载和 --appendonly yes。2.2 推荐的方式挂载 redis.conf 配置文件生产环境我不会用命令行参数太零散不好维护。更推荐的做法是把 redis.conf 配置文件放到宿主机然后挂载进容器通过配置文件集中管理 Redis 的各项设置。先在宿主机准备一个 redis.conf以 Redis 7.0 为例# 设置访问密码 requirepass MyStrongPass123 # 开启 AOF 持久化 appendonly yes appendfilename appendonly.aof # 数据目录后面挂载数据卷时对应 dir /data # 保护模式建议开启 protected-mode yes # 监听所有网卡。生产环境请根据实际安全策略调整 bind 0.0.0.0 # 日志输出到 stdout方便 docker logs 查看 logfile 然后启动容器时挂载配置文件docker run -d \ --name redis-prod \ -p 6379:6379 \ -v /home/user/redis/redis.conf:/etc/redis/redis.conf \ -v /home/user/redis/data:/data \ redis:7.0 \ redis-server /etc/redis/redis.conf关键点redis-server 后面要带上配置文件路径。官方镜像默认不带任何配置文件启动时用的是镜像内置的默认配置你必须显式告诉它读取挂载进去的这个文件。这里有一个常见的坑如果你挂载了 redis.conf 进去但 redis-server 启动时没有指定这个配置文件Redis 会使用默认配置启动你写在文件里的 requirepass 完全不会生效。很多新手在这一步栽跟头以为挂载文件就万事大吉结果容器跑起来照样免密。另外关于 bind 配置我说一下我的理解。docker run 使用 -p 做端口映射时Redis 容器内部的网络接口是独立的如果 redis.conf 里 bind 127.0.0.1宿主机通过映射端口访问时数据包进入容器后目的地址是容器的 eth0 网卡地址而不是回环地址所以会导致连接被拒。要么 bind 0.0.0.0要么 bind 容器的网卡 IP。设置密码的前提下bind 0.0.0.0 配合 protected-mode yes 是常见组合。当然如果你在云服务器上使用最好在云服务商的安全组层面把 6379 端口限制为可信 IP这才是第一道闸门。2.3 应急的方式进入容器动态修改有时候 Redis 已经跑起来了不方便重启或者你想临时改个密码看看效果可以直接用 CONFIG SET 动态修改不需要重启容器。# 修改密码 docker exec -it redis-test redis-cli CONFIG SET requirepass NewPass456 # 修改后再次执行命令需要重新认证 docker exec -it redis-test redis-cli -a NewPass456 ping PONG注意CONFIG SET 是对运行中的实例直接生效但这个修改默认不会被持久化到配置文件。一旦容器重启密码就会回到之前的值。这是最容易被忽略的点。如果想持久化可以在修改后执行 CONFIG REWRITEdocker exec -it redis-test redis-cli -a NewPass456 CONFIG REWRITE OK执行 CONFIG REWRITE 的前提是 Redis 启动时指定了配置文件路径否则它不知道写回哪个文件会返回一个错误。这也解释了为什么挂载配置文件的方式更适合生产环境你可以动态改配置也可以持久化还能保证容器重建后配置不丢。2.4 三条路线怎么选这三种方式不是互斥的我一般是这样用的方式适用场景优点缺点docker run 启动参数测试、临时环境、快速验证一条命令搞定不用准备配置文件参数多时命令很长不利于维护挂载 redis.conf生产环境、数据要持久化、配置项多配置集中管理、可 CONFIG REWRITE 持久化需要准备配置文件挂载路径要小心CONFIG SET 动态修改Redis 已运行、临时改密码、排查问题不用重启容器立即生效重启后丢失需要再持久化生产环境我的建议是挂载 redis.conf并在配置里把 requirepass、appendonly、dir 这些一次性配好。镜像版本、挂载路径、数据卷这些用 docker-compose 统一管理后面升级、迁移都省事。3. 设置完密码后如何验证和连接3.1 容器内验证密码是否生效容器内验证是最直接的因为不需要考虑网络问题。密码设置完成后我一般按这个顺序检查# 1. 不认证执行命令看是否报 NOAUTH docker exec -it redis-test redis-cli ping # 2. 错误密码认证看是否报 WRONGPASS docker exec -it redis-test redis-cli -a wrongpassword ping # 3. 正确密码认证看是否返回 PONG docker exec -it redis-test redis-cli -a MyStrongPass123 ping PONG三步走完基本能确定密码配置是正常的。如果第一步没有报 NOAUTH说明 Redis 里 requirepass 根本没生效回到第二章检查配置方式。3.2 宿主机和局域网连接验证容器内验证通过后还要从容器外部验证一次特别是排查端口映射和网络问题。在宿主机上执行redis-cli -h 127.0.0.1 -p 6379 -a MyStrongPass123 ping PONG如果宿主机没装 redis-cli也可以用 Docker 里某个容器作为客户端来连docker exec -it redis-test redis-cli -h 127.0.0.1 -p 6379 -a MyStrongPass123 ping这里有个细节如果你在容器内用 127.0.0.1 连 Redis走的是容器的回环接口只要 Redis 监听在 6379 端口就能通。要从局域网里的另一台机器连这台服务器的 Redis要用服务器的内网 IP同时要确保防火墙没挡 6379 端口。还有一种比较坑的情况Docker 容器内部执行 redis-cli 时连 127.0.0.1 没问题但宿主机上执行时报 Connection refused。这时候不要急着怀疑密码配置先检查端口映射docker port redis-test 6379/tcp - 0.0.0.0:6379如果输出正常再检查宿主机防火墙。Ubuntu 上 ufw status、CentOS 上 firewall-cmd --list-all都可能挡掉 6379。密码配好了连不上经常是这层问题。3.3 可视化客户端连接现在大家为了方便喜欢用可视化工具连 Redis。常见的有 Redis Desktop ManagerRDM和 Another Redis Desktop ManagerARDM这两个工具连接时都需要填密码。连接配置其实很简单host 填服务器 IPport 填 6379password 填你设置的密码其他默认就行。如果连接失败优先看错误信息Connection refused网络不通检查端口映射和防火墙NOAUTH Authentication required你没填密码或者填了空的WRONGPASS invalid username-password pair密码填错了Connection reset by peer通常是被防火墙或者云安全组拦截有一点我想提醒可视化工具会把密码存在本地配置里如果你用的是公用电脑记得勾选“不保存密码”之类的选项。3.4 应用代码连接时的注意点应用连接 Redis 时密码一般写在配置文件里。我举几个常见语言的示例Spring Boot 的 application.ymlspring: redis: host: 127.0.0.1 port: 6379 password: MyStrongPass123Python 的 redis-pyimport redis r redis.Redis( host127.0.0.1, port6379, passwordMyStrongPass123, decode_responsesTrue ) print(r.ping())Node.js 的 ioredisconst Redis require(ioredis); const redis new Redis({ host: 127.0.0.1, port: 6379, password: MyStrongPass123 });代码里我建议密码不要硬编码尤其不要提交到 Git 仓库。可以通过环境变量注入或者使用配置中心管理。这是很基础但很重要的安全习惯。另外一个容易被坑的点如果你修改了 Redis 密码应用里的连接池不会立刻重建之前建立的长连接可能还保活。你改了密码后测试发现旧连接还能用就误以为密码没改成功。实际上 Redis 只对新连接做认证校验已经认证过的连接不会因为改密码而中断。这一点在轮换密码时特别重要要预留应用重启窗口。4. 主从复制场景下的密码配置4.1 为什么从节点只配 requirepass 不够很多人在单机 Redis 上设置了 requirepass然后搭主从复制搭完了发现从节点同步不了数据。看从节点日志会看到类似这样的信息MASTER aborted replication with error: NOAUTH Authentication required.原因很简单requirepass 是配置“本实例对外提供服务时需要认证”但主从复制时从节点需要主动连接主节点并执行 PSYNC 命令。这时候主节点要求从节点提供认证信息从节点没有配置认证信息自然被拒绝。所以从节点上除了配置 requirepass保护从节点自身还必须配置 masterauth告诉从节点“你连主节点时用这个密码”。有一个容易被忽略的点如果主节点用的是 ACL 用户Redis 6.0 之后支持还要确认从节点用的用户有复制权限并且从节点要配置 masteruser。这里不展开但你要知道 requirepass 和 ACL 是两套体系混用时会有额外细节。4.2 docker-compose 部署带密码的 Redis 主从用 docker-compose 来管理主从比裸 docker run 清晰很多。我提供一个可直接使用的配置Redis 版本用的 7.0version: 3.8 services: redis-master: image: redis:7.0 container_name: redis-master command: [redis-server, --requirepass, masterpass, --appendonly, yes] ports: - 6379:6379 volumes: - redis-master-data:/data redis-slave: image: redis:7.0 container_name: redis-slave command: [redis-server, --replicaof, redis-master, 6379, --masterauth, masterpass, --appendonly, yes] depends_on: - redis-master ports: - 6380:6379 volumes: - redis-slave-data:/data volumes: redis-master-data: redis-slave-data:在 compose 文件所在目录执行 docker-compose up -d两个容器就起来了。这里我说一下 command 用数组方式的理由YAML 解析时如果写成纯字符串密码里的特殊符号可能在 shell 转义时出问题写成数组每个参数原样传入更可控。比如 --replicaof 后面跟着 redis-master 和 6379数组形式把这两个参数明确分开就不会被空格拆错。起来之后验证主从状态# 连主节点 docker exec -it redis-master redis-cli -a masterpass info replication # 查看 role:masterconnected_slaves:1 # 连从节点 docker exec -it redis-slave redis-cli -a masterpass info replication # 查看 role:slavemaster_host:redis-mastermaster_link_status:up主从复制正常时从节点的 master_link_status 是 up。如果你看到 down就要进入下一步排查了。4.3 主从认证失败怎么排查从节点连不上主节点最常见的原因就是 masterauth 没配置或密码不对。排查思路按这个顺序来第一先确认从节点配置的 masterauth 和主节点的 requirepass 是否一致。密码不一致是最高频的问题特别是你后来单独改了主节点的密码忘了同步更新从节点。第二确认从节点能不能通过网络访问主节点。在 compose 网络里服务名 redis-master 可以被其他容器解析如果你用 docker run 分别启动容器要确保它们在同一网络否则要用主节点的 IP 代替服务名。第三看从节点日志。docker logs redis-slave 里会明确告诉你认证失败原因。常见的是# MASTER - REPLICA sync: Master replied to PING, replication can continue... # MASTER aborted replication with error: NOAUTH Authentication required.看到 NOAUTH就是 masterauth 没配置或者配错了。修改后重启从节点容器或者执行 CONFIG SET masterauth 再执行 REPLICAOF 重新建立复制。如果后续要在主从基础上加哨兵还需要在哨兵配置里加一句sentinel auth-pass mymaster masterpass否则哨兵无法和主节点正常通信会一直判定主节点下线。5. 常见问题与避坑实录5.1 高频问题速查表这里把我在 Docker 环境下配置 Redis 密码时遇到的高频问题整理成一张表错误 / 现象可能原因解决办法NOAUTH Authentication required.没输入密码或密码为空连接时用 -a 或客户端配置密码WRONGPASS invalid username-password pair密码输错了检查密码注意大小写和空格容器重启后密码没了用 CONFIG SET 改的密码没持久化执行 CONFIG REWRITE 或改用配置文件方式配置文件挂载了但不生效redis-server 没指定配置路径启动时加 redis-server /etc/redis/redis.conf从节点同步报 NOAUTH从节点没配 masterauth设置 masterauth 为主节点密码连接 Connection refused防火墙、端口映射、bind 配置问题检查 docker port 和防火墙Docker Desktop 启动报虚拟化问题本机虚拟化没启用或 WSL2 没装好检查 BIOS 虚拟化开关、安装 WSL25.2 NOAUTH 和 WRONGPASS 是两码事很多新手分不清这两个报错。NOAUTH 代表“你没有提供认证信息”Redis 在配置了 requirepass 后收到未认证的命令会直接拒绝。WRONGPASS 代表“你提供的认证信息不对”通常是密码错误或者 ACL 用户不存在。我见过一个有意思的案例同事把密码输错了Redis 返回 WRONGPASS他以为是 Redis 配置里密码没生效反复改了几轮配置都没用。后来发现是客户端配置文件里密码多了一个空格。这种细节很容易被忽略排查时建议先在 redis-cli 里手动认证一次确认密码本身没问题再往应用层查。5.3 容器重启后密码“消失”了这个是 Docker Redis 最常见的坑。你通过 docker exec 执行 CONFIG SET requirepass 设置密码后容器一重启密码就没了。原因是 CONFIG SET 修改的是运行中的内存配置如果不执行 CONFIG REWRITE不会写回配置文件。而且如果你启动容器时根本没挂载配置文件CONFIG REWRITE 也无法生效。所以如果你的密码通过 CONFIG SET 设置并且容器重启过连接时发现不需要密码不要惊讶。重建容器后没有重新设置密码也会出现同样的情况。解决办法就是前面说的要么启动参数带上 --requirepass要么挂载 redis.conf。5.4 配置文件挂载了却不生效挂载了 redis.conf也指定了 redis-server /etc/redis/redis.conf但密码还是不生效。这种情况我遇到过几次原因千奇百怪挂载的宿主机文件路径不对容器里看到的实际是空目录redis.conf 里 requirepass 前有空格或者被注释了配置文件里 requirepass 被写成 requirepassxxx格式不对容器内 redis 用户对挂载的文件没有读取权限排查时先进入容器看配置docker exec -it redis-prod redis-cli config get requirepass如果返回空说明配置没加载成功。再检查容器内配置文件是否存在、内容是否正常docker exec -it redis-prod cat /etc/redis/redis.conf挂载目录也要检查。宿主机上的 conf 文件如果放在 /home/user/conf/ 下但挂载时写的是 /home/user/redis.conf容器里自然是空的。这些小问题排查起来很浪费时间但按顺序一步步查很快能定位。5.5 Docker 环境本身的坑也不得不提配置密码的过程中如果你连 Docker 都没跑起来那前面所有步骤都白搭。在 Windows 上使用 Docker Desktop 时最常见的启动报错是Docker Desktop failed to start because virtualisation support wasnt detected这个跟 Redis 密码无关但会卡住很多新手。解决办法一般是进入 BIOS开启 Intel VT-x 或 AMD-V确保 Windows 功能里启用 Hyper-V 和“适用于 Linux 的 Windows 子系统”如果系统提示已启用但还是不行检查 WSL 版本执行 wsl --set-default-version 2这些具体操作和系统版本关系很大不同 Windows 版本界面有差异建议根据自己的系统版本搜索解决方案。我这里就不展开讲偏了。还有一个小问题国内拉 Docker 镜像经常超时。如果 docker pull redis:7.0 一直失败可以配置镜像加速器在 Docker Desktop 的 Settings - Docker Engine 里添加 registry-mirrors或者写进 /etc/docker/daemon.json。6. 设置密码之后还应该做的安全加固6.1 不要只靠密码bind 与 protected-mode密码不是万能钥匙Redis 的安全应该是一个纵深防御体系。比如在云服务器上即使设置了密码也可以用安全组规则限制 6379 端口只允许你公司出口 IP 访问。这样即使密码泄露外部网络也进不来。Redis 的 protected-mode 也要理解清楚。当 protected-mode yes 且没有设置 requirepass 时Redis 只允许本机回环地址连接外部 IP 会被拒绝。这其实是一个保底机制哪怕你忘了设密码服务也不会直接裸奔到公网。但如果你设置了 requirepassprotected-mode 对配置了密码的实例就不构成外部连接限制了。生产环境我的建议配置组合是protected-mode yes requirepass 强密码 防火墙/安全组限定来源 IP。这样就算某一层被突破还有其他层兜底。6.2 密码怎么管理不容易泄露Docker 环境下密码的管理方式和宿主机不太一样。我见过直接把密码写在 docker-compose.yml 里的这样最省事但确实不安全尤其 compose 文件会提交到 Git 仓库的话。更稳妥的方式是使用环境变量。比如在 docker-compose 里services: redis: image: redis:7.0 environment: - REDIS_PASSWORDMyStrongPass123 command: [sh, -c, redis-server --requirepass \$REDIS_PASSWORD\]这个命令里用到了 sh -c因为要让 shell 先对环境变量做展开再把它传给 redis-server。如果你直接写 redis-server --requirepass $REDIS_PASSWORDYAML 解析时不会做 shell 展开密码可能变成空字符串。但要注意环境变量本身也是明文存在容器配置里的所以这只能算一个“避免硬编码进代码库”的改进方案。如果是生产环境建议用 Docker Secrets、K8s Secret或者外部的密钥管理系统来管理密码。6.3 用 rename-command 禁用危险命令Redis 里有几个命令在泄露或未授权的情况下特别致命FLUSHALL、FLUSHDB、CONFIG、KEYS、SHUTDOWN、DEBUG 等。通过 rename-command 可以把这些命令重命名或者禁用rename-command FLUSHALL rename-command FLUSHDB rename-command CONFIG CONFIG_2f4a9d第一个命令把 FLUSHALL 重命名为空字符串等于禁用了。第二个也是。第三个把 CONFIG 改成一个比较隐蔽的名称这样你自己还能用但外部攻击者不知道。不过在 Docker 容器里用 rename-command 有个实际影响如果你把 CONFIG 改了名后面用 CONFIG SET 动态改密码、CONFIG REWRITE 持久化配置都需要用新的命令名。我建议在配置里明确记录好改名后的命令别自己把自己锁在门外。6.4 密码轮换与变更的实操习惯密码不是设置一次就一劳永逸。我给自己定的规矩是生产环境的 Redis 密码至少每 90 天轮换一次。轮换时最怕的是直接改密码然后线上应用全部连接失败。正确的流程应该是先在 Redis 上设置新密码但保留旧密码一段时间。Redis 6.0 之前做不到一个实例同时支持两个密码通常的做法是低峰期改密码 - 同时更新应用配置 - 重启应用。Redis 6.0 之后可以用 ACL 创建多个用户一个用户对应一个密码轮换时先创建新用户应用切换到新用户再下线旧用户。这种方式不影响存量连接比较优雅。当然Docker 环境下的密码轮换还涉及到容器重建。如果你使用的是挂载配置文件方式修改宿主机 redis.conf 里的 requirepass然后 docker restart 容器就完成了轮换。建议轮换前先在测试环境跑一遍别直接动生产。另外一个好习惯无论用什么方式部署 Redis都把密码、配置文件、端口映射方案记在运维文档里。很多事故都是“人走文档留”新来的同事接手时根本不知道密码存在哪里只能重置。写到文档里后面无论是排查问题还是灾备恢复都能快速定位。最后分享一点个人体会我在实际部署中一个很深的体会是Docker 让 Redis 的启动变得极其简单一条 docker run 就能跑起来这种“简单”掩盖了很多生产环境必须考虑的细节。密码只是其中一环但它是最基础、最不能省略的一环。我自己无论在哪台机器上部署 Docker Redis第一件事就是生成一个随机强密码写进 redis.conf然后才启动。宁可前期多花两分钟也不要等到服务器被扫描工具盯上、数据被加密勒索之后再后悔。如果你现在正在用 Docker 跑 Redis而且还没设密码建议现在就动手处理掉把上面这些配置和检查过一遍顺手也检查下端口暴露范围。这件事做完你后面会少很多麻烦。
返回列表