Discourse + Let's Encrypt, 여러 호스트네임 사용

Discourse를 백업을 통해 한 호스트명에서 다른 호스트명으로 마이그레이션할 때, 관리자는 기존 호스트명을 유지하면서 웹 서버가 요청을 새로운 정규(canonical) 이름으로 재작성하도록 설정할 수 있습니다. 예를 들어:

https://old.example.org/t/my-great-topic/12345 :arrow_heading_down:
https://new.example.org/t/my-great-topic/12345

불행히도 이는 DNS에서 CNAME 또는 A 레코드만 변경하는 것만으로 해결되지 않습니다. Discourse 설치 시 Let’s Encrypt를 통해 생성된 인증서와 호스트명이 일치하지 않기 때문에 브라우저 클라이언트에서 오류/경고가 발생합니다.

웹 서버를 수동으로 설치한 경우라면, cerbot 유틸리티를 사용하여 여러 이름이 할당된 인증서를 생성할 수 있습니다. 그러나 Discourse 설정은 Let’s Encrypt에 Discourse 호스트명(만)을 전달하며, Discourse 설정에서 별칭(alias)에 대한 개념을 찾을 수 없습니다.

이러한 구성을 설정해 보신 분, 또는 이를 가장 잘 달성하는 방법에 대한 추측이 있는 분은 계신가요? SSL 및 Let’s Encrypt 템플릿에 대한 몇 가지 해킹(hacking)이 필요할 것으로 예상됩니다.

Discourse에 특화된 내용은 아니지만, HTTPS로 리다이렉트해야 할 때는 그냥 모든 것을 리다이렉트합니다. 즉, 기존 주소에서 새 주소로 리다이렉트를 처리하는 서버를 하나 두는 방식이죠. 일부 호스팅 옵션에서는 이 작업을 백그라운드에서 처리해 주는데, 보통 "도메인 리다이렉트"나 “포워딩” 서비스라고 부릅니다.

다른 방법을 시도해 볼 때마다 매번 너무 번거로워서 이제는 이 방식을 생각조차 하지 않습니다. :thinking:

Discourse는 하나의 호스트명에 대해서만 응답합니다. 여러 호스트명을 사용하려는 경우(예: 이전 호스트명을 지원하는 사용 사례가 가장 일반적임)에는 한 호스트명을 다른 호스트명으로 리디렉션해야 합니다.

이 작업은 Discourse 외부에서 수행하는 것을 권장합니다. 자체 인증서를 보유하고, 이전 호스트명을 위한 server_name을 설정한 nginx 구성을 만들어 모든 요청을 새 호스트명으로 리디렉션하면 됩니다.

실제로 서버당 단일 인스턴스 기반의 Docker 설치 방식을 사용 중인 경우, DNS 항목이 여러 개 있어도 정상적으로 응답합니다(nginx는 모든 호스트명에 대해 포트 80과 443에서 리스닝을 수행하며), URL을 정통 “새” 호스트명으로 올바르게 다시 작성(rewrite)하는 부분도 문제없이 잘 작동합니다. 이 부분은 무리 없이 잘 작동하고 있습니다. (이것을 테스트해 보고 싶은 분은 로컬호스트 파일에 가짜 도메인 이름을 추가하고, 좋아하는 Discourse 사이트의 IP 주소로 가리켜 보세요.)

제가 겪고 있는 유일한 문제는 SSL 경고입니다. nginx의 다시 작성(respose) 응답이 https://old.example.org/foo에서 오면서 새 URL로 HTTP 302 리디렉션을 보내기 때문입니다.

가능하다면, 리라이팅 규칙 하나를 수행하기 위해 별도의 웹 서버를 유지 관리하고 싶지는 않습니다. :slight_smile:

그렇게 할 필요는 없습니다. 설정 파일에 server 섹션을 하나 더 추가하기만 하면 됩니다.

Set up Let’s Encrypt with multiple domains / redirects 를 참고해 보세요.

업데이트: 스탠드얼론 설치 환경에서 보조 도메인 이름을 성공적으로 구성하고, 기존 이름과 새 이름 모두를 처리하기 위해 Let’s Encrypt 인증서를 발급했습니다.

