前情提要

经历了在腾讯云服务器上部署网站,使用 dnspod 管理域名之后,又出现了希望使用 https 访问网站的需求。同时由于资源,外链越挂越多,想要套上一个 cdn 加快网站的访问速度。当然,我不太可能去使用付费的服务,所以优先考虑了免费的解决方案。我曾经想过使用 cloudfare 的 cdn 以及它提供的 ssl 证书,但是由于 cloudfare 的国内访问速度并不理想,所以作罢。后来,我第一次在 这里 了解了又拍云,加入又拍云联盟之后就可以每年申请免费的 cdn 加速以及免费的域名证书。cdn 的配置体验比较地完美,参考了又拍云自己每一步都有的教程,以及一些博客的指导,除了在配置 dns 的 cname 解析记录的时候,提示需要删除原来的解析记录,将域名解析到 cdn 服务商提供的地址。这个操作没有被提到,但是自己了解以后可知是正常的操作之外,体验都很好。

只是,在 ssl 证书的配置上,我倒是遇到了不小的困难,折腾了一个上午的时间。

ssl 证书配置

问题描述

具体问题是这样的。按照又拍云的教程购买好 ssl 证书并且激活之后,首次访问发现网站无法访问,同时弹出响应类似如下:

1
{"msg": connection refused, "id": xxxxxxx, "code": xxxxxx}

如果是有经验的老手应该能通过提示信息得知是什么返回这样的提示信息,可惜我是第一次遇到,卡在了这里。

解决经历

阶段一

由于教程也没法预知你的错误所在,当你不知到背后的原理然后盲目的跟着教程走的时候,只要结果不对,很可能无边无际的折腾之路就开始了。

首先想到的是跟着教程某一步做错了。但是不幸地是没有发现问题。上网上求解,大多没有对问题本身的描述的简明分析。但是看了一部分问题解答,以及结合自己的一点猜想和积累,我还是觉得问题会出在服务器端的 nginx 配置上。毕竟,cdn 和证书的服务商只能帮你解决域名解析到你的服务器以及资源提供方面的问题,如果你自己的服务器没有接受或者处理发来的信息,那也是访问不到的。同时,之前配置 nginx 的时候,我在 service 主机配置块中我只配置了该 server 监听 80 端口,并提供服务。对于 443 端口没有配置,也没有监听。

想到问题,开始着手去解决。首先在服务器上查看一下 443 端口的服务状态:

1
netstat -an |grep 443

得到的回馈是 没有

对比一下 80 端口的反馈:

1
2
3
4
5
6
7
8
netstat -an |grep 80

tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN
tcp 0 0 172.17.0.5:38632 169.254.0.138:8086 ESTABLISHED
tcp6 0 0 :::80 :::* LISTEN
udp6 0 0 fe80::5054:ff:fe2b::123 :::*
unix 3 [ ] STREAM CONNECTED 14807 /run/systemd/journal/stdout
unix 2 [ ] DGRAM 18580

得出第一个结论:443 端口没有监听,没有提供服务、

正常的反馈应该是这样:

1
2
tcp        0      0 0.0.0.0:443             0.0.0.0:*               LISTEN
tcp6 0 0 :::443 :::* LISTEN

给 nginx 服务块配置监听了 443 端口的,上述的端口监听反馈顺利产生

但是问题没有解决。

阶段二

如此修改错误信息发生了变化。返回响应变成了类似:

1
{"msg": handshake failure, "id": xxx, "code": xxxx}

虽然刚刚没有解决问题,但是可以知道肯定和 nginx 的配置有关系,只是也不能认为是 nginx 的错,因为是加入了第三方提供的 ssl 证书才出现的问题

查询相关的资料指导,handshake failure 是 ssl 证书配置问题。访问 nginx 开放的 443 端口,发现端口没有给出应有的 ssl 证书,于是抛出 handshake failure。但是我申请了 ssl 证书呀。这里就体现出不懂得底层原理的问题所在。

经过大概了解得知,cdn 的工作原理类似于将站长的网站数据缓存在距离用户较近的 cdn 提供商服务器上,访问者不直接访问呢站长的服务器,而是访问 cdn 提供商的服务器,由其提供代理的服务,因此 cdn 服务商可以提供不需要再站长本地服务器进行配置的 ssl 证书。

但是,可以理解到,我们既然更新站点都是先更新自己服务器上的内容,和cdn服务器没直接接触,必然cdn需要在某时获得我们更新的。但是 cdn 不会主动来拉取这些更改,而是在用户前去访问这些内容的时候,回源到站长自己的服务器拉取所需的数据。这是就涉及到 cdn 回源的操作。这其中 cdn 服务器与站长服务器之间的通讯就类似于原本用户直接和站长服务器通讯的情况。

以上得出的结论是:出现 nginx 报错的原因是,cdn 回源到源服务器的时候,源服务器无法提供服务。 而发生的原因,是 回源协议问题。这是在 cdn 配置时候的一个选项。我第一次的回源协议是 协议跟随 + 证书验证 。本来协议跟随会使得的 cdn 回源的时候遵循用户给定的协议,当使用 http 访问域名的时候,会使用 http 回源。但是证书验证的话就强制使用 443 端口(https)回源,当时我甚至没有监听这个端口,别提提供服务了,所以自然拒绝访问。

而第二次的 handshake failure,问题应该出在 443 端口配置不全。协议跟随是一样的,问题还是出在证书验证。因为开启了证书验证,cdn 服务器会验证站长的 源服务器 ssl 证书是否有与在 cdn 服务商这里注册的证书相同。显然我都没有在源服务器上的证书,必然用户会被拒绝访问。这应该适用什么情况呢?适用的情况是上传自己的证书到 cdn 服务商的情况。用户已经有自购证书,并上传了,所以源站上会有和 cdn 服务器相同的证书。注意这两种证书应当是不同类型的。cdn 提供的免费证书应该是共享 ssl 证书,原理似乎是不同的。

那关闭证书验证是否就可以?不全可以,如果你使用协议跟随,默认 https 访问网站的时候吗,依然会出现第一次相同的 connection refused 错误,当然 http 访问的时候是可以的,只不过就失去了我们希望有 https 服务的初衷。关键在于:使用了这种 ssl 证书,原站的 443 端口本不应该提供服务,你的 ssl 证书是由 cdn 代理服务器上提供的!访问你的服务器只是用来回源,使用 http 协议就可以了, 我们应当关闭证书验证,然后换到 http 回源。这样才能解决问题。

省流

方法其实在这篇 博客 中:在 cdn 回源协议配置中,关闭证书验证,选择 http 回源。

总结

很多时候,一键配置的省力,往往带来的是出错时不懂原理的瞎折腾。当你不知到背后的原理然后盲目的跟着教程走的时候,只要结果不对,很可能无边无际的折腾之路就开始了。