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:
-
nameserversuna 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. *retransel 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 es5veces. Al intentar retransmitir la consulta, se seleccionará el siguiente servidor de nombres según el algoritmo de round-robin. *timeoutel 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 detimeouttambién se puede cambiar llamando al métodoset_timeout. La configuración detimeoutpor defecto es de 2000 milisegundos, o 2 segundos. *no_recurseun indicador booleano que controla si se desactiva la bandera de "recursión deseada" (RD) en la solicitud UDP. Por defecto es
false. *no_randomun 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 esfalse.
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:
-
nameEl nombre del registro de recurso. *
typeEl 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. *addressLa dirección IPv4 o IPv6 en sus representaciones textuales cuando el tipo de registro de recurso es
1(TYPE_A) o28(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áticocompress_ipv6_addr. *sectionEl identificador de la sección a la que pertenece el registro de respuesta actual. Los valores posibles son
1(SECTION_AN),2(SECTION_NS), y3(SECTION_AR). *cnameEl valor de datos del registro (decodificado) para los registros de recurso
CNAME. Solo presente para registrosCNAME. *ttlEl valor de tiempo de vida (TTL) en segundos para el registro de recurso actual. *
classLa clase actual del registro de recurso, los valores posibles son
1(CLASS_IN) o cualquier otro valor permitido por la RFC 1035. *preferenceEl número entero de preferencia para registros de recurso
MX. Solo presente para registros de tipoMX. *exchangeEl nombre de dominio de intercambio para registros de recurso
MX. Solo presente para registros de tipoMX. *nsdnameUn nombre de dominio que especifica un host que debe ser autoritativo para la clase y dominio especificados. Generalmente presente para registros de tipo
NS. *rdataLos datos de recurso en bruto (RDATA) para registros de recurso que no son reconocidos. *
txtEl 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. *ptrdnameEl valor del registro para registros
PTR.
Este método también toma una tabla de argumento opcional options, que toma los siguientes campos:
-
qtypeEl 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 es1(TYPE_A). *authority_sectionCuando se establece en un valor verdadero, el valor de retorno
answersincluye la sección deAuthorityde la respuesta DNS. Por defecto esfalse. *additional_sectionCuando se establece en un valor verdadero, el valor de retorno
answersincluye la sección deAdditionalde la respuesta DNS. Por defecto esfalse.
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*, yheader_filter_by_lua*donde la API de cosocket de ngx_lua no está disponible. - La instancia del objeto
resty.dns.resolverno 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 deresty.dns.resolver. Siempre debes iniciar los objetosresty.dns.resolveren variables locales de función o en la tablangx.ctx. Estos lugares tienen sus propias copias de datos para cada solicitud.
Ver También
- el módulo ngx_lua: https://github.com/openresty/lua-nginx-module/#readme
- la biblioteca lua-resty-memcached.
- la biblioteca lua-resty-redis.
- la biblioteca lua-resty-mysql.
GitHub
Puedes encontrar consejos de configuración adicionales y documentación para este módulo en el repositorio de GitHub para nginx-module-dns.