Jede Zeile im Zugriffslog zeigt dieselbe Loopback-Adresse, während die Seite selbst lädt und das Zertifikat korrekt geprüft wird. Nichts ist in dem Sinne kaputt, dass es unerreichbar wäre, und genau deshalb überlebt dieser Fall monatelang. Kaputt ist die Zuordnung: Das Web-Backend protokolliert die Adresse des Hops, der die Verbindung weitergeleitet hat, und jede nachgelagerte Entscheidung, die dem Log vertraute, erbte dieselbe falsche Antwort.

Die folgenden Prüfungen laufen vom Kabel nach innen. Halten Sie bei der ersten an, die den Wert erklärt, den Sie sehen, denn jeder spätere Schritt setzt voraus, dass die früheren bereits zutreffen. Keine von ihnen verlangt, dass Sie die Frontend-Konfiguration raten.

Folgen Sie den zwei Verbindungen, nicht der Logzeile

Port-Share multiplext zwei Protokolle auf einem TCP-Listener. OpenVPN prüft die ersten Bytes einer eingehenden Verbindung, behält sie, wenn sie wie sein eigenes Protokoll aussieht, und übergibt alles andere an ein zweites Ziel. Dieses Ziel ist normalerweise ein Webserver auf demselben Host, sodass der angenommene Socket des Webservers den OpenVPN-Prozess als Gegenstelle hat.

ss -ltnp 'sport = :443'
ss -tnp 'dport = :8080'

Der erste Befehl zeigt, wem der öffentliche Listener gehört. Der zweite zeigt die Verbindungen, die am Backend-Port ankommen, und ihre Gegenstellenadresse ist die, die das Zugriffslog ausgeben wird. Ist diese Gegenstelle Loopback, ist das Log korrekt und die Information ging eine Schicht früher verloren. Das Umbenennen des Logfelds oder das Aktivieren eines Client-IP-Moduls holt keine Adresse zurück, die die Verbindung nie getragen hat.

Diese Prüfung entscheidet alles Weitere. Ist die Gegenstelle des Backends die Adresse des Browsers, ist Port-Sharing nicht Ihr Problem, sondern das Logformat.

Transportweiterleitung ist keine Header-Injektion

Der Weiterleiter verschiebt Bytes. Er parst kein HTTP, kann also keinen Header in eine Anfrage einfügen, die er nie als Anfrage liest, und kann einem TLS-Stream nichts hinzufügen, den er unberührt durchreicht. Ein Modul, das einen Proxy-Header erwartet, wartet weiter auf einen, den keine Komponente erzeugt.

Zwei Mechanismen können die Originaladresse tragen, und beide erfordern Einigkeit auf beiden Seiten. Das PROXY-Protokoll stellt der Verbindung einen kleinen Header voran, und das Backend muss darauf vorbereitet sein; ein Backend, das ihn nicht erwartet, hält diese Bytes für den Beginn der Anfrage und scheitert auf verwirrende Weise. Vertrauenswürdige Proxy-Header wie X-Forwarded-For funktionieren, wenn ein HTTP-fähiger Proxy sie setzt und dem Backend mitgeteilt wird, welche Gegenstellen sie behaupten dürfen.

Nichts davon ist automatisch, und nichts davon ist standardmäßig sicher. Ein Backend, das jedem eingehenden weitergeleiteten Header glaubt, lässt einen Client seine eigene Adresse wählen, was eine Ungenauigkeit im Log in eine Umgehung der Zugriffskontrolle verwandelt. Führen Sie die Demonstration einmal auf einem Staging-Host aus, bevor Sie irgendeinem Header auf einem Produktionspfad vertrauen.

curl -sS -H 'X-Forwarded-For: 127.0.0.1' https://site.example.com/health
grep -F '127.0.0.1' /var/log/nginx/access.log | tail -n 3

Das optionale Quellzuordnungsverzeichnis

Port-Sharing hat ein optionales Verzeichnisargument, das eine Zuordnung zwischen der ursprünglichen Quelladresse und der weiterleitenden Verbindung aufzeichnet, auf Plattformen, auf denen die Funktion implementiert ist. Die Webseite soll diese Zuordnung konsultieren; sie ist kein Header und wird nicht in die Anfrage injiziert.

