Let's Encrypt:「无法获取颁发者证书」(错误 2),强制重新签发及速率限制

Discourse 的安装或重建可能会拒绝有效的 Let’s Encrypt 证书,并出现以下错误:

C=US, O=Let's Encrypt, CN=YR2
error 2 at 1 depth lookup: unable to get issuer certificate
error fullchain.cer: verification failed

我在 RSA 证书成功签发后立即复现了此问题。失败的检查会触发不必要的 --force 请求,这可能导致 HTTP 429 / rateLimited / “too many certificates”(证书过多)错误。在我的安装环境中,随后 ECDSA 证书的签发被阻止,HTTPS 未能启动。

对于受影响的证书链,每次重建或容器重启都可能触发 RSA 和 ECDSA 证书的不必要强制重新签发,即使这些证书仍然有效。成功的重新签发会消耗 Let’s Encrypt 的配额,因此反复重建或重启可能会迅速达到速率限制。

如果 HTTPS 继续使用已安装的证书正常工作,这种情况可能未被察觉。删除并使用相同主机名重新安装实例并不会重置该配额。如果新安装中缺少所需的证书,受速率限制的签发可能会阻止 HTTPS 启动。

修复方案

在 templates/web.letsencrypt.ssl.template.yml 文件中,cert_exists() 函数内部,请替换以下内容:

- openssl verify -CAfile <(openssl x509 -in ca.cer) fullchain.cer
+ openssl verify -untrusted ca.cer fullchain.cer

保持该函数的其余部分不变。修正后的模板必须包含在下一次容器重建中,以更新 /usr/local/bin/letsencrypt。

如果您已经受到速率限制,补丁不会重置配额。请应用此修复,并尊重 retry after(重试后)时间,然后再尝试进一步的签发。

PR: FIX: preserve the Let's Encrypt issuer chain in cert_exists - Pull Request #1136 - discourse/discourse_docker - GitHub

确认证书链验证问题

在容器内受影响的证书目录中(/shared/letsencrypt/<hostname> 或 <hostname>_ecc),请比较以下命令的输出:

openssl verify -CAfile <(openssl x509 -in ca.cer) fullchain.cer
openssl verify -untrusted ca.cer fullchain.cer

在复现的案例中,第一个命令以错误 2 失败;第二个命令返回:

fullchain.cer: OK

原因与验证

openssl x509 -in ca.cer 仅从颁发者捆绑包中读取第一个证书。在观察到的 YR2 链中,它丢弃了到达 ISRG Root X1 所需的交叉签名 Root YR。

-untrusted ca.cer 提供了完整的颁发者捆绑包,同时将信任锚保留在系统存储中,从而保留了 2021 年修复 (#576) 的初衷。

我在容器内验证了实际的 RSA 和 ECDSA 证书链。使用补丁进行完全重建后,复用了两个现有证书:出现两条 Skip 消息,两个证书链均已安装,且 nginx -t 测试通过。

相关报告

411097
413078

PR: FIX: preserve the Let's Encrypt issuer chain in cert_exists - Pull Request #1136 - discourse/discourse_docker - GitHub

1 个赞