Pular para conteúdo

multiplexer: Multiplexador de serviço de porta transparente para subsistema de stream

Instalação

Se você ainda não configurou a assinatura do repositório RPM, inscreva-se. Em seguida, você pode prosseguir com os seguintes passos.

CentOS/RHEL 7 ou 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

Para usar esta biblioteca Lua com NGINX, certifique-se de que o nginx-module-lua está instalado.

Este documento descreve lua-resty-multiplexer v0.2 lançado em 31 de agosto de 2020.


Esta biblioteca implementou um multiplexador de serviço de porta transparente, que pode ser usado para executar múltiplos serviços TCP na mesma porta.

Observe que o módulo stream do nginx e o stream-lua-nginx-module são necessários.

Testado no OpenResty >= 1.13.6.1.

Com o OpenResty 1.13.6.1, um patch personalizado de @fcicq é necessário. A discussão original pode ser encontrada aqui. E o proxy nativo não é suportado, pois reqsock:peek está ausente.

A partir do OpenResty 1.15.8.1, apenas o proxy nativo é suportado e nenhum patch é necessário. O proxy em Lua será possível quando o stream-lua-nginx-module implementar tcpsock:receiveany.

Sinopse

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;

    # para OpenResty >= 1.13.6.1, proxy nativo do 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;
    }

    # para OpenResty < 1.13.6.1, proxy em 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()
                }
            }
    }
}

Este módulo consiste em duas partes: identificadores de protocolo e correspondências.

Os identificadores de protocolo precisam ser carregados através de load_protocols na diretiva init_by_lua_block. Consulte a seção protocol para protocolos atualmente suportados e um guia para adicionar um novo protocolo.

As regras são definidas através de set_rules para rotear o tráfego para diferentes upstreams. Para cada correspondência que é definida na regra, a correspondência correspondente é carregada automaticamente. Consulte a seção matcher para correspondências atualmente implementadas e um guia para adicionar uma nova correspondência.

Consulte a seção API para a sintaxe de load_protocols e set_rules.

As regras definidas têm prioridade. No exemplo acima, definimos uma regra tal que:

  • Se o endereço do cliente for 10.0.0.1, proxy para internal-host.com:80
  • Se o protocolo for HTTP e o endereço do cliente for 10.0.0.2, proxy para internal-host:8001
  • Se o protocolo for SSH, proxy para github.com:22
  • Se o protocolo for DNS, proxy para 1.1.1.1:53
  • Se o protocolo for SSL/TLS e o minuto atual estiver entre 0 e 30, proxy para twitter:443
  • Se o protocolo for SSL/TLS e o minuto atual estiver entre 31 e 59, proxy para www.google.com:443
  • Caso contrário, proxy para 127.0.0.1:80

Protocolo

A parte do protocolo analisa a primeira solicitação que é enviada do cliente e tenta correspondê-la usando assinaturas de protocolo conhecidas.

Atualmente suportados: dns, http, ssh, tls, xmpp. Com base nos bytes da assinatura, cada protocolo pode ter diferentes possibilidades de ser identificado erroneamente.

Protocolo Comprimento da assinatura Taxa de erro
dns 9 1/4 5.29e-23
http 4 2.33e-10
ssh 4 2.33e-10
tls 6 3.55e-15
xmpp 6 em 8 1/4 ?

Adicionar novo protocolo

Crie um novo arquivo protocol_name.lua em resty/multiplexer/protocol no formato:

return {
    required_bytes = ?,
    check = function(buf)
    -- verificar com o buf e retornar true se o protocolo for identificado
    end
}

required_bytes é o comprimento dos bytes que precisamos ler antes de identificar o protocolo.

Correspondência

client-host

Correspondência se $remote_addr for igual ao valor esperado.

protocol

Correspondência se o protocolo for igual ao valor esperado.

time

Correspondência se o horário atual estiver no intervalo configurado em mul.matcher_config.time. Se nenhum intervalo for definido, a correspondência sempre retornará false.

Por exemplo, para corresponder ao ano 2018, Janeiro e Março e hora 6 a 24, exceto para a hora 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 = {}, -- dia do mês
        day_not_match = {},
        hour_match = {{6, 24}},
        hour_not_match = {{12}},
        minute_match = {},
        minute_not_match = {},
        second_match = {},
        second_not_match = {},
    }
 }

default

Sempre corresponde.

Adicionar nova correspondência

Crie um novo arquivo matcher_name.lua em resty/multiplexer/matchers no formato:

local _M = {}

function _M.match(protocol, expected)
    -- retornar true se for uma correspondência
end

return _M

Onde protocol é o protocolo identificado em string minúscula, e expected é o valor esperado para esta correspondência definido em set_rules.

API

multiplexer.new

sintaxe: multiplexer:new(connect_timeout, send_timeout, read_timeout)

Inicializa a instância do multiplexador. E define o limite de tempo de conexão, limite de tempo de envio e limite de tempo de leitura, conforme em tcpsock:settimeouts.

multiplexer.load_protocols

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

Carrega os módulos de protocolo na memória.

Os protocolos suportados podem ser encontrados em protocol.

multiplexer.set_rules

sintaxe: multiplexer:set_rules(rule1, rule2, ...)

Carrega regras na ordem. Cada regra é uma tabela de array que está no formato:

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

Os correspondentes suportados podem ser encontrados em matcher.

Veja Também

GitHub

Você pode encontrar dicas adicionais de configuração e documentação para este módulo no repositório do GitHub para nginx-module-multiplexer.