Bearbeitung von http- und https-Anfragen über einen einzigen Port mit nginx

16

Ich habe mich gefragt, ob Nginx HTTP- und HTTPS-Anforderungen auf demselben Port verarbeiten kann . [*]

Das versuche ich zu tun. Ich verwende einen Webserver (lighttpd), der http-Anforderungen verarbeitet, und ein C-Programm, das über https einen bestimmten Abschnitt der Dokumentstruktur bedient. Diese beiden Prozesse werden auf demselben Server ausgeführt.

Auf der Firewall-Ebene kann ich nur einen Port für die Weiterleitung von Datenverkehr auf diesen Server haben . Also, was ich tun möchte, ist, Nginx auf diesem Server so einzurichten, dass es auf Anfragen an einem einzelnen Port wartet und dann:

A) leitet alle http://myhost.com/ * Anfragen um, so dass sie zu localhost gehen: 8080 (wo lighttpd lauscht)

B) Wenn ein Benutzer eine URL anfordert, die beispielsweise mit https: // myhost.com/app beginnt, sendet er diese Anforderung an localhost: 8008 (C-Programm). Beachten Sie, dass in diesem Fall der Datenverkehr zwischen dem Remote-Browser und Nginx verschlüsselt werden muss.

Glaubst du, das könnte möglich sein? Wenn ja, wie geht das?

Ich weiß, wie das mit zwei verschiedenen Ports geht. Die Herausforderung, der ich gegenüberstehe, besteht darin, dies mit nur einem einzigen Port zu tun (leider habe ich keine Kontrolle über die Firewall-Konfiguration in dieser bestimmten Umgebung, das ist also eine Einschränkung, die ich nicht vermeiden kann). Techniken wie Reverse-Port-Weiterleitung über ssh zur Umgehung der Firewall werden ebenfalls nicht funktionieren, da dies für Remotebenutzer funktionieren sollte, die nur einen Webbrowser und eine Internetverbindung haben.

Wenn dies über die Möglichkeiten von nginx hinausgeht, kennen Sie ein anderes Produkt, das diese Anforderungen erfüllen könnte? (Bisher habe ich es nicht geschafft, dies mit lighttpd und pound einzurichten). Ich würde es auch vorziehen, Apache zu meiden (obwohl ich bereit bin, es zu verwenden, wenn es die einzig mögliche Wahl ist).

Danke im Voraus, Alex

[*] Um es klar zu sagen, ich spreche über die Behandlung von verschlüsselten und unverschlüsselten HTTP-Verbindungen über denselben Port. Es spielt keine Rolle, ob die Verschlüsselung über SSL oder TLS erfolgt.

alemartini
quelle
HTTPS-Anforderungen werden standardmäßig an Port 443 gesendet. Selbst wenn dies funktioniert (und ich denke, dass dies mit ein bisschen Hackery möglich ist), müssen Sie als Links yourhost.com und yourhost.com:80 verwenden (oder) yourhost.com:443 und yourhost.com ).
Zanchey
Ok, ich bin neu bei Server Fault und weiß nicht, ob ich meine eigene Frage löschen kann. Da es den Anschein hat, dass das Problem nicht klar genug formuliert wurde, werde ich stattdessen eine neue Frage öffnen. Trotzdem vielen Dank an alle, die mit nützlichen Vorschlägen zu diesem Thema beigetragen haben.
Alemartini

Antworten:

17

Für diejenigen, die vielleicht suchen:

Fügen Sie Ihrer Serverdefinition ssl on;und hinzu error_page 497 $request_uri;.

HoverHell
quelle
8
Kleine Verbesserung der HoverHells-Antwort: Fügen Sie ssl on;und error_page 497 =200 $request_uri;zu Ihrer Serverdefinition hinzu . Dies ändert den Statuscode auf 200.
Mawi12345
Diese Zustellungsfehlerantwort erklärt, warum diese Lösung funktioniert.
SiliconMind
17

Laut Wikipedia-Artikel zu Statuscodes hat Nginx einen benutzerdefinierten Fehlercode, wenn HTTP-Datenverkehr an den https-Port gesendet wird (Fehlercode 497).

Und laut nginx docs auf error_page können Sie einen URI definieren, der für einen bestimmten Fehler angezeigt wird.
Auf diese Weise können wir eine URL erstellen, an die Clients gesendet werden, wenn der Fehlercode 497 ausgelöst wird.

nginx.conf

#lets assume your IP address is 89.89.89.89 and also that you want nginx to listen on port 7000 and your app is running on port 3000

server {
    listen 7000 ssl;

    ssl_certificate /path/to/ssl_certificate.cer;
    ssl_certificate_key /path/to/ssl_certificate_key.key;
    ssl_client_certificate /path/to/ssl_client_certificate.cer;

    error_page 497 301 =307 https://89.89.89.89:7000$request_uri;

    location / {
        proxy_pass http://89.89.89.89:3000/;

        proxy_pass_header Server;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Protocol $scheme;
    }
}

Wenn ein Client jedoch eine Anforderung über eine andere Methode als ein GET sendet, wird diese Anforderung in ein GET umgewandelt. So erhalten Sie die Anforderungsmethode, über die der Client hereinkam; Wir verwenden Weiterleitungen zur Fehlerverarbeitung, wie in den Nginx-Dokumenten auf error_page gezeigt

Und deshalb verwenden wir die 301 =307Umleitung.

Mit der hier gezeigten Datei nginx.conf können http und https auf demselben Port abhören

Komu
quelle
4

Wenn Sie wirklich clever sein möchten, können Sie mit einem Verbindungsproxy die ersten paar Bytes des eingehenden Datenstroms abrufen und die Verbindung basierend auf dem Inhalt von Byte 0 übergeben: Wenn es 0x16 ist (SSL / TLS). Handshake-Byte), leiten Sie die Verbindung an die SSL-Seite weiter. Wenn es sich um ein alphabetisches Zeichen handelt, führen Sie normales HTTP aus. Mein Kommentar zur Portnummerierung trifft zu .

Zanchey
quelle
2

Ja, es ist möglich, muss aber den Nginx-Quellcode patchen (HoverHell hat eine Lösung ohne Patchen). Nginx behandelt dies eher als Fehlkonfiguration als als gültige Konfiguration.

Die Variable $ ssl_session_id kann verwendet werden, um zwischen einer normalen und einer SSL-Verbindung zu unterscheiden.

Patch gegen Nginx-0.7.65:

--- src/http/ngx_http_request.c-orig    2011-05-03 15:47:09.000000000 +0200
+++ src/http/ngx_http_request.c 2011-05-03 15:44:01.000000000 +0200
@@ -1545,12 +1545,14 @@

    c = r->connection;

+    /* disable plain http over https port warning
     if (r->plain_http) {
         ngx_log_error(NGX_LOG_INFO, c->log, 0,
                       "client sent plain HTTP request to HTTPS port");
         ngx_http_finalize_request(r, NGX_HTTP_TO_HTTPS);
         return;
     }
+    */

#if (NGX_HTTP_SSL)

Serverkonfiguration:

server {
    listen 80;
    index index.html;

    location / {
        root html;
        if ($ssl_session_id) {
            root html_ssl;
        }
    }

    ssl on;
    ssl_certificate cert.crt;
    ssl_certificate_key cert.key;
}
yaplik
quelle
1

Ich glaube nicht, dass es irgendetwas gibt, das mit zwei verschiedenen Protokollen an einem einzigen Port umgehen kann ...

Ich bin gespannt, warum Sie nur einen Port weiterleiten können, aber abgesehen davon ... ist es nicht ideal, aber wenn ich in Ihren Schuhen stecke, würde ich alles über https bedienen.

William Hilsum
quelle
Hallo Wil, und danke für deine Antwort! Ich denke, dass es eine Option sein könnte, alles über https bereitzustellen, obwohl ich dies gerne so einrichten möchte, wie ich es beschrieben habe. Möglicherweise könnte dies geschehen, wenn der Front-Webserver (der als Reverse-Proxy fungiert) eine normale http-Sitzung aufbauen und diese dann auf https aktualisieren könnte, ohne die Ports zu ändern. Ich denke, dass dieses Verhalten in RFC2817 (Upgrade auf TLS mit HTTP / 1.1) beschrieben ist, aber ich bin nicht sicher, ob Nginx oder andere Webserver wissen, wie sie mit diesem Standard umgehen sollen.
Alemartini
Ich habe keine Zeit, einen ganzen RFC zu lesen (und bin mir nicht sicher, ob ich klug genug bin, ihn zu verstehen!), Aber sprechen Sie über die Standardverhandlung, bevor die sichere Sitzung aufgebaut wird oder über verschiedene Sitzungen? Ich glaube, ich verstehe ein bisschen mehr - der Server bedient zwei Ports und es ist der Proxy, der die Anfrage weiterleitet - hört sich cool an, aber ich habe es noch nie gesehen. Vielleicht könnte eine Lösung darin bestehen, eine einzige sichere Site an einem Port zu erstellen und ein ganzes virtuelles Verzeichnis zu haben, das einfach die andere Website erbt / importiert? Es werden nicht alle Probleme gelöst, sondern es funktioniert möglicherweise nur: S
William Hilsum
1

