前言

由于证书透明度日志的存在,在crt.sh等网站能够轻易查询到申请证书的子域名,造成子域名的暴露。

这种被动的子域名的暴露,存在潜在风险,也容易让有心的攻击者找到着力点。

而使用泛域名证书能够一定程度上规避这种风险。

作为轻量的个人用户,我使用Caddy来管理Web服务,而Caddy默认自动申请的是子域名证书,这代表着每一个申请证书的子域名,将永久的被暴露。

论坛内无Caddy申请泛域名证书的相关教程,故写此教程。

声明

测试的服务器为root用户,非root用户部分命令需添加sudo提权运行

本教程纯手敲,无AI

如有纰漏,请批评指正

前置

  • [ ] 一台公网服务器/VPS
  • [ ] 已使用/将使用Caddy作为Web服务器
  • [ ] 一个已解析的域名,例如example.com,并确保你对该域名有控制权

Caddy安装DNS模块

Caddy本体默认不具备泛域名证书自动申请的能力。

泛域名证书的申请通常使用DNS-01验证,需要在域名的DNS设置中添加特定的TXT记录。而Caddy默认只支持HTTP质询TLS-ALPN质询

因此需要获取带有DNS模块的Caddy。

最简单的获取方法是访问caddyserver.com/download,选择对应的系统平台和模块,下载即可。

下载完成后可以获取到名称类似caddy_linux_amd64_custom的一个二进制可执行文件。

[!NOTE]
也可以使用xcaddy在本地进行编译,但需要安装Go,我嫌麻烦没使用这种方案。

下面代码仅作参考

xcaddy build --with github.com/caddy-dns/cloudflare

将下载完成的caddy_linux_amd64_custom上传到目标服务器

先备份原来安装的Caddy,防止错误发生

mv /usr/bin/caddy /usr/bin/caddy.bak

将刚才下载到的caddy_linux_amd64_custom替换过去

mv caddy_linux_amd64_custom /usr/bin/caddy

给予执行权限

chmod +x /usr/bin/caddy

重启Caddy

systemctl restart caddy

查看模块是否成功安装

caddy list-modules | grep cloudflare

此时应输出

dns.providers.cloudflare

创建Cloudflare API令牌

前面提到,泛域名证书的申请通常使用DNS-01验证,需要在域名的DNS设置中添加特定的TXT记录。

因此要想使Caddy全自动申请,需要给它修改DNS记录的权限。

去到Cloudflare主页,点击右上角选择配置文件

来到API令牌页面,点击创建令牌,选择创建自定义令牌

按如下设置并继续

复制其中的令牌备用。

Caddyfile编辑

编辑Caddy的配置文件

nano /etc/caddy/Caddyfile

在最上方指定ACME服务器

# 全局配置:指定 ACME 服务器(Let's Encrypt 生产环境,测试用 staging 地址){    acme_ca https://acme-v02.api.letsencrypt.org/directory    # acme_ca https://acme-staging-v02.api.letsencrypt.org/directory    # 测试环境(避免频繁申请触发限制):https://acme-staging-v02.api.letsencrypt.org/directory}

在确保一切正确之前,建议先使用第二个ACME服务器,防止触发限制(限制时间累计上去还是很长的)。

添加泛域名的配置

*.yourdomain.com {    tls {        dns cloudflare xxxxxxxxxxxxxxxxx    }}

其中xxxxxxxxxxxxxxxxx即为上一节最后所复制的令牌。

这边也可以使用环境变量,更加安全

*.yourdomain.com {    tls {        dns cloudflare {env.CF_API_TOKEN}    }}

系统环境变量的添加这里不过多赘述。

之后添加的所有三级子域名就可以通用一个证书,要添加四级子域名的泛域名也是同理

*.test.yourdomain.com {    tls {        dns cloudflare xxxxxxxxxxxxxxxxx    }}

注意,不能使用*.*.yourdomain.com这种格式。

一个完整的配置文件示例

# The Caddyfile is an easy way to configure your Caddy web server.## Unless the file starts with a global options block, the first# uncommented line is always the address of your site.## To use your own domain name (with automatic HTTPS), first make# sure your domain's A/AAAA DNS records are properly pointed to# this machine's public IP, then replace ":80" below with your# domain name.# 全局配置:指定 ACME 服务器(Let's Encrypt 生产环境,测试用 staging 地址){    acme_ca https://acme-v02.api.letsencrypt.org/directory	# acme_ca https://acme-staging-v02.api.letsencrypt.org/directory    # 测试环境(避免频繁申请触发限制):https://acme-staging-v02.api.letsencrypt.org/directory}# 三级泛域名*.yourdomain.com {    tls {        dns cloudflare xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx    }}# %%%%%%.yourdomain.com {    header Strict-Transport-Security max-age=31536000;    reverse_proxy https://127.0.0.1:10000 {		header_up X-Real-IP {http.request.remote}		header_up X-Forwarded-Port {http.request.port}     		transport http {            tls_insecure_skip_verify        }    }}# %%%%%%.yourdomain.com {    header Strict-Transport-Security max-age=31536000;    reverse_proxy 127.0.0.1:10001}# %%%%%%.yourdomain.com {    header Strict-Transport-Security max-age=31536000;    reverse_proxy http://127.0.0.1:10002    log {	    output file /var/log/caddy/%%%.yourdomain.com.log    }}# 四级泛域名*.%%%.yourdomain.com {    tls {        dns cloudflare xxxxxxxxxxxxxxxxxxxxxxxxxx    }}# %%%%%%.%%%.yourdomain.com {	root * /var/www/wordpress	encode	php_fastcgi unix//run/php/php-version-fpm.sock	file_server}%%%.%%%.yourdomain.com {    header Strict-Transport-Security max-age=31536000;    reverse_proxy http://127.0.0.1:10004}# Refer to the Caddy docs for more information:# https://caddyserver.com/docs/caddyfile

运行测试

编辑好Caddyfile文件保存好后,重启caddy

systemctl restart caddy

查看caddy日志

journalctl -f -u caddy.service

当出现如下日志时,说明证书申请成功

Dec 19 15:42:01 ser873368355349 caddy[1490187]: {"level":"info","ts":1766130121.0519037,"logger":"tls.obtain","msg":"certificate obtained successfully","identifier":"*.yourdomain.com","issuer":"acme-v02.api.letsencrypt.org-directory"}

最后如果使用的是ACME测试服务器,记得换回去。