[英]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_core
的listen
指令的工作方式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.