Let's Encrypt与DNS轮循

时间:2021-11-01 19:12:39

Let's Encrypt与DNS轮循

本文由网络安全研究员、securityheaders.ioreport-uri.io创始人Scott Helme发布在其个人博客中。描述了如何使用Let's Encrypt的同时兼容DNS轮循。

早些时候,我所经营的securityheaders.io经历了一段负载特别高的时期。在那段时间里,我尝试研究并整理其根本原因,想在网站后端投入更多的云资源来强化它。

DNS轮循

我想转移另一台服务器,将负载从当前的securityheaders.io运行的单个实例中分离出来。计划很简单:创建服务器,向DNS添加IP地址。 DNS循环允许您在同一个域中拥有2个(或更多)IP地址,并以不同的顺序返回,以便客户端使用不同的IP地址。这个想法最终达到了相当基本的负载平衡,使得每个服务器均分50%的流量。这么做在解决了我的负载问题同时,引入了一个新问题——更新证书。

Let's Encrypt

我使用了Let's Encrypt的证书,他们通过在你主机里的某个特殊路径里放一个简单的HTML文件,来实现DV challenge。这个工作机制在仅针对一个服务器时效果拔群,但问题是,我现在有两个。要是我其中一个服务器请求证书、托管这个HTML文件,结果Let's Encrypt将我的IP地址解析成另一个服务器的IP,但这个服务器上没有响应文件,那岂不是很尴尬?证书请求失败,那就玩不下去了?幸好,我们可以用Nginx来解决!

Nginx命名地点

在Nginx中,我们通常定义一个位置块和路径。 以下是我常用的接受ACME challenge的位置信息:

location /.well-known/acme-challenge/ {
alias /home/acme/challenges/;
try_files $uri;
}

该路径将匹配/.well-known/acme-challenge/,我已将其别名到写入challenge文件的文件系统文件夹中,try_files将检查文件的存在。 这一切都很好,直到该文件可能在另一个服务器上,这就是所在的位置。try_files指令可以采用多个参数,Nginx会遍历它们,直到匹配。 我们现在可以引入另一个参数并创建一个命名的位置:

location /.well-known/acme-challenge/ {
alias /home/acme/challenges/;
try_files $uri @proxy;
}

这告诉Nginx在本地查找该文件,如果没有找到,那么将其传递给@proxy。 我们现在需要定义命名的位置:

location @proxy {
proxy_pass http://162.243.159.108:8080;
}

在这个位置,我正在使用proxy_pass指令将请求传递给另一个服务器。 第二台服务器正在监听端口8080,专门针对已经通过的ACME challenge:

server {
listen 192.241.216.219:8080;
server_name 192.241.216.219;

location / {
return 301 https://securityheaders.io$request_uri;
}

location /.well-known/acme-challenge/ {
alias /home/acme/challenges/;
try_files $uri =404;
}
}

第二台服务器被配置为监听IP,并回答ACME challenge,或将流量重定向并返回到securityheaders.io域。 我还向UFW添加了一条规则,只允许每个服务器根据其IP地址与端口8080上的其他服务器进行通信。

解决这个问题的最佳方法可能是使用DNS challenge而不是HTTP。 之前Let's Encrypt增加了对此的支持。不过可惜的是,我是用的客户端acme_tiny并不支持这项功能,而我只想走一条快速简洁的捷径。因而上述做法仅适用于几台服务器之间周转,扩展性不佳。但如果你和我一样只想快速搞定,这个方法可以一试!