lua-resty-multiplexer

流子系统透明端口服务多路复用器

$ opm get fffonion/lua-resty-multiplexer

名称

lua-resty-multiplexer - 流子系统透明端口服务多路复用器

描述

该库实现了一个透明端口服务多路复用器,可用于在同一端口上运行多个 TCP 服务。

请注意,需要 nginx 流模块stream-lua-nginx-module

在 OpenResty >= 1.13.6.1 上测试。

使用 OpenResty 1.13.6.1,需要来自 @fcicq 的定制 补丁。原始讨论可以在这里找到 这里。由于缺少 reqsock:peek,因此不支持原生代理。

从 OpenResty 1.15.8.1 开始,只支持原生代理,不需要补丁。当 stream-lua-nginx-module 实现 tcpsock:receiveany 时,Lua 域代理将成为可能。

状态

实验性的。

概述

    stream {
        init_by_lua_block {
            local mul = require("resty.multiplexer")
            mul.load_protocols(
                "http", "ssh", "dns", "tls", "xmpp"
            )
            mul.set_rules(
                {{"client-host", "10.0.0.1"}, "internal-host", 80},
                {{"protocol", "http"}, {"client-host", "10.0.0.2"}, "internal-host", 8001},
                {{"protocol", "http"}, "example.com", 80},
                {{"protocol", "ssh"}, "github.com", 22},
                {{"protocol", "dns"}, "1.1.1.1", 53},
                {{"protocol", "tls"}, {"time", nil}, "twitter.com", 443},
                {{"protocol", "tls"}, "www.google.com", 443},
                {{"default", nil}, "127.0.0.1", 80}
            )
            mul.matcher_config.time = {
                minute_match = {0, 30},
                minute_not_match = {{31, 59}},
            }
        }
    
        resolver 8.8.8.8;
    
        # for OpenResty >= 1.13.6.1, native Nginx proxying
        lua_add_variable $multiplexer_upstream;
        server {
                error_log /var/log/nginx/multiplexer-error.log error;
                listen 443;
    
                resolver 8.8.8.8;
    
                preread_by_lua_block {
                    local mul = require("resty.multiplexer")
                    local mp = mul:new()
                    mp:preread_by()
                }
                proxy_pass $multiplexer_upstream;
        }
    
        # for OpenResty < 1.13.6.1, Lua land proxying
        server {
                error_log /var/log/nginx/multiplexer-error.log error;
                listen 443;
    
                resolver 8.8.8.8;
    
                server {
                    listen 80;
                    content_by_lua_block {
                        local mul = require("resty.multiplexer")
                        local mp = mul:new()
                        mp:content_by()
                    }
                }
        }
    }

该模块包含两个部分:协议标识符和匹配器。

协议标识符需要通过 init_by_lua_block 指令中的 load_protocols 加载。有关当前支持的协议和添加新协议的指南,请参见 协议 部分。

规则通过 set_rules 定义,以将流量路由到不同的上游。对于规则中定义的每个匹配器,都会自动加载相应的匹配器。有关当前实现的匹配器和添加新匹配器的指南,请参见 匹配器 部分。

有关 load_protocolsset_rules 语法的详细信息,请参见 API 部分。

定义的规则按优先级排序。在上面的示例中,我们定义了一个规则,这样

  • 如果客户端地址是 10.0.0.1,则代理到 internal-host.com:80

  • 如果协议是 HTTP 且客户端地址是 10.0.0.2,则代理到 internal-host:8001

  • 如果协议是 SSH,则代理到 github.com:22

  • 如果协议是 DNS,则代理到 1.1.1.1:53

  • 如果协议是 SSL/TLS 且当前分钟数介于 030 之间,则代理到 twitter:443

  • 如果协议是 SSL/TLS 且当前分钟数介于 3159 之间,则代理到 www.google.com:443

  • 否则,代理到 127.0.0.1:80

协议

协议部分分析从客户端发送的第一个请求,并尝试使用已知的协议签名对其进行匹配。