Sie können HTTP und HTTPS nicht über denselben Port unterstützen, da beide Enden der Verbindung eine bestimmte Sprache erwarten und nicht klug genug sind, um herauszufinden, ob das andere Ende etwas anderes spricht.

Wie Ihr Kommentar zu Wils Antwort andeutete, könnten Sie ein TLS-Upgrade verwenden (ich glaube, neuere nginx-Releases unterstützen es, obwohl ich es nicht ausprobiert habe), aber auf diesem Server werden weder HTTP noch HTTPS ausgeführt, sondern nur HTTP mit TLS-Upgrade. Das Problem ist immer noch die Browserunterstützung - die meisten Browser unterstützen sie (immer noch) nicht. Wenn Sie einen begrenzten Pool an Kunden haben, ist dies jedoch möglich.

womble
quelle
1
Verschlüsselter und unverschlüsselter HTTP-Verkehr kann über einen einzelnen Port abgewickelt werden. Ich würde gerne wissen, ob dies durch die Verwendung von Nginx oder anderen Produkten (wie LightTPD) als Reverse-Proxys möglich ist. Wahrscheinlich kann diese Art von Setup von Apache gehandhabt werden, aber ich habe in meiner ursprünglichen Frage vergessen zu erwähnen, dass ich lieber keinen Apache verwenden möchte (obwohl ich es tun würde, wenn es unter Linux keine andere Wahl gibt Plattform, um dies zu erreichen).
Alemartini
Wie ich in meiner Antwort sagte, "Ich glaube, neuere Nginx-Release-Unterstützung [TLS-Upgrade], obwohl ich nicht versucht habe". Wenn ich das Handbuch für Sie lesen muss, haben Sie Pech.
womble
Es tut mir leid, wenn ich den Eindruck habe, dass ich jemanden brauche, der ein Handbuch für mich liest. Es scheint nur, dass das Problem (und die Fragen dazu) nicht genau genug beschrieben wurden (mein Fehler), was zu unterschiedlichen Interpretationen dessen führte, was ich fragte oder brauchte. Deshalb habe ich mich entschlossen, eine neue Frage zu diesem Thema zu eröffnen und zu versuchen, jede mögliche Verwirrung in Bezug auf das Problem oder die spezifischen Fragen zu vermeiden. Wie auch immer, vielen Dank für Ihre Zeit und für Ihren Einblick.
alemartini
0

Ich bin mir nicht sicher, wie es funktioniert, aber CUPSD antwortet sowohl auf http als auch auf https auf Port 631. Wenn nginx das jetzt nicht kann, können sie vielleicht daraus lernen, wie das CUPS-Team es funktioniert, aber CUPS ist unter dem GPL, daher muss Nginx möglicherweise eine Änderung der Lizenz in Betracht ziehen, wenn sie eine solche Funktion implementieren möchten und den entsprechenden Code nicht an anderer Stelle finden können.

Ryan Grange
quelle
0

Theoretisch könnten Sie eine Webseite haben, auf die über HTTP zugegriffen werden kann und die in der Lage ist, ein WebSocket unter https: 443 an einer beliebigen Stelle zu öffnen . Der anfängliche WebSocket-Handshake ist HTTP. Ja, es ist möglich, eine unsicher aussehende Seite tatsächlich für eine sichere Kommunikation zu machen. Sie können dies mit der Netty Library tun .

Djangofan
quelle
0

Dies ist seit 1.15.2 endlich richtig möglich. Siehe die Informationen hier .

Fügen Sie in Ihrer nginx.conf einen Block wie diesen hinzu (außerhalb des http-Blocks):

stream {
    upstream http {
        server localhost:8000;
    }

    upstream https {
        server localhost:8001;
    }

    map $ssl_preread_protocol $upstream {
        default https;
        "" http;
    }

    server {
        listen 8080;
        listen [::]:8080;
        proxy_pass $upstream;
        ssl_preread on;
    }
}

Dann können Sie Ihren normalen Serverblock erstellen, aber diese verschiedenen Ports abhören:

server {
    listen 8000;
    listen [::]:8000;
    listen 8001 ssl;
    listen [::]:8001 ssl;
...

Auf diese Weise kann der Stream-Block vorlesen und erkennen, ob es sich um TLS handelt oder nicht (in diesem Beispiel an Port 8080). Anschließend leitet der Proxy ihn lokal an den richtigen Server-Port weiter.

Sam Bull
quelle