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 서버를 통해 884 포트의 discourse 컨테이너로 전달됩니다. 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(또는 그 설정)의 새 버전이 포함되어 이 문제를 유발했을 것 같다는 추측은 해볼 수 있지만, 순수한 가설일 뿐입니다 :wink:
nginx에서 CSRF를 비활성화하는 방법을 찾아보았지만(GitHub - gartnera/nginx_csrf_prevent: Prevent CSRF with nginx · GitHub) nginx를 다시 컴파일해야 하는 것 같고, 이를 위해 Discourse의 완전한 개발 환경이 필요한지 모르겠습니다.

불행히도 이 문제는 여전히 해결되지 않았습니다. 로그인 시 "unknown error"가 발생하며, 매번 시도할 때마다 로그에 다음과 같은 내용이 표시됩니다:

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로 업데이트하기 전에는 패치가 필요하지 않아서 그냥 매끄럽게 작동했거든요.

네, 여기서 'zoo’란 Discourse가 실행 중인 컨테이너를 의미하는 것입니다. “rails enter app” 명령어로 컨테이너에 진입할 수 있습니다.

여기서 추가 디버깅을 수행해 보았습니다: 문제는 컨테이너 내부의 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 설정을 사용 중입니다:

## 빌드 후 실행할 사용자 지정 명령
run:
  - exec: echo "Beginning of custom commands"
  ## 첫 번째 등록 시 '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.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; <-- 제가 수정한 부분
        proxy_pass http://discourse;
        types {
  - exec: echo "End of custom commands"

그 위치에 proxy_pass가 필요 없다고 생각합니다. 제 스탠스는 다음과 같은 형태입니다:

  after_bundle_exec:
    # IP 주소를 Discourse로 전달하는 마법 같은 설정입니다.
    # 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 "Beginning of custom commands"
  ## 첫 번째 가입 시 '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 "End of custom commands"