Gestire le richieste http e https usando una singola porta con nginx


16

mi chiedevo se nginx è in grado di gestire le richieste http e https sulla stessa porta . [*]

Questo è quello che sto cercando di fare. Sto eseguendo un web server (lighttpd) che gestisce le richieste http e un programma C che serve una particolare sezione dell'albero del documento tramite https. Questi due processi vengono eseguiti sullo stesso server.

A livello di firewall, posso avere solo un port forwarding del traffico su questo server . Quindi quello che mi piacerebbe fare è configurare nginx su questo server in modo che ascolti le richieste su una singola porta e quindi:

A) reindirizza tutte le richieste http://myhost.com/ * in modo che vengano indirizzate a localhost: 8080 (dove lighttpd è in ascolto)

B) se un utente richiede un URL che inizia con, ad esempio, https: // myhost.com/app, invia tale richiesta a localhost: 8008 (programma C). In questo caso, il traffico tra il browser remoto e nginx deve essere crittografato.

Pensi che questo potrebbe essere possibile? Se lo è, come può essere fatto?

So come fare usando due porte diverse. La sfida che sto affrontando sta facendo questo con una sola porta (sfortunatamente, non ho il controllo della configurazione del firewall su questo particolare ambiente, quindi questa è una restrizione che non posso evitare). L'uso di tecniche come il reverse port fowarding tramite ssh per bypassare il firewall non funzionerà neanche, perché questo dovrebbe funzionare per gli utenti remoti che non hanno nient'altro che un browser web e un collegamento Internet.

Se questo va oltre le capacità di nginx, conosci altri prodotti che potrebbero soddisfare questi requisiti? (finora non ho avuto successo nel configurarlo con lighttpd e pound). Preferirei anche evitare Apache (anche se sono disposto a usarlo se è l'unica scelta possibile).

Grazie in anticipo, Alex

[*] Per essere chiari, sto parlando della gestione di connessioni HTTP crittografate e non crittografate attraverso la stessa porta. Non importa se la crittografia viene eseguita tramite SSL o TLS.


Le richieste HTTPS vanno alla porta 443 per impostazione predefinita, quindi anche se riesci a farlo funzionare (e penso che sia possibile con un po 'di hacker), dovresti usare yourhost.com e yourhost.com:80 come link (o yourhost.com:443 e yourhost.com ).
Zanchey,

Ok, sono nuovo su Server Fault e non so se posso cancellare la mia domanda. Dal momento che sembra che il problema non sia stato formulato in modo abbastanza chiaro, aprirò invece una nuova domanda. Ad ogni modo, grazie mille a tutti coloro che hanno contribuito con suggerimenti utili per questo problema.
alemartini,

Risposte:


18

Secondo l'articolo di Wikipedia sui codici di stato, Nginx ha un codice di errore personalizzato quando il traffico http viene inviato alla porta https (codice di errore 497)

E secondo i documenti nginx su pagina_errore , è possibile definire un URI che verrà mostrato per un errore specifico.
Pertanto, possiamo creare un uri a cui verranno inviati i client quando viene generato il codice di errore 497.

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;
    }
}

Tuttavia, se un client effettua una richiesta tramite qualsiasi altro metodo tranne un GET, tale richiesta verrà trasformata in un GET. Pertanto, per preservare il metodo di richiesta tramite il quale il client è entrato; usiamo i reindirizzamenti dell'elaborazione degli errori, come mostrato nei documenti nginx su pagina_errore

Ed è per questo che usiamo il 301 =307reindirizzamento.

Usando il file nginx.conf mostrato qui, possiamo avere http e https in ascolto sulla stessa porta


17

Per coloro che potrebbero essere alla ricerca:

Aggiungi ssl on;e error_page 497 $request_uri;alla definizione del tuo server.


8
Piccoli miglioramenti alla risposta HoverHells: aggiungi ssl on;e error_page 497 =200 $request_uri;alla definizione del tuo server. Questo cambierà il codice di stato in 200.
Mawi12345

Questa risposta di errore serve a spiegare perché questa soluzione funziona.
SiliconMind,

4

Se vuoi essere davvero intelligente, potresti utilizzare una cosa proxy di connessione per annusare i primi due byte del flusso di dati in entrata e distribuire la connessione in base al contenuto del byte 0: se è 0x16 (SSL / TLS ' handshake 'byte), passa la connessione al lato SSL, se è un carattere alfabetico, esegui il normale HTTP. Si applica il mio commento sulla numerazione delle porte .


2

Sì, è possibile, ma ha bisogno di patchare il codice sorgente di nginx (HoverHell ha una soluzione senza patchare). Nginx lo considera come una configurazione errata piuttosto che una configurazione valida.

La variabile $ ssl_session_id può essere utilizzata per differenziare tra connessione plain e ssl.

