前言
最近小站新增了几个二级域名,如果给每个二级域名都单独申请一个 SSL/TLS 证书未免太过麻烦,因此这次希望申请一个通配符证书,使其支持所有二级域名。
此前我一直用 Certbot 申请 Let’s Encrypt 证书,这也是 Let’s Encrypt 官方推荐的证书管理工具,具体使用方法请见我的另一篇博文 『申请Let’s Encrypt免费SSL证书并在Node.js建立HTTPS服务』。但是后来我发现 Certbot 有些不方便,因为如果用 pip
进行安装很容易遇到 Python2 和 Python3 的兼容性问题,不过后来改用 certbot-auto
脚本解决了大部分问题。
尝试过 acme.sh 后我彻底抛弃了 Certbot,因为它使用纯 Shell 语言编写,对各大发行版都很友好,也自然不存在 Python 的兼容问题。最重要的是 acme.sh 和 Certbot 都支持申请通配符证书,而且 acme.sh 在安装的时候就自动通过 crontab
配置好了自动更新证书的定时任务,不需要我们自己手动配置。因此 acme.sh 总体给我的感觉就是比 Certbot 省事不少。
使用 acme.sh 申请证书
安装 acme.sh
安装过程很简单,一句话就能搞定:1
curl https://get.acme.sh | sh
这个脚本会将 acme.sh 安装在你的家目录下的 ~/.acme.sh/
,不会污染系统和其他任何地方的文件。安装过程中会自动创建 bash 的一个 alias:alias acme.sh=~/.acme.sh/acme.sh
,因为我用的是 zsh
所以需要重新载入一下配置文件:1
source ~/.zshrc
这样一来,我们就可以在任意地方直接使用 acme.sh
命令了。此外,我们还可以设置一下电子邮箱,以便在证书过期前可以收到通知(此步骤可省略):1
acme.sh --update-account --accountemail example@example.com
申请证书
和 Certbot 一样,acme.sh 实现了 ACME 协议支持的所有验证协议,最常使用的两种方式验证是 HTTP 和 DNS 验证。因为我们要申请的是通配符证书,所以只能选择 DNS 方式验证。
acme.sh 支持很多常用 DNS 提供商的 API,具体列表及使用方式请见 官方 Wiki。如果你的 DNS 提供商不在这里,那么你就只能通过手动设置 DNS 的方式进行验证,而且没法自动更新证书,因为每次更新证书都需要进行 DNS 验证。
我使用的是腾讯云的 DNS 解析服务(其实就是 DNSPod),因此我们需要先取得 DNSPod 的密钥:登录 DNSPod(不是腾讯云),通过控制台进入密钥管理界面,新建一组密钥,保存好 ID
和 Token
。
接下来将刚刚取得的 ID
和 Token
写入环境变量:1
2export DP_Id="your ID"
export DP_Key="your Token"
然后我们就可以通过 acme.sh 申请 Let’s Encrypt 证书了,注意将 example.com
换成你的域名:1
acme.sh --issue --dns dns_dp -d example.com -d *.example.com
如果顺利的话,就会成功生成一张同时支持主域(example.com
)和所有二级域名(*.example.com
)的通配符证书。同时,acme.sh 会在本地保存你的 ID
和 Token
以便之后自动更新证书时重新验证 DNS 用。
通过 acme.sh --list
命令可以看到我们生成的证书列表,证书相关文件保存在 ~/.acme.sh/你的域名
下,其中 fullchain.cer
和 你的域名.key
分别是完整证书文件和私钥文件,是接下来配置服务端需要用到的两个文件。
安装证书到 Nginx
我们已经通过 acme.sh 申请好了证书,接下来需要将证书配置到服务端,使其支持 HTTPS。我在 『申请Let’s Encrypt免费SSL证书并在Node.js建立HTTPS服务』 一文中曾经介绍了如何在 Node.js 中配置证书,因此这里我主要介绍如何给 Nginx 配置 HTTPS 服务。
在 Nginx 配置文件 nginx.conf
中的 http 模块中新增如下配置:1
2
3
4
5
6
7
8http {
... ...
ssl_protocols TLSv1.1 TLSv1.2; # 支持协议类型,为了安全性建议这么配置
ssl_prefer_server_ciphers on;
ssl_certificate ~/.acme.sh/example.com/fullchain.cer; # 证书文件
ssl_certificate_key ~/.acme.sh/example.com/example.com.key; # 私钥文件
... ...
}
关于 ssl_protocols
的配置,其默认值为 TLSv1 TLSv1.1 TLSv1.2
,但是根据最新的 PCI DSS 合规判定标准,如果开启 TLS1.0 或更早的加密协议将会判定为不合规,因此为了满足更高的要求,推荐大家禁用 TLS1.0 ,但这样做会导致诸如 IE8 等一些老旧浏览器无法兼容,所以如果你想要支持那些古董浏览器,那么就不得不牺牲安全性了。但目前 99.999% 的浏览器都是支持 TLS1.1 及更新标准的,喜欢折腾的还可以让 Nginx 支持最新的 TLS1.3标准。
接下来,在配置文件的 server 模块中加入 443 端口的监听。例如,如果我们希望用户可以同时通过 HTTP 和 HTTPS 访问的话,可以同时监听 80 和 443 端口:1
2
3
4
5
6
7
8server {
... ...
listen 80;
listen [::]:80;
listen 443 ssl;
listen [::]:443 ssl;
... ...
}
然后,我们检查 Nginx 配置文件并重启 Nginx 即可开启 HTTPS 访问:1
2sudo nginx -t # 检查配置文件
sudo service nginx restart # 重启 Nginx 服务
我们配置完成后,可以在 MySSL 上检测一下我们部署了 SSL/TLS 的网站是否符合各项行业标准和规范。如果你禁用了 TLS1.0 的话,大概会得到这样的评级:
评到了 A
级代表我们网站的 HTTPS 服务已经十分安全,但如果你有强迫症想要评到 A+
级则需要开启 HSTS(HTTP Strict Transport Security,HTTP严格传输安全协议)。
关于 HTTP 强制跳转到 HTTPS 的实现方法以及 HSTS 更多信息,可以看看我的另一篇博文 『浅谈 HTTPS 强制跳转以及 HSTS』。