2.5.0.beta4 へのアップグレード後の CSRF ログインエラー

2.5.0.beta4 へのアップグレード後、本番環境のログで CSRF エラーが表示されます。

Processing by SessionController#csrf as JSON
Completed 200 OK in 1ms (Views: 0.1ms | ActiveRecord: 0.0ms | Allocations: 351)
Started POST "/session" for 127.0.0.1 at 2020-05-05 09:25:17 +0000
Processing by SessionController#create as */*
  Parameters: {"login"=>"admin", "password"=>"[FILTERED]", "second_factor_method"=>"1", "timezone"=>"Europe/Berlin"}
Can't verify CSRF token authenticity.
  Rendering text template
  Rendered text template (Duration: 0.0ms | Allocations: 1)
Filter chain halted as :verify_authenticity_token rendered or redirected
Completed 403 Forbidden in 2ms (Views: 0.7ms | Allocations: 1100)

また、Discourse Doctor は以下を表示します。

========================================
Discourse 2.5.0.beta4
Discourse version at forum.netzwissen.de: Discourse 2.5.0.beta4
Discourse version at localhost: NOT FOUND
==================== DNS PROBLEM ====================
This server reports NOT FOUND, but forum.netzwissen.de reports Discourse 2.5.0.beta4 .
This suggests that you have a DNS problem or that an intermediate proxy is to blame.
If you are using Cloudflare, or a CDN, it may be improperly configured.

質問:サーバー自体は異なる DNS 名を持つ複数のサービスをホストしています。Discourse の前面には SSL 終端を処理する HAProxy サーバーがあります。このエラーメッセージが理解できません。

“Discourse version at localhost: NOT FOUND”

CSRF エラーは、このエラーメッセージに関連している可能性がありますか?

Discourse-doctorは、あなたのようないくつもの複雑な設定を診断できるとは主張していません。これは、ローカルホストとDNSが同じ値を返すかどうかを比較しているに過ぎません。あなたの設定では、それらが異なることが期待されています。

ただし、実際の問題については何も手がかりがありません。申し訳ありません。

こんにちは

はい、別のアカウントでテストしましたが、同じエラーメッセージが表示されます。ログインが完全にブロックされているようで、CSRF エラーが根本的な原因のようです。

さらにデバッグするためのアイデアはありますか?私の app.yml は非常に標準的ですが、以下のように設定されています。

expose:
  - "127.0.0.1:884:80"   # http

受信リクエストは haproxy サーバーから Discourse コンテナの 884 ポートへ転送されます。SSL/HTTPS は haproxy によって処理されます。

OAuth2(Google)を通じて新規ユーザーを登録する際、CSRF エラーも発生します。

 Rendered common/_discourse_stylesheet.html.erb (Duration: 0.4ms | Allocations: 206)
  Rendered application/_header.html.erb (Duration: 0.3ms | Allocations: 142)
Completed 200 OK in 23ms (Views: 20.4ms | ActiveRecord: 0.0ms | Allocations: 4636)
Started GET "/latest.json?order=default" for 127.0.0.1 at 2020-05-05 11:43:08 +0000
Processing by ListController#latest as JSON
  Parameters: {"order"=>"default"}
Completed 200 OK in 30ms (Views: 0.1ms | ActiveRecord: 0.0ms | Allocations: 10224)
Started GET "/u/hp.json" for 127.0.0.1 at 2020-05-05 11:43:08 +0000
Processing by UsersController#get_honeypot_value as JSON
Completed 200 OK in 3ms (Views: 0.1ms | ActiveRecord: 0.0ms | Allocations: 1049)
Started GET "/session/csrf" for 127.0.0.1 at 2020-05-05 11:43:38 +0000
Processing by SessionController#csrf as JSON
Completed 200 OK in 1ms (Views: 0.2ms | ActiveRecord: 0.0ms | Allocations: 355)
Started POST "/auth/google_oauth2" for 127.0.0.1 at 2020-05-05 11:43:38 +0000
(google_oauth2) Setup endpoint detected, running now.
(google_oauth2) Request phase initiated.
Started GET "/auth/failure?message=csrf_detected" for 127.0.0.1 at 2020-05-05 11:43:38 +0000
Processing by Users::OmniauthCallbacksController#failure as HTML
  Parameters: {"message"=>"csrf_detected"}
  Rendering users/omniauth_callbacks/failure.html.erb within layouts/no_ember
  Rendered users/omniauth_callbacks/failure.html.erb within layouts/no_ember (Duration: 0.1ms | Allocations: 20)
  Rendered layouts/_head.html.erb (Duration: 11.7ms | Allocations: 3551)
  Rendered common/_discourse_stylesheet.html.erb (Duration: 0.5ms | Allocations: 213)
  Rendered application/_header.html.erb (Duration: 0.9ms | Allocations: 555)
