Laravel 学院今天凌晨四点到上午十点不能访问问题定位及修复细节通报
今天早上起来(起来有点晚,九点多了),像往常一样打开学院网站,竟然报错了:
然后瞅一眼 QQ 群、微信,各路人马都来反馈问题,于是赶紧打开邮件,查看 Sentry 报警,竟然有这么多报警邮件:
这些报错邮件都无一例外都指向了同一个问题:
MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for details about the error
大意是 Redis 被配置为保存 RDB 快照,但是现在无法写入磁盘,那么这无非是两个问题,一个是没有权限,一个是空间不足,于是立即登录到 Redis 所在服务器,分别查看了权限和空间都没有问题,当务之急是先修复问题,然后再定位原因,所以立即在服务启动 redis-cli
,将 stop-writes-on-bgsave-error
设置为 on
(该选项在 Redis 配置文件中默认配置为 yes
,表示 Redis 报错时停止写入并报错,我们将其设置为 on
表示忽略报错,继续执行):
#redis-cli
127.0.0.1:6379> config set stop-writes-on-bgsave-error no
运行完这条命令再次访问学院,已经恢复访问了,但是这是个治标不治本的临时解决方案,只是忽略错误,但是错误仍然在发生,所以我回到服务器上继续定位问题。
我打开 Redis Server 的日志文件 /var/log/redis/redis-server.log
,想从这里寻找蛛丝马迹,果不其然,发现了大量报错日志:
14632:C 09 Nov 08:58:14.020 # Failed opening .rdb for saving: Read-only file system
30211:M 09 Nov 08:58:14.120 # Background saving error
这里显示的错误信息将问题的源头指向了 .rdb
是一个只读文件,可是明明系统默认的 dump.rdb
文件时有写入权限的啊(在 Redis 配置文件中,可以看到 dbfilename
选项和 dir
选项,分别用于配置 RDB 文件名和所在目录)。于是我在 Google 上搜索了这个报错信息,果然找到了我要的答案:Redis Getshell漏洞。我在 redis-cli 交互 Shell 中查看 CONFIG GET dir
和 CONFIG GET dbfilename
命令,得到了这篇文章中一样的结果,返回的目录和文件名分别是 /var/spool/cron
和 root
,与 Redis 默认配置不符。
故障的原因很简单,就是因为 redis-server
是公开运行的,而且我是两台机器访问这台 Redis 服务器,所以把 Redis 配置文件中 bind
选项配置为了 0.0.0.0
,以便让多个机器可以访问,但是没有配置任何权限和 iptables
过滤规则,这样导致的结果是外网可以自由访问我的 Redis 服务器,这种故障会导致 Redis 被 block,无法处理和响应请求(具体原因为何绕过默认配置文件写入/var/spool/cron
可以看这个issue),幸而 Redis 服务器默认通过 redis
用户运行,而不是 root
,否则后果更严重!(使用阿里云 ECS 的童鞋要注意这个问题了!!)
既然问题的根找到了,解决起来也就有了方向,那就是在 iptables
配置过滤规则,只让自己信任的 IP 访问 Redis 服务器的 6379 端口,举个例子,我们配置允许 192.168.0.1
和 192.168.0.2
这两个 IP 访问 6379 端口(具体 IP 以你自己服务器的公网 IP 为准,允许自身访问 IP 可以配置为 127.0.0.1
):
# 禁止任何 IP 访问 6379 端口
iptables -I INPUT -p TCP --dport 6379 -j REJECT
# 允许指定 IP 访问 6379 端口
iptables -I INPUT -s 192.168.0.1 -p tcp --dport 6379 -j ACCEPT
iptables -I INPUT -s 192.168.0.2 -p tcp --dport 6379 -j ACCEPT
# 保存并生效
iptables-save
然后我们需要通过 kill -9
杀死 redis-server
进程,并通过 redis-server /etc/redis/redis.conf
重新启动 Redis 服务器,再次访问学院,可以发现能够正常访问,Redis日志文件也不再有报错信息了,在本地测试连接 Redis 服务器,也拒绝访问了,至此,问题完全修复,大功告成。
再次提醒,使用阿里云 ECS 的同学,或者其他云服务的同学,需要自查下自己的 Redis 服务器,如果也是配置允许多台机器访问的话,有没有做这种过滤,否则某一天也会有这种被 block 导致依赖 Redis 的服务挂掉的风险。
注:如果你是将 Redis 服务器安装在应用所在服务器,并且只有这么一台机器,不用这么大费周章的配置,只需在 Redis 配置文件中将
bind
选项设置为127.0.0.1
只允许本机访问即可。
7 Comments
哈哈,这是我在学院发布的第999篇文章,马上破一千了,Mark一下:
很强啊,不愧学院君
学习了,多谢分享这些实用的知识点
强行破千。。。
学院君,难道你之前的redis服务没有设置密码吗? 感觉为redis设置一个密码不就可以解决非法链接的客户端了吗?
对 设置密码也可以 之前没有设置 忘掉了
学习了 每天必看