Let's Encrypt 问题

据称,自 8 月 1 日起,letsencrypt 服务发生了一些变化。我的证书直到今天才过期,因此我仅在今天受到了这些变化的影响。Discourse 用于管理 letsencrypt 服务的脚本已更新,默认使用名为 ZeroSSL 的新服务,而非 letsencrypt。不幸的是,ZeroSSL 似乎要求先注册一个电子邮件地址才能工作,因此今天早上当我的现有证书过期时,网站无法正常运行。

经过大量调查,我弄清楚了问题所在。虽然我已经暂时绕过了这个问题,但我认为我所做的修复未必是“正确”的解决方案。首先,以下是我在日志中看到的错误信息:

[Wed 01 Sep 2021 05:33:58 PM UTC] Reload error for :
[Wed 01 Sep 2021 05:34:03 PM UTC] Using CA: https://acme.zerossl.com/v2/DV90
[Wed 01 Sep 2021 05:34:04 PM UTC] No EAB credentials found for ZeroSSL, let's get one
[Wed 01 Sep 2021 05:34:04 PM UTC] acme.sh is using ZeroSSL as default CA now.
[Wed 01 Sep 2021 05:34:04 PM UTC] Please update your account with an email address first.
[Wed 01 Sep 2021 05:34:04 PM UTC] acme.sh --register-account -m my@example.com
[Wed 01 Sep 2021 05:34:04 PM UTC] See: https://github.com/acmesh-official/acme.sh/wiki/ZeroSSL.com-CA
[Wed 01 Sep 2021 05:34:04 PM UTC] Please check log file for more details: /shared/letsencrypt/acme.sh.log

我尝试在容器内手动注册账户,虽然系统提示注册成功,但重启容器后仍然出现相同的错误。于是,我追踪了相关脚本,执行该操作的脚本位于容器内的 /etc/runit/1.d/letsencrypt。以下是原始脚本:

#!/bin/bash
/usr/sbin/nginx -c /etc/nginx/letsencrypt.conf

issue_cert() {
  LE_WORKING_DIR="${LETSENCRYPT_DIR}" /shared/letsencrypt/acme.sh --issue $2 -d mudtoemanor.baronshire.org --keylength $1 -w /var/www/discourse/public
}

cert_exists() {
  [[ "$(cd /shared/letsencrypt/mudtoemanor.baronshire.org$1 && openssl verify -CAfile ca.cer fullchain.cer | grep "OK")" ]]
}

########################################################
# RSA cert
########################################################
issue_cert "4096"

if ! cert_exists ""; then
  # Try to issue the cert again if something goes wrong
  issue_cert "4096" "--force"
fi

LE_WORKING_DIR="${LETSENCRYPT_DIR}" /shared/letsencrypt/acme.sh \
  --installcert \
  -d mudtoemanor.baronshire.org \
  --fullchainpath /shared/ssl/mudtoemanor.baronshire.org.cer \
  --keypath /shared/ssl/mudtoemanor.baronshire.org.key \
  --reloadcmd "sv reload nginx"

########################################################
# ECDSA cert
########################################################
issue_cert "ec-256"

if ! cert_exists "_ecc"; then
  # Try to issue the cert again if something goes wrong
  issue_cert "ec-256" "--force"
fi

LE_WORKING_DIR="${LETSENCRYPT_DIR}" /shared/letsencrypt/acme.sh \
  --installcert --ecc \
  -d mudtoemanor.baronshire.org \
  --fullchainpath /shared/ssl/mudtoemanor.baronshire.org_ecc.cer \
  --keypath /shared/ssl/mudtoemanor.baronshire.org_ecc.key \
  --reloadcmd "sv reload nginx"

if cert_exists "" || cert_exists "_ecc"; then
  grep -q 'force_https' "/var/www/discourse/config/discourse.conf" || echo "force_https = 'true'" >> "/var/www/discourse/config/discourse.conf"
fi

/usr/sbin/nginx -c /etc/nginx/letsencrypt.conf -s stop

由于电子邮件注册未能生效,我决定在脚本中添加选项,让 acme.sh 脚本改回使用原始的 letsencrypt,而不是 ZeroSSL。相关操作说明可参考以下网站:https://community.letsencrypt.org/t/the-acme-sh-will-change-default-ca-to-zerossl-on-august-1st-2021/144052

首先,我尝试将该命令作为脚本的第三行,紧跟在 /usr/sbin/nginx -c /etc/nginx/letsencrypt.conf 语句之后:

/shared/letsencrypt/acme.sh --set-default-ca --server letsencrypt

日志显示默认 CA 已更改,但紧接着又出现了关于 ZeroSSL 未注册电子邮件的相同错误。日志中该消息与错误信息之间约有 6 秒的延迟,这让我推测 acme.sh 脚本可能通过环境变量维护状态信息,而后续命令执行时处于不同的上下文环境中,导致变量丢失。因此,我最终不得不修改 letsencrypt 脚本中所有调用 acme.sh 的地方,在每个命令中添加 --server letsencrypt 参数。完成此操作并重启容器后,letsencrypt 生成了新证书(而非 ZeroSSL),网站也恢复正常。

以下是我使用的修改后的 letsencrypt 脚本:

