需求

之前使用腾讯云的证书,只有 90 天的有效期,每次到期了就要手动部署,下载 .crt.pri 文件(或者是 .key ,吧,记不清了),然后手动移动到对应的文件夹。一个域名就要花好几分钟,效率很低。

因此问了问 AI,有什么更好的方案。最终决定采用 Let’s Encrypt 和 Certbot 结合的自动化方案。

哪个证书

这里需要澄清一个问题。Cloudflare 有一个 Edge Certficate 的方案,可以自动化地续期证书,这个证书是浏览器访问域名时用到的证书。

然而在 Cloudflare 的服务器和源服务器之间,仍然需要自动化处理证书。本文要解决的是后者。

1
用户 -- Edge Certificate --> CF 代理服务器 -- 本文要解决的证书 --> 源服务器

环境配置

安装 snapd

1
2
sudo apt update
sudo apt install snapd -y

安装 Certbot

1
2
3
sudo snap install core  # 确保 snap 运行环境是最新的
sudo snap refresh core
sudo snap install --classic certbot # 安装 Certbot

创建软链接

1
sudo ln -s /snap/bin/certbot /usr/bin/certbot

一番操作下来,Azure 的网络是最好的,aws 还是慢。但 Azure 好贵啊,额度用完后估计要迁移到 aws 了,又是一番折腾。

申请证书

1
sudo certbot --nginx -d example.com -d www.example.com

理想很丰满,实际上会报错:

1
2
3
4
5
6
7
8
9
10
Certbot failed to authenticate some domains (authenticator: nginx). The Certificate Authority reported these problems:
Domain: 1.xialing.icu
Type: unauthorized
Detail: 【ipv6 地址】: Invalid response from http://1.xialing.icu/.well-known/acme-challenge/XXD52kwolPjOLvUbELt9FrNr2FM

Domain: xk.xialing.icu
Type: unauthorized
Detail: 【ipv6 地址】: Invalid response from http://xk.xialing.icu/.well-known/acme-challenge/K0Y6JWQN4OKW0fB9-ZjBp3jKRL

Hint: The Certificate Authority failed to verify the temporary nginx configuration changes made by Certbot. Ensure the listed domains er and that it is accessible from the internet.

原因是因为我的服务器走了 Cloudflare 的代理,导致域名解析到的不是源服务器,而是 Cloudflare 的代理服务器。

难不成每次都要关闭代理?肯定有更好的方案。

使用 Cloudflare API 进行 DNS 方式验证

获取 Cloudflare API 令牌

令牌管理页面,新建一个 API 令牌。

使用“编辑 DNS”权限(只修改 DNS 记录,不影响其他功能)

这里需要注意的就是,限制一下允许调用这个 API 的 IP 范围,设置为自己的服务器 IP 即可。不设置的话,不允许前进。

安装 Cloudflare DNS 插件

1
sudo apt install python3-certbot-dns-cloudflare

配置 API 令牌

1
2
mkdir -p ~/.secrets/certbot
nano ~/.secrets/certbot/cloudflare.ini

添加:

1
dns_cloudflare_api_token = YOUR_CLOUDFLARE_API_TOKEN

设置权限:

1
chmod 600 ~/.secrets/certbot/cloudflare.ini

申请证书

1
sudo certbot certonly --dns-cloudflare --dns-cloudflare-credentials ~/.secrets/certbot/cloudflare.ini -d example.com -d www.example.com

这里注意,有可能失败,报错:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Certbot failed to authenticate some domains (authenticator: dns-cloudflare). The Certificate Authority reported these problems:
Domain: tongji.xialing.icu
Type: dns
Detail: DNS problem: NXDOMAIN looking up TXT for _acme-challenge.tongji.xialing.icu - check that a DNS record exists for this domain

Domain: www.xialing.icu
Type: dns
Detail: DNS problem: NXDOMAIN looking up TXT for _acme-challenge.www.xialing.icu - check that a DNS record exists for this domain

Domain: xialing.icu
Type: dns
Detail: DNS problem: NXDOMAIN looking up TXT for _acme-challenge.xialing.icu - check that a DNS record exists for this domain

Hint: The Certificate Authority failed to verify the DNS TXT records created by --dns-cloudflare. Ensure the above domains are hosted by this DNS provider, or try increasing --dns-cloudflare-propagation-seconds (currently 10 seconds).

Hint 中提示的很清晰了,可以尝试增大等待时间,对我来说,25 秒可行。

最后返回的成功信息是:

1
2
3
4
5
6
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/www.xialing.icu/fullchain.pem
Key is saved at: /etc/letsencrypt/live/www.xialing.icu/privkey.pem
This certificate expires on 2025-06-25.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.

配置 Nginx

接下来需要在 nginx 中配置使用的证书:

1
2
3
4
5
6
7
8
9
10
11
12
server {
listen 443 ssl;
server_name example.com www.example.com;

ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

location / {
root /var/www/html;
index index.html index.htm;
}
}

因为这里可能要多次复制粘贴,所以分享几个 vim 的使用技巧:

  • 复制两行:2yy
  • 粘贴:p
  • 打开新文件::tabe
  • 切换到下一个文件::tabn
  • 切换到上一个文件::tabp

然后测试一下配置是否正确:

1
sudo nginx -t

最后重启 nginx

1
sudo systemctl restart nginx

设置自动续期

Certbot 会自动续期:

1
Certbot has set up a scheduled task to automatically renew this certificate in the background.

不过它并不会自动重启 nginx,所以导致 nginx 可能使用的仍然是旧证书。

为此,设置一个钩子:

1
sudo vim /etc/letsencrypt/renewal-hooks/post/nginx-reload.sh

写入:

1
2
#!/bin/bash
systemctl reload nginx

然后赋予执行权限:

1
sudo chmod +x /etc/letsencrypt/renewal-hooks/post/nginx-reload.sh

終わり。