Saltar a contenido

multiplexer: Multiplexor de servicio de puerto transparente para el subsistema de transmisión

Instalación

Si no has configurado la suscripción al repositorio RPM, regístrate. Luego puedes proceder con los siguientes pasos.

CentOS/RHEL 7 o 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 con NGINX, asegúrate de que nginx-module-lua esté instalado.

Este documento describe lua-resty-multiplexer v0.2 lanzado el 31 de agosto de 2020.


Esta biblioteca implementa un multiplexor de servicio de puerto transparente, que se puede usar para ejecutar múltiples servicios TCP en el mismo puerto.

Ten en cuenta que se requiere el módulo de transmisión de nginx y el stream-lua-nginx-module.

Probado en Openresty >= 1.13.6.1.

Con OpenResty 1.13.6.1, se necesita un parche personalizado de @fcicq. La discusión original se puede encontrar aquí. Y el proxy nativo no es compatible ya que falta reqsock:peek.

A partir de OpenResty 1.15.8.1, solo se admite el proxy nativo y no se necesita ningún parche. El proxy de Lua será posible cuando el stream-lua-nginx-module implemente tcpsock:receiveany.

Sinopsis

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 de 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 de 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 consta de dos partes: identificadores de protocolo y comparadores.

Los identificadores de protocolo deben cargarse a través de load_protocols en la directiva init_by_lua_block. Consulta la sección protocol para los protocolos actualmente soportados y una guía para agregar un nuevo protocolo.

Las reglas se definen a través de set_rules para enrutar el tráfico a diferentes upstreams. Para cada comparador que se define en la regla, el comparador correspondiente se carga automáticamente. Consulta la sección matcher para los comparadores actualmente implementados y una guía para agregar un nuevo comparador.

Consulta la sección API para la sintaxis de load_protocols y set_rules.

Las reglas definidas tienen prioridad. En el ejemplo anterior, definimos una regla tal que:

  • Si la dirección del cliente es 10.0.0.1, proxy a internal-host.com:80
  • Si el protocolo es HTTP y la dirección del cliente es 10.0.0.2, proxy a internal-host:8001
  • Si el protocolo es SSH, proxy a github.com:22
  • Si el protocolo es DNS, proxy a 1.1.1.1:53
  • Si el protocolo es SSL/TLS y el minuto actual está entre 0 y 30, proxy a twitter:443
  • Si el protocolo es SSL/TLS y el minuto actual está entre 31 y 59, proxy a www.google.com:443
  • De lo contrario, proxy a 127.0.0.1:80

Protocolo

La parte del protocolo analiza la primera solicitud que se envía desde el cliente e intenta hacer coincidirla utilizando firmas de protocolo conocidas.

Actualmente soportados: dns, http, ssh, tls, xmpp. Según los bytes de la firma, cada protocolo puede tener diferentes posibilidades de ser identificado erróneamente.

Protocolo Longitud de la firma Tasa de error
dns 9 1/4 5.29e-23
http 4 2.33e-10
ssh 4 2.33e-10
tls 6 3.55e-15
xmpp 6 en 8 1/4 ?

Agregar nuevo protocolo

Crea un nuevo archivo protocol_name.lua en resty/multiplexer/protocol en el siguiente formato:

return {
    required_bytes = ?,
    check = function(buf)
    -- verifica con el buf y devuelve true si se identifica el protocolo
    end
}

required_bytes es la longitud de bytes que necesitamos leer antes de identificar el protocolo.

Comparador

client-host

Coincide si $remote_addr es igual al valor esperado.

protocol

Coincide si el protocolo es igual al valor esperado.

time

Coincide si la hora actual está en el rango configurado en mul.matcher_config.time. Si no se define ningún rango, el comparador siempre devolverá false.

Por ejemplo, para coincidir el año 2018, enero y marzo y la hora 6 a 24 excepto por la 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 = {}, -- día del mes
        day_not_match = {},
        hour_match = {{6, 24}},
        hour_not_match = {{12}},
        minute_match = {},
        minute_not_match = {},
        second_match = {},
        second_not_match = {},
    }
 }

default

Siempre coincide.

Agregar nuevo comparador

Crea un nuevo archivo matcher_name.lua en resty/multiplexer/matchers en el siguiente formato:

local _M = {}

function _M.match(protocol, expected)
    -- devuelve true si hay coincidencia
end

return _M

Donde protocol es el protocolo identificado en minúsculas, y expected es el valor esperado para este comparador definido en set_rules.

API

multiplexer.new

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

Inicializa la instancia del multiplexor. Y establece el umbral de tiempo de conexión, el umbral de tiempo de envío y el umbral de tiempo de lectura, como en tcpsock:settimeouts.

multiplexer.load_protocols

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

Carga los módulos de protocolo en memoria.

Los protocolos soportados se pueden encontrar en protocol.

multiplexer.set_rules

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

Carga reglas en orden. Cada regla es una tabla de arreglo que tiene el formato de:

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

Los comparadores soportados se pueden encontrar en matcher.

Ver También

GitHub

Puedes encontrar consejos de configuración adicionales y documentación para este módulo en el repositorio de GitHub para nginx-module-multiplexer.