Ошибка CSRF при входе после обновления до 2.5.0.beta4

После обновления до версии 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 стоит сервер HAProxy для завершения SSL. Я не понимаю смысл сообщения об ошибке

“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 (или его конфигурацию), что и привело к этой проблеме (но это лишь гипотеза ;-)).
Я попытался найти способ отключить CSRF в nginx (GitHub - gartnera/nginx_csrf_prevent: Prevent CSRF with nginx · GitHub), но, кажется, 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 необходимости в применении патчей к нему не было, всё работало гладко.

Да, если под зоопарком вы имеете в виду контейнер, в котором запущен 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 секунд назад     Up 32 секунд               127.0.0.1:884->80/tcp   app
43406c37f403        discourse/base:2.0.20200512-1735   "ruby -e 'require 'y…"   2 часа назад        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 "Начало пользовательских команд"
  ## Если вы хотите установить адрес электронной почты в поле '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 "Конец пользовательских команд"

Я думаю, что proxy_pass вам там в app.yml не нужен. Мой аналогичный блок выглядит так:

  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

и посмотрите, что произойдёт…

Совет Джейса оказался верным: я просто удалил 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 "Конец пользовательских команд"