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


16

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

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

ファイアウォールレベルでは、このサーバーにトラフィックを転送するポートを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とpoundで設定することに失敗しました)。また、Apacheを避けることも好みます(ただし、Apacheが唯一の選択肢である場合は使用してもかまいません)。

事前に感謝、アレックス

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


HTTPSリクエストはデフォルトでポート443に送信されるため、これが機能するようになったとしても(そして、ちょっとしたハッキン​​グで可能だと思う)、リンクとしてyourhost.comyourhost.com:80を使用する必要があります(またはyourhost.com:443およびyourhost.com)。
ザンチー2009

OK、私はサーバーフォールトの初心者で、自分の質問を削除できるかどうかわかりません。問題が十分に明確に定式化されていないようであるため、代わりに新しい質問を開きます。とにかく、この問題に対して有用な提案を提供してくれたすべての人に感謝します。
alemartini 09

回答:


17

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

追加ssl on;およびerror_page 497 $request_uri;サーバー定義へ。


8
HoverHellsの答えはほとんど改善されていません。サーバー定義を追加ssl on;error_page 497 =200 $request_uri;てください。これにより、ステータスコードが200に変更されます。
Mawi1234512年

このサービス障害の回答は、このソリューションが機能する理由を説明しています。
SiliconMind

17

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

また、error_pageのnginxのドキュメントによると、特定のエラーに対して表示される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ドキュメントに示されているように、エラー処理リダイレクトを使用します

そしてそれが我々が使用する理由です 301 =307リダイレクトです。

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


4

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


2

はい、可能ですが、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;
}

1

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

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


こんにちは、ウィル、そしてあなたの答えに感謝します!私が説明した方法でこれを設定できるようにしたいのですが、httpsを介してすべてを提供することはオプションであると思います。フロントWebサーバー(リバースプロキシとして機能)が通常のhttpセッションを確立し、ポートを変更せずにhttpsにアップグレードできる場合は、これを行うことができます。この動作はRFC2817(HTTP / 1.1を使用したTLSへのアップグレード)に記述されていると思いますが、nginxまたは他のWebサーバーがその標準に対処する方法を知っているかどうかはわかりません。
アレマルティーニ09

RFC全体を読む時間はありません(それを理解するのに十分賢いのかわかりません!)が、安全なセッションが確立される前、またはさまざまなセッションが本格的に開始される前の標準的なネゴシエーションについて話していますか?私はもう少し理解していると思う-サーバーは2つのポートでサービスを提供しており、リクエストを参照するのはプロキシです-クールに聞こえますが、それを見たことはありません。おそらく、ソリューションは、1つのポートに単一の安全なサイトを作成し、他のWebサイトを単純に継承/インポートする仮想ディレクトリ全体を保持することによって実現できますか?すべての問題を解決するわけではありませんが、うまくいくかもしれません:S
ウィリアムヒルサム2009

1

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

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


1
暗号化されたHTTPトラフィックと暗号化されていないHTTPトラフィックは、単一のポートを介して処理できます。私が知りたいのは、nginxまたはリバースプロキシとして機能する他の製品(lighttpdなど)を使用してこれが可能かどうかです。おそらくこの種のセットアップはApacheで処理できますが、元の質問でApacheを使用する必要はないことを忘れていました(Linuxに他の選択肢がない場合はそれを行いますがこれを達成するためのプラットフォーム)。
alemartini 09

私の答えで言ったように、「私は試していませんが、新しいnginxリリースのサポート[TLSアップグレード]を信じています」。あなたのためにマニュアルを読む必要があるなら、あなたは運が悪い。
ワンブル

誰かが私のためにマニュアルを読む必要があるという印象を与えたら申し訳ありません。問題(およびそれについての質問)が十分に正確に記述されていなかったようで(私の間違い)、私が求めていることや必要としていることの異なる解釈につながったようです。だから私はこの問題について新しい質問を開き、問題またはそれに関する特定の質問に関して起こりうる混乱を避けることを試みることにしました。とにかく、あなたの時間とあなたの洞察を共有してくれてありがとう。
アレマルティーニ09

0

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


0

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


0

これは、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で)を事前に読み取って検出でき、プロキシはそれをローカルの正しいサーバーポートに渡します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.