Saltar a contenido

dns: Resolutor DNS para nginx-module-lua

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-dns

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

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

Para usar esta biblioteca Lua con NGINX, asegúrate de que nginx-module-lua esté instalado.

Este documento describe lua-resty-dns v0.23 lanzado el 06 de agosto de 2023.


Esta biblioteca Lua proporciona un resolutor DNS para el módulo ngx_lua de nginx:

https://github.com/openresty/lua-nginx-module/#readme

Esta biblioteca Lua aprovecha la API de cosocket de ngx_lua, que garantiza un comportamiento 100% no bloqueante.

Ten en cuenta que se requiere al menos ngx_lua 0.5.12 o OpenResty 1.2.1.11.

Además, también se requiere la biblioteca bit. Si estás usando LuaJIT 2.0 con ngx_lua, entonces la biblioteca bit ya está disponible por defecto.

Ten en cuenta que esta biblioteca está empaquetada y habilitada por defecto en el paquete de OpenResty.

IMPORTANTE: para poder generar IDs únicos, el generador aleatorio debe ser correctamente inicializado usando math.randomseed antes de usar este módulo.

Sinopsis

server {
    location = /dns {
        content_by_lua_block {
            local resolver = require "resty.dns.resolver"
            local r, err = resolver:new{
                nameservers = {"8.8.8.8", {"8.8.4.4", 53} },
                retrans = 5,  -- 5 retransmisiones en caso de tiempo de espera
                timeout = 2000,  -- 2 seg
                no_random = true, -- siempre comenzar con el primer servidor de nombres
            }

            if not r then
                ngx.say("falló al instanciar el resolutor: ", err)
                return
            end

            local answers, err, tries = r:query("www.google.com", nil, {})
            if not answers then
                ngx.say("falló al consultar el servidor DNS: ", err)
                ngx.say("historial de reintentos:\n  ", table.concat(tries, "\n  "))
                return
            end

            if answers.errcode then
                ngx.say("el servidor devolvió el código de error: ", answers.errcode,
                        ": ", answers.errstr)
            end

            for i, ans in ipairs(answers) do
                ngx.say(ans.name, " ", ans.address or ans.cname,
                        " tipo:", ans.type, " clase:", ans.class,
                        " ttl:", ans.ttl)
            end
        }
    }
}

Métodos

new

syntax: r, err = class:new(opts)

Crea un objeto dns.resolver. Devuelve nil y una cadena de mensaje en caso de error.

Acepta un argumento de tabla opts. Las siguientes opciones son compatibles:

  • nameservers

    una lista de servidores de nombres a utilizar. Cada entrada de servidor de nombres puede ser una cadena de nombre de host única o una tabla que contenga tanto la cadena de nombre de host como el número de puerto. El servidor de nombres se selecciona mediante un simple algoritmo de round-robin para cada llamada al método query. Esta opción es obligatoria. * retrans

    el número total de veces que se retransmite la solicitud DNS cuando se agota el tiempo de espera de respuesta DNS según la configuración de timeout. Por defecto es 5 veces. Al intentar retransmitir la consulta, se seleccionará el siguiente servidor de nombres según el algoritmo de round-robin. * timeout

    el tiempo en milisegundos para esperar la respuesta para un solo intento de transmisión de solicitud. Ten en cuenta que este no es el tiempo máximo total de espera antes de rendirse, el tiempo máximo total de espera se puede calcular mediante la expresión timeout x retrans. La configuración de timeout también se puede cambiar llamando al método set_timeout. La configuración de timeout por defecto es de 2000 milisegundos, o 2 segundos. * no_recurse

    un indicador booleano que controla si se desactiva la bandera de "recursión deseada" (RD) en la solicitud UDP. Por defecto es false. * no_random

    un indicador booleano que controla si se selecciona aleatoriamente el servidor de nombres a consultar primero; si es true, siempre comenzará con el primer servidor de nombres listado. Por defecto es false.

destroy

syntax: r:destroy()

Destruye el objeto dns.resolver liberando todos los recursos internos ocupados.

query

syntax: answers, err, tries? = r:query(name, options?, tries?)

Realiza una consulta DNS estándar a los servidores de nombres especificados por el método new, y devuelve todos los registros de respuesta en una tabla Lua similar a un array. En caso de errores, devolverá nil y una cadena que describe el error en su lugar.

