Errore di login CSRF dopo l'aggiornamento a 2.5.0.beta4

Dopo l’aggiornamento alla versione 2.5.0.beta4, vedo errori CSRF nel log di produzione:

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)

E Discourse Doctor mostra:

========================================
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.

Domanda: il server stesso ospita più servizi con nomi DNS diversi. Davanti a Discourse abbiamo un server HAProxy per gestire la terminazione SSL. Non capisco il messaggio di errore

“Discourse version at localhost: NOT FOUND”

È possibile che l’errore CSRF sia collegato a questo messaggio di errore?

Discourse-doctor non pretende di poter diagnosticare una configurazione complessa come la tua. Si limita a verificare se l’host locale e il DNS restituiscono lo stesso valore. Per la tua configurazione, ci si aspetta che siano diversi.

Tuttavia, non ho alcun suggerimento riguardo al tuo problema effettivo. Mi dispiace.

Ciao,
Ok, ho provato con un altro account e ricevo lo stesso messaggio di errore; sembra che gli accessi siano completamente bloccati ora e che l’errore CSRF possa essere la causa principale…

Hai qualche idea per ulteriori debug? Il mio app.yml è piuttosto standard, tranne per il fatto che:

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

Le richieste in entrata vengono inoltrate da un server haproxy al container di Discourse sulla porta 884. SSL/HTTPS è gestito da haproxy.

Quando si registra un nuovo utente tramite oauth2 (Google), si verifica anche un errore CSRF:

 Renderizzato common/_discourse_stylesheet.html.erb (Durata: 0,4ms | Allocazioni: 206)
  Renderizzato application/_header.html.erb (Durata: 0,3ms | Allocazioni: 142)
Completato 200 OK in 23ms (Viste: 20,4ms | ActiveRecord: 0,0ms | Allocazioni: 4636)
Inizio GET "/latest.json?order=default" per 127.0.0.1 alle 2020-05-05 11:43:08 +0000
Elaborazione da parte di ListController#latest come JSON
  Parametri: {"order"=>"default"}
Completato 200 OK in 30ms (Viste: 0,1ms | ActiveRecord: 0,0ms | Allocazioni: 10224)
Inizio GET "/u/hp.json" per 127.0.0.1 alle 2020-05-05 11:43:08 +0000
Elaborazione da parte di UsersController#get_honeypot_value come JSON
Completato 200 OK in 3ms (Viste: 0,1ms | ActiveRecord: 0,0ms | Allocazioni: 1049)
Inizio GET "/session/csrf" per 127.0.0.1 alle 2020-05-05 11:43:38 +0000
Elaborazione da parte di SessionController#csrf come JSON
Completato 200 OK in 1ms (Viste: 0,2ms | ActiveRecord: 0,0ms | Allocazioni: 355)
Inizio POST "/auth/google_oauth2" per 127.0.0.1 alle 2020-05-05 11:43:38 +0000
(google_oauth2) Endpoint di configurazione rilevato, in esecuzione.
(google_oauth2) Fase di richiesta avviata.
Inizio GET "/auth/failure?message=csrf_detected" per 127.0.0.1 alle 2020-05-05 11:43:38 +0000
Elaborazione da parte di Users::OmniauthCallbacksController#failure come HTML
  Parametri: {"message"=>"csrf_detected"}
  Rendering di users/omniauth_callbacks/failure.html.erb all'interno di layouts/no_ember
  Renderizzato users/omniauth_callbacks/failure.html.erb all'interno di layouts/no_ember (Durata: 0,1ms | Allocazioni: 20)
  Renderizzato layouts/_head.html.erb (Durata: 11,7ms | Allocazioni: 3551)
  Renderizzato common/_discourse_stylesheet.html.erb (Durata: 0,5ms | Allocazioni: 213)
  Renderizzato application/_header.html.erb (Durata: 0,9ms | Allocazioni: 555)
Completato 200 OK in 19ms (Viste: 16,4ms | ActiveRecord: 0,0ms | Allocazioni: 7652)

Ho avuto esattamente lo stesso problema dopo l’aggiornamento alla versione 2.5.0.beta4 (Moved site behind proxy, favicon and header not using https anymore - #7 by rossierd).

Sei riuscito a risolvere il problema? Immagino che l’aggiornamento abbia incluso una nuova versione di nginx (o della sua configurazione) che ha causato questo problema (ma è solo un’ipotesi ;-))
Ho cercato un modo per disabilitare il CSRF in nginx (GitHub - gartnera/nginx_csrf_prevent: Prevent CSRF with nginx · GitHub), ma credo che nginx debba essere ricompilato, e non so se sia necessario l’ambiente di sviluppo completo di Discourse per farlo.

Purtroppo il problema non è ancora risolto qui. L’accesso fallisce con “errore sconosciuto” e ad ogni tentativo vedo questo nel log:

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)

Il file app.yml contiene

## 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"

come raccomandato in https://meta.discourse.org/t/haproxy-and-discourse-ip-issue/92387

Prova questo: Moved site behind proxy, favicon and header not using https anymore - #12 by rossierd

Con questo ho effettuato l’accesso con successo, ma fai attenzione a dove applichi il comando proxy_pass. Funziona solo all’interno di location @discourse { .. }

Ok, solo per capire: stiamo parlando di nginx all’interno dello “zoo dei container”, corretto? Perché, prima dell’aggiornamento alla versione 2.5.0beta4, non c’era alcuna necessità di applicare patch a questo, funzionava semplicemente senza problemi.

Sì, se per zoo intendi il contenitore che esegue Discourse. Puoi accedere al contenitore con “rails enter app”.

Abbiamo eseguito ulteriori debug: il problema inizia nel server nginx all’interno del container. Non riconosce la direttiva proxy_pass e sembra quindi crashare, ma perché:

root@develd:/var/discourse# docker ps -a
CONTAINER ID        IMAGE                              COMMAND                  CREATED             STATUS                     PORTS                   NAMES
f8f6103a036d        local_discourse/app                "/sbin/boot"             35 secondi fa       Up 32 secondi              127.0.0.1:884->80/tcp   app
43406c37f403        discourse/base:2.0.20200512-1735   "ruby -e 'require 'y…"   2 ore fa            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: la direttiva "proxy_pass" non è consentita qui in /etc/nginx/conf.d/discourse.conf:10

Sto utilizzando questa configurazione nginx in app.yml:

## Qualsiasi comando personalizzato da eseguire dopo la build
run:
  - exec: echo "Inizio dei comandi personalizzati"
  ## Se vuoi impostare l'indirizzo email 'From' per la tua prima registrazione, decommenta e modifica:
  ## Dopo aver ricevuto la prima email di iscrizione, ricommenta la riga. Deve essere eseguita solo una volta.
  ## - 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; <-- Ciò che ho modificato
        proxy_pass http://discourse;
        types {
  - exec: echo "Fine dei comandi personalizzati"

Credo che non ti serva quel proxy_pass proprio lì nel tuo app.yml. La mia configurazione è del genere:

  after_bundle_exec:
    # Questo è il trucco per trasmettere gli indirizzi IP a Discourse
    # Vedi 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 {

Ma il mio codice potrebbe essere precedente al passaggio a Debian nei container.

Potresti provare a modificare direttamente quel file all’interno del container e riavviare nginx.

Per essere sicuri, all’interno del contenitore stesso, modifica /etc/nginx/conf.d/discourse.conf e applica la patch al file come indicato sopra.
Quindi riavvia nginx nel modo seguente:

$ service nginx stop
$ service nginx start

e osserva cosa succede…

Il suggerimento di Jays era corretto: ho appena rimosso proxy_pass e poi ha funzionato. La configurazione finale in app.yml è:

## Eventuali comandi personalizzati da eseguire dopo la compilazione
run:
  - exec: echo "Inizio dei comandi personalizzati"
  ## Se desideri impostare l'indirizzo email 'From' per la tua prima registrazione, decommenta e modifica:
  ## Dopo aver ricevuto la prima email di iscrizione, ricommenta la riga. Deve essere eseguita solo una volta.
  ## - 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; <-- Ciò che ho modificato
        types {
  - exec: echo "Fine dei comandi personalizzati"