Letsencryptの問題

8月1日以降、Let’s Encrypt サービスに変更が生じたようです。私の証明書は今日まで有効だったため、この変更の影響は今日初めて受けました。Discourse が Let’s Encrypt サービスを管理するために使用するスクリプトは、デフォルトで Let’s Encrypt の代わりに新しいサービスである ZeroSSL を使用するよう更新されました。残念ながら、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 スクリプトがデフォルトで ZeroSSL の代わりに元の Let’s Encrypt を使用するようオプションを追加することにしました。その方法は以下のウェブサイトで説明されています:https://community.letsencrypt.org/t/the-acme-sh-will-change-default-ca-to-zerossl-on-august-1st-2021/144052

まず、スクリプトの3行目、/usr/sbin/nginx -c /etc/nginx/letsencrypt.conf の直後に以下の行を追加してみました。

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

ログにはデフォルトが変更されたというメッセージが表示されましたが、その直後に ZeroSSL 用のメール未登録に関する同じエラーが発生しました。ログにはそのメッセージとエラーメッセージの間に約6秒の遅延があり、acme.sh スクリプトが環境変数を通じて状態情報を保持しており、その後のコマンド実行が異なるコンテキストで実行されたため変数が失われたのではないかと思われます。そのため、最終的に let’s encrypt スクリプト内の acme.sh のすべての呼び出しに --server letsencrypt という引数を追加するように変更しました。その上でコンテナを再起動すると、ZeroSSL ではなく Let’s Encrypt によって新しい証明書が生成され、サイトが復旧しました。

以下は私が使用した修正版の 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 のすべての呼び出しで使用する Let’s Encrypt サービスを明示的に定義するか、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 スクリプトのバージョンに戻す必要がありますか?それとも、再ビルドで自動的に更新されますか?アップデートのたびに再ビルドを行うべきなのでしょうか?それとも、再ビルドが必要になった際に通知される仕組みがあるのでしょうか?これまで、約 6 ヶ月前にサイトを最初に構築して以来、アップデートを適用してはきましたが、再ビルドを行ったことはありません。

Web インターフェースからアプリを更新することはできますが、Discourse が動作する基盤環境(コンテナイメージ)の更新を不定期にリリースする必要があります。これらの環境更新には、再ビルドが必要です。

ローカルの変更を元に戻すには、以下のように変更を一時保存(stash)できる場合があります。

cd /var/discourse
git stash
./launcher rebuild app
「いいね!」 1

これらの「環境アップデート」は、管理者設定ページの「ここでアップグレードを実行」リンクをクリックした後に表示される「docker-manager」のアップデートのことでしょうか? どのアップデートをどこからいつ入手すべきか、またリビルドが必要になるタイミングについて、少し混乱しています。そのページにはアップグレードが表示されているものの、前のページでは「最新の状態です」と表示されたままです。

いいえ、それらを取得するには、コマンドラインを介してサーバーにアクセスし、再構築を実行する必要があります。

OK。再構築は完了しましたが、現在証明書が有効期限切れではないため、90日後に新しい証明書の取得を試みるかどうかは確信が持てません。letsencrypt スクリプトを確認すると、更新日が今日に変更されていることが確認できましたが、元のバージョン(私が変更を加える前のもの)と比較すると、内容は全く同じです。出力メッセージのコメントに記載されていた通り、コンテナ内から–force オプションを使ってスクリプトを手動で実行してみましたが、うまくいきませんでした。そのため、現時点では、証明書が期限切れになった際に正常に更新されることを信じるしかありません。