Si el servidor devuelve un código de error distinto de cero, los campos errcode y errstr se establecerán en consecuencia en la tabla Lua devuelta.

Cada entrada en la tabla answers devuelta también es una tabla Lua similar a un hash que generalmente toma algunos de los siguientes campos:

  • name

    El nombre del registro de recurso. * type

    El tipo actual del registro de recurso, los valores posibles son 1 (TYPE_A), 5 (TYPE_CNAME), 28 (TYPE_AAAA), y cualquier otro valor permitido por la RFC 1035. * address

    La dirección IPv4 o IPv6 en sus representaciones textuales cuando el tipo de registro de recurso es 1 (TYPE_A) o 28 (TYPE_AAAA), respectivamente. Los grupos sucesivos de ceros de 16 bits en las direcciones IPv6 no se comprimirán por defecto; si deseas eso, debes llamar al método estático compress_ipv6_addr. * section

    El identificador de la sección a la que pertenece el registro de respuesta actual. Los valores posibles son 1 (SECTION_AN), 2 (SECTION_NS), y 3 (SECTION_AR). * cname

    El valor de datos del registro (decodificado) para los registros de recurso CNAME. Solo presente para registros CNAME. * ttl

    El valor de tiempo de vida (TTL) en segundos para el registro de recurso actual. * class

    La clase actual del registro de recurso, los valores posibles son 1 (CLASS_IN) o cualquier otro valor permitido por la RFC 1035. * preference

    El número entero de preferencia para registros de recurso MX. Solo presente para registros de tipo MX. * exchange

    El nombre de dominio de intercambio para registros de recurso MX. Solo presente para registros de tipo MX. * nsdname

    Un nombre de dominio que especifica un host que debe ser autoritativo para la clase y dominio especificados. Generalmente presente para registros de tipo NS. * rdata

    Los datos de recurso en bruto (RDATA) para registros de recurso que no son reconocidos. * txt

    El valor del registro para registros TXT. Cuando hay solo una cadena de caracteres en este registro, entonces este campo toma una sola cadena Lua. De lo contrario, este campo toma una tabla Lua que contiene todas las cadenas. * ptrdname

    El valor del registro para registros PTR.

Este método también toma una tabla de argumento opcional options, que toma los siguientes campos:

  • qtype

    El tipo de la pregunta. Los valores posibles son 1 (TYPE_A), 5 (TYPE_CNAME), 28 (TYPE_AAAA), o cualquier otro valor de QTYPE especificado por la RFC 1035 y RFC 3596. Por defecto es 1 (TYPE_A). * authority_section

    Cuando se establece en un valor verdadero, el valor de retorno answers incluye la sección de Authority de la respuesta DNS. Por defecto es false. * additional_section

    Cuando se establece en un valor verdadero, el valor de retorno answers incluye la sección de Additional de la respuesta DNS. Por defecto es false.

El parámetro opcional tries se puede proporcionar como una tabla vacía, y se devolverá como un tercer resultado. La tabla será un array con el mensaje de error para cada intento fallido (si los hay).

Cuando ocurre una truncación de datos, el resolutor volverá a intentar automáticamente utilizando el modo de transporte TCP para consultar el servidor de nombres actual. Todas las conexiones TCP son de corta duración.

tcp_query

syntax: answers, err = r:tcp_query(name, options?)

Al igual que el método query, pero obliga al modo de transporte TCP en lugar de UDP.

Aquí hay un ejemplo:

    local resolver = require "resty.dns.resolver"

    local r, err = resolver:new{
        nameservers = { "8.8.8.8" }
    }
    if not r then
        ngx.say("falló al instanciar el resolutor: ", err)
        return
    end

    local ans, err = r:tcp_query("www.google.com", { qtype = r.TYPE_A })
    if not ans then
        ngx.say("falló al consultar: ", err)
        return
    end

    local cjson = require "cjson"
    ngx.say("registros: ", cjson.encode(ans))

set_timeout

syntax: r:set_timeout(time)

Sobrescribe la configuración actual de timeout por el argumento time en milisegundos para todos los pares de servidores de nombres.

compress_ipv6_addr

syntax: compressed = resty.dns.resolver.compress_ipv6_addr(address)

Comprime los grupos sucesivos de ceros de 16 bits en el formato textual de la dirección IPv6.

Por ejemplo,

    local resolver = require "resty.dns.resolver"
    local compress = resolver.compress_ipv6_addr
    local new_addr = compress("FF01:0:0:0:0:0:0:101")

dará como resultado FF01::101 en el valor de retorno new_addr.

expand_ipv6_addr

syntax: expanded = resty.dns.resolver.expand_ipv6_addr(address)

Expande los grupos sucesivos de ceros de 16 bits en el formato textual de la dirección IPv6.

Por ejemplo,

    local resolver = require "resty.dns.resolver"
    local expand = resolver.expand_ipv6_addr
    local new_addr = expand("FF01::101")

dará como resultado FF01:0:0:0:0:0:0:101 en el valor de retorno new_addr.

arpa_str

syntax: arpa_record = resty.dns.resolver.arpa_str(address)

Genera el nombre de dominio inverso para búsquedas PTR tanto para direcciones IPv4 como IPv6. Las direcciones IPv6 comprimidas se expandirán automáticamente.

Por ejemplo,

    local resolver = require "resty.dns.resolver"
    local ptr4 = resolver.arpa_str("1.2.3.4")
    local ptr6 = resolver.arpa_str("FF01::101")

dará como resultado 4.3.2.1.in-addr.arpa para ptr4 y 1.0.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.F.F.ip6.arpa para ptr6.

reverse_query

syntax: answers, err = r:reverse_query(address)

Realiza una búsqueda PTR tanto para direcciones IPv4 como IPv6. Esta función es básicamente un envoltorio para el comando query que utiliza el comando arpa_str para convertir la dirección IP sobre la marcha.

Constantes

TYPE_A

El tipo de registro de recurso A, igual al número decimal 1.

TYPE_NS

El tipo de registro de recurso NS, igual al número decimal 2.

TYPE_CNAME

El tipo de registro de recurso CNAME, igual al número decimal 5.

TYPE_SOA

El tipo de registro de recurso SOA, igual al número decimal 6.

TYPE_PTR

El tipo de registro de recurso PTR, igual al número decimal 12.

TYPE_MX

El tipo de registro de recurso MX, igual al número decimal 15.

TYPE_TXT

El tipo de registro de recurso TXT, igual al número decimal 16.

TYPE_AAAA

syntax: typ = r.TYPE_AAAA

El tipo de registro de recurso AAAA, igual al número decimal 28.

TYPE_SRV

syntax: typ = r.TYPE_SRV

El tipo de registro de recurso SRV, igual al número decimal 33.

Consulta la RFC 2782 para más detalles.

TYPE_SPF

syntax: typ = r.TYPE_SPF

El tipo de registro de recurso SPF, igual al número decimal 99.

Consulta la RFC 4408 para más detalles.

CLASS_IN

syntax: class = r.CLASS_IN

El tipo de registro de recurso Internet, igual al número decimal 1.

SECTION_AN

syntax: stype = r.SECTION_AN

Identificador de la sección de Respuesta en la respuesta DNS. Igual al número decimal 1.

SECTION_NS

syntax: stype = r.SECTION_NS

Identificador de la sección de Autoridad en la respuesta DNS. Igual al número decimal 2.

SECTION_AR

syntax: stype = r.SECTION_AR

Identificador de la sección de Adicional en la respuesta DNS. Igual al número decimal 3.

Registro Automático de Errores

Por defecto, el módulo subyacente ngx_lua realiza el registro de errores cuando ocurren errores de socket. Si ya estás manejando adecuadamente los errores en tu propio código Lua, se recomienda desactivar este registro automático de errores desactivando la directiva lua_socket_log_errors de ngx_lua, es decir,

    lua_socket_log_errors off;

Limitaciones

  • Esta biblioteca no se puede usar en contextos de código como set_by_lua*, log_by_lua*, y header_filter_by_lua* donde la API de cosocket de ngx_lua no está disponible.
  • La instancia del objeto resty.dns.resolver no se puede almacenar en una variable Lua a nivel de módulo Lua, porque entonces será compartida por todas las solicitudes concurrentes manejadas por el mismo proceso trabajador de nginx (ver https://github.com/openresty/lua-nginx-module/#data-sharing-within-an-nginx-worker) y resultará en malas condiciones de carrera cuando las solicitudes concurrentes intenten usar la misma instancia de resty.dns.resolver. Siempre debes iniciar los objetos resty.dns.resolver en variables locales de función o en la tabla ngx.ctx. Estos lugares tienen sus propias copias de datos para cada solicitud.

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-dns.