【问题标题】:Nginx TCP forwarding based on hostname基于主机名的 Nginx TCP 转发
【发布时间】:2023-04-03 17:09:01
【问题描述】:

随着 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 guidesmodule documentation,但似乎没有很好的参考。如果有人能指出我正确的方向,我将不胜感激。

ServerFault 相关问题:Can a Reverse Proxy use SNI with SSL pass through?

【问题讨论】:

  • 我的回答有什么遗漏或不清楚的地方吗?
  • -1 未能授予承诺的赏金,即使提供了全面和参考的答案,也没有寻求额外的澄清或指出问题
  • 感谢您最终接受并赞成我的回答,但是,我很困惑为什么在同一分钟内,我的其他一些不相关且无可争议的答案却毫无理由地被否决了;发布赏金,悄悄地无缘无故地不授予它,然后当它被指出时,默默地否决帮助你的人的其他答案,这是新事物吗?
  • Stack Overflow 是一个编程和开发问题的网站。这个问题似乎离题了,因为它与编程或开发无关。请参阅帮助中心的What topics can I ask about here。也许Unix & Linux Stack ExchangeInformation Security Stack Exchange 会是一个更好的提问地点。
  • @jww 请标记该主题以进行审核并帮助关闭它。

标签: nginx tcp load-balancing virtualhost nginx-reverse-proxy


【解决方案1】:

现在可以通过在 Nginx 1.11.5 中添加 ngx_stream_ssl_preread module 和在 1.11.2 中添加 ngx_stream_map module 来实现。

这允许 Nginx 读取 TLS 客户端 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;
    }
}

【讨论】:

  • 对于未来到达这里的人认为这可以用于 SSH 连接 - 不要打扰,SSH 客户端不会传递 DNS 名称。
  • @Dae,SSH 甚至与 SSL 无关。
  • 我必须设置 proxy_protocol on; 才能使其正常工作,也许其他人也需要这样做......
  • @Carel proxy_protocol 只有在上游服务器上使用时才需要这里
  • @rMili 你可以,是的,它可以。在这个阶段证书还没有发送,所以没关系。
【解决方案2】:

假设

如果我理解正确,您实际上希望 nginx 侦听单个 IP 地址和 TCP 端口组合(例如,listen 10.0.0.1:443),然后根据传入 TCP 流流量的特征,将其路由到一个3 个不同的 IP 地址。

您没有明确提到您希望它如何区分三个不同的域,但我的假设是您假设它只是 TLS,并且必须要使用某种 TLS SNI(服务器名称指示) 基于领域的差异化机制。

我相信http://nginx.org/docs/ 提供的与流相关的文档对于所涉及的模块来说是非常权威和详尽的(我在这里列出了所有这些,因为显然还没有交叉引用的中心位置,例如,还没有从“流核心”模块到子模块的引用(docs/stream/ 只是重定向回docs/),这确实很令人困惑,因为像http://nginx.org/r/upstream 这样的东西只记录在适用于http,没有提及对stream 的适用性,即使最终指令大致相同):


回答

请注意,每个模块中的每个 nginx 指令都有有限数量的适用Context

因此,很遗憾,这里根本没有窥探 SNI 的指令!

相反,it's actually documented in stream_core 引用“Different servers must listen on different address:port pairs.”,正如您可能注意到的,这也与listen directive works within the more-common http_core 的方式相反,并且是一个相当明确的参考,即没有任何种类目前对stream 中的listen 实现了SNI 支持。


讨论

作为讨论点和解决建议,OpenVPN 流量只是带有可窥探 SNI 的 TLS 的假设也不一定正确(但我对 OpenSSL 或 SNI 不太熟悉):

  • 请考虑,即使 SNI 现在是被动可窥探的,这显然与 TLS 保持连接安全的承诺背道而驰,因此可能会在未来版本的 TLS 中发生变化。

    李>
  • 为了便于讨论,如果 OpenVPN 仅使用 TLS 连接,而如果它不是 使用 TLS 来使用用户证书对用户进行身份验证(其中会使 MitM 流变得更加困难,但仍然始终携带身份验证数据),然后,理论上,如果 nginx 确实支持 SNIstream 内的 listen 周围,那么你可能已经能够使用 nginx 主动 MitM 它(因为 proxy_ssl is already supported in stream_proxy)。

最重要的是,我相信 OpenVPN 最好运行在它自己的基于 UDP 的协议上,在这种情况下,您可以为一个基于 TCP 的 https 实例和另一个基于 UDP 的实例使用相同的 IP 地址和端口号- 基于 OpenVPN 没有冲突。

最后,你可能会问,流模块到底有什么用处呢?我相信它的目标受众是,(0),负载平衡HTTP/2 与多个upstream 服务器,基于客户端IP 地址的hash,例如,和/或,(1), stunnel 的更直接且与协议无关的替代品。

【讨论】:

  • @TorstenBronger,我认为人们不会阅读问题的正文,它专门针对 OpenVPN。 OpenVPN 本身是否也支持 SNI?
  • @TorstenBronger,顺便说一句,事实证明 OpenSSL 不支持 SNI,所以,这个问题没有实际意义,因此另一个答案正在回答一个不同的问题。来源:stackoverflow.com/questions/42078600/…
  • 一旦 nginx 通过主机名路由,OpenVPN 就不再需要 SNI。
  • 这个答案现在已经过时了。查看其他答案。
  • @KhakharShyam 抱歉,我不擅长那个领域。考虑发布有关您的具体问题的问题。
【解决方案3】:

正如@Lochnair 提到的,您可以使用ngx_stream_map modulevariable $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

【讨论】:

  • 本例按IP地址选择。这不是问题的全部内容,需要按 SNI 名称进行选择。当然,如果您可以为每个 HTTPS 域提供单独的 IPv4 地址,一切都会变得容易得多。
猜你喜欢
  • 2017-04-12
  • 2021-12-13
  • 1970-01-01
  • 1970-01-01
  • 2013-08-21
  • 2013-12-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多