web-dev-qa-db-ja.com

nginxで単一のポートを使用してhttpおよびhttpsリクエストを処理する

nginxがhttpおよびhttpsリクエストを同じポートで処理できるかどうか疑問に思っていました。 [*]

これは私がやろうとしていることです。私は、httpリクエストを処理するWebサーバー(lighttpd)と、httpsを介してドキュメントツリーの特定のセクションを処理するCプログラムを実行しています。これら2つのプロセスは同じサーバー上で実行されます。

ファイアウォールレベルでは、iは、このサーバーにトラフィックを転送するポートを1つだけ持つことができます。だから私がしたいのは、このサーバーでnginxを設定して、単一のポートでリクエストをリッスンするようにし、次に:

A)すべての http://myhost.com/ *リクエストをリダイレクトして、それらがlocalhost:8080(lighttpdが待機している場所)に移動するようにします

B)ユーザーがhttps:// myhost.com/appなどで始まるURLを要求すると、その要求はlocalhost:8008(Cプログラム)に送信されます。この場合、リモートブラウザとnginx間のトラフィックは暗号化する必要があることに注意してください。

これは可能だと思いますか?もしそうなら、どのようにそれを行うことができますか?

2つの異なるポートを使用してこれを行う方法を知っています。私が直面している課題は、1つのポートだけでこれを行うことです(残念ながら、この特定の環境ではファイアウォール構成を制御できないため、回避できない制限があります)。 sshを介したリバースポートフォワーディングなどの手法を使用してファイアウォールをバイパスすることもできません。これは、Webブラウザーとインターネットリンクしか持たないリモートユーザーに対して機能するためです。

これがnginxの機能を超えている場合、この要件を満たすことができる他の製品を知っていますか? (これまでのところ、これをlighttpdとポンドで設定することに失敗しました)。また、Apacheを回避することもお勧めします(可能な場合は、Apacheを使用するつもりなら、それを使用するつもりです)。

事前にありがとう、アレックス

[*]明確にするために、暗号化されたおよび暗号化されていないHTTP接続を同じポートを介して処理することについて話しています。暗号化がSSLとTLSのどちらで行われるかは関係ありません。

16
alemartini

ステータスコードに関するウィキペディアの記事によると、httpトラフィックがhttpsポートに送信されると、Nginxにはカスタムエラーコードがあります(エラーコード497)

error_pageのnginx docs によると、特定のエラーに対して表示されるURIを定義できます。
これにより、エラーコード497が発生したときにクライアントに送信されるURIを作成できます。

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

ただし、クライアントがGET以外のメソッドを介してリクエストを行う場合、そのリクエストはGETに変換されます。したがって、クライアントが経由してきたリクエストメソッドを保存するには、 error_pageのnginx docs に示すように、エラー処理リダイレクトを使用します

そして、それが301 =307リダイレクト。

ここに示されているnginx.confファイルを使用して、httpとhttpsを同じポートでリッスンすることができます

19
Komu

検索しているかもしれない人のために:

追加 ssl on;およびerror_page 497 $request_uri;をサーバー定義に追加します。

17
HoverHell

本当に賢くしたい場合は、接続プロキシを使用して、受信データストリームの最初の数バイトを探り、バイト0の内容に基づいて接続を渡すことができます。これが0x16の場合(SSL/TLS 'ハンドシェイク 'バイト)、接続をSSL側に渡します。アルファベット文字の場合は、通常のHTTPを実行します。 ポート番号付けに関する私のコメントが適用されます

4
Zanchey

はい、可能ですが、nginxソースコードにパッチを適用する必要があります(HoverHellにはパッチを適用しないソリューションがあります)。 Nginxはこれを有効な構成ではなく、誤った構成として扱います。

変数$ ssl_session_idを使用して、プレーン接続とssl接続を区別できます。

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)

サーバー構成:

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

1つのポートで2つの異なるプロトコルを処理できるものはないと思います...

なぜ1つのポートしか転送できないのか知りたいのですが、それはさておき、それは理想的ではありませんが、私があなたの立場にあれば、すべてをhttpsで提供します。

1
William Hilsum

同じポートでHTTPとHTTPSの両方をサポートすることはできません。接続の両端が特定の言語を話すことを期待しており、もう一方の端が他の何かを話している場合、それらはうまく機能しないためです。

Wilの回答へのコメントが示唆しているように、あなたはcould TLSアップグレードを使用します(私は試していませんが、新しいnginxリリースはそれをサポートしていると思います)が、それはHTTPとHTTPSを実行していません。 TLSアップグレード。問題は依然としてブラウザーのサポートです-ほとんどのブラウザーは(まだ)サポートしていません。ただし、クライアントのプールが限られている場合は、これが可能です。

1
womble

理論的には、HTTP経由でアクセス可能なWebページを作成できます。これにより、https:443で WebSocket を必要な場所に開くことができます。 WebSocketの初期ハンドシェイクはHTTPです。そのため、はい、実際には安全な通信が可能な、安全ではないように見えるページを作成することは可能です。 Netty Library でこれを行うことができます。

0
djangofan

これは、1.15.2以降、正しく実行できるようになりました。 こちら の情報を参照してください。

Nginx.confに次のようなブロックを追加します(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;
    }
}

次に、通常のサーバーブロックを作成できますが、次の異なるポートでリッスンします。

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

このようにして、ストリームブロックはTLSであるかどうか(この例ではポート8080)を先読みして検出でき、プロキシはそれをローカルの正しいサーバーポートに渡します。

0
Sam Bull

どのように引き離すのかはわかりませんが、CUPSDはポート631でhttpとhttpsの両方に応答します。nginxがこれを実行できない場合は、CUPSチームが引き離す方法から学習できるかもしれませんが、CUPSはGPLなので、nginxは、そのような機能を実装したいが他の場所で実行するコードが見つからない場合、ライセンスの変更を検討する必要があるかもしれません。

0
Ryan Grange