web-dev-qa-db-ja.com

Nginx TCPホスト名に基づく転送

NginxコミュニティバージョンのTCPロードバランシングのリリースで、OpenVPNとSSLパススルーデータを混在させたいと思います。 Nginxがトラフィックをルーティングする方法を知る唯一の方法は、ドメイン名を使用することです。

 vpn1.app.com ─┬─► nginx at 10.0.0.1 ─┬─► vpn1  at 10.0.0.3
 vpn2.app.com ─┤                      ├─► vpn2  at 10.0.0.4
https.app.com ─┘                      └─► https at 10.0.0.5

TCPガイドモジュールのドキュメント を見てきましたが、よく参照されているようには見えません。誰かが私を正しい方向に向けることができたら、感謝します。

ServerFaultに関する関連質問: リバースプロキシはSSLパススルーでSNIを使用できますか?

41
James Wong

仮定

正しく理解できれば、nginxが単一のIPアドレスとTCPポートの組み合わせ(たとえば、listen 10.0.0.1:443)でリッスンし、その後、着信TCPの特性に応じて、トラフィックをストリーミングし、3つの異なるIPアドレスのいずれかにルーティングします。

危機に3している3つの異なるドメインをどのように区別するかについては明示的に言及していませんが、私の想定は、それがすべてTLSであり、何らかのTLS SNI(サーバー名表示)メカニズムを採用する必要があると仮定することですドメインベースの差別化。

http://nginx.org/docs/ で提供されているストリーム関連のドキュメントは、問題のモジュールに対して非常に権威があり、網羅的であると信じています(明らかに、これを相互参照する中心的な場所はまだありません。たとえば、「ストリームコア」モジュールからサブモジュールへの参照はまだありません(そしてdocs/stream/は単にdocs/をリ​​ダイレクトし直します) http: //nginx.org/r/upstream は、httpへの適用可能性については言及せずに、streamに適用するためにのみ文書化されています。


回答

各モジュールの各nginxディレクティブには、適用可能なContextの数が限られていることに注意してください。

そのため、残念ながら、ここでSNIにスヌープする指示はありません。

それとは反対に、 実際にはstream_coreで文書化されていますDifferent servers must listen on different address:port pairs.」を引用すると、 listenディレクティブがより一般的なhttp_core であり、listen内のstreamに対して現在実装されているSNIサポートの種類はないという事実に対するかなり明確な参照です。


討論

議論のポイントおよび解決策の提案として、OpenVPNトラフィックがスヌープ可能なSNIを備えた単なるTLSであるという仮定も必ずしも正しいとは限りません(ただし、OpenSSLまたはSNIについてあまり詳しくはありません)。

  • 今日SNIが受動的にスヌーピング可能であっても、TLSが接続を安全に保つという約束に明らかに反することを考慮してください。したがって、TLSの将来のバージョンでは変更される可能性があります。

  • 議論のために、OpenVPN is TLS接続を使用する場合、およびis NOTユーザー証明書を使用してユーザーを認証するためにTLSを使用する場合(これははるかに困難になります)ストリームをMitMにし、まだ認証データを保持します)、その後、理論的にはnginxがSNIをサポートしていた場合listen内のstreamの周りで、おそらくMitMをアクティブにできたはずですnginxで( proxy_sslstream_proxyで既にサポートされているため ).

最も重要なことは、OpenVPNは独自のUDPベースのプロトコルで実行するのが最適だと考えていることです。その場合、TCPベースのhttpsの1つのインスタンスとUDPベースのOpenVPNの別のインスタンスに同じIPアドレスとポート番号を使用できます競合なし。

最後に、とにかくストリームモジュールは何に役立つのでしょうか?そのターゲットオーディエンスは、(0)、たとえば、クライアントのIPアドレスのupstreamに基づいて、複数のhashサーバーでHTTP/2をロードバランシングすること、および/または、(1)、より簡単でプロトコル的な stunnel の不可知な置き換え。

21
cnst

これは、Nginx 1.11.5で ngx_stream_ssl_prereadモジュール が追加され、1.11.2で ngx_stream_mapモジュール が追加されたことで可能になりました。

これにより、NginxはTLS Client Helloを読み取り、使用するバックエンドをSNI拡張に基づいて決定できます。

stream {

    map $ssl_preread_server_name $name {
        vpn1.app.com vpn1_backend;
        vpn2.app.com vpn2_backend;
        https.app.com https_backend;
        default https_default_backend;
    }

    upstream vpn1_backend {
        server 10.0.0.3:443;
    }

    upstream vpn2_backend {
        server 10.0.0.4:443;
    }

    upstream https_backend {
        server 10.0.0.5:443;
    }

    upstream https_default_backend {
        server 127.0.0.1:443;
    }

    server {
        listen 10.0.0.1:443;
        proxy_pass $name;
        ssl_preread on;
    }
}
58
Lochnair

@Lochnairが言及したように、この問題を解決するには ngx_stream_map module および variable $ server_addr を使用できます。これが私の例です。

私のホストIPは192.168.168.22であり、eth0にkeepalivedバウンド2仮想IPを使用します。

$Sudo ip a
...
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000
link/ether 5c:f3:fc:b9:f0:84 brd ff:ff:ff:ff:ff:ff
inet 192.168.168.22/24 brd 192.168.168.255 scope global eth0
   valid_lft forever preferred_lft forever
inet 192.168.168.238/32 scope global eth0
   valid_lft forever preferred_lft forever
inet 192.168.168.239/32 scope global eth0
   valid_lft forever preferred_lft forever

$nginx -v
nginx version: nginx/1.13.2

$cat /etc/nginx/nginx.conf
...
stream {
    upstream pod53{
        server 10.1.5.3:3306;
    }
    upstream pod54{
        server 10.1.5.4:3306;
    }

    map $server_addr $x {
        192.168.168.238 pod53;
        192.168.168.239 pod54;
    }
    server {
        listen 3306;
        proxy_pass $x;
    }
}

したがって、異なるVIPを介して同じポート3306で異なるMySQLサービスにアクセスできます。異なるserver_nameを介して同じポートで異なるHTTPサービスにアクセスするのと同じです。

192.168.168.238 -> 10.1.5.3
192.168.168.239 -> 10.1.5.4
5
aloisio