使用 Nginx + acme.sh 配置 HTTPS 网站
使用 Nginx + acme.sh 配置 HTTPS 网站
之前我发起了免费域名项目 zz.nic。所有 zz.ac 域名要求必须使用 HTTPS 建站,这就涉及到 SSL 证书。虽然市面上有很多商业 HTTPS 证书可选,但基于 ACME 标准申领的免费 SSL DV 证书完全够用了。可我发现好多朋友还是不会申请和配置。本文分享基于 Nginx 和 acme.sh 工具来自动化免费申请和更新 ACME SSL DV 证书的方案。
ACME 标准是一项自动验证域名所有权并对域名签发 SSL DV 证书的技术。SSL 证书分 DV/OV/EV 三种,DV 只验证域名所有权。其他两种请参考我的专门 文章。
验证域名有多种方法,称其为 challenge。最简单的办法就是 HTTP-01 验证。你先把域名解析到服务器,然后在 80 端口跑 HTTP 服务。在发起 ACME 证书请求之前,你需要先生根据一定的规则生成 token,而且这个 token 需要通过 http://<你的域名>/.well-known/acme-challenge/<TOKEN>
来访问。接着就向支持 ACME 的 CA 请求签发 DV 证书。CA 收到请求后会访问你的验证 Token。如果匹配成本,就会返回签发的证书。
这种方法只能验证你可以控制域名,但无法知晓域名的过期时间。所以 CA 签发的证书不能太长,一般是三个月有效。最近 Let’s Encrypt 正在推动将 ACME 证书的有效时间进一步缩短为六天。
很多朋友总觉得九十天太多,不如商业证书一年有效方便。其实不然。现在整个证书行业都在向自动化方向演进。哪怕是一年有效的证书,最好也能实现自动化签发和更新,不然每年手动更换也很麻烦,而且容易出问题。
如果实现了自动化更新,到底是几天过期就不重要了,一年、半年、六天,都没区别。但总得来说,证书的有效时间越短,潜在的安全隐患就越小。
有人会说自动化哪有那么容易?其实现在已经很容易了。本文跟大家分享的 acme.sh 就是其中的后起之秀,全部使用 shell 脚本实现,非常轻量,支持多家 CA 和多种验证方式。因为是脚本,所以几乎支持各种类 UNIX 平台,应用十分广泛。
另外,本文假设使用 Nginx 作为 HTTP 服务器。
首先,将域名,比如 example.zz.ac,解析到你的服务器。服务器需要有公网 IP 地址。
⚠️Important接下来所有步骤请使用 root 账号执行!
然后配置 Nginx 在 80 端口对外提供 HTTP 服务。这一步是为了让 CA 能正常下载 HTTP-01 Token。
server {
listen 80;
server_name _;
root /var/www/html;
}
接着我们安装 acme.sh 工具:
curlhttps://get.acme.sh|[email protected]
这里需要指定自己的电子邮箱。之前 Let’s Encrypt 会给该邮箱发送证书过期提醒,现在这个服务取消了。但无论如何,需要提供邮箱。有些 CA 还要求注册用户,我建议就别考虑了。
脚本执行完毕会把 acme.sh 安装到 /root/.acme.sh
目录。
接下来我们申请 ACME 免费 DV 证书。
这里的 --issue
表示签改新证书, -d example.zz.ac
指定证书域名,可以多次使用 -d
参数指定多个域名。 -w
表示 HTTP 服务的根目录,acme.sh 会在该目录下生成 .well-known/acme-challenge/<TOKEN>
。
acme.sh 默认使用 ZeroSSL 来签发证书。你也可以通过 --server letsencrypt
参数来要求使用 Let’s Encrypt 签发证书。早期 Let’s Encrypt 不支持签发 ECC 椭圆曲线证书, ZeroSSL 支持。便现在 Let’s Encrypt 也支持了。不过 ZeroSSL 了限频策略要比 Let’s Encrypt 要宽松许多。我通常使用 ZeroSSL,如果遇到问题会切换到 Let’s Encrypt。两者没有本质区别。
命令执行后会稍等片刻就会完成验证并从 CA 处下载证书文件到 /root/.acme/example.zz.ac_ecc
目前。注意,现在默认都使用 ECC 证书,原来的 RSA 证书因为同等加密强度下需要更长的密钥,为了加快 TLS 握手,已经不推荐使用 RSA 证书了。
在证书文件目录中有两个文件最重要,分别是:
-
example.zz.ac.key
证书私钥 -
fullchain.cer
证书文件并附件中间 CA 证书链
现在我们可以把证书配置到 Nginx 服务器了:
server {
listen 443 ssl;
server_name example.zz.ac;
ssl_certificate /root/.acme.sh/example.zz.ac_ecc/fullchain.cer;
ssl_certificate_key /root/.acme.sh/example.zz.ac_ecc/example.zz.ac.key;
# ...
}
这就配置好了 HTTPS 站点。为了让大家默认使用 HTTPS 站,我们还需要将 HTTP 请求重定向到 HTTPS 站。但这里有一个问题,不能影响 HTTP-01 验证!
server {
listen 80;
server_name _;
location / {
return 301 https://$host$request_uri;
}
location /.well-known/acme-challenge/ {
root /var/www/html;
}
}
在上面的配置中,我们单独处理 /.well-known/acme-challenge/
路径,指定它的根目录为 /var/www/html
。其他所有请求都会命中 location /
从而被 301 跳转到对应的 HTTPS 路径上。Nginx 在匹配规则的时候路径越长优先其越高。虽然 .well-known
的规则在下面,但它不会受 location /
影响。
最后我们配置定时任务在每天的凌晨一点自动更新 SSL 证书,并且还会让 Nginx 重新加载配置。这样就再也不用担心 SSL 证书过期了✌️
0 1 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null && /usr/sbin/nginx -s reload
我现在维护的网站和 Web 服务越来越多。除了少数使用 Go 语言开发直接对外不使用 Nginx 代理之外,其他站点都基于 Nginx + acme.sh 实现自动申请和更新 SSL 证书,基本不会有什么问题。
心动不如行动。赶紧申请你的 zz.ac域名并建立自己的 HTTPS 站点吧~