#!/bin/bash
/usr/sbin/nginx -c /etc/nginx/letsencrypt.conf
/shared/letsencrypt/acme.sh --set-default-ca --server letsencrypt

issue_cert() {
  LE_WORKING_DIR="${LETSENCRYPT_DIR}" /shared/letsencrypt/acme.sh --server letsencrypt --issue $2 -d mudtoemanor.baronshire.org --keylength $1 -w /var/www/discourse/public
}

cert_exists() {
  [[ "$(cd /shared/letsencrypt/mudtoemanor.baronshire.org$1 && openssl verify -CAfile ca.cer fullchain.cer | grep "OK")" ]]
}

########################################################
# RSA cert
########################################################
issue_cert "4096"

if ! cert_exists ""; then
  # Try to issue the cert again if something goes wrong
  issue_cert "4096" "--force"
fi

LE_WORKING_DIR="${LETSENCRYPT_DIR}" /shared/letsencrypt/acme.sh \
  --installcert \
  -d mudtoemanor.baronshire.org \
  --fullchainpath /shared/ssl/mudtoemanor.baronshire.org.cer \
  --keypath /shared/ssl/mudtoemanor.baronshire.org.key \
  --server letsencrypt \
  --reloadcmd "sv reload nginx"

########################################################
# ECDSA cert
########################################################
issue_cert "ec-256"

if ! cert_exists "_ecc"; then
  # Try to issue the cert again if something goes wrong
  issue_cert "ec-256" "--force"
fi

LE_WORKING_DIR="${LETSENCRYPT_DIR}" /shared/letsencrypt/acme.sh \
  --installcert --ecc \
  -d mudtoemanor.baronshire.org \
  --fullchainpath /shared/ssl/mudtoemanor.baronshire.org_ecc.cer \
  --keypath /shared/ssl/mudtoemanor.baronshire.org_ecc.key \
  --server letsencrypt \
  --reloadcmd "sv reload nginx"

if cert_exists "" || cert_exists "_ecc"; then
  grep -q 'force_https' "/var/www/discourse/config/discourse.conf" || echo "force_https = 'true'" >> "/var/www/discourse/config/discourse.conf"
fi

/usr/sbin/nginx -c /etc/nginx/letsencrypt.conf -s stop

我还保留了我之前插入的用于设置默认服务的原始命令,以防万一,尽管它可能并非必需。

因此,需要对该脚本进行如下修改:要么在所有 acme.sh 调用中明确指定使用 letsencrypt 服务;要么弄清楚 acme.sh 是如何保存状态信息,从而使得单次默认命令调用即可生效;最后一种方案是为 ZeroSSL 提供支持,并实现收集与保存电子邮件地址的功能。

我假设,如果不进行上述修改,下次升级版本时我的更改可能会被覆盖,届时需要重新操作。

如果我在文中遗漏了什么,或者该问题已通过其他方式解决而无需修改脚本,请随时告知。

1 个赞

只需在配置文件中添加您的电子邮件地址。

/var/discourse/containers/app.yml

## 如果您添加了 Lets Encrypt 模板,请取消注释以下行以获取免费 SSL 证书
LETSENCRYPT_ACCOUNT_EMAIL: gavin@truecode.co.za

运行 ./launcher rebuild app

之后您就可以正常使用了。

我们在几周前已经修复了这个问题,你尝试过重建吗?

3 个赞

没有,我还没有尝试。我当前运行的是 2.8.0.beta4 版本,系统提示已是最新。更新后是否必须执行重新构建?或者在更新过程中是否会动态下载某些需要重新构建才能生效的内容?我的 LETSENCRYPT_ACCOUNT_EMAIL 环境变量中确实配置了电子邮件地址。

我可以执行重新构建。但我是否需要先恢复 letsencrypt 脚本的原始版本,还是重新构建会自动刷新该脚本?是否每次更新后都需要重新构建?有没有某种机制可以通知我何时需要执行重新构建?到目前为止,自从大约六个月前首次搭建网站后,我从未进行过重新构建,尽管期间一直有应用更新。

虽然您可以通过 Web 界面更新应用程序,但偶尔我们需要为 Discourse 运行的底层环境(容器镜像)发布更新。这些环境更新需要重新构建。

若要撤销您的本地更改,您可以尝试使用以下命令暂存这些更改:

cd /var/discourse
git stash
./launcher rebuild app
1 个赞

这些“环境更新”是指我在管理设置页面点击“在此执行升级”链接后看到的“docker-manager”更新吗?我现在有点困惑,不确定应该从哪里、何时获取哪些更新,以及何时需要进行重建。虽然我在该页面上看到了升级选项,但上一页仍显示我已为最新版本。

不,要获取这些更新,您必须通过命令行访问服务器并执行重建操作。

好的。我已经执行了重建,但不确定 90 天后是否有效,因为证书尚未过期,所以系统并未尝试获取新证书。我检查了 letsencrypt 脚本,显示其修改日期为今天,但将其与旧版本脚本(原始版本,而非我修改后的版本)对比时,两者完全一致。我尝试按照输出信息中的注释,在容器内使用 --force 参数手动运行脚本,但未能成功。因此,目前我只能相信,当证书过期时,它会自动正常续期。