Перейти к содержанию

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

Чтобы использовать эту библиотеку Lua с NGINX, убедитесь, что установлен nginx-module-lua.

Этот документ описывает lua-resty-multiplexer v0.2, выпущенный 31 августа 2020 года.


Эта библиотека реализует прозрачный мультиплексор портов, который можно использовать для запуска нескольких TCP-сервисов на одном порту.

Обратите внимание, что требуется модуль nginx stream и stream-lua-nginx-module.

Тестировалось на Openresty >= 1.13.6.1.

С OpenResty 1.13.6.1 требуется пользовательский патч от @fcicq. Обсуждение можно найти здесь. И нативное проксирование не поддерживается, так как отсутствует reqsock:peek.

Начиная с OpenResty 1.15.8.1, поддерживается только нативное проксирование, и патч не нужен. Проксирование в пространстве Lua станет возможным, когда в stream-lua-nginx-module будет реализован tcpsock:receiveany.

Синопсис

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.1, проксирование в пространстве Lua
    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()
                }
            }
    }
}

Этот модуль состоит из двух частей: идентификаторов протоколов и матчеров.

Идентификаторы протоколов необходимо загрузить через load_protocols в директиве init_by_lua_block. См. раздел protocol для текущих поддерживаемых протоколов и руководства по добавлению нового протокола.

Правила определяются через set_rules для маршрутизации трафика к различным upstream. Для каждого матчера, определенного в правиле, соответствующий матчеры загружается автоматически. См. раздел matcher для текущих реализованных матчеров и руководства по добавлению нового матчера.

См. раздел API для синтаксиса load_protocols и set_rules.

Определенные правила имеют приоритет. В приведенном выше примере мы определили правило, согласно которому:

  • Если адрес клиента 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 и текущая минута между 0 и 30, проксировать на twitter:443
  • Если протокол SSL/TLS и текущая минута между 31 и 59, проксировать на www.google.com:443
  • В противном случае, проксировать на 127.0.0.1:80

Протокол

Часть протокола анализирует первый запрос, отправленный от клиента, и пытается сопоставить его с известными сигнатурами протоколов.

Текущие поддерживаемые: dns, http, ssh, tls, xmpp. В зависимости от байтов сигнатуры, каждый протокол может иметь разные возможности для ложного определения.

Протокол Длина сигнатуры Уровень ложных срабатываний
dns 9 1/4 5.29e-23
http 4 2.33e-10
ssh 4 2.33e-10
tls 6 3.55e-15
xmpp 6 из 8 1/4 ?

Добавить новый протокол

Создайте новый файл protocol_name.lua в каталоге resty/multiplexer/protocol в следующем формате:

return {
    required_bytes = ?,
    check = function(buf)
    -- проверьте с buf и верните true, если протокол определен
    end
}

required_bytes — это длина байтов, которые необходимо прочитать перед определением протокола.

Матчер

client-host

Сопоставляет, если $remote_addr равен ожидаемому значению.

protocol

Сопоставляет, если протокол равен ожидаемому значению.

time

Сопоставляет, если текущее время находится в заданном диапазоне в mul.matcher_config.time. Если диапазон не определен, матчеры всегда будут возвращать false.

Например, чтобы сопоставить год 2018, январь и март, а также часы с 6 до 24, кроме часа 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

Всегда совпадает.

Добавить нового матчера

Создайте новый файл matcher_name.lua в каталоге resty/multiplexer/matchers в следующем формате:

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, ...)

Загружает правила в порядке. Каждое правило — это массив таблиц, который имеет следующий формат:

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

Поддерживаемые матчеры можно найти в matcher.

См. также

GitHub

Вы можете найти дополнительные советы по конфигурации и документацию для этого модуля в репозитории GitHub для nginx-module-multiplexer.