2024年10月5日,由于停电,导致所有服务不可用,但恢复供电后,部分服务却无法公网访问,只能内网访问。这是怎么回事呢?且听我娓娓道来。
一、组网环境:埋下的伏笔
2019年的某一天,运营商突然禁止了443端口的入站访问,拥有IPv4公网IP却还需要用非常规端口,而当时许多三方软件对非常规端口的支持不佳,种种因素,使得DDNS自建Nextcloud网盘方案迎来了总结。
要如何继续维持这种大流量服务的运转呢?既然无法直接连接服务器了,那就找服务器中转一下吧。于是哼哧哼哧地搭好了中转。家里的服务器主动连接到中转服务器,然后由中转服务器来提供服务。数据就这么通过中转服务器转发一下,顺利交给家里的服务器来处理。
但长期使用下来,显然在家里访问服务器,是没必要再通过中转服务器绕一圈回来的。于是几个月前,正好闲着,于是就学着公司的架构,在路由器上做了个简单的DNS分流,将相关域名的返回值设置成了内网地址。然而谁知道,这个小小的优化,却为这次事件埋下了邪恶的种子。
二、故障发生:突然不可用
时间来到了10月5日,变故也正是在这天发生的。这一天,服务器所在位置刚好停电检修,于是整个服务器掉电了,这也是整个事件的导火索。于是Mewwoof的所有服务,从早上八点开始就无法访问了。不过嘛,反正国庆在出去玩,白天也没有访问服务的需求,也就顺其自然了。
然而,当在外面玩了一整天,晚上回来需要往网盘上传些照片和视频时,问题突然就爆发了出来。三台设备,两台能正常访问和上传视频,而另一台设备传了两张图片后,突然就死活连不上了,报502 bad gateway。清除应用缓存,重连WiFi,切换移动数据,问题依旧存在。在那台设备上使用浏览器访问网盘,也是报502。但是换其他设备,一切又都正常了。真是怪哉!!!
三、排障过程:艰难的定位
“要不更新一下软件试试?”我尝试使用那台设备下载最新版本的软件,结果下载速度不过几kb/s,这要下到猴年马月啊,我在其他设备下载好后,想通过微信传输安装包,微信却又重命名为了.apk.1,还没法重命名。而要是把安装包通过网盘传输……想多了,设备就是访问不了网盘呢,怎么下载。看来更新软件的路是走不通了,况且还已经成功上传了一两张照片,要是客户端问题,显然不应该是这种现象。
于是我抛开了节日的快乐,迅速打开了笔记本,开始检查服务问题。既然报502,那说明大概率是网关问题。网盘服务流经两个网关,一个是公网的网关,另一个是内网的网关。内网的网关直接与CGI应用程序通信,并服务内网设备,公网的网关则通过反向长连接与内网网关通信,为公网设备提供服务。
不假思索地,就可以排查掉内网网关的问题。因为一旦是内网网关报的502,那理应所有设备都异常,不可能是部分设备能用,而其他设备不能用。
那似乎只能是公网网关的异常了。可我百思不得其解,为什么内网设备还是会访问公网服务,以及公网服务到底出了什么问题?
我首先验证确实是公网网关的问题:curl 公网网关IP,发现确实返回了502;而curl 内网网关IP,则正常返回302到登录页面:
curl -kv --header "Host: drive.example.com" https://public.example.com
curl -kv --header "Host: drive.example.com" https://10.8.8.1
问题出在哪儿呢?不妨继续思考。502表示无法连接到服务器,而且之前从来没有发生过,因此可以排除配置的原因,因为最近没有修改过配置。
那只能是因为,真的无法连接到内网网关了。而这个连接由内网网关主动建立并维持,那么问题可能出现在这个通道上。
难道是frps服务挂了?但是通过 ss -talnp 可以查到frps的进程呀,端口也监听在正确的地址。
那大概率是穿透客户端,即内网网关的问题了。到内网网关机器上查frpc日志,发现果然报了大量无法建立连接的错误。为什么无法连接呢?ping一下公网服务器,发现无论是ipv4,还是ipv6,都非常顺畅啊。
那怎么办?查配置呗。于是打开frpc的配置文件,一行一行检查。先检查目标服务器,正确的。正确吗?嗯?不对。
于是默默修改了一个参数,重启服务,curl一下,啪,通了,再看那台设备,刷新,啪,页面打开了。
原因让人啼笑皆非。frpc穿透设置的服务器地址是drive.example.com。而之前对这个域名做了分流,内网的直接解析到内网地址……于是这次服务重启后,frpc将公网服务器解析到了内网地址,不断尝试连接的实际上是内网服务器。
而解决办法,就是将这个配置修改成未被分流的域名 public.example.com。
至于为什么只有那台设备在尝试访问公网的服务器,原因也很简单,那台设备的DNS设置没有遵循DHCP提供的路由器地址,而是用了dnspod的。
四、痛定思痛:大坑的防御
航空界有个经典的奶酪模型。指的是一个事故,往往是击溃了多条防线,或者说多条防线同时失效,才能得以爆发。而将其应用到我们的这个案例中,这个问题也是击溃了多条防线。下面进行列举并给出可能的解决方案:
第一、域名使用规范标准的防线。专用的服务要用专用的域名,不能和其他业务共用,避免相互影响;
第二、应急预案演练的防线。这个问题只需要重启一次穿透客户端就会暴露,为什么不进行几次断网断电演练呢,需要周期性地找服务的低谷期来提前暴露问题,顺便还可以打打系统补丁;
第三、服务维护变更标准化流程的防线。在变更后,应该全局搜索一下配置关键字,这样就可能扫描出意料之外的影响。
OK,连花了两个晚上完成了这篇总结,洗洗睡了。想到啥以后再补充。