跳转至

multiplexer: 透明端口服务复用器,用于流子系统

安装

如果您还没有设置 RPM 仓库订阅,请 注册。然后您可以继续以下步骤。

CentOS/RHEL 7 或 Amazon Linux 2

yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install lua-resty-multiplexer

CentOS/RHEL 8+、Fedora Linux、Amazon Linux 2023

dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-multiplexer

要在 NGINX 中使用此 Lua 库,请确保已安装 nginx-module-lua

本文档描述了 lua-resty-multiplexer v0.2,于 2020 年 8 月 31 日发布。


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

请注意,需要 nginx stream modulestream-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;

    # 对于 OpenResty >= 1.13.6.1,原生 Nginx 代理
    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;
    }

    # 对于 OpenResty < 1.13.6.1Lua 领域代理
    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 加载。有关当前支持的协议和添加新协议的指南,请参见 protocol 部分。

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

有关 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)
    -- 使用 buf 进行检查,如果识别到协议则返回 true
    end
}

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

匹配器

client-host

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

protocol

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

time

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

例如,要匹配年份 20181 月和 3 月,以及小时 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_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)
    -- 如果匹配则返回 true
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", ...)

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

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

multiplexer.set_rules

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

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

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

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

另见

GitHub

您可以在 nginx-module-multiplexer 的 GitHub 仓库 中找到此模块的其他配置提示和文档。