전제 조건: 아직 DNS를 변경하여 사용자를 새 사이트로 유도하지 않았습니다. 사전에 모든 것이 정상 작동하는지 확인하고 싶었기 때문입니다. 따라서 테스트 머신에는 로컬호스트 항목을 사용했습니다. 이로 인해 일반적인 Let’s Encrypt 인증을 수행할 수 없었고, 대신 acme.sh의 “DNS” 방법을 사용해야 했습니다. 이 방법은 자동 갱신이 불가능하기 때문에 일반적으로 권장되지 않습니다. 저에게는 문제가 되지 않습니다. “초기”(새로 발급된 2개 이름) 인증서의 30일 유효기간 내에 전환을 완료할 예정이기 때문입니다.

  1. web_only.yml 파일(사용자가 app.yml을 사용 중일 수 있음)의 hooks: 섹션에서 플러그인 이전에 다음 내용을 추가했습니다:
  after_ssl:
    - replace:
        filename: "/etc/runit/1.d/letsencrypt"
        from: /--keylength/
        to: "-d second-domain.com --keylength"
  1. DNS에서 _acme-challenge.old.example.org에 가짜/임시 TXT 레코드를 5분 TTL(더 짧게 설정할 수도 있음)로 설정하고, acme.sh(Let’s Encrypt) 인증 키로 빠르게 변경할 수 있도록 전파되도록 기다립니다.

  2. 사이트를 중지하고 디버그(테스트) 검증을 발급하여 시스템이 새 항목의 짧은 TTL을 인식하도록 합니다:

./launcher enter app
sv stop nginx
/usr/sbin/nginx -c /etc/nginx/letsencrypt.conf
LE_WORKING_DIR=/shared/letsencrypt DEBUG=1 /shared/letsencrypt/acme.sh --issue -d new.example.org -d old.example.org -k 4096 -w /var/www/discourse/public --dns --yes-I-know-dns-manual-mode-enough-go-ahead-please --force
  1. 이번에는 디버그 설정 없이 실제 요청을 실행합니다. 그러면 2단계에서 생성한 항목에 DNS에 사용할 값에 대한 경고가 표시됩니다:
LE_WORKING_DIR=/shared/letsencrypt /shared/letsencrypt/acme.sh --issue -d new.example.org -d old.example.org -k 4096 -w /var/www/discourse/public --dns --yes-I-know-dns-manual-mode-enough-go-ahead-please --force
  1. DNS를 업데이트한 후 5분 동안 기다립니다. 네, Let’s Encrypt의 제한 사항에 부딪히지 않도록 전체 시간을 기다려야 합니다.

  2. 마지막 명령과 유사한 버전을 실행하되 renew 모드로 실행합니다:

LE_WORKING_DIR=/shared/letsencrypt /shared/letsencrypt/acme.sh --issue -d new.example.org -d old.example.org -k 4096 -w /var/www/discourse/public --dns --yes-I-know-dns-manual-mode-enough-go-ahead-please --force --renew
  1. 확인 메시지가 표시되어야 합니다. 다음을 실행하여 정리를 하고 새 인증서를 적용합니다:
LE_WORKING_DIR=/shared/letsencrypt /shared/letsencrypt/acme.sh --installcert -d new.example.org -d old.example.org --fullchainpath /shared/ssl/new.example.org.cer --keypath /shared/ssl/new.example.org.key --reloadcmd "sv reload nginx"
/usr/sbin/nginx -c /etc/nginx/letsencrypt.conf -s stop
exit
  1. 이제 런처를 벗어난 상태에서의 마지막 단계:
rm -rf /var/discourse/shared/standalone/ssl
rm -rf /var/discourse/shared/standalone/letsencrypt
./launcher rebuild app

사이트가 다시 시작되면 새 인증서로 실행되어야 합니다. 브라우저의 저장된 인증서를 삭제해야 할 수 있으며, 이는 독자의 연습 문제이자 좋아하는 검색 엔진에 맡기겠습니다.

정확한 방향으로 안내해 주신 분들께 감사드립니다!

이것은 Cloudflare 규칙에 완벽한 적용 사례입니다. 서버 설정이 전혀 필요하지 않습니다. 기존 도메인에 동일한 URL 매개변수를 적용하기만 하면 됩니다.

기존 도메인 이름에 주황색 구름(Proxied)이 활성화되어 있어야 합니다.

네, Cloudflare와 같은 리버스 프록시 환경(또는 Discourse 사이트 앞에 배치한 자체 리버스 프록시)은 리라이팅 규칙을 "한 단계 위"에서 구현하여 이를 우회할 수 있습니다. 이 경우 Cloudflare는 보안 및 도덕적 이유로 선택지가 아니었습니다. :slight_smile:

(스탠드얼론 솔루션은 위에서 확인하세요.)