簡體   English   中英

基於主機名的 Nginx TCP 轉發

[英]Nginx TCP forwarding based on hostname

隨着 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 上的相關問題: 反向代理可以使用 SNI 和 SSL 傳遞嗎?

這是現在可以用另外的ngx_stream_ssl_preread模塊在Nginx的1.11.5添加和ngx_stream_map模塊中添加1.11.2。

這允許 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;
    }
}

假設

如果我理解正確,您實際上希望 nginx 偵聽單個 IP 地址和 TCP 端口組合(例如, listen 10.0.0.1:443 ),然后,根據傳入 TCP 流流量的特征,將其路由到一個3 個不同的 IP 地址。

您沒有明確提及您希望它如何區分 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. ”,正如您可能注意到的,這也與在更常見的http_corelisten指令的工作方式http_core ,並且是對當前沒有為stream內的listen實現任何類型的 SNI 支持這一事實的相當明確的參考。


討論

作為討論點和解決建議,OpenVPN 流量只是帶有可監聽 SNI 的 TLS 的假設也不一定正確(但我對 OpenSSL 或 SNI 不太熟悉):

  • 考慮到即使 SNI 在今天是被動監聽的,這顯然與 TLS 保持連接安全的承諾背道而馳,因此,在未來的 TLS 版本中可能會發生變化。

  • 為了討論的緣故,如果OpenVPN只使用一個TLS連接,如果它使用TLS驗證用戶的身份用戶證書(這將使它更難以MITM流,但仍然承載認證數據都一起) ,那么,理論上,如果 nginx 確實stream內的listen周圍有 SNI 支持,那么您可能已經能夠使用 nginx 主動地對它進行中間人(因為proxy_ssl已經在stream_proxy受支持)。

最重要的是,我相信 OpenVPN 可能最好通過其自己的基於 UDP 的協議運行,在這種情況下,您可以為一個基於 TCP 的 https 實例和另一個基於 UDP 的 OpenVPN 實例使用相同的 IP 地址和端口號沒有沖突。

最后,您可能會問,那么流模塊有什么用呢? 我相信它的目標觀眾是,(0),負載平衡HTTP/2與多個upstream服務器,基於所述hash客戶端的IP地址,例如,和/或,(1),更直接和stunnel協議無關替代品。

正如@Lochnair 提到的,你可以使用ngx_stream_map 模塊變量 $server_addr來解決這個問題。 這是我的例子。

我的主機 IP 是192.168.168.22 ,我使用 keepalived 綁定 2 個虛擬 IP 到eth0

$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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM