Let'sEncrypt DNS 验证模板使用 Cloudflare

Hi @LotusJeff,

是的,DNS 验证仍然可用,但请不要再使用我第一篇文章中的模板。它与当前的 discourse_docker 已经不同步:

  • 其中的 cert_exists 检查在当前 Let’s Encrypt 证书链下会失败,导致每次容器启动时都会强制生成新证书,可能会触发重复证书的频率限制。
  • 其中的 nginx replace 步骤针对的行已经移动,而 add_header 步骤现在会覆盖一个无关的 ETag 标头。

你现在不再需要自定义模板。保留官方的 Let’s Encrypt 模板,并在 app.yml 中将其切换为 DNS 验证:

templates:
  # ...
  - "templates/web.ssl.template.yml"
  - "templates/web.letsencrypt.ssl.template.yml"

env:
  # ...
  CF_Token: "YOUR_TOKEN"        # 具有此区域 Zone > DNS > Edit 权限的 Cloudflare API 令牌
  CF_Zone_ID: "YOUR_ZONE_ID"

run:
  - exec:
      cmd:
        - mkdir -p /opt/dnsapi
        - curl -fsSL https://raw.githubusercontent.com/acmesh-official/acme.sh/3.0.6/dnsapi/dns_cf.sh -o /opt/dnsapi/dns_cf.sh
  - replace:
      filename: /usr/local/bin/letsencrypt
      from: "-w /var/www/discourse/public"
      to: "--dns dns_cf"

此配置的作用:

  • 官方模板仅下载主要的 acme.sh 脚本,不包含 DNS 插件。exec 步骤添加了 Cloudflare 插件,官方启动脚本会将其复制到 /shared/letsencrypt/dnsapi/。请保持 URL 中的版本与 templates/web.letsencrypt.ssl.template.yml 中的 acme.sh 版本一致(目前是 3.0.6)。
  • run: 部分在模板之后运行,因此 replace 步骤将官方启动脚本从 HTTP 验证切换为 DNS 验证。
  • acme.sh 在验证期间从环境中读取 CF_Token 和 CF_Zone_ID,并将它们与证书一起保存。每日 cron 作业无法看到容器环境,因此续期操作会使用保存的副本。

如果你使用了我的旧模板,请从 templates: 中移除相关行,并将 LETSENCRYPT_CF_TOKEN 和 LETSENCRYPT_CF_ZONE_ID 重命名为 CF_Token 和 CF_Zone_ID。

如果你已经拥有证书(来自 HTTP 验证或我的旧模板),请在重建之前将旧的 acme.sh 文件夹移至别处:

cd /var/discourse
mv shared/standalone/letsencrypt shared/standalone/letsencrypt.old
./launcher rebuild app

如果不这样做,acme.sh 可能会跳过 DNS 步骤(它会保留旧方法直到需要续期,且 Let’s Encrypt 可以复用近期的验证记录)。结果导致令牌永远不会被保存,且第一次 cron 续期会失败。使用全新的文件夹后,acme.sh 将注册一个新账户并执行真正的 DNS 验证,因此首次启动大约会多花一分钟。

在新证书正常工作之前,请保留 .old 文件夹。如果 DNS 步骤失败,启动脚本仍会覆盖 shared/ssl 中的文件,nginx 将无法启动。

acme.sh 以纯文本形式将令牌存储在 shared/standalone/letsencrypt/ 下,因此请仅授予令牌所需的权限。

截至 2026 年 11 月,已在 discourse_docker 和 Discourse latest 版本上测试通过。