Let's Encrypt 관련 문제

8월 1일부로 letsencrypt 서비스에서 일부 변경 사항이 발생한 것 같습니다. 제 인증서는 오늘까지 유효했기 때문에, 저는 오늘 변경 사항의 영향을 받았습니다. discourse가 letsencrypt 서비스를 관리하는 데 사용하는 스크립트는 기본적으로 letsencrypt 대신 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

이메일 등록이 작동하지 않아서, ZeroSSL 대신 원래의 letsencrypt를 사용하도록 acme.sh 스크립트를 변경하기로 결정했습니다. 이를 수행하는 방법은 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

로그에는 기본값이 변경되었다는 메시지가 표시되었지만, 바로 그 직후 ZeroSSL에 등록된 이메일이 없다는 동일한 오류가 발생했습니다. 해당 메시지와 오류 메시지 사이에 약 6초의 지연이 로그에 기록되었는데, 이는 acme.sh 스크립트가 환경 변수를 통해 상태 정보를 유지하고 있으며, 이후 명령 실행이 다른 컨텍스트에서 이루어져 변수가 유실되었기 때문이라고 추측됩니다. 결국 제가 해야 했던 일은 letsencrypt 스크립트 내의 모든 acme.sh 호출에 “–server letsencrypt” 이라는 옵션을 추가하는 것이었습니다. 이렇게 하고 컨테이너를 재시작하자 ZeroSSL 대신 letsencrypt에 의해 새로운 인증서가 생성되었고 사이트가 다시 작동했습니다.

제가 사용한 수정된 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

## If you added the Lets Encrypt template, uncomment below to get a free SSL certificate
 LETSENCRYPT_ACCOUNT_EMAIL: gavin@truecode.co.za

다음으로 다음 명령을 실행하세요:

do ./launcher rebuild app

그러면 준비가 완료됩니다.

몇 주 전에 이 문제를 수정했습니다. 재빌드(rebuild)를 시도해 보셨나요?

3개의 좋아요

아니요, 아직 안 해봤습니다. 현재 2.8.0.beta4 빌드를 사용 중이며, 업데이트가 최신 상태라고 표시됩니다. 업데이트 후에도 재빌드가 필요한가요, 아니면 업데이트 사이에 다운로드되는 항목들이 있어 재빌드가 필요한 건가요? LETSENCRYPT_ACCOUNT_EMAIL 에 이메일 주소를 설정해 두었습니다.

재빌드를 할 수는 있습니다. 먼저 letsencrypt 스크립트의 원래 버전을 복원해야 하는지, 아니면 재빌드 시 자동으로 갱신되는지 궁금합니다. 매 업데이트 후 재빌드를 해야 하는 건가요, 아니면 재빌드가 필요할 때 알려주는 방법이 있나요? 사이트가 처음 구축된 지 약 6개월이 지났지만, 그동안 업데이트는 적용해 왔음에도 재빌드를 한 번도 수행해 본 적이 없습니다.

웹 인터페이스에서 앱을 업데이트할 수 있지만, 간혹 Discourse가 실행되는 기반 환경(컨테이너 이미지)에 업데이트를 배포해야 하는 경우가 있습니다. 이러한 환경 업데이트는 재빌드를 요구합니다.

로컬 변경 사항을 되돌리기 위해 다음 명령으로 변경 사항을 스테시(stash)할 수 있습니다.

cd /var/discourse
git stash
./launcher rebuild app
1개의 좋아요

이 "환경 업데이트"는 관리 설정 페이지의 “여기서 업그레이드 수행” 링크를 클릭한 후 보이는 “docker-manager” 업데이트를 의미합니까? 이제 어떤 업데이트를 어디서, 언제 받아야 하는지, 그리고 언제 리빌드를 해야 하는지 조금 혼란스럽습니다. 해당 페이지에 업그레이드가 표시되지만, 이전 페이지에서는 여전히 최신 상태라고 나와 있습니다.

아니요, 그 업데이트를 받으려면 명령줄을 통해 서버에 접속하여 재빌드(rebuild)를 실행해야 합니다.

네. 재구성을 수행했지만, 현재 인증서가 만료되지 않았기 때문에 새 인증서를 발급받으려는 시도가 이루어지지 않아 90일 후 정상 작동할지 확신할 수 없습니다. letsencrypt 스크립트를 확인해 보니 수정된 날짜가 오늘로 표시되어 있지만, 기존 스크립트(내 수정 사항이 적용되기 전 원본)와 비교해 보면 내용이 동일합니다. 출력 메시지 주석 중 하나에 따라 컨테이너 내부에서 --force 옵션을 사용하여 스크립트를 수동으로 실행해 보았지만, 작동하지 않았습니다. 따라서 지금은 인증서가 만료될 때 정상적으로 갱신되리라고 믿고 기다려야 할 것 같습니다.