当前位置:  开发笔记 > 后端 > 正文

Nginx基于主机名的TCP转发

如何解决《Nginx基于主机名的TCP转发》经验,为你挑选了3个好方法。

随着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通过吗?



1> 小智..:

这是现在可以用另外的ngx_stream_ssl_preread模块在Nginx的1.11.5添加和ngx_stream_map模块中添加1.11.2.

这允许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无关.

2> cnst..:
假设

如果我理解正确,你实际上希望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,即使指令是关于到底是相同的):

http://nginx.org/docs/stream/ngx_stream_core_module.html

http://nginx.org/docs/stream/ngx_stream_access_module.html

http://nginx.org/docs/stream/ngx_stream_limit_conn_module.html

http://nginx.org/docs/stream/ngx_stream_proxy_module.html

http://nginx.org/docs/stream/ngx_stream_ssl_module.html

http://nginx.org/docs/stream/ngx_stream_upstream_module.html


回答

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

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

相反,它实际上记录在stream_core引用中," Different servers must listen on different address:port pairs.",正如您可能注意到的那样,这也违背了该listen指令在更常见的情况下的工作方式http_core,并且是一个相当明确的参考指出这一事实. SNI支持目前正在为listen内部实施stream.


讨论

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

考虑到即使SNI今天被动地窥探,这显然违背了TLS保证连接安全的承诺,因此,可能会在未来版本的TLS中发生变化.

为了讨论的缘故,如果OpenVPN的只使用一个TLS连接,如果它使用TLS验证用户的身份用户证书(这将使它更难以MITM流,但仍然承载认证数据都一起)那么,理论上,如果nginx确实listen内部有SNI支持stream,那么你可能已经能够使用nginx主动MitM它(因为proxy_ssl已经支持stream_proxy).

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

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



3> aloisio..:

如@Lochnair所述,您可以使用ngx_stream_map模块和$ server_addr变量来解决此问题。这是我的例子。

我的主机IP是192.168.168.22,我使用keepalived绑定2虚拟IP来eth0

$sudo ip a
...
2: eth0:  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服务。就像通过diffrent使用同一端口访问不同的HTTP服务一样server_name

192.168.168.238 -> 10.1.5.3
192.168.168.239 -> 10.1.5.4

推荐阅读
贾志军
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有