最近突然发现咱的博客打不开了,一个来自 Cloudflare 的大大的 SSL 握手失败糊在咱的脸上。(瞳孔地震)

不是说 Cloudflare 会自动续证书的喵???(小小的眼睛充满大大的疑惑)

两个证书

咱直接爬起床打开 Cloudflare,研究了会儿,终于知道是什么情况。

由于咱的博客是部署在 GitHub Pages 上,Cloudflare 只是一层代理,用户访问的时候是访问到的 Cloudflare,实际是 Cloudflare 帮咱们访问了 GitHub Pages,不过 Cloudflare 会做一层缓存。

那么这个过程中其实存在两次访问:用户访问 Cloudflare,Cloudflare 访问 GitHub Pages。

既然有两次不同的访问,那么自然也就存在两次不同的 SSL 握手,也就是说:

Cloudflare 访问 GitHub Pages 时需要一个证书;用户访问 Cloudflare 时也需要一个证书。

其中只有后者是 Cloudflare 自动为咱们管理并续期的,而前者是需要用户手动续期的。

Cloudflare设置

这也正对应着 Cloudflare 设置中的两种证书。

边缘证书(Edge-Certificate)是用户到 Cloudflare 的证书;源服务器证书是 Cloudflare 到 GitHub Pages 的证书。

而咱们在网站上查看到的是边缘证书,因为咱们作为用户,访问的是 Cloudflare。

certificate-edge

咱打开「源服务器」一项一看,果然是这个过期了,而且刚好是前几天过期的。

临时解决方法

咱尝试过把源服务器页面中的旧证书吊销,然后申请一个新的证书。也试过去 GitHub 里重新添加 CNAME,但是都没有用。

尝试访问 http 协议的网站,但是被 Cloudflare 强行重定向到 https 协议了。

关闭强制 HTTPS

可以在网站中关闭强制 HTTPS 的选项,这样用户可以通过 HTTP 协议访问网站。

但这种方法其实算是「解决不了问题就解决提出问题的人」了,因为连 SSL 都给扔了那肯定不会存在 SSL 握手失败问题了啊。

关闭源服务器的 SSL 验证

前面说到咱这种情况其实是 Cloudflare 到 GitHub Pages 的证书过期了,而用户到 Cloudflare 的证书其实是正常的。所以咱们可以让 Cloudflare 在这一阶段走 http,对于用户来说仍然是 https 协议。

在 Cloudflare 的 SSL/TLS 选项中将加密模式更改为「完全」或者「灵活」,即可让网站临时可以通过 https 访问,只不过此时 Cloudflare 在获取 GitHub Pages 内容的时候走的是 http。

这种方式时效性比较高,也可以长期使用,不过咱个人还是希望全程加密好一些,就是明明知道可以做得更好,却又浅尝辄止,心里头挺难受的。

真正的解决方法

GitHub Pages 使用的 CA —— Let’s encrypt 所颁发的证书只有短短 3 个月,而使用 Cloudflare DNS 时默认会对网站进行代理,使得 GitHub Pages 无法识别网站的 DNS 记录来自动申请证书,所以每 3 个月就会出现 GitHub Pages 无法给你自动续期证书的问题。

所以真正的最佳实践是这样子的:

  1. 关闭源服务器的 SSL 验证,使得网站暂时可以通过 https 访问。(见本文上一节)
  2. 启用证书透明监控。
  3. 关闭所有指向 GitHub Pages 的 DNS 记录的代理功能。
  4. 访问部署了 GitHub Pages 的仓库里的 Settings 的 Pages 选项,自动激活申请证书流程。
  5. 证书申请完毕并被 Cloudflare 检测到的时候,第二步的功能会给你发送邮件,此时去上一步的页面里启用「Enforce HTTPS 」。
  6. 去 Cloudflare 调整 SSL/TLS 模式为「完全(严格)」。
  7. (可选)将指向 GitHub Pages 的 DNS 记录的代理功能重新打开。

如果重新打开代理功能的话,意味着 3 个月后你需要重复上面的步骤。因为 Let’s encrypt 证书的有效期是 3 个月,而你开着代理功能会导致 GitHub Pages 无法自动续期。

但如果不重新打开代理功能,可能会导致其它 Cloudflare 相关的功能失效。具体要不要重新打开需要根据自己的需求权衡。

参考

  1. Why does my Github Pages service always show that SSL certificates are not allocated?