Patch contro 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)

Config server:

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;
}

1

Non credo che ci sia qualcosa in grado di gestire due protocolli diversi su una singola porta ...

Sono curioso di sapere perché puoi inoltrare solo una porta, ma a parte questo ... non è l'ideale ma se fossi nei tuoi panni, servirei tutto su https.


Ciao Wil, e grazie per la tua risposta! Penso che servire tutto su https possa essere un'opzione, anche se mi piacerebbe essere in grado di configurarlo nel modo che ho descritto. Forse questo potrebbe essere fatto se il server Web frontale (che funge da proxy inverso) è in grado di stabilire una normale sessione http e quindi aggiornarla a https senza cambiare le porte. Penso che questo comportamento sia descritto in RFC2817 (aggiornamento a TLS con HTTP / 1.1) ma non sono sicuro che nginx o altri server web sappiano come gestire tale standard.
alemartini,

Non ho tempo di leggere un intero RFC (e non sono sicuro di essere abbastanza intelligente da capirlo!) Ma stai parlando della negoziazione standard prima che venga stabilita la sessione sicura o che le sessioni siano completamente diverse? Penso di capire un po 'di più - il server sta servendo su due porte ed è il proxy che fa riferimento alla richiesta - suona bene ma non l'ho mai visto fatto. Forse una soluzione potrebbe essere quella di creare un singolo sito sicuro su una porta e avere un'intera directory virtuale che eredita / importa semplicemente l'altro sito Web? Non
risolve

1

Non è possibile supportare sia HTTP che HTTPS sulla stessa porta, poiché entrambe le estremità della connessione si aspettano di parlare una determinata lingua e non sono abbastanza intelligenti da capire se l'altra estremità parla qualcos'altro.

Come il vostro commento alla risposta di Wil ha suggerito, si potrebbe utilizzare l'aggiornamento TLS (credo release nginx più recenti supportano, anche se non ho provato), ma che non è in esecuzione HTTP e HTTPS, che è solo in esecuzione HTTP con aggiornamento TLS. Il problema è ancora il supporto del browser - la maggior parte dei browser (ancora) non lo supporta. Se hai un pool limitato di clienti, questa è comunque una possibilità.


1
Il traffico HTTP crittografato e non crittografato può essere gestito attraverso un'unica porta. Quello che vorrei sapere è se ciò è possibile utilizzando nginx o altri prodotti (come lighttpd) che agiscono come proxy inversi. Molto probabilmente questo tipo di installazione può essere gestita da Apache, ma ho dimenticato di menzionare nella mia domanda originale che preferirei non dover usare Apache (anche se lo farei se non ci fosse altra scelta su Linux piattaforma per raggiungere questo obiettivo).
alemartini,

Come ho detto nella mia risposta, "credo che il supporto della versione nginx più recente [aggiornamento TLS], sebbene non ci abbia provato". Se hai bisogno che io legga il manuale per te, allora sei sfortunato.
womble

Mi dispiace se ho dato l'impressione di aver bisogno di qualcuno che leggesse un manuale per me. Sembra che il problema (e le domande al riguardo) non siano state descritte in modo sufficientemente preciso (errore mio), portando a interpretazioni diverse di ciò che stavo chiedendo o di cui avevo bisogno. Così ho deciso di aprire una nuova domanda su questo problema e cercare di evitare ogni possibile confusione riguardo al problema o alle domande specifiche al riguardo. Comunque, grazie per il tuo tempo e per aver condiviso la tua visione.
alemartini,

0

Non sono sicuro di come lo risolva, ma CUPSD risponde sia a http che a https sulla porta 631. Se nginx non può farlo ora, forse possono imparare da come il team CUPS lo esegue, ma CUPS è sotto GPL, quindi nginx potrebbe dover cercare di cambiare la propria licenza se vogliono implementare tale capacità e non riescono a trovare il codice per farlo altrove.


0

In teoria, potresti avere una pagina web accessibile via HTTP, che è in grado di aprire un WebSocket su https: 443 ovunque tu voglia. L'handshake iniziale di WebSocket è HTTP. Quindi, sì, è possibile rendere una pagina dall'aspetto insicuro effettivamente in grado di comunicare in modo sicuro. Potresti farlo con la Netty Library .


0

Questo è finalmente possibile farlo correttamente dall'1.15.2. Vedi le informazioni qui .

Nel tuo nginx.conf aggiungi un blocco come questo (fuori dal blocco http):

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;
    }
}

Quindi puoi creare il tuo normale blocco server, ma ascoltando su queste diverse porte:

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

In questo modo, il blocco di flusso è in grado di pre-leggere e rilevare se è TLS o meno (sulla porta 8080 in questo esempio), quindi il proxy lo passa localmente alla porta del server corretta.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.