Completed 200 OK in 19ms (Views: 16.4ms | ActiveRecord: 0.0ms | Allocations: 7652)

2.5.0.beta4 へのアップグレード後、全く同じ問題が発生しました(Moved site behind proxy, favicon and header not using https anymore - #7 by rossierd

問題は解決できましたか?アップグレードに伴い nginx(またはその設定)の新しいバージョンが導入され、これが問題の原因になっている可能性があります(ただし、これは純粋な仮説ですが ;-))。

nginx で CSRF を無効化する方法を探してみましたが(https://github.com/gartnera/nginx_csrf_prevent)、nginx の再コンパイルが必要ではないかと考えられます。そのためには Discourse の完全な開発環境が必要かどうかはわかりません。

残念ながら、この問題はまだ解決していません。ログイン時に「不明なエラー」が発生し、試すたびにログに以下が表示されます。

root@develd:/var/discourse# tail -f /var/log/discourse-rails/production.log
Processing by SessionController#csrf as JSON
Completed 200 OK in 1ms (Views: 0.1ms | Allocations: 351)
Started POST "/session" for 127.0.0.1 at 2020-06-07 06:58:19 +0000
Processing by SessionController#create as */*
  Parameters: {"login"=>"admin", "password"=>"[FILTERED]", "second_factor_method"=>"1", "timezone"=>"Europe/Berlin"}
Can't verify CSRF token authenticity.
  Rendering text template
  Rendered text template (Duration: 0.0ms | Allocations: 1)
Filter chain halted as :verify_authenticity_token rendered or redirected
Completed 403 Forbidden in 2ms (Views: 0.8ms | ActiveRecord: 0.0ms | Allocations: 1100)
Started GET "/session/csrf" for 127.0.0.1 at 2020-06-07 07:00:45 +0000
Processing by SessionController#csrf as JSON
Completed 200 OK in 1ms (Views: 0.2ms | Allocations: 351)
Started POST "/session" for 127.0.0.1 at 2020-06-07 07:00:45 +0000
Processing by SessionController#create as */*
  Parameters: {"login"=>"admin", "password"=>"[FILTERED]", "second_factor_method"=>"1", "timezone"=>"Europe/Berlin"}
Can't verify CSRF token authenticity.
  Rendering text template
  Rendered text template (Duration: 0.0ms | Allocations: 1)
Filter chain halted as :verify_authenticity_token rendered or redirected
Completed 403 Forbidden in 2ms (Views: 0.9ms | Allocations: 1100)

app.yml には以下が含まれています。

## Any custom commands to run after building
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='noreply-discourse@netzwissen.de'"
  - replace:
      filename: /etc/nginx/conf.d/discourse.conf
      from: "types {"
      to: |
        set_real_ip_from 127.0.0.0/24;
        real_ip_header X-Forwarded-For;
        real_ip_recursive on;
        types {
  - exec: echo "End of custom commands"

これは https://meta.discourse.org/t/haproxy-and-discourse-ip-issue/92387 で推奨された設定です。

これを試してみてください:Moved site behind proxy, favicon and header not using https anymore - #12 by rossierd

これで正常にログインできましたが、proxy_pass コマンドをパッチする場所には注意してください。location @discourse { .. } の内側でのみ機能します。

はい、理解のため確認ですが、「コンテナ・ズー」内の nginx についてお話ししているのでしょうか?バージョン 2.5.0beta4 へのアップデート以前は、この nginx にパッチを当てる必要はなく、問題なく動作していました。

はい、zoo とは Discourse を実行しているコンテナを指すのであれば可能です。“rails enter app” コマンドでコンテナにアクセスできます。

ここでさらにデバッグを行いました:問題はコンテナ内の nginx サーバーで発生しています。nginx は proxy_pass ディレクティブを理解できないため、クラッシュしているように見えます。なぜでしょうか:

root@develd:/var/discourse# docker ps -a
CONTAINER ID        IMAGE                              COMMAND                  CREATED             STATUS                     PORTS                   NAMES
f8f6103a036d        local_discourse/app                "/sbin/boot"             35 seconds ago      Up 32 seconds              127.0.0.1:884->80/tcp   app
43406c37f403        discourse/base:2.0.20200512-1735   "ruby -e 'require 'y…"   2 hours ago         Created


docker exec -it f8f6103a036d /bin/bash

root@forum:/# tail -f /var/log/nginx/error.log
2020/06/08 19:05:03 [emerg] 288#288: "proxy_pass" directive is not allowed here in /etc/nginx/conf.d/discourse.conf:10

app.yml で使用している nginx 設定は以下の通りです:

## Any custom commands to run after building
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='noreply-discourse@netzwissen.de'"
  - replace:
      filename: /etc/nginx/conf.d/discourse.conf
      from: "types {"
      to: |
        set_real_ip_from 127.0.0.0/24;
        real_ip_header X-Forwarded-For;
        real_ip_recursive on;
        proxy_set_header Host $http_host;
        proxy_set_header X-Request-Start "t=${msec}";
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https; # $thescheme; <-- What I modified
        proxy_pass http://discourse;
        types {
  - exec: echo "End of custom commands"

その app.ymlproxy_pass は不要ではないでしょうか。私の設定は以下のようになっています:

  after_bundle_exec:
    # これは Discourse に IP アドレスを伝達するための魔法のような設定です
    # 詳細は https://meta.discourse.org/t/last-ip-address-and-action-dispatch-trusted-proxies/50098/3?u=pfaffman を参照してください
    - replace:
        filename: /etc/nginx/conf.d/discourse.conf
        from: "types {"
        to: |
          set_real_ip_from 192.168.1.0/24;
          set_real_ip_from 172.18.0.0/24;
          set_real_ip_from 172.17.0.0/24;
          real_ip_recursive on;
          real_ip_header X-Forwarded-For;
          types {

ただし、私のコードはコンテナが Debian に移行する前のものである可能性があります。

コンテナ内でそのファイルを直接編集し、nginx を再起動してみるのも一案です。

念のため、コンテナ内で /etc/nginx/conf.d/discourse.conf を編集し、上記のようにファイルをパッチしてください。
その後、以下のように nginx を再起動します。

$ service nginx stop
$ service nginx start

そして、どのような結果になるか確認してください。

Jays のヒントは正しかったです:proxy_pass を削除したら動作しました。app.yml の最終設定は以下の通りです:

## ビルド後に実行するカスタムコマンド
run:
  - exec: echo "カスタムコマンドの開始"
  ## 最初の登録時の 'From' メールアドレスを設定したい場合は、以下の行のコメントを外して変更してください:
  ## 最初のサインアップメールを受け取ったら、再度コメントアウトしてください。一度だけ実行すれば十分です。
  ## - exec: rails r "SiteSetting.notification_email='noreply-discourse@netzwissen.de'"
  - replace:
      filename: /etc/nginx/conf.d/discourse.conf
      from: "types {"
      to: |
        set_real_ip_from 127.0.0.1/24;
        real_ip_header X-Forwarded-For;
        real_ip_recursive on;
        proxy_set_header Host $http_host;
        proxy_set_header X-Request-Start "t=${msec}";
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https; # $thescheme; <-- 変更した箇所
        types {
  - exec: echo "カスタムコマンドの終了"