Gestion des requêtes http et https à l'aide d'un seul port avec nginx

16

je me demandais si nginx est capable de gérer les requêtes http et https sur le même port . [*]

C'est ce que j'essaie de faire. J'exécute un serveur Web (lighttpd) gérant les requêtes http et un programme C qui dessert une section particulière de l'arborescence de documents via https. Ces deux processus s'exécutent sur le même serveur.

Au niveau du pare-feu, je ne peux avoir qu'un seul port redirigeant le trafic vers ce serveur . Donc, ce que je voudrais faire, c'est configurer nginx sur ce serveur pour qu'il écoute les requêtes sur un seul port, puis:

A) redirige toutes les requêtes http://myhost.com/ * afin qu'elles se dirigent vers localhost: 8080 (où lighttpd écoute)

B) si un utilisateur demande une URL commençant par, par exemple, https: // myhost.com/app, il envoie cette demande à localhost: 8008 (programme C). Notez que dans ce cas, le trafic entre le navigateur distant et nginx doit être chiffré.

Pensez-vous que cela pourrait être possible? Si tel est le cas, comment le faire?

Je sais comment faire cela en utilisant deux ports différents. Le défi auquel je suis confronté consiste à le faire avec un seul port (malheureusement, je n'ai pas de contrôle sur la configuration du pare-feu dans cet environnement particulier, c'est donc une restriction que je ne peux pas éviter). L'utilisation de techniques telles que la redirection de port inverse via ssh pour contourner le pare-feu ne fonctionnera pas non plus, car cela devrait fonctionner pour les utilisateurs distants n'ayant rien de plus qu'un navigateur Web et un lien Internet.

Si cela dépasse les capacités de nginx, connaissez-vous un autre produit qui pourrait répondre à ces exigences? (jusqu'à présent, je n'ai pas réussi à configurer cela avec lighttpd et pound). Je préfère également éviter Apache (même si je suis prêt à l'utiliser si c'est le seul choix possible).

Merci d'avance, Alex

[*] Juste pour être clair, je parle de la gestion des connexions HTTP chiffrées et non chiffrées via le même port. Peu importe que le cryptage soit effectué via SSL ou TLS.

alemartini
la source
Les demandes HTTPS vont au port 443 par défaut, donc même si vous pouvez faire fonctionner cela (et je pense que c'est possible avec un peu de piratage), vous devez utiliser yourhost.com et yourhost.com:80 comme liens (ou yourhost.com:443 et yourhost.com ).
Zanchey
Ok, je suis nouveau chez Server Fault et je ne sais pas si je peux supprimer ma propre question. Puisqu'il semble que le problème n'a pas été formulé suffisamment clairement, je vais ouvrir une nouvelle question à la place. Quoi qu'il en soit, merci beaucoup à tous ceux qui ont contribué avec des suggestions utiles pour ce numéro.
alemartini

Réponses:

18

Selon un article de wikipedia sur les codes d'état, Nginx a un code d'erreur personnalisé lorsque le trafic http est envoyé au port https (code d'erreur 497)

Et selon les documents nginx sur error_page , vous pouvez définir un URI qui sera affiché pour une erreur spécifique.
Ainsi, nous pouvons créer un uri auquel les clients seront envoyés lorsque le code d'erreur 497 sera généré.

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

Cependant, si un client fait une demande via une autre méthode que GET, cette demande sera transformée en GET. Ainsi, pour conserver la méthode de demande par laquelle le client est entré; nous utilisons des redirections de traitement d'erreur comme indiqué dans les documents nginx sur error_page

Et c'est pourquoi nous utilisons la 301 =307redirection.

En utilisant le fichier nginx.conf montré ici, nous pouvons écouter http et https sur le même port

Komu
la source
17

Pour ceux qui pourraient chercher:

Ajoutez ssl on;et error_page 497 $request_uri;à votre définition de serveur.

HoverHell
la source
8
Petite amélioration de la réponse HoverHells: ajoutez ssl on;et error_page 497 =200 $request_uri;à la définition de votre serveur. Cela changera le code de statut en 200.
Mawi12345
Cette réponse de panne explique pourquoi cette solution fonctionne.
SiliconMind
4

Si vous vouliez être vraiment intelligent, vous pouvez utiliser un proxy de connexion pour renifler les deux premiers octets du flux de données entrant et transmettre la connexion en fonction du contenu de l'octet 0: si c'est 0x16 (le SSL / TLS ' handshake 'byte), passez la connexion au côté SSL, si c'est un caractère alphabétique, faites HTTP normal. Mon commentaire sur la numérotation des ports s'applique .

Zanchey
la source
2

Oui, c'est possible, mais il faut patcher le code source nginx (HoverHell a une solution sans patcher). Nginx traite cela comme une mauvaise configuration plutôt qu'une configuration valide.

La variable $ ssl_session_id peut être utilisée pour faire la différence entre une connexion simple et une connexion ssl.

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

Configuration du serveur:

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
la source
1

Je ne pense pas qu'il y ait quoi que ce soit qui puisse gérer deux protocoles différents sur un seul port ...

Je suis curieux de savoir pourquoi vous ne pouvez transférer qu'un seul port, mais cela mis à part ... ce n'est pas idéal mais si j'étais à votre place, je servirais tout sur https.

William Hilsum
la source
Salut Wil, et merci pour ta réponse! Je pense que tout servir sur https pourrait être une option, bien que j'aimerais pouvoir configurer cela de la manière que j'ai décrite. Peut-être que cela pourrait être fait si le serveur Web avant (agissant comme un proxy inverse) pouvait établir une session http normale et la mettre à niveau vers https sans changer de port. Je pense que ce comportement est décrit dans RFC2817 (mise à niveau vers TLS avec HTTP / 1.1) mais je ne sais pas si nginx ou d'autres serveurs Web savent comment gérer cette norme.
alemartini
Je n'ai pas le temps de lire un RFC entier (et je ne suis pas sûr d'être assez intelligent pour le comprendre!) Mais parlez-vous de la négociation standard avant que la session sécurisée soit établie ou de différentes sessions complètes? Je pense que je comprends un peu plus - le serveur sert sur deux ports et c'est le proxy qui renvoie la demande - ça a l'air cool mais je ne l'ai jamais vu faire. Peut-être qu'une solution pourrait être de créer un seul site sécurisé sur un port et d'avoir un répertoire virtuel complet qui hérite / importe simplement l'autre site Web? Il ne résout pas tous les problèmes, mais peut simplement fonctionner: S
William Hilsum
1

Vous ne pouvez pas prendre en charge HTTP et HTTPS sur le même port, car les deux extrémités de la connexion s'attendent à parler une certaine langue et ne sont pas assez intelligentes pour déterminer si l'autre extrémité parle autre chose.

Comme le suggère votre commentaire sur la réponse de Wil, vous pouvez utiliser la mise à niveau TLS (je pense que les versions plus récentes de nginx le prennent en charge, bien que je n'aie pas essayé), mais cela n'exécute pas HTTP et HTTPS, il exécute simplement HTTP avec la mise à niveau TLS. Le problème est toujours la prise en charge du navigateur - la plupart des navigateurs (toujours) ne la prennent pas en charge. Si vous avez un bassin limité de clients, c'est une possibilité, cependant.

womble
la source
1
Le trafic HTTP chiffré et non chiffré peut être géré via un seul port. Ce que j'aimerais savoir, c'est si cela est possible en utilisant nginx ou d'autres produits (comme lighttpd) agissant en tant que proxy inverses. Très probablement, ce type de configuration peut être géré par Apache, mais j'ai oublié de mentionner dans ma question d'origine que je préférerais ne pas avoir à utiliser Apache (bien que je le ferais s'il n'y a pas d'autre choix sur Linux). plate-forme pour y parvenir).
alemartini
Comme je l'ai dit dans ma réponse, "je crois que la nouvelle version de nginx prend en charge [mise à niveau TLS], même si je n'ai pas essayé". Si vous avez besoin de moi pour lire le manuel pour vous, alors vous n'avez pas de chance.
womble
Je suis désolé si j'ai donné l'impression que j'avais besoin que quelqu'un me lise un manuel. Il semble simplement que le problème (et les questions à son sujet) n'aient pas été décrits avec suffisamment de précision (mon erreur), ce qui a conduit à des interprétations différentes de ce que je demandais ou dont j'avais besoin. J'ai donc décidé d'ouvrir une nouvelle question sur ce problème et d'essayer d'éviter toute confusion possible en ce qui concerne le problème ou les questions spécifiques à ce sujet. Quoi qu'il en soit, merci pour votre temps et pour avoir partagé vos idées.
alemartini
0

Je ne sais pas comment il le retire, mais CUPSD répond à la fois à http et https sur le port 631. Si nginx ne peut pas le faire maintenant, ils peuvent peut-être apprendre de la façon dont l'équipe CUPS le retire, mais CUPS est sous le GPL, donc nginx devra peut-être envisager de modifier sa licence s'il souhaite implémenter une telle capacité et ne peut pas trouver le code pour le faire ailleurs.

Ryan Grange
la source
0

En théorie, vous pourriez avoir une page Web accessible via HTTP, qui est capable d'ouvrir un WebSocket sur https: 443 à l'endroit où il le souhaite. La poignée de main initiale de WebSocket est HTTP. Donc, oui, il est possible de créer une page d'apparence non sécurisée réellement capable de sécuriser la communication. Vous pouvez le faire avec la bibliothèque Netty .

djangofan
la source
0

C'est enfin possible de faire correctement depuis la 1.15.2. Voir les informations ici .

Dans votre nginx.conf, ajoutez un bloc comme celui-ci (en dehors du bloc 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;
    }
}

Ensuite, vous pouvez créer votre bloc serveur normal, mais en écoutant sur ces différents ports:

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

De cette façon, le bloc de flux peut lire et détecter s'il s'agit de TLS ou non (sur le port 8080 dans cet exemple), puis le proxy le transmet localement au port de serveur correct.

Sam Bull
la source