ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Docker中Redis密码设置全攻略:从快速方案到生产级配置

Docker中Redis密码设置全攻略:从快速方案到生产级配置 先说个真实场景上个月帮朋友处理一次线上事故他们团队把 Redis 部署在公网服务器上端口一暴露第二天数据就被人清空了勒索提示挂在了首页。原因非常简单——Redis 默认没有密码谁连上谁就是管理员而服务又被 docker 的端口映射直接暴露到了公网。这个锅配置的人至少要背一半。所以今天我想把一件事彻底讲透用 Docker 跑 Redis 时密码到底该怎么设置才能既快速又稳还能扛住生产环境的检验。这篇文章会很实用从最快的一行命令方案讲到我个人更推荐的生产级配置挂载方案再配合验证手段和排查技巧保证你看完能直接动手复制。这篇内容适合这几类人刚接触 Docker 和 Redis 的新手想给自己本地开发环境补上密码这道锁也适合已经在生产环境跑 Redis 但密码配置还停留在临时加一下的运维同学查漏补缺。不绕弯子我们直接开始。1. 为什么非得给容器里的 Redis 加把锁1.1 一句话讲清 Redis 的密码机制Redis 的密码认证核心其实就一个配置项requirepass。你在redis.conf里写上这一行或者启动时通过参数传进去之后客户端每次连接后都必须执行AUTH 密码命令认证通过才能继续发其他指令。否则服务端会直接返回NOAUTH Authentication required。这里有个容易忽略的点requirepass设置的是默认用户的密码。Redis 6.0 之后引入了 ACL 访问控制列表你可以创建更多细粒度的用户并单独给每个用户分配权限。但requirepass仍然是绝大多数场景里最基础、最常用的认证开关也是这篇文章的核心。很多同学第一次在 Docker 里跑 Redis 时看到官方镜像一把梭docker run -d --name redis -p 6379:6379 redis:7.0这样跑起来确实方便但注意-p 6379:6379会把容器端口映射到宿主机所有网卡的 6379 端口上。如果这台服务器有公网 IP那么公网所有人只要扫到端口就能连上你的 Redis然后执行FLUSHALL、SET key 勒索信息、往 cron 里写挖矿脚本——一条龙服务比你自己部署还顺畅。1.2 不加密码的代价裸奔的 Redis 有多危险我见过很多开发环境无所谓的态度但问题的关键在于你自己觉得无所谓不代表公网扫描器也觉得无所谓。互联网上随时有大量自动化脚本在扫 6379 这类默认端口扫到就尝试连接连上就执行危险命令。一旦 Redis 以 root 权限运行甚至可能通过写文件的方式反打服务器后果远超丢数据三个字。有些人会说我服务器有防火墙只放行部分 IP。但配置防火墙的人会离职安全组规则会配错Docker 的端口映射也可能因为各种原因把端口送到公网。密码这道门是最后一层也是最直接的一层防线。不是说有了密码就万事大吉但没有密码前面几道防线只要任何一个环节漏了你就是裸奔。1.3 Docker 场景下的特殊坑在普通本机安装 Redis默认配置下它只监听127.0.0.1外部网络访问不了风险相对可控。但到了 Docker 里很多人会写-p 6379:6379效果等同于强制让 Redis 对外网开放。你如果在宿主机上执行netstat -tlnp会看到 6379 端口监听在0.0.0.0上。这个半开门的状态很容易被忽略因为本地开发时你自己连接一切正常完全感受不到外部风险。所以我很早就养成一个习惯只要是用 Docker 跑 Redis哪怕只是本机开发环境我都会顺手设一个密码。成本不过一秒钟收益是省掉无数个被入侵后的救火之夜。2. Docker 里设置 Redis 密码的三条路怎么选2.1 三条路径对比给容器里的 Redis 开启认证常见做法有三种我把它们的核心差异总结成一张表方案操作方式持久性适用场景主要缺点启动命令传参redis-server --requirepass 密码依赖启动命令容器重建不丢本地开发、临时环境密码会出现在 shell 历史/进程列表里不适合生产配置文件挂载宿主机写好redis.conf用-v挂载进容器稳定容器重建后配置仍在生产环境、需要精细控制配置的场景需要先准备好配置文件多一步操作容器内临时设置redis-cli CONFIG SET requirepass不持久重启后丢失应急修改、临时调试重启即失效容易造成密码怎么又没了的困惑从持久性来看前两种才是真正值得日常使用的第三种只是救急用的临时手段。2.2 我的选型逻辑如果是本地起一个 Redis 给项目联调用我会直接用命令行参数方案因为快。一条命令起一个带密码的 Redis完事。如果是生产环境或者这个 Redis 要长期跑、要主从复制、要调各种参数那我几乎无脑选配置文件挂载的方式。理由很实在配置文件的表达力比命令行参数强太多requirepass只是其中一行你还可以同时管理bind、protected-mode、maxmemory、appendonly等一堆关键项。而且 Docker Compose 管理起来也顺一份 yaml 文件把端口、挂载、网络全管了比人脑记命令强太多。2.3 先弄懂安全三件套在动手之前我建议你先理解 Redis 安全相关的三个配置项否则可能会遇到设置了密码却依然被连上的诡异情况。第一个是requirepass不多说了这就是认证开关。第二个是protected-mode。它默认是yes。在 protected-mode 开启且没有配置密码的情况下Redis 只允许本机回环地址连接外部连接会被拒绝。但注意逻辑顺序一旦你设置了密码protected-mode 就会被绕过Redis 会接受带密码的外部连接。所以设置密码后服务从拒绝外网变成了允许外网但必须凭密码进入这时你要确保密码强度足够。第三个是bind。它决定 Redis 监听哪些网卡地址。容器里通常不强行改bind因为 Docker 的网络映射有自己的规则但你要心里有数-p 6379:6379就是打开门bind和protected-mode是在门上加的门闩和锁requirepass则是最后那把钥匙。整套组合拳打起来才稳。3. 5分钟快速方案docker run 命令行直接指定密码3.1 一条命令搞定密码设置最快速的做法是在启动 Redis 时把密码作为redis-server的参数传进去这会覆盖镜像默认的启动命令docker run -d \ --name redis \ -p 6379:6379 \ redis:7.0 \ redis-server --requirepass MyPass123456!拆开看一下这段命令--name redis给容器命名后续管理方便-p 6379:6379做端口映射redis:7.0是镜像最后面的redis-server --requirepass MyPass123456!会把默认 CMD 替换掉等价于容器启动后执行带--requirepass参数的 redis-server 命令。有一点要特别提醒如果你的密码里有$、、!、空格、单双引号等特殊字符务必用单引号把密码包住防止 shell 把它当成特殊语法。我甚至建议养成习惯密码里就算只有字母数字也统一加单引号省得哪次密码里带个$结果被环境变量替换掉半天排查不出来。3.2 已经跑起来的 Redis 容器怎么办已经用无密码参数跑起来的容器如果你只是想临时加个密码可以直接进容器执行docker exec -it redis redis-cli CONFIG SET requirepass MyPass123456!执行成功后再连接这个 Redis 就必须带密码了否则任何命令都会被拒绝。但这里有个大坑CONFIG SET 只是临时改内存配置容器一重启就会丢失。所以这个命令只适合应急不适合作为长期方案。真正想要让配置在容器重建后依然生效还是要回到命令行参数或者配置文件挂载的路子上。如果是已经跑起来的容器想彻底改成带密码启动干脆删掉重建最干净docker stop redis docker rm redis # 然后用带密码的完整命令重新 run容器删除前记得确认数据有没有通过 volume 做持久化。如果启动时没挂载数据卷容器一删数据就跟着没了这一点务必先确认。3.3 如何验证密码真的生效配置完不是结束验证才是关键。我一般会执行三步第一步不输入密码直接连docker exec -it redis redis-cli PING如果密码已生效这里会返回NOAUTH Authentication required说明服务端确实在要求认证。第二步用正确密码认证后 pingdocker exec -it redis redis-cli -a MyPass123456! PING正常会返回PONG。注意-a是直接在命令行把密码传给 redis-cli执行后 Redis 会自动执行 AUTH。这一步如果通了说明密码本身没问题。第三步故意试一个错误密码docker exec -it redis redis-cli -a WrongPass PING此时应该返回WRONGPASS invalid username-password pair。出现这个报错说明认证机制是好的只是密码不对。三步走完密码这块就心里有底了。4. 生产环境推荐redis.conf 挂载方式一次配好4.1 先弄到一份官方的默认配置为什么生产环境我不推荐命令行传参因为命令行的方式把密码暴露在 docker inspect 能看到的进程参数里了而且也不方便管理杂七杂八的 Redis 配置项。配置文件挂载的优势在于所有配置都在一个文件里有注释、有选项、可审计、可版本管理。第一步先从官方镜像里把默认配置拷出来docker run -d --name redis-temp redis:7.0 docker cp redis-temp:/usr/local/etc/redis/redis.conf ./redis.conf docker rm -f redis-temp有的镜像里可能没有现成的redis.conf路径也可能不同。如果docker cp提示文件不存在可以改用下面的方式直接从容器内查找docker run --rm -it redis:7.0 find / -name redis.conf 2/dev/null找到路径后再用docker cp拷出来。拿到官方默认配置后在宿主机上编辑它就拥有了一份稳定可控的 Redis 配置底座。4.2 修改配置文件里的关键行打开redis.conf找到requirepass这一行。默认情况下它是被注释掉的# requirepass foobared把它取消注释改成你自己设置的强密码requirepass MyPass123456!同时建议你检查一下这几个关键行bind 127.0.0.1在容器默认网络下bind 不是最重要的但你可以保留默认不强行改成0.0.0.0。protected-mode yes可以保留开启用于没有密码时的保护措施设置密码后它不再阻拦外部认证连接。appendonly yes如果数据不能丢把 AOF 持久化打开避免容器一停数据全空。这里还有个关于密码性命攸关的细节Redis 的密码在配置文件里是明文的。所以这个文件本身要当成敏感文件管好不要随手提交到公开仓库。尤其是一些公司内部仓库对配置文件不做权限控制密码就这么躺在代码库里跟没设一样。4.3 用卷挂载方式启动配置文件准备好之后启动命令就变成了docker run -d \ --name redis \ -p 6379:6379 \ -v /path/to/redis.conf:/usr/local/etc/redis/redis.conf \ redis:7.0 \ redis-server /usr/local/etc/redis/redis.conf注意最后的路径redis-server 后面跟的路径是容器内的路径要和挂载的右边一致不能写成宿主机路径不然 redis-server 根本找不到配置文件会直接报错退出。挂载文件时还有两个小坑第一个宿主机上 redis.conf 的权限不要设成 777。容器内 Redis 进程通常以 redis 用户运行如果配置文件权限过于开放或者宿主目录没给读权限可能出现 Redis 启动失败或者读不到配置。一般644是比较稳妥的权限。第二个如果同一个目录下还有其他 Redis 相关文件挂载整个目录可能覆盖镜像里默认目录的原有文件。所以精确挂载单个配置文件最省心。4.4 用 Docker Compose 管起来更省心正经生产环境谁还一行行敲docker run用 Docker Compose 才能把配置沉淀成代码别人 clone 下来就能跑。我这里给一个可以直接抄作业的docker-compose.ymlversion: 3.8 services: redis: image: redis:7.0 container_name: redis restart: always ports: - 6379:6379 volumes: - ./redis.conf:/usr/local/etc/redis/redis.conf - ./data:/data command: [redis-server, /usr/local/etc/redis/redis.conf]然后在同目录下执行docker compose up -d这样 Redis 的配置、数据目录、重启策略全都固化下来了。以后同事在新机器上要起一个一模一样的环境只需要一份 yaml 加一份 redis.conf。有一点补充如果你不想让密码明文躺在 redis.conf 里可以用环境变量做一层间接替换。常见做法是用envsubst在容器启动时把模板里的${REDIS_PASSWORD}替换成环境变量比如environment: REDIS_PASSWORD: ${REDIS_PASSWORD}这样一来redis.conf 模板文件里写着requirepass ${REDIS_PASSWORD}而实际密码从宿主机环境变量里读取。这样至少能避免运维同学从 Redis 配置就直接看到生产密码。不过我还是要泼一盆冷水这种做法增加了模板渲染的复杂度核心风险依然存在因为最终 redis.conf 在容器运行时还是明文密码。如果对安全要求极高那就应该把密码托管到专门的密钥管理服务启动前拉取写入这已经不是一篇博文能覆盖完的范围了。对绝大多数团队来说redis.conf 放在受控目录 限制文件读取权限已经能挡住大部分低级泄露。5. 设置完密码后必做的三件验证事5.1 用命令行亲自验证一遍配置完 Redis 后在宿主机上执行docker exec -it redis redis-cli进入交互界面后输入PING如果提示NOAUTH Authentication required说明密码已生效。接着执行AUTH MyPass123456! PING返回PONGOK。这说明密码认证完整可用。这一步虽然简单但很多人会跳过结果后面连业务代码一直报错排查半天才发现密码根本没配置上。养成改完配置立刻在命令行验证一次的习惯能省掉大量后续排查时间。5.2 用客户端和可视化工具测一遍真实连接命令行验证通过后还要从外部视角再验证一次毕竟业务代码不会通过docker exec连 Redis。我平时常用 Redis Desktop Manager 或 Another Redis Desktop Manager 这类可视化客户端来测。连接时填主机 IP、端口、密码能正常连上并看到 DB 里的 key就说明从外部网络访问也是通的。如果是用连接串访问Redis 的标准 URI 格式长这样redis://:MyPass123456!127.0.0.1:6379/0注意主机名前面那个冒号表示用户名为空后面紧跟密码。这种格式在很多语言的 Redis 客户端和网页工具里通用。但我建议不要把这串东西粘贴到带日志采集的网页工具或 CI 配置里因为密码一旦进了日志就是二次泄露。5.3 业务代码和主从场景都要测业务侧的验证往往被忽略。举个例子如果你的项目用 Spring Bootapplication.yml里就要这样配置spring: redis: host: 127.0.0.1 port: 6379 password: MyPass123456!如果你用 Pythonredis-py 的话import redis r redis.Redis(host127.0.0.1, port6379, passwordMyPass123456!) print(r.ping()) # True如果业务代码里没配密码启动后你会发现所有缓存读写都在报NOAUTH Authentication required这其实反而是好消息说明服务端认证已经生效。另外如果你的 Redis 是主从架构还有一个极其容易踩的坑从节点同步主节点数据也需要密码。只给主节点设置requirepass是不够的从节点必须额外配置masterauth项否则从节点会一直报MASTER - REPLICA sync started之后的认证失败同步永远处于断线状态。# 从节点配置文件里要加这一行 masterauth MyPass123456!这也是很多同学在单机上测试没问题一上主从就发现数据不同步的原因——密码挡在了主从之间而你只开了主节点的门。6. 常见问题与排查技巧实录6.1 问题速查表把我在实际环境里遇到过的典型问题整理成一份速查表建议收藏问题现象可能原因解决方案连接后执行命令报NOAUTH Authentication required客户端没配密码或密码没认证在客户端配置密码或先执行AUTH 密码报WRONGPASS invalid username-password pair密码错误或用户名不对检查密码是否有多余空格/特殊字符重新确认容器起来了但外部连不上端口映射写错、防火墙限制、bind 限制检查docker ps端口映射宿主机放行对应端口重启容器后密码丢失用的是CONFIG SET临时设置改为配置文件挂载或在启动命令传参主从同步一直失败从节点没有配置masterauth在从节点配置文件加masterauth 密码配置文件挂载后 Redis 启动失败路径写错、挂载文件权限不足、配置文件格式有误看docker logs检查路径和权限用redis-server /path/redis.conf本地验证配置设置了密码但docker inspect里能看到明文密码写在了命令行参数或环境变量里生产环境用配置文件挂载并限制文件权限6.2 我的排查三板斧遇到 Redis 连接不上或者认证异常我一般按三步排查速度最快第一步看容器日志docker logs redis日志里如果出现Ready to accept connections说明 Redis 已经启动成功如果是配置解析错误Redis 通常会打印出具体哪一行有问题。这一步能过滤掉一半以上的低级错误。第二步确认容器内实际生效的配置docker exec -it redis redis-cli CONFIG GET requirepass如果返回空结果或者(error) NOAUTH Authentication required说明密码已经生效。这一步能直接看出我以为配置了和实际是否配置了之间的差距。第三步确认端口映射docker port redis如果这里显示的 IP 是0.0.0.0说明端口是对外开放的。检查完这三步绝大多数连接问题都能定位到具体环节。6.3 几个独家避坑小技巧第一个技巧不要用 root 用户去跑 Redis 容器也尽量别把端口映射到宿主机高位端口就觉得安全。秩序上端口改了扫描器照样能发现你。安全感的来源只能是密码 网络策略 持久化三层同时到位。第二个技巧密码中包含特殊字符时先在外面用单引号包住再传入测试无误后再考虑写进配置文件。写进配置文件时不需要额外转义但如果你用envsubst之类工具做模板替换就要小心特殊字符会不会在渲染时被吞掉。第三个技巧本地开发环境也建议设置密码但可以设得弱一点比如dev-redis-pass这种。这样既不影响开发效率又避免了裸奔成习惯上了生产忘了改的问题。很多生产事故都是开发习惯无意识地带到了生产环境。第四个技巧Redis 6.0 的 ACL 能力值得关注。如果你希望只给某个应用开放有限的命令权限和 key 空间可以在配置文件里定义自定义用户user appuser on AppPass123! ~cache:* read write这比全局requirepass更精细应用即使被拖库或者被注入能造成的破坏也有限。不过注意ACL 配置相对复杂至少先把requirepass这套基础认证做扎实再考虑升级到 ACL。第五个技巧密文不进版本库密钥不走日志。这一点再怎么强调都不为过。凡是包含 Redis 密码的文件仓库里要么忽略要么用密钥管理系统的引用代替。日志打印连接串时把密码打码或者直接不打印。最后再分享一个我个人的习惯不管什么环境只要用了 Docker 跑 Redis我都会顺手把密码设上。这个习惯救过我很多次因为容器化的服务太容易因为一个-p参数把自己暴露出去。你永远不知道哪个端口映射会在某一天变成一个意外的大门但密码这道锁能让门开了也不会被轻易闯入。安全这件事很多时候就是多花一分钟做对一件小事然后省掉后面好多个熬大夜的补窟窿时间。
返回列表