基于主机名的 Nginx TCP 转发问题

2021-02-20 17:50:19 +08:00
 pydiff

nginx 可以通过不同的主机名相同端口号来区分不同的 http 服务,今天想利用 nginx 来转发 tcp(非 http)的端口,找遍了百度都是引用了 sf 上的一个帖子的设置方法:地址:https://stackoverflow.com/questions/34741571/nginx-tcp-forwarding-based-on-hostname

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;
    }
}

然而我对着设置了却报这个错,

2021/02/20 09:31:32 [error] 23#23: *22 connect() failed (111: Connection refused) while connecting to upstream, client: 1.1.1.1, server: 192.168.0.5:9443, upstream: "127.0.0.1:9443", bytes from/to client:0/0, bytes from/to upstream:0/0 

实在找不到解决方法了想过来问问各位大佬

现在我已经用 8443 端口在跑着 web 服务了,还想通过改下主机名来代理不同的服务,如 mysql.test.com 代理 mysql 的 3306,redis.test.com 代理 redis 的 6379, 另外如果将上面的 upstream 配置中 443 改成 8443 则报错

nginx: [emerg] bind() to 192.168.0.5:8443 failed (98: Address already in use)

想懂的大佬指点下

3894 次点击
所在节点    程序员
20 条回复
fucUup
2021-02-20 18:00:38 +08:00
大哥你标题写错了吧, tcp 转发应该是 https

tcp 哪有域名的呀大哥

全文都在讲 https sni 嗅探
villivateur
2021-02-20 18:03:45 +08:00
域名是 http 里面的概念,tcp 不存在域名一说
ysc3839
2021-02-20 18:03:51 +08:00
你贴的这段配置前面有一段话

This allows Nginx to read the TLS Client Hello and decide based on the SNI extension which backend to use.
pydiff
2021-02-20 18:04:58 +08:00
@fucUup 可能我的表达有误,我的需求就是 mysql.test.com:8443 代理 mysql 的 3306,redis.test.com:8443 代理 redis 的 6379,能搞定这个就行了
FingerLiu
2021-02-20 18:09:42 +08:00
你这个只能按分配不同的端口来区分,不能通过域名,因为 tcp 连接没有域名的概念
delpo
2021-02-20 18:21:21 +08:00
同楼上所说,stream 转发的是 tcp 链接,所以你的需求是不能通过这个方法完成的。
但是如果你开启了 tls,就可以通过 pread server name 来进行区分,这个指令只能区分 client hello 的域名
noe132
2021-02-20 18:42:02 +08:00
tcp 没有域名。你连接前域名已经被 dns 解析成 ip 后才建立的 tcp 链接。tcp 是根据 ip+端口区分链接的。
delpo
2021-02-20 18:42:28 +08:00
pread --> preread
Qetesh
2021-02-20 18:47:22 +08:00
haproxy 轻松四层负载
snoopyxdy1
2021-02-20 18:54:30 +08:00
@pydiff 也可以实现,配置 2 台 nginx 服务器,然后将域名 mysql.test.comredis.test.com 分别指向这 2 台 nginx 服务器的 ip 地址即可
iseki
2021-02-20 18:55:04 +08:00
做不到的吧,这个功能是 TLS SNI proxy,得是带有 SNI 的 TLS 链接才起作用
pydiff
2021-02-20 20:11:54 +08:00
@FingerLiu
@delpo
@noe132 好的,学习了,多谢大佬指点
pydiff
2021-02-20 20:12:16 +08:00
@Qetesh 没用过,改天有空时试试
also24
2021-02-20 20:18:46 +08:00
原理上的错误前面很多人说了,不多赘述。

你这个原始需求也有点奇怪啊,为什么要把 mysql 和 redis 跑在 80 端口呢?

直接:
mysql.test.com:3306
also24
2021-02-20 20:20:27 +08:00
直接:

mysql 点 test 点 com:3306
redis 点 test 点 com:6379

这样使用不好么

(前面没打完不小心按到了回复)
pydiff
2021-02-20 20:21:30 +08:00
@also24 因为不想映射太多端口号出来呀,想着用多个主机名来区分,还容易记
also24
2021-02-20 20:27:15 +08:00
@pydiff #16
emmmm 反正前面已经很多人科普了,你的思路在原理上是不可行的,还是按常规操作来吧。
(其实到也不是完全不可行,自己做一套解析协议的分发,也许可以做到)
pydiff
2021-02-20 20:31:51 +08:00
@also24 #17
这个还是算了,菜鸡一个,哪会这种东西
xinyifly
2021-02-21 18:22:08 +08:00
我也作过这个尝试,如果我没理解错的话,我再详细解释一下 @noe132 的答案:

1. mysql 客户端收到 mysql.test.com:8443 的连接配置
2. mysql 通过操作系统的 DNS 功能查询到 mysql.test.com 的 ip 为 69.172.200.109
3. mysql 向 69.172.200.109:8443 发起连接

对于 redis 也是一样的。

所以你可以看到,对于提供 TCP 服务(非 HTTP )的服务器,是收不到域名(或主机名)信息的。

总结:对于目前大部分基于 TCP 而非 HTTP 的服务而言,想通过域名或主机名区分流量是不可能的,因为客户端并未发送域名(或主机名)信息。

其它思路:Linux 内核提供了多个服务复用同一个端口的功能,也有通过嗅探连接类型作转发的代理服务(比如 sslh ),我没有尝试过,楼主有需要可以参考一下。
pydiff
2021-02-22 09:39:59 +08:00
@xinyifly #19
好的,谢谢解答哈

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/754689

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX