V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
pydiff
V2EX  ›  程序员

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

  •  
  •   pydiff · 2021-02-20 17:50:19 +08:00 · 3880 次点击
    这是一个创建于 1362 天前的主题,其中的信息可能已经有所发展或是发生改变。

    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)
    

    想懂的大佬指点下

    20 条回复    2021-02-22 09:39:59 +08:00
    fucUup
        1
    fucUup  
       2021-02-20 18:00:38 +08:00
    大哥你标题写错了吧, tcp 转发应该是 https

    tcp 哪有域名的呀大哥

    全文都在讲 https sni 嗅探
    villivateur
        2
    villivateur  
       2021-02-20 18:03:45 +08:00 via Android
    域名是 http 里面的概念,tcp 不存在域名一说
    ysc3839
        3
    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
        4
    pydiff  
    OP
       2021-02-20 18:04:58 +08:00
    @fucUup 可能我的表达有误,我的需求就是 mysql.test.com:8443 代理 mysql 的 3306,redis.test.com:8443 代理 redis 的 6379,能搞定这个就行了
    FingerLiu
        5
    FingerLiu  
       2021-02-20 18:09:42 +08:00
    你这个只能按分配不同的端口来区分,不能通过域名,因为 tcp 连接没有域名的概念
    delpo
        6
    delpo  
       2021-02-20 18:21:21 +08:00 via Android   ❤️ 1
    同楼上所说,stream 转发的是 tcp 链接,所以你的需求是不能通过这个方法完成的。
    但是如果你开启了 tls,就可以通过 pread server name 来进行区分,这个指令只能区分 client hello 的域名
    noe132
        7
    noe132  
       2021-02-20 18:42:02 +08:00 via Android
    tcp 没有域名。你连接前域名已经被 dns 解析成 ip 后才建立的 tcp 链接。tcp 是根据 ip+端口区分链接的。
    delpo
        8
    delpo  
       2021-02-20 18:42:28 +08:00
    pread --> preread
    Qetesh
        9
    Qetesh  
       2021-02-20 18:47:22 +08:00 via iPhone
    haproxy 轻松四层负载
    snoopyxdy1
        10
    snoopyxdy1  
       2021-02-20 18:54:30 +08:00
    @pydiff 也可以实现,配置 2 台 nginx 服务器,然后将域名 mysql.test.comredis.test.com 分别指向这 2 台 nginx 服务器的 ip 地址即可
    iseki
        11
    iseki  
       2021-02-20 18:55:04 +08:00
    做不到的吧,这个功能是 TLS SNI proxy,得是带有 SNI 的 TLS 链接才起作用
    pydiff
        12
    pydiff  
    OP
       2021-02-20 20:11:54 +08:00
    @FingerLiu
    @delpo
    @noe132 好的,学习了,多谢大佬指点
    pydiff
        13
    pydiff  
    OP
       2021-02-20 20:12:16 +08:00
    @Qetesh 没用过,改天有空时试试
    also24
        14
    also24  
       2021-02-20 20:18:46 +08:00 via Android
    原理上的错误前面很多人说了,不多赘述。

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

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

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

    这样使用不好么

    (前面没打完不小心按到了回复)
    pydiff
        16
    pydiff  
    OP
       2021-02-20 20:21:30 +08:00
    @also24 因为不想映射太多端口号出来呀,想着用多个主机名来区分,还容易记
    also24
        17
    also24  
       2021-02-20 20:27:15 +08:00
    @pydiff #16
    emmmm 反正前面已经很多人科普了,你的思路在原理上是不可行的,还是按常规操作来吧。
    (其实到也不是完全不可行,自己做一套解析协议的分发,也许可以做到)
    pydiff
        18
    pydiff  
    OP
       2021-02-20 20:31:51 +08:00
    @also24 #17
    这个还是算了,菜鸡一个,哪会这种东西
    xinyifly
        19
    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
        20
    pydiff  
    OP
       2021-02-22 09:39:59 +08:00
    @xinyifly #19
    好的,谢谢解答哈
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3497 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 46ms · UTC 04:50 · PVG 12:50 · LAX 20:50 · JFK 23:50
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.