Bootstrap

https 握手失败问题排查全记录

缘起

事情是这样的,系统中有一个功能是发起 http 请求,请求地址是可配置的,系统已经在测试环境跑了有段时间了,无论是http,还是 https 请求,那都是经历过历史考验的。然而,上线后,测试同学配置了个 https 请求进行回归测试的时候,神奇的事情发生了,报错 503,报错信息: 。

首先判断是不是配置的 url 地址有问题,执行 curl 命令:

然而,控制台显示了正常的返回信息。

现在分析现在的情况:

打开搜索引擎,输入: https handshake failed,然后一排排结果显示在了屏幕上。发扬蚂蚁啃骨头的精神,试图从一条条冷冰冰的文字中找到一些线索。然而,这触及到了知识盲区,并没有找到确切的答案。

这次搜索结果主要包含如下可能:

...

为什么只列上面的这些成果,因为本人的知识储备只能支撑他对这些结果有些印象。

俗话说的好:网络不通,还得抓包。祭出神器——tcpdump。先抓了一个请求失败的包:

前三条是 tcp 三次握手,说明网络是通了,然后客户端发起了 Client Hello 包,这是 https 握手的第一步,下一行是服务端正常的 ack 响应。奇怪的是最后一行,服务端发送了 RST 包,终止连接,这也就是请求报 handshake failed 的原因了。于是缩小问题排查范围,再次打开搜索引擎,输入:https handshake failed rst ack,新的结果显示在页面上,又是枯燥的找线索环节。这次搜索到的结果也差不多。

那就把搜索到的线索一个个进行排查吧。

首先,证书问题,应该不太可能,因为有的 https 地址可以调通,有的不可以。排除掉该原因,当然也可能是误判。

第二条,服务端不支持客户端的加密算法,感觉很像这个原因,因为存在如下情况:请求失败的 url 指向的服务器不支持客户端的加密算法,请求成功的 url 指向的服务器支持客户端的加密算法,这能说得通。

怎么排查呢?先看一下客户端发送 Client Hello 所支持的算法:

这,怎么下手?还是先看下通过 curl 调用该接口成功时使用了什么算法吧。

不愧是调用成功了,这数据包看着都舒服。https 加密算法是服务端来确定的,在 Server Hello 包中发送给了客户端,那就看看这个包的内容。

然鹅,这个加密算法客户端发送的 Client Hello 包是包含的。再次经历 debug 滑铁卢。这可咋办,那只能看看第三原因了,也就是 SNI。可这完全不懂啊。

所谓书到用时方恨少,不会的早晚要补上。只能先简单看下 SNI 是个什么东西了。于是又是一波搜索引擎走起。关于 SNI 就不做太多介绍了,大家可以自己搜一下。简单来说,就是一台服务器配置了多个服务,即有多个域名,当然相应的配置了多个证书,他们监听了相同的端口,这样客户端发送 Client Hello 后,服务端不知道该用哪个证书进行回应,因此服务端就选择困难症犯了,干脆撂挑子不玩了。而 SNI 就是解决这个问题的,SNI 是 TLS 的一个扩展,客户端发送 Client Hello 包时会携带一个域名,这样服务端接收到请求后就可以根据这个域名去找相应的证书了。

那就看看是不是这个原因,先看看成功的请求有没有带这个东西。打开 Client Hello 包的详情,还真找到了。

图中打码的地方就是域名了。再看看请求失败的包,有没有该扩展:

没有!!!胜利就在眼前了,赶紧试一试是不是这个原因。本人用的 lua-resty-request 这个库,搜一下,只要加个 server_name 参数就可以了。赶紧改了一下,发布,测试!成了!!!

总结

排查问题是枯燥,需要不断的搜索,尝试,尤其是在自己不熟悉的领域,更是需要大量的尝试。但问题解决后带来的快乐是十分值得的。

不过,还是得多读书,懂的越多,会的工具越多,排查起问题来才能有更多思路,共勉。

排查问题三板斧: