Diskurs hinter Reverse Proxy und HTTPS

Ich versuche, Discourse hinter meinem Apache-Reverse-Proxy einzurichten, bekomme es aber mit HTTPS nicht richtig zum Laufen.

Ich hatte bereits viele Probleme, bis hierher zu kommen. Momentan läuft Discourse auf einem Server, und davor steht ein Apache-Server, der als Reverse-Proxy fungiert. Anfangs hatte ich große Schwierigkeiten, Discourse hinter einem Reverse-Proxy zum Laufen zu bringen, da Discourse immer eine Weiterleitung zur in der app.yaml festgelegten Hostname-Adresse erzwingen wollte.

Irgendwie habe ich es nun doch zum Laufen gebracht, aber ich bekomme Mixed-Content-Warnungen in meinem Browser.
In Apache habe ich eine Weiterleitung von HTTP zu HTTPS eingerichtet, die funktioniert einwandfrei. Dennoch liefert Discourse weiterhin einige Inhalte über HTTP aus, und ich kann nicht herausfinden, wie ich es dazu zwingen kann, auf HTTPS umzustellen.

Zum Beispiel wird das Favicon über HTTP ausgeliefert, und ich weiß nicht, wie ich das ändern soll.

Kann ich Discourse dazu bringen, alle Links auf HTTPS umzustellen, ohne dass Discourse den HTTPS-Datenverkehr selbst verarbeiten muss?

Ich habe versucht, Folgendes zu setzen:

Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains"

in Apache, aber das scheint nicht zu helfen.

Auch das Aktivieren der Option „force https“ in Discourse bringt nichts; es zerstört lediglich die Seite, da dann alles über HTTP ignoriert wird.

Was muss ich tun, um die Mixed-Content-Probleme zu beheben?

Apache2 wird dir viele Probleme bereiten. Erwäge einen Wechsel zu nginx, caddy, traefik oder haproxy.

Ich habe Apache2 in einem Testsystem „ohne Probleme

Danke für die Antworten, aber ich habe Apache nicht auf demselben Server wie Discourse. Das habe ich vielleicht nicht klar genug ausgedrückt.
Ich habe einen bestehenden Apache-Server mit vielen Websites und muss ihn so konfigurieren, dass er Discourse auf einem anderen Server als Reverse-Proxy verwendet, sodass ich keine Sockets nutzen kann.

Viele Grüße.

Ich habe diese Methode nicht ausprobiert, aber du könntest in Erwägung ziehen, dein entferntes Dateisystem einzubinden und zu prüfen, ob du so auf einen Unix-Socket zugreifen kannst – besonders wenn sich die Server im selben Rechenzentrum befinden und die Netzwerkleistung über ein Weitverkehrsnetz (WAN) kein Problem darstellt.

Ohne Angaben zur Architektur, zum Betriebssystem, zur Netzwerkkonfiguration usw. ist es schwierig, eine Antwort zu geben, und vielleicht fällt dies hier bei Meta-Discourse sogar aus dem Rahmen.

Sei kreativ!

Das letzte Mal, als ich diese Konfiguration mit Apache2 verwendet habe, trat bei der Verbindung zum Discourse Message Bus ein Verbindungsfehler auf. Das ist jedoch bereits über ein Jahr her. Die Seite scheint zwar ordnungsgemäß zu laden, doch in der Entwicklerkonsole mit F12 wurde deutlich angezeigt, dass WSS-Verbindungen nach einigen ersten Versuchen zeitüberschritten wurden.

Wie man an der von mir geposteten Beispielkonfiguration deutlich sehen kann, haben wir kein wss verwendet.

wss ist nicht gleichbedeutend mit Apache2 Reverse Proxy (es ist nur eine Möglichkeit, dies umzusetzen, und wir verwenden kein wss)

Tatsächlich verwenden wir nur nginx- und Apache2-Reverse-Proxy-Konfigurationen mit Unix-Domain-Sockets, weil:

  • Ich faul bin und einfache, leicht zu debuggende Konfigurationen mag.
  • Unix-Domain-Sockets einfach und leicht zu debuggen sind.
  • In nginx können wir den Reverse Proxy mit einem Container über eine symbolische Verknüpfung wechseln.
  • Apache2 (Reverse Proxy zu Container) funktioniert nicht mit einer symbolischen Verknüpfung, sodass ein Neustart des Webservers erforderlich ist.

Allerdings hat @Grunskin etwas angesprochen, das wir noch nicht konfiguriert haben: den Reverse Proxy auf einem Host und den Container auf einem anderen Host auszuführen.

Wenn ich Zeit habe, werde ich dies sowohl für nginx als auch für Apache2 im selben Rechenzentrum testen und prüfen, ob ich dies zum Laufen bekomme, indem ich das entfernte Dateisystem mounte und einen Unix-Socket verwende.

Bis dahin…

Hinweis: Meiner Meinung nach ist dieses Problem weder für nginx noch für Apache2 relevant, die lediglich als Reverse Proxies fungieren (wie erwähnt, habe ich die Konfiguration für den Remotezugriff jedoch noch nicht getestet, daher kann ich dazu keine weiteren Kommentare abgeben).

Warum ist das notwendig?

Discourse ist eine Anwendung, keine Website. Sobald das anfängliche JavaScript-Paket an Ihren Browser übertragen wurde, hängen viele Funktionen von einer schnellen Verbindung zum Discourse-Server ab. Die Weiterleitung über ein anderes System führt zu Latenz und verschlechtert die Benutzererfahrung erheblich.

Können Sie uns Ihre Gründe für diese Anforderung erläutern?

Es gibt viele Gründe, einen Reverse-Proxy zu verwenden.
Zum Beispiel, wenn ich nur eine öffentliche IP-Adresse habe und mehrere Webserver öffentlich auf den Ports 80/443 erreichbar sein müssen, und ich Discourse in diesem Beispiel nicht auf diesem bestimmten Webserver ausführen kann.
Man kann ihn als SSL-Offloading nutzen, damit der Endserver sich nicht mit der Verschlüsselung befassen muss.
Die Angriffsfläche auf dem Endserver wird verringert, indem man ihn hinter einen Reverse-Proxy stellt.
Es gibt viele legitime Gründe dafür, und ich kann nicht verstehen, warum dies nicht möglich sein sollte.

Nach einer Weile fiel mir ein, dass ich bereits einen anderen Server mit Discourse habe, der hinter meinem Apache-Server läuft und seit mindestens 2 Jahren einwandfrei funktioniert. Ich habe den neuen Discourse genauso konfiguriert, kann aber nicht erreichen, dass er einige Inhalte nicht mehr über HTTP ausliefert. Der einzige Unterschied zwischen den Discourse-Servern besteht darin, dass der alte Server 2.4 und der neue 2.5 läuft. Ich weiß also nicht, ob es dort Unterschiede gibt.

Wie ich im ersten Beitrag erwähnt habe, macht force_https die Seite unbrauchbar, sodass es unmöglich wird, sich anzumelden, Einladungen anzunehmen usw. Es scheint, als würden einige JavaScript-Dateien nicht ausgeführt, da sie wahrscheinlich über HTTP ausgeliefert werden.
Wäre es nicht sinnvoller, dass force_https alle HTTP-Links in HTTPS umschreibt, anstatt sie zu verwerfen? Zumindest sollte dies als Option verfügbar sein.

Was ist die empfohlene Methode, Discourse öffentlich einzurichten? Einen Server in einer DMZ mit einer eigenen externen IP-Adresse aufsetzen?

Ich empfehle, sich Traefik anzusehen.
Es funktioniert hervorragend und verwaltet SSL-Zertifikate automatisch.

Es gibt viele Gründe, einen Reverse-Proxy einzusetzen. Ich bin mir ziemlich sicher, dass die Infrastruktur von Discourse.org hinter HAproxy läuft.

Ich verwende Traefik und Caddyserver und habe in der Vergangenheit auch Nginx, HAproxy und sogar Apache zum Laufen gebracht (für eine WordPress-Installation in einem Unterordner).

Das ist dein Problem. Du musst force_https aktivieren und herausfinden, warum es die Seite beschädigt. Es abzuschalten ist keine Option. Du hast kostenlose Unterstützung angefragt, und diejenigen, die geantwortet haben, haben keine Lösung für Apache. Daher musst du selbst die Führung bei der Apache-Gruppe übernehmen.

Ja. Wir stimmen absolut zu.

Wir nutzen das „Zwei-Container-Setup mit Reverse-Proxy

Ich möchte nur ergänzen, dass Discourse kein WSS verwendet. Der Message Bus nutzt Long Polling mit Chunked Encoding (Streaming).

Du musst diesen Hostnamen auf den festlegen, über den auf Discourse zugegriffen wird. Wenn der Domainname in app.yml nicht der ist, den die Leute in ihren Browser eingeben, um zu deiner Seite zu gelangen, wird es nicht funktionieren.

Hast du die ssl- oder letsencrypt-Vorlagen nicht in deiner app.yml?

Du musst force_https aktiviert haben.

Und du musst einige Umwege gehen, damit Long Polling funktioniert. Ich erinnere mich nicht mehr, welche das sind.

Hallo @Grunskin,

wir verstehen deine Frustration. Wenn du jedoch folgendes setzt:

force_https = true

ist das nicht das Problem. Wie @pfaffman bereits erwähnt hat (mindestens zweimal, einer der Top-Migrationsexperten von Meta):

Du “musst” bei force_https = true bleiben und dann das “echte Problem” herausfinden.

Wenn ich an deiner Stelle wäre, basierend auf dem, was ich gelesen habe:

Zuerst würde ich deinen Reverse-Proxy auf demselben Server wie die Discourse-Container(n) einrichten, um das Problem zu vereinfachen – nur zum Testen und zur Fehlersuche. Stelle sicher, dass dieser einfache Testfall einwandfrei funktioniert. Sobald er auf demselben Host läuft, deaktiviere diesen Test-Reverse-Proxy und wechsle zu deinem gewünschten Zwei-Server-Setup.

Das ist ein interessantes Problem. Du kannst es lösen, wenn du bei force_https = true bleibst und eine strukturierte, schrittweise Fehlersuchmethode anwendest. Solche Probleme sind einfach IT-Rätsel, die darauf warten, gelöst zu werden.

Du schaffst das.


PS: Wenn du mit diesem Rätsel frustriert oder gelangweilt bist, kannst du immer etwas Geld an @pfaffman oder eine andere erfahrene Person von Meta zahlen, damit sie dich über diese Hürde hinwegbringen und dir den Weg in klarere Zukunft bahnen.


Hinweis: Wir hatten keinerlei Probleme, Apache2 als Reverse-Proxy mit einem Unix-Domain-Socket auf demselben Server zum Laufen zu bringen. Meine aufrichtigen Entschuldigungen, dass ich keine persönliche Zeit habe, die Zwei-Server-Konfiguration einzurichten und die Apache2-Reverse-Proxy-Methode auf zwei verschiedenen Servern zum Laufen zu bringen. Wir sind derzeit mit den finalen Migrationsaufgaben beschäftigt, bei denen wir das „wahnsinnige Missbrauch von BBCode“ in Markdown-Probleme umwandeln, und das nimmt mehr Zeit in Anspruch als ursprünglich erwartet.

Ich habe mich dafür entschieden, Discourse lokal auf Port 8443 mit SSL laufen zu lassen, Port 8443 mit UFW (Firewall) zu sperren und Port 443 mit Apache2 auf 8443 zu proxyen. Ich versuche es jetzt.

                ProxyPass / "https://localhost:8443"
                ProxyPassReverse  / "https://localhost:8443"

