Nginx selektiver TLS-Passthrough-Reverseproxy basierend auf SNI

Nginx selektiver TLS-Passthrough-Reverseproxy basierend auf SNI

Ich habe ein System von IoT-Geräten hinter einem NAT, sodass sie nicht über das öffentliche Internet zugänglich sind (obwohl dies erwünscht ist). Um dies zu umgehen, habe ich sie in ein VPN eingebunden, wobei ein Mitglied dem öffentlichen Internet ausgesetzt ist und als Gateway fungiert. Das VPN hat eine interne Domäne eingerichtet und jedes Mitglied des Netzwerks hat eine Subdomäne basierend auf einer eindeutigen ID (nehmen wir die MAC-Adresse), wie folgt:12a4f81ead4e.vpn.example.com

Ich möchte einen Reverseproxy für GatwayProxy-Anfragen erstellen, der Nginx ausführt.

Der Plan ist, einen DNS-Eintrag für das Gateway zu erstellen *.gateway.comund den Datenverkehr von/nach zu umzuleiten (ähm, zu proxyen) 12a4f81ead4e.gateway.com. 12a4f81ead4e.vpn.example.comDer Endbenutzer müsste dann nur noch 12a4f81ead4e.gateway.comin seinen Browser eingeben, um auf sein Gerät zuzugreifen. Ich würde gerne nginx verwenden, da das Gateway bereits für andere Zwecke nginx ausführt.

Ich gehe davon aus, dass HTTP-Anfragen einfach sind und mit einer sorgfältig ausgearbeiteten Nginx- proxy_passDirektive ausgeführt werden können.

Aber was ist mit HTTPS-Anfragen? Soweit ich weiß, wird TLS-Passthrough basierend auf SNI jetzt von nginx implementiert, aber alle Beispiele, die ich bisher gesehen habe, erstellen eine statische Zuordnung für ... nun ja, die Zuordnung des eingehenden SNI zu einem Upstream-Ziel:

stream {
  map $ssl_preread_server_name $selected_upstream {
    example.org upstream_1;
    example.net upstream_2;
    example.com upstream_3;
    default upstream_4;
  }
  upstream upstream_1 { server 10.0.0.1:443; }
  upstream upstream_2 { server 10.0.0.2:443; }
  upstream upstream_3 { server 10.0.0.3:443; }
  upstream upstream_4 { server 10.0.0.4:443; }
  server {
    listen 10.0.0.5:443;
    proxy_pass $selected_upstream;
    ssl_preread on;
  }
}

Das Problem besteht darin, dass Geräte dynamisch zum VPN hinzugefügt/entfernt werden und ich die Nginx-Konfigurationsdateien nicht ständig neu schreiben möchte. Wenn das Lesen der Karte aus einer Datei möglich ist, ist das ein Schritt in die richtige Richtung, obwohl ich denke, dass Nginx bei jeder Änderung neu geladen werden müsste, was zu Berechtigungsproblemen führt, die natürlich mit Sudo-Regeln umgangen werden könnten, aber nicht die beste Lösung sind.

Außerdem möchte ich nur eingehende Anfragen als Proxy weiterleiten *.gateway.comund andere HTTPS-Anfragen normal an die vorhandenen virtuellen Hosts weiterleiten. Wenn möglich, möchte ich das Beenden der SSL-Verbindung vermeiden. Das ist zwar keine wirklich zwingende Anforderung, aber ich möchte es so implementieren, wenn es technisch machbar ist. Auch nur zum Spaß.

Ich komme gut damit zurecht, intern auf einem alternativen Port auf die anderen virtuellen Hosts zu lauschen. Ich habe etwas Ähnliches für HTTP gemacht, als ich einen „globalen“ Standort festlegen wollte, und habe alle HTTP-vHosts auf Port 81 verschoben und einen Catch-All-vHost auf Port 80 implementiert, der den „globalen“ Standort bediente und alles andere per Proxy auf Port 81 umgeleitet hat. :)

Also... was ich bräuchte, wäre so etwas wie das hier (funktioniert offensichtlich nicht):

stream {
  map $ssl_preread_server_name $selected_upstream {
    (.*).gateway.com $1.vpn.example.com;
    default normal_serve;
  }

  upstream normal_serve { server 127.0.0.1:8443; }

  server {
    listen 0.0.0.0:443;
    proxy_pass $selected_upstream;
    ssl_preread on;
  }

  server {
    listen 127.0.0.1:8443;
    server_name other.website.com;

    (...)
  }
}

Antwort1

So funktioniert es:

stream {
  resolver 8.8.8.8;

  map $ssl_preread_server_name $selected_upstream {
    ~(.*).gateway.example.com $1.vpn.example.com:443;
    default 127.0.0.1:8443;
  }

  server {
    listen 0.0.0.0;
    proxy_pass $selected_upstream;
    ssl_preread on;
  }
}

http {
  resolver 8.8.8.8;
  server {
    listen 127.0.0.1:8443 ssl;
    (...)
  }
}

verwandte Informationen