Dieses Design lohnt sich zu kennen, weil es erklärt, warum manche Installationen echte Adressen melden und andere nicht, bei scheinbar identischer Frontend-Konfiguration. Der Unterschied liegt auf der Empfängerseite, und er wohnt meist in einem Modul oder Patch, das die meisten paketierten Webserver nicht standardmäßig aktivieren. Lesen Sie in der Dokumentation Ihres Webservers nach, welchen genauen Hook er bietet, und bestätigen Sie, dass das Modul geladen ist, statt es anzunehmen.

Wenn Sie diesen Weg gehen, behandeln Sie ihn als Integration mit zwei Hälften: die Zuordnung, die der Weiterleiter schreibt, und der Abgleich, den das Backend durchführt. Prüfen Sie beides und bestätigen Sie, dass das Verzeichnis einen Neustart übersteht und auf einem stark belasteten Listener nicht unbegrenzt wächst.

Entscheiden Sie, welche Komponente für die Adresse bürgt

Bevor Sie die Konfiguration ändern, benennen Sie die Komponente, die eine Client-Adresse behaupten darf, und die Komponenten, die ihr glauben dürfen. Eine einzige schriftlich festgehaltene Antwort verhindert die übliche Drift, bei der drei verschiedene Schichten jeweils einer anderen Quelle vertrauen.

Drei Designs decken die meisten Bedürfnisse ab. Halten Sie den öffentlichen Listener und den Web-Listener getrennt, sodass der Webserver echte Gegenstellen sieht und kein Zuordnungsproblem entsteht. Setzen Sie einen HTTP-fähigen Reverse-Proxy davor, beenden oder parsen Sie die Anfrage dort, und lassen Sie ihn einen weitergeleiteten Header für ein Backend setzen, das nur diesem Proxy vertraut. Oder behalten Sie Port-Sharing und akzeptieren Sie, dass Logs den Hop zeigen, mit Zugriffsregeln gegen die Anwendungsidentität statt gegen eine Adresse.

Was nicht funktioniert, ist, eine weitergeleitete Loopback-Adresse als Beweis zu behandeln, dass die Anfrage auf dieser Maschine entstanden ist. Diese Schlussfolgerung ist genau umgekehrt: Die Adresse ist gerade deshalb vorhanden, weil eine lokale Komponente die Verbindung hergestellt hat. Eine so geschriebene Regel lässt jeden herein, der den Listener erreichen kann.

Testen Sie die Regel, nicht die Seite

Eine Seite, die lädt, beweist, dass der Weiterleitungspfad funktioniert, und sagt nichts über die Zuordnung. Testen Sie den Verbraucher der Adresse: die Zugriffsregel, das Ratenlimit, den Audit-Eintrag, die Geo-Abfrage.

curl -sS -o /dev/null -w '%{http_code}\n' https://site.example.com/
tail -n 5 /var/log/nginx/access.log
grep -c '' /var/log/nginx/access.log

Vergleichen Sie eine autorisierte externe Anfrage mit den Aufzeichnungen sowohl des Frontends als auch des Backends und bestätigen Sie, dass eine Regel, die ablehnen sollte, weiterhin ablehnt, und eine, die zulassen sollte, weiterhin zulässt. Wenn die Logzeile korrekt ist, die Regel sich aber weiterhin so verhält, als wären alle lokal, liest die Regel ein anderes Feld als das, das Sie korrigiert haben.

Schließen Sie ab, indem Sie die Zuordnungskette in die Deployment-Notizen schreiben: welcher Listener, welcher Weiterleiter, welcher Header oder welche Zuordnung und welches Backend dafür konfiguriert ist, ihr zu vertrauen. Die nächste Person, die eine Logzeile liest, braucht alle vier, und das Fehlen dieser Notiz ist der Grund, warum dasselbe Ticket ein Jahr später wieder aufgeht. Halten Sie sie neben der Konfiguration fest statt in einem Chat-Thread.

Ähnliche Artikel