Ich habe unser Discourse auf ein neues Setup migriert und habe jetzt Probleme mit http 403 bei POST session während des Logins. Ich vermute ein CSRF-Problem, aber im Moment habe ich keine Ahnung, wo ich mit dem Debugging anfangen soll, und /var/log/discourse-var-log/production.log und production_error.log sind alle 0 Bytes groß …

Irgendwelche Ideen, wie man das richtig debuggen kann?

Das aktuelle Setup:
1. haproxy als zentraler https Load Balancer/Accelerator (für Discourse und andere Dienste)

forum >> develd apache rev. proxy p.82
backend forum-backend
   mode http
   server forum.netzwissen.de 10.10.10.14:83 cookie A check
   http-request set-header X-Forwarded-Port %[dst_port]
   http-request add-header X-Forwarded-Proto https if { ssl_fc }
   # HSTS header, 16000000 Sekunden: etwas mehr als 6 Monate
   http-response set-header Strict-Transport-Security "max-age=16000000; includeSubDomains; preload;"

2. lokaler Apache als Reverse Proxy

   <IfModule proxy_module>
    ## <https://meta.discourse.org/t/running-other-websites-on-the-same-machine-as-discourse/17247>
    ProxyPreserveHost On
    # ProxyRequests Off     
    RequestHeader set X-Forwarded-Proto expr=%{REQUEST_SCHEME}
    RequestHeader set X-Real-IP expr=%{REMOTE_ADDR}
    ProxyPass /  unix:/var/discourse/shared/web-only/apache.http.sock|http://localhost/
    ProxyPassReverse  / unix:/var/discourse/shared/web-only/apache.http.sock|http://localhost/
    </IfModule>

3) Discourse läuft mit separaten Web_only- und Datencontainern, Web_only bereitgestellt mit - “templates/web.socketed.template.yml”

Dies ist die Sitzungsanfrage beim Login, die fehlschlägt:

POST /session HTTP/1.1
Host: forum.netzwissen.de
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:97.0) Gecko/20100101 Firefox/97.0
Accept: */*
Accept-Language: de,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: 89
X-CSRF-Token: dtV0N6faVQSWZsg6z9ZGOxQBjuTpBZk6tAMRxaXJdwozF1kObw9UuiFnxbLf5OGDeL1DWDgZ5W3oJP7CY+LwRw==
Discourse-Present: true
X-Requested-With: XMLHttpRequest
Origin: https://forum.netzwissen.de
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Referer: https://forum.netzwissen.de/
Connection: keep-alive

Antwort vom Webserver der web_only-Container:

HTTP/1.1 403 Forbidden
date: Sun, 13 Mar 2022 16:41:56 GMT
server: nginx
content-type: text/plain; charset=utf-8
x-frame-options: SAMEORIGIN
x-xss-protection: 1; mode=block
x-content-type-options: nosniff
x-download-options: noopen
x-permitted-cross-domain-policies: none
referrer-policy: strict-origin-when-cross-origin
vary: Accept
x-request-id: 778da942-3c1c-493b-946b-478984f53a8c
x-runtime: 0.003623
transfer-encoding: chunked
strict-transport-security: max-age=16000000; includeSubDomains; preload;

Ich bin nicht vertraut mit dem CSRF-Kram und auch nicht mit Nginx (der externe Webserver ist ein Apache 2.4), aber ich bin mir ziemlich sicher, dass CSRF hier mein Problem ist, da Discourse ohne Anmeldung einwandfrei funktioniert und nur die POST-Anfragen, die für die Anmeldung verwendet werden, hier fehlschlagen. Mein zentraler HAProxy hat die interne IP 10.10.10.21, deshalb habe ich

set_real_ip_from 10.10.10.21/24;

in die yml für den Discourse.conf im Web_only-Container eingefügt. Ich habe auch mit dem Standard 127.0.0.1/24; versucht, aber beides führt zum gleichen 403-Fehler bei der Anmeldung.