DiscourseConnect 即使在强制启用 force_https 也会生成 HTTP 重定向

我正在自定义设置的 Traefik 后面运行 Discourse - 在这里无法为 Discourse 分配独立的虚拟机。

我的 Discourse 没有启用 SSL/Let’s Encrypt 模板,因为 Traefik 不允许纯 HTTP 请求到达容器 - 它被设置为将 HTTP 请求重定向到 HTTPS。

我在设置 DiscourseConnect 时遇到问题,因为 Traefik -> nginx[Discourse] 请求是通过纯文本 HTTP 发送的(因为 nginx 没有设置 SSL),/etc/nginx/conf.d/discourse.conf 中试图“保留协议,必须在 http 上下文中”的规则使 Discourse(Rails 应用程序)收到纯文本 HTTP 请求,因此返回纯文本 HTTP 重定向到 /session/sso - 即使我启用了 force_https

我认为这是一个 bug:无论我的设置如何,在启用 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}/\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-the-rails-app 应该尊重它,而不管反向代理或其他方如何处理。

我们在托管平台上就是这样做的;我们有一个负载均衡器层,它设置 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
1 个赞