Nginx davor, privates Backend hinter dem VPN
Der Browser spricht mit Nginx, und Nginx öffnet eine zweite Verbindung in den Tunnel. Beide Wege routen, beide Zertifikate prüfen und öffentliche Weiterleitungen öffentlich halten.
Verfolgen Sie eine Anfrage von einem Handy im Zug bis zu einem Backend, das nur in einem privaten Netz existiert. Das Handy löst einen öffentlichen Namen auf, verbindet sich mit einer öffentlichen Adresse auf Port 443 und führt einen normalen TLS-Handshake durch. Nginx nimmt diese Verbindung an und öffnet dann eine zweite, völlig separate zu einer Adresse, die es nur über eine VPN-Schnittstelle erreicht. Am Handy ändert sich nichts, und das Handy erfährt nie, dass ein Tunnel im Spiel war.
Eine Anfrage durch zwei Verbindungen verfolgen
Der eingehende Weg ist eine öffentliche HTTP-Transaktion. Er hat einen öffentlichen Namen, ein öffentliches Zertifikat und einen Antwortpfad, der für jede Adresse funktionieren muss, die der Besucher gerade hat. Jede Änderung, die diesen Pfad stört, bricht alle Besucher auf einmal. Deshalb ist der öffentliche Weg derjenige, an dem Sie am wenigsten experimentieren sollten.
Der ausgehende Weg ist privat. Nginx wird zum Client, löst einen internen Namen auf oder nutzt eine feste Adresse und erreicht einen Dienst, der eine Verbindung aus dem Internet ablehnen würde.
Public client -> frontend public address:443 (TLS, public name)
Frontend -> VPN private backend 10.40.0.20:8443 (TLS, private name)
Halten Sie beides getrennt im Kopf, dann wird die Konfiguration offensichtlich. Vermischen Sie beides, versuchen Sie am Ende, einen öffentlichen Client an einem Tunnel teilnehmen zu lassen, und genau daraus entsteht der größte Teil der Verwirrung in diesem Design. Der private Weg darf fragil sein. Der öffentliche Weg nicht.
Bevor Sie irgendetwas davon bauen, bestätigen Sie, dass die Veröffentlichung der Anwendung beabsichtigt ist. Ein funktionierender Proxy ist auch eine funktionierende Exposition, und das Authentifizierungsmodell muss eine Entscheidung sein und kein Nebeneffekt.
Der Gateway-Name muss den Tunnel überleben
In der Namensauflösung steckt eine subtile Falle. Wenn privates DNS denselben Namen überschreibt, den das VPN-Profil für sein eigenes Gateway verwendet, kann ein Wiederverbinden am Ende versuchen, dieses Gateway durch den Tunnel zu erreichen, den es erst aufbauen soll. Der Client braucht diesen Namen aufgelöst, bevor der Tunnel existiert. Löst er ihn erst innerhalb des Tunnels auf, kann die Verbindung von Anfang an nicht aufgebaut werden.
Verwenden Sie getrennte, freigegebene Namen für Gateway und Backend und legen Sie die Resolver-Richtlinie so fest, dass jeder Name zu der Adresse auflöst, die sein Nutzer braucht. Das Ändern des Host-Headers einer Anwendung hilft hier nicht, weil der VPN-Client die Auflösung durchführt.
Denken Sie dann an den Umfang. Das Frontend braucht eine Route zur Backend-Adresse und muss zugleich weiter öffentliche Clients bedienen. Ein voller Tunnel auf diesem Host erfüllt die erste Anforderung und bricht die zweite.
Wo die Richtlinie es erlaubt, ist ein Tunnel zu einem bestimmten Ziel das sauberere Design. Wo die Richtlinie einen vollen Tunnel vorschreibt, gehört das Frontend in einen separaten Netzwerkkontext, statt mit Ad-hoc-Routen für bekannte Besucher geflickt zu werden.
Den Server-Block so schreiben, dass beide Wege prüfen lassen
Nehmen Sie an, das Backend zeigt ein Zertifikat für backend.corp.example und antwortet auf einer privaten Adresse. Sich mit einer Adresse zu verbinden und dabei einen Namen zu prüfen, ist genau der Zweck der SNI- und Namensoptionen des Proxy-Moduls.
upstream private_app {
server 10.40.0.20:8443;
}
server {
listen 443 ssl;
server_name app.example.com;
ssl_certificate /etc/nginx/tls/app-fullchain.pem;
ssl_certificate_key /etc/nginx/tls/app-key.pem;
location / {
proxy_pass https://private_app;
proxy_ssl_server_name on;
proxy_ssl_name backend.corp.example;
proxy_ssl_trusted_certificate /etc/nginx/trust/backend-ca.pem;
proxy_ssl_verify on;
proxy_ssl_verify_depth 3;
proxy_set_header Host app.example.com;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-For $remote_addr;
}
}
Jeder Pfad und jede Adresse oben ist ein Platzhalter. Wichtig sind die Teile, die die Prüfung eingeschaltet lassen: ein Name zum Prüfen, ein Trust-Bundle zum Prüfen und eine Tiefe, die zur freigegebenen Kette passt.
proxy_set_header Host app.example.com ist Absicht. Das Backend sollte so konfiguriert sein, dass es diesen öffentlichen Ursprung akzeptiert, und es sollte weitergeleiteten Headern nur von diesem Frontend vertrauen. Das Überschreiben von X-Forwarded-For mit der Client-Adresse ist bei einem einzelnen Proxy richtig und bei einer Kette falsch, daher braucht ein Multi-Proxy-Deployment seine eigene Regelung für vertrauenswürdige Adressen.
Den öffentlichen Ursprung wie einen öffentlichen Auftritt verhalten lassen
Die Anwendung sollte absolute URLs, Weiterleitungen und Cookies für den öffentlichen Ursprung ausgeben. Wenn sie einen Besucher auf backend.corp.example weiterleitet, landet dieser Besucher bei einem Namen, den er nicht auflösen kann.
Beheben Sie das über die unterstützte Einstellung für externe URLs der Anwendung und eine geprüfte Weiterleitungsrichtlinie, nicht durch Umschreiben von Hostnamen in Antwortkörpern. String-Ersetzung über beliebige Inhalte bricht signierte Nutzlasten und erzeugt Fehler, die schwer zu verfolgen sind.
Cookies verdienen dieselbe Sorgfalt. Ein Cookie, das auf den privaten Namen beschränkt ist, wird von einem Browser, der nur den öffentlichen kennt, nie gesendet.
Bestätigen Sie außerdem, dass Nginx den öffentlichen Socket wirklich besitzen kann. Ein vorhandener Webdienst auf 443 muss weichen, oder das Frontend muss woanders laufen, denn zwei Prozesse können nicht dieselbe Adresse und denselben Port binden.
Jeden Weg dort prüfen, wo er tatsächlich läuft
Validieren Sie die Konfiguration mit dem installierten Parser vor jedem Reload, damit ein Tippfehler nicht den Listener abschießt, der gerade bedient.
Testen Sie dann den Backend-Weg vom Frontend-Host selbst, im selben Netzwerkkontext, den Nginx nutzt. Ein Ping durch den Tunnel beweist Erreichbarkeit und nichts über TLS, Autorisierung oder die Anwendung.
Stellen Sie schließlich eine frische öffentliche Anfrage an den echten Hostnamen mit normaler Zertifikatsprüfung und beobachten Sie den Antwortpfad und nicht nur den Antwortkörper.
Bewahren Sie beide Beobachtungen mit der deployten Konfiguration auf und halten Sie fest, was passiert, wenn der Tunnel nicht verfügbar ist. Das Frontend sollte einen kontrollierten Upstream-Fehler liefern und nicht still auf ein anderes, nirgends dokumentiertes Backend zurückfallen. Ein 502 mit einer klaren Logzeile ist ein gutes Ergebnis. Eine zwischengespeicherte Seite von einem unbeabsichtigten Ursprung ist es nicht.