工具
让我们加密是业余爱好者级别的几乎所有 HTTPS SSL/TLS 证书的来源,使用通过 HTTP 或 DNS 提供的挑战提供证书的自动颁发和更新功能。
具体来说,网站必须通过 DNS 质询才能获得 形式的域名的通配符证书,方法是将相关域名(或该域名的 ,Letsencrypt 会尊重该域名的 )的记录*.example.org
设置为特定值。和 的控制均通过检查处的记录进行验证。TXT
CNAME
example.org.
*.example.org.
TXT
_acme-challenge.example.org.
Letsencrypt 证书的自动续订通常由Certbot。
鸭子DNS,虽然不是那么普遍,但却是一个流行的免费动态 DNS 提供商。帐户可以注册表单的域example.duckdns.org.
并通过简单的 HTTP API更新其A
/记录。通常,它与传统 DNS 提供商一起使用,通过指向(例如) at 。AAAA
www.example.org.
CNAME
example.duckdns.org.
此外,相同的 API 还允许用户设置或清除TXT
其域的记录,特别是为了与 letsencrypt 互操作。(为 设置的所有A
/ AAAA
/TXT
记录example.duckdns.org.
都镜像到*.example.duckdns.org.
。)
目标
目标是使用合理标准的 Letsencrypt/Certbot 设置来通过 DuckDNS API 传递 DNS 挑战。
具体来说,我们希望为example.org
和都提供证书*.example.org
,其中我们控制example.duckdns.org.
并且普通 DNS 提供商提供服务*.example.org. CNAME example.duckdns.org.
。
如果我们有多个前端域名都指向同一个后端网络服务器,我们甚至可能希望处理、、、、所有四个example.org
域名的*.example.org
续订example.com
。*.example.com
简单的解决方案
要使用 DuckDNS API,请按照其规格,我们可以把这一行放在一个简单的 shell 脚本中(在重命名相应的环境变量,并对 DuckDNS 域和身份验证令牌进行硬编码)
V1='example.duckdns.org'
V2='our-duckdns-auth-token'
V3="$CERTBOT_VALIDATION"
curl -s -S "https://www.duckdns.org/update?domains=${V1}&token=${V2}&txt=${V3}"
并要求 Certbot 使用以下命令运行它
certbot certonly -v --manual --manual-auth-hook my-script.sh -d '*.example.org.'
此后将自动处理续订。
问题
DuckDNSTXT
每次只为一个域名保留一条记录,任何附加记录都会覆盖旧记录。至少在使用默认 Certbot 时,Letsencrypt 会要求请求证书的客户端一次性通过所有挑战。
也就是说,虽然上述简单的解决方案适用于*.example.org.
,但如果我们将其扩展到
-d 'example.org' -d '*.example.org'
然后 Letsencrypt 将运行两个挑战,每个域一个,并期望TXT
同时存在两个不同的记录_acme-challenge.example.org.
。Certbot 将运行两次挂钩脚本,TXT
添加的第二条记录将覆盖第一条记录,只留下一条。因此,至少有一次挑战将失败,无法颁发完整证书。在问题的四域扩展版本中,将有一次成功和三次失败。
由于 Letsencrypt 无处不在,DuckDNS 也相当流行,因此有零散的参考资料 [1][2][3] 在互联网上对此问题进行了讨论(包括非公开的 DuckDNS google 群组中的更多内容),但我在任何 Stack exchange 网站上都找不到任何相关信息。
答案1
下面整理了我发现的一些解决方案。
混合挑战
DNS 质询仅对通配符证书是绝对必要的。我们可以要求 Certbot 使用 HTTP 质询(如果可用)--preferred-challenges
。
certbot certonly -v --manual \
--preferred-challenges 'http,dns' \
--manual-auth-hook my-script.sh \
-d 'example.org.' -d '*.example.org.';
这需要一个--manual-auth-hook
可以设置任一挑战的脚本。再次咨询文档,其形式大致为
if [ -z "$CERTBOT_TOKEN" ]; then
# DNS auth
# See original question
else
# HTTP auth
# echo "$CERTBOT_VALIDATION" > "/the/correct/dir/$CERTBOT_TOKEN"
fi;
加上相应的清理逻辑。这样就可以example.org
顺利*.example.org
地并行验证。这也是我选择的方法,因为它最干净。
为了比较,
- 这基本上是涉及的所有工具的正确使用。
- 设置稍微复杂一些。
- 它不处理问题中的(更一般的)四域
.org
&情况,因为两个不同的通配符挑战仍然会发生冲突。.com
example.org.
和域名example.com.
可以指向单独的 DuckDNS 域名,例如example0.duckdns.org.
和example1.duckdns.org.
,但免费 DuckDNS 帐户一次最多只能使用五个域名。只要只有一个通配符证书,任意域名组合(例如 、*.example.org
、example.org
、example.com
)都www.example.com
可以正常工作。
暴力破解串行挑战
Certbot 将始终尝试并行运行所有挑战,但只要一个域名的挑战成功,通过该挑战的 Certbot 客户端将继续被授权在三十天内兑换该域名的证书。
也就是说,虽然简单的解决方案
certbot certonly -v --manual --manual-auth-hook my-script.sh \
-d 'example.org' -d '*.example.org' ;
第一次执行会失败,但第二次执行会成功,因为的控制example.org
已经过验证并且*.example.org
不会遇到冲突。
续订应该以类似的方式进行。典型的 Certbot 安装将每天检查即将到期的证书,并尝试续订剩余时间少于 30 天(总共 90 天)的证书。第一次尝试续订此证书将失败,这意味着 Certbot 将在一天后再次尝试,届时它将成功。(我还没有找到一种无需等待 60 天即可正确测试此证书的方法。)
为了比较,
- 虽然这基本上无害,但这在某种程度上滥用了 Letsencrypt API。我不确定速率限制是否值得关注。
- 设置非常简单,但是每次更新时都会产生大量虚假错误消息。
- 除了速率限制之外,这还可以处理大量碰撞挑战。
不同的客户端,上游修复
从我在(非公开的)DuckDNS Google 群组中看到的情况来看,DuckDNS 管理员最近才被告知存在此问题,并正在考虑将限制增加到TXT
一次两个记录。这将完美地涵盖最常见的情况。我不确定他们是否想提高限制,因为整体服务必须尽可能简单(且操作成本低廉)。
据我所知,Certbot 不必并行运行所有挑战(尽管这可能是使用 ACME 协议更“自然”的方式)。甚至似乎还存在其他可以连续运行挑战的客户端,但截至撰写本文时,我还没有仔细研究过任何客户端。这应该完全是客户端实现的问题,因为 Letsencrypt 授权缓存行为无论如何都存在。
当然,理想的解决方案是 Certbot 具有连续挑战功能。我不知道为什么这会很难实现,除非它只在像这样的某些狭隘情况下有用。