当前支持:dnshttpsshtlsxmpp。根据签名字节,每个协议可能具有不同的错误识别可能性。

| 协议 | 签名长度 | 错误率 | |---|---|---| | dns | 9 1/4 | 5.29e-23 | | http | 4 | 2.33e-10 | | ssh | 4 | 2.33e-10 | | tls | 6 | 3.55e-15 | | xmpp | 6 in 8 1/4 | ? |

添加新协议

resty/multiplexer/protocol 下创建名为 protocol_name.lua 的新文件,格式为

    return {
        required_bytes = ?,
        check = function(buf)
        -- check with the buf and return true if the protocol is identified
        end
    }

required_bytes 是标识协议之前需要读取的字节长度。

匹配器

client-host

如果 $remote_addr 等于预期值,则匹配。

protocol

如果协议等于预期值,则匹配。

time

如果当前时间在 mul.matcher_config.time 中的配置范围内,则匹配。如果没有定义范围,则匹配器将始终返回 false

例如,要匹配 2018 年、一月三月,以及 624 小时,但除 12 小时外

     init_by_lua_block {
        local mul = require("resty.multiplexer")
        mul.load_protocols(
            "http", "ssh", "dns", "tls", "xmpp"
        )
        mul.set_rules(
            {{"time", ""}, "twitter.com", 443}
        )
        mul.matcher_config.time = {
            year_match = {2018},
            year_not_match = {},
            month_match = {{1}, {3}},
            month_not_match = {},
            day_match = {}, -- day of month
            day_not_match = {},
            hour_match = {{6, 24}},
            hour_not_match = {{12}},
            minute_match = {},
            minute_not_match = {},
            second_match = {},
            second_not_match = {},
        }
     }

default

始终匹配。

添加新匹配器

resty/multiplexer/matchers 下创建名为 matcher_name.lua 的新文件,格式为

    local _M = {}
    
    function _M.match(protocol, expected)
        -- return true if it's a match
    end
    
    return _M

其中 protocol 是以小写字符串表示的标识的协议,而 expected 是在 set_rules 中定义的此匹配器的预期值。

API

multiplexer.new

语法: multiplexer:new(connect_timeout, send_timeout, read_timeout)

初始化多路复用器实例。并设置连接超时阈值、发送超时阈值和读取超时阈值,如 tcpsock:settimeouts 中所述。

multiplexer.load_protocols

语法: multiplexer:load_protocols("protocol-1", "protocol-2", ...)

将协议模块加载到内存中。

支持的协议可以在 协议 中找到。

multiplexer.set_rules

语法: multiplexer:set_rules(rule1, rule2, ...)

按顺序加载规则。每个 rule 都是一个数组表,格式为

    {{"matcher-1", "expected-value-1"}, {"matcher-2", "expected-value-2"}, ..., "upstream_host", upstream_port}

支持的匹配器可以在 匹配器 中找到。

TODO

  • 添加测试。

版权和许可

该模块根据 BSD 许可证授权。

版权所有 (C) 2018,由 fffonion <fffonion@gmail.com>。

保留所有权利。

只要满足以下条件,无论是否修改,都允许以源代码和二进制形式重新分发和使用本软件。

  • 源代码的重新分发必须保留上述版权声明、此条件列表和以下免责声明。

  • 二进制形式的重新分发必须在随分发提供的文档和/或其他材料中复制上述版权声明、此条件列表和以下免责声明。

本软件由版权所有者和贡献者“按原样”提供,不附带任何明示或暗示的保证,包括但不限于对适销性、适合特定目的的暗示保证。在任何情况下,版权所有者或贡献者均不对因使用本软件而造成的任何直接、间接、偶然、特殊、示例性或后果性损害(包括但不限于替代商品或服务的采购;使用、数据或利润损失;或业务中断)负责,无论是在合同、严格责任或侵权行为(包括疏忽或其他原因)的情况下,即使已告知存在此类损害的可能性。

另请参阅

作者

fffonion

许可证

3bsd

依赖项

luajit

版本