据称,自 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 提供支持,并实现收集与保存电子邮件地址的功能。
我假设,如果不进行上述修改,下次升级版本时我的更改可能会被覆盖,届时需要重新操作。
如果我在文中遗漏了什么,或者该问题已通过其他方式解决而无需修改脚本,请随时告知。