force_https가 켜져 있어도 DiscourseConnect가 HTTP 리다이렉트를 생성합니다

커스텀 환경에서 Traefik 뒤에 Discourse를 실행 중입니다. Discourse에 전용 VM을 할당하는 것은 여기서는 불가능합니다.

Traefik이 평문 HTTP 요청이 컨테이너에 도달하는 것을 허용하지 않기 때문에(HTTP 요청을 HTTPs로 리디렉션하도록 설정되어 있음), 제 Discourse에는 SSL/Let’s Encrypt 템플릿이 활성화되어 있지 않습니다.

DiscourseConnect 설정에 문제가 발생하고 있습니다. Traefik -> nginx[Discourse] 요청이 평문 HTTP로 전송되기 때문이죠(nginx에 SSL이 설정되어 있지 않으므로). /etc/nginx/conf.d/discourse.conf에 있는 to preserve the proto, must be in http context 규칙으로 인해 Discourse(Rails 앱)가 평문 HTTP 요청을 받게 되고, 결과적으로 force_https가 활성화되어 있더라도 /session/sso로 평문 HTTP 리디렉션을 반환합니다.

저는 이것이 버그라고 생각합니다. 제 설정과 무관하게, force_https가 활성화된 경우 Discourse는 항상 HTTPs URL을 생성해야 하는데 그렇게 하지 않고 있습니다.

문제가 되는 코드는 application_controller#redirect_to_login인 것 같지만, Discourse 소스 코드를 충분히 깊이 파고들지 않아 확신은 하지 못합니다.

이 문제는 코드 자체에서 해결할 수 있을까요?

우회책으로, nginx의 discourse.conf에 규칙을 패치하여 해당 규칙을 제거하는 방법을 시도하고 있습니다.

포워디드 포 헤더에 대한 지원을 제공해야 합니다. 이를 통해 NGINX에 원본 출처를 알릴 수 있습니다.

다음 설정을 했나요?

proxy_set_header X-Forwarded-Proto https;

나에게 가장 쉬웠던 방법은 Discourse의 app.yml에 추가 라벨을 설정하여 Traefik이 X-Forwarded-Proto: https 헤더를 추가하도록 하는 것이었지만, 그 후 nginx가 해당 매개변수를 자체 버전으로 덮어쓰는 문제가 발생했습니다.

그리고 Discourse의 nginx 설정이 여기에 영향을 미칩니다:

여기서 Discourse는 원래 요청에서 프로토콜을 추측하려 합니다(제 환경에서는 Traefik이 전송하는 것이 항상 평문 텍스트이므로 항상 평문이 됩니다). 그리고 나서 그것을 사용하여 여러 번 X-Forwarded-Proto를 설정합니다.

결국, 저는 containers/app.yml을 편집하여 해당 헤더를 https로 하드코딩했습니다:

run:
  - exec: echo "Beginning of custom commands"
  ## If you want to set the 'From' email address for your first registration, uncomment and change:
  ## After getting the first signup email, re-comment the line. It only needs to run once.
  # - exec: rails r "SiteSetting.notification_email='no-reply@forum.cabana.network'"
  - replace:
     filename: "/etc/nginx/conf.d/discourse.conf"
     from: /# attempt to preserve the proto, must be in http context\nmap \$http_x_forwarded_proto \$thescheme {\n  default \$scheme;\n  "~https\$" https;\n\}/
     to: |
       # force https scheme so Discourse generates HTTPs links and redirects (ie, `/login`)
  - replace:
     filename: "/etc/nginx/conf.d/discourse.conf"
     from: "$thescheme"
     global: "true"
     to: "https"
  - exec: echo "End of custom commands"

다시 한번 말씀드리자면, force_https 설정이 있다면, 역프록시나 다른 당사자가 처리하든 말든 관계없이 Discourse-rails 앱은 이를 존중해야 한다고 생각합니다.

저희 호스팅 플랫폼에서는 이렇게 처리합니다. X-Forwarded-Proto를 설정하여 하위 nginx+Discourse가 소비하도록 하는 로드 밸런서 레이어가 있습니다.

이것이 작동하도록 하기 위해 추가적인 조작은 필요하지 않습니다 - 여기서 무엇이 잘못되고 있는지 정확히 알 수 없습니다.

실제로 그렇게 동작합니다:

  def self.generate_sso(return_path = "/", secure_session:)
    sso = new(secure_session: secure_session)
    sso.nonce = SecureRandom.hex
    sso.register_nonce(return_path)
    sso.return_sso_url = Discourse.base_url + "/session/sso_login"
    sso
  end

그리고 base_url은 다음에서 가져옵니다:

  def self.base_protocol
    SiteSetting.force_https? ? "https" : "http"
  end

  def self.base_url_no_prefix
    "#{base_protocol}://#{current_hostname_with_port}"
  end

  def self.base_url
    base_url_no_prefix + base_path
  end