最近,在同事多次推荐下,终于将家里 NAS 的 Nginx 换成了 Caddy,顺便看了一下相关的各种资料,写篇文章记录一下

为什么要申请 HTTPS 证书?

因为 http 是明文的,从客户端到服务器会经历多跳路由,其中任意一个开启捕获模式,都会将 http 的明文会话记录下来。为了避免密码明文在网上传输,以及照片、文本被窥视,还是套上一层加密比较稳妥。之前我的选择是用 ssh 隧道映射端口到本地,再用普通的 http 访问。但是 iOS 的后台保活时间比较短,ssh 隧道很容易断,而 ssh 建立连接的步骤又比较多,在移动网络等复杂环境下,体验很差,有一次在坐小火车去公司的路上,拉个 RSS 多次断开链接后,一怒之下决定换装 https 了

HTTPS 证书如何申请?

之前就折腾过 Let’s Encrypt 的证书,用的 certbot 做证书申请,当时自己搞了个域名,用的 HTTP-01 方式完成 ACME 挑战的。ACME 是 Automatic Certificate Management Environment(自动证书管理)的简称,ACME 挑战是为了验证申请人对证书附带域名具有所有权。常见的方式有两种,一种是 HTTP-01 方式,需要在服务器的 80 端口提供服务,在指定路径下放置指定密文,密文验证通过后即可获颁证书;另一种是 DNS-01 方式,需要对域名设置 TXT 记录,在记录中包含密文。后一种方式权限更高,可以申请通配符证书(应对二级、三级域名)。因为是自用, 家庭宽带不适宜直接在 80 端口和 443 端口提供服务 ,所以我选择了 DNS-01 方式做验证

顺便说一下,联合创办 Let’s Encrypt CA,发起 Let’s Encrypt 和 HTTPS Everywhere 的计算机科学家 Peter Eckersley,已经在 9 月 5 号去世了,RIP

HTTPS 证书一般由什么组成?

跟 ssh 的公钥私钥有点像,也是有公钥和私钥的区分。私钥一般是一个后缀为 .key 的文本文件,里面记着 RSA 的私钥。公钥一般是名为 fullchain.cer 的文本文件,包含了完整的证书链,即个人站点的证书由中间证书的公钥签发,验证通过后证明个人站点证书非伪造,的确来自中间商,然后中间证书一般由各大根证书商签发,各大根证书商的证书是预装到操作系统和浏览器里的,可以轻易验证

为什么用 Caddy?

Caddy 的全自动 HTTPS 很方便,只要你的域名提供商支持 API,而 Caddy 有对应的插件,填上密钥后 Caddy 就会自动申请证书了。不过之前证书都申请下来了,目前没有使用这个功能。主要是 Caddy 的配置简化一点,不需要针对 Websocket 额外配置,反向代理也不用像 Nginx 一样配一堆 proxy_set_header。Caddy 令我最惊喜的是 v2.6.1 已经支持了 HTTP3,即以前 Google 开源的 QUIC 协议

HTTPS 的未来

最早的 HTTPS 协议只是在 HTTP 协议上套上了一层 TLS 加密,但是实际使用的场景只有银行、政府等领域比较多一点,因为速度比较慢,不仅要经历 TCP 的三次握手,应用层的 TLS 还得做一次握手,每次请求资源都握手一遍,那肯定受不了了。虽然根据 HTTP 1.1 协议,一个连接可以发多个请求,握手时间平摊到每个请求上能显得小点,但是必须等一个请求完成后才能发下一个,如果请求涉及数据库查询,比较耗时,那还是得等。Google 为此推出了 SPDY 协议,即后面的 HTTP 2,支持了多路复用,针对 HTTP Header 做了 HPACK 压缩,冗余数据不用发,同时连接实现了复用,不会因为一个耗时请求阻塞后续请求的发送,提升了性能。HTTP 2 剩余的问题是 TCP 层面的,一个是 TCP 固有的三次握手,其次是偶尔丢包时 TCP 拥堵调度算法的慢启动(Google 为此开源了 BBR 算法并进入 Linux 内核),以及 TCP 丢包引起的类似 HTTP 1.1 耗时请求的 head of line blocking(HOL blocking)。换成 QUIC(后面标准化后的 HTTP 3)协议,可以省掉 TCP 层握手的时间,直接进入 TLS 握手和业务数据的发送阶段。同时,后续可以更具灵活性的做拥堵控制算法了。没有了固定连接,也就不会有 TCP 的 HOL blocking 问题了。还能通过前向纠错以一点带宽为代价,将偶尔丢的包补回来,减少时延

那怎么知道一个服务器能不能支持 HTTP 3 呢?可以通过 HTTP 2 查询,不过开销会比较大,所以后面 DNS 记录新增了一种 HTTPS 记录,用来记录是否支持 HTTP 3 及服务的 UDP 端口。客户端同时发送 HTTPS 记录和 A/AAAA 记录查询,如果有 HTTPS 记录则针对对应的端口做访问,目前 HTTP 3 已经正式标准化,支持 HTTPS 记录的客户端还没完全跟上,有兴趣可以 阅读这篇文章