Let's Encrypt通配证书

正如 Let's Encrypt 验证(challenge) 所述,要实现域名证书的通配证书,需要使用 DNS-01 验证 :

  • DNS-01 验证 可以用于无法连接互联网或者80端口被阻塞的WEB服务器证书生成和配置

  • Let's Encrypt 使用 DNS-01 验证 来支持域名通配符的认证

DNS-01 验证 的原理就是通过在域名的TXT记录中放置特定值来证明用户控制域名的DNS系统,所以本文的手工配置部分完整体现这个过程。然后在后半段自动脚本配置部分,就是通过Cloudflare提供的DNS API来完成自动化过程。

准备工作

Debian 安装 Certbot
apt-get update
apt-get install software-properties-common
add-apt-repository ppa:certbot/certbot
apt-get update
apt-get install python3-certbot-nginx
CentOS / Rocky Linux 安装 Certbot
dnf install epel-release
dnf install certbot python3-certbot-nginx
# 如果是Apache,则使用
# dnf install certbot python3-certbot-apache
FreeBSD 安装 Certbot
pkg install security/py-certbot-nginx

手工配置

DNS-01 验证 要求在DNS服务器上提供一个特定的TXT记录 _acme-challenge.<YOUR_DOMAIN> 。举例,我的域名是 cloud-atlas.dev ,则我需要生成一个 _acme-challenge.cloud-atlas.dev 记录:

生成 cloud-atlas.dev 域名的 acme challenge DNS记录TXT
certbot certonly -d *.cloud-atlas.dev -d cloud-atlas.dev \
--preferred-challenges dns-01 --manual -m admin@cloud-atlas.dev

备注

certbot 支持多个域名签发同一个证书,我这里使用了域名通配符 *.cloud-atlas.dev 以及一个根域名 cloud-atlas.dev 。我验证过了 *.cloud-atlas.dev 不能代表 cloud-atlas.dev ,所以至少需要2个配置项,否则实际访问时 docs.cloud-atlas.devwww.cloud-atlas.dev 正常,而 cloud-atlas.dev 则会报告证书不正确

此时输出内容类似:

生成 cloud-atlas.dev 域名的 acme challenge DNS记录TXT
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for *.cloud-atlas.dev

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please deploy a DNS TXT record under the name:

_acme-challenge.cloud-atlas.dev.

with the following value:

QE7TvP8sgOgfOzHYcTeePwf5IrobSO04Lb22rHh_cmU

Before continuing, verify the TXT record has been deployed. Depending on the DNS
provider, this may take some time, from a few seconds to multiple minutes. You can
check if it has finished deploying with aid of online tools, such as the Google
Admin Toolbox: https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.cloud-atlas.dev.
Look for one or more bolded line(s) below the line ';ANSWER'. It should show the
value(s) you've just added.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue

Successfully received certificate.
Certificate is saved at: /usr/local/etc/letsencrypt/live/cloud-atlas.dev/fullchain.pem
Key is saved at:         /usr/local/etc/letsencrypt/live/cloud-atlas.dev/privkey.pem
This certificate expires on 2025-11-24.
These files will be updated when the certificate renews.

NEXT STEPS:
- This certificate will not be renewed automatically. Autorenewal of --manual certificates requires the use of an authentication hook script (--manual-auth-hook) but one was not provided. To renew this certificate, repeat this same certbot command before the certificate's expiry date.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you like Certbot, please consider supporting our work by:
 * Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
 * Donating to EFF:                    https://eff.org/donate-le
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

注意,其中在 certbot 输出了 DNS TXT record 之后,需要将这个记录添加到Cloudflare的DNS记录中,然后按照提示访问 https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.cloud-atlas.dev. 查看记录是否生效。

也可以手工执行命令 dig _acme-challenge.cloud-atlas.dev. TXT 查看添加的DNS TXT记录是否已经生效。

当记录生效以后,再按下回车键,让certbot完成证书生成。按照提示,这个证书有3个月有效期

NGINX配置

我在 Nginx配置文件的include 方法拆解修订了 NGINX反向代理https 配置,已经运行成功。这次在 在FreeBSD Jail中运行Nginx 重新部署,借用和整理上述 Nginx配置文件的include 配置:

tree 输出配置文件列表
.
├── conf.d
│   └── cloud-atlas.dev.conf
├── includes
│   ├── proxy
│   │   └── proxy_set.conf
│   ├── server_name.conf
│   └── ssl
│       └── ssl_set.conf

options-ssl-nginx.confssl-dhparams.pem

在上述结合 Let's encrypt cert配置中 ssl_set.conf 中,有两个特殊文件:

/usr/local/etc/letsencrypt/options-ssl-nginx.conf
# Intermediate: General-purpose servers with a variety of clients, recommended for almost all systems

    # intermediate configuration
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ecdh_curve X25519:prime256v1:secp384r1;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
    ssl_prefer_server_ciphers off;

    # see also ssl_session_ticket_key alternative to stateful session cache
    ssl_session_timeout 1d;
    ssl_session_cache shared:MozSSL:10m;  # about 40000 sessions
  • ssl-dhparams.pem 是一个包含Diffie-Hellman擦书的文件,这些参数对于SSL/TLS连接中启用完美前向保密(Perfect Forward Secrecy, PFS)至关重要,尤其在使用Diffie-Hellman密钥交换密码时。虽然Let's Encrypt本身提供了SSL/TLS证书,但是它并不直接生成或管理 ssl-dhparams.pem 文件。这个文件是一个独立组件,可以增强SSL/TLS配置的安全性,尤其与 NginxApache 等WEB服务器一起使用

执行以下敏玲可以生成 4096-bit Diffie-Hellman参数并保存为 dhparam.pem (注意,这个4096-bit Diffie-Hellman参数提供了强级别安全,但是会花费很长时间生成):

使用 openssl 生成 dhparam.pem
openssl dhparam -out /usr/local/etc/letsencrypt/dhparam.pem 4096

另一个生成方法是参考 moz://a SSL Configuration Generator 配置说明中方法(我没有使用)

生成 dhparam.pem
# curl https://ssl-config.mozilla.org/ffdhe2048.txt > /path/to/dhparam
curl https://ssl-config.mozilla.org/ffdhe2048.txt > /usr/local/etc/letsencrypt/dhparam.pem

自动配置

备注

由于无法解决阿里云强制"网站备案"对HTTPS TLS握手的 SNI(Server Name Indication) 嗅探和TCP reset,我目前放弃了自建 NGINX反向代理https ,改为采用 Cloudflare Tunnel 来实现内部服务器对外输出服务。所以,暂时不再搞Let's encrypt证书,这段 acme.sh 脚本自动申请和更新证书的实践我没有做。

看以后需要再搞,这里仅记录备查。

GitHub: acmesh-official/acme.sh 提供了一个完全采用shell编写的ACME客户端协议实现,非常方便用于申请证书和保持证书更新:

  • 完全采用shell编写的ACME协议客户端

  • 完整支持了ACME协议实现

  • 支持ECDSA certs

  • 支持SAN和wildcard certs

  • 只使用一个脚本来自动完成 issue, renew 和 install 证书

  • 不需要root/sudoers权限

  • 支持Docker运行

  • 支持IPv6

  • 通过cron实现更新或错误通知

待实践...

参考