dns: Resolvedor DNS para nginx-module-lua
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-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 com o NGINX, certifique-se de que o nginx-module-lua esteja instalado.
Este documento descreve o lua-resty-dns v0.23 lançado em 06 de agosto de 2023.
Esta biblioteca Lua fornece um resolvedor DNS para o módulo ngx_lua do NGINX:
https://github.com/openresty/lua-nginx-module/#readme
Esta biblioteca Lua aproveita a API de cosocket do ngx_lua, que garante um comportamento 100% não bloqueante.
Observe que pelo menos ngx_lua 0.5.12 ou OpenResty 1.2.1.11 é necessário.
Além disso, a biblioteca bit também é necessária. Se você estiver usando LuaJIT 2.0 com ngx_lua, a biblioteca bit já está disponível por padrão.
Observe que esta biblioteca está empacotada e habilitada por padrão no pacote OpenResty.
IMPORTANTE: para poder gerar IDs únicos, o gerador aleatório deve ser devidamente inicializado usando math.randomseed antes de usar este módulo.
Sinopse
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 retransmissões em caso de timeout de recebimento
timeout = 2000, -- 2 seg
no_random = true, -- sempre começar com o primeiro nameserver
}
if not r then
ngx.say("falha ao instanciar o resolvedor: ", err)
return
end
local answers, err, tries = r:query("www.google.com", nil, {})
if not answers then
ngx.say("falha ao consultar o servidor DNS: ", err)
ngx.say("histórico de tentativas:\n ", table.concat(tries, "\n "))
return
end
if answers.errcode then
ngx.say("servidor retornou código de erro: ", answers.errcode,
": ", answers.errstr)
end
for i, ans in ipairs(answers) do
ngx.say(ans.name, " ", ans.address or ans.cname,
" tipo:", ans.type, " classe:", ans.class,
" ttl:", ans.ttl)
end
}
}
}
Métodos
new
syntax: r, err = class:new(opts)
Cria um objeto dns.resolver. Retorna nil e uma string de mensagem em caso de erro.
Aceita um argumento de tabela opts. As seguintes opções são suportadas:
-
nameserversuma lista de nameservers a serem usados. Cada entrada de nameserver pode ser uma string de hostname única ou uma tabela contendo tanto a string de hostname quanto o número da porta. O nameserver é escolhido por um simples algoritmo de round-robin para cada chamada do método
query. Esta opção é obrigatória. *retranso número total de vezes que a solicitação DNS será retransmitida quando o recebimento de uma resposta DNS expirar de acordo com a configuração de
timeout. O padrão é5vezes. Ao tentar retransmitir a consulta, o próximo nameserver de acordo com o algoritmo de round-robin será escolhido. *timeouto tempo em milissegundos para aguardar a resposta para uma única tentativa de transmissão de solicitação. Observe que este não é o tempo máximo total de espera antes de desistir; o tempo máximo total de espera pode ser calculado pela expressão
timeout x retrans. A configuração detimeouttambém pode ser alterada chamando o métodoset_timeout. A configuração padrão detimeouté 2000 milissegundos, ou 2 segundos. *no_recurseum flag booleano que controla se deve desabilitar a flag "recursão desejada" (RD) na solicitação UDP. O padrão é
false. *no_randomum flag booleano que controla se deve escolher aleatoriamente o nameserver a ser consultado primeiro; se
true, sempre começará com o primeiro nameserver listado. O padrão éfalse.
destroy
syntax: r:destroy()
Destrói o objeto dns.resolver liberando todos os recursos internos ocupados.
query
syntax: answers, err, tries? = r:query(name, options?, tries?)
Realiza uma consulta padrão DNS aos nameservers especificados pelo método new, e retorna todos os registros de resposta em uma tabela Lua semelhante a um array. Em caso de erros, retornará nil e uma string descrevendo o erro.
Se o servidor retornar um código de erro diferente de zero, os campos errcode e errstr serão definidos de acordo na tabela Lua retornada.
Cada entrada na tabela answers retornada também é uma tabela Lua semelhante a um hash que geralmente contém alguns dos seguintes campos:
-
nameO nome do registro de recurso. *
typeO tipo atual do registro de recurso, os valores possíveis são
1(TYPE_A),5(TYPE_CNAME),28(TYPE_AAAA), e quaisquer outros valores permitidos pela RFC 1035. *addressO endereço IPv4 ou IPv6 em suas representações textuais quando o tipo do registro de recurso é
1(TYPE_A) ou28(TYPE_AAAA), respectivamente. Grupos sucessivos de zeros de 16 bits em endereços IPv6 não serão comprimidos por padrão; se você quiser isso, precisará chamar o método estáticocompress_ipv6_addr. *sectionO identificador da seção à qual o registro de resposta atual pertence. Os valores possíveis são
1(SECTION_AN),2(SECTION_NS), e3(SECTION_AR). *cnameO valor de dados do registro (decodificado) para registros de recurso
CNAME. Presente apenas para registrosCNAME. *ttlO valor de tempo de vida (TTL) em segundos para o registro de recurso atual. *
classA classe atual do registro de recurso, os valores possíveis são
1(CLASS_IN) ou quaisquer outros valores permitidos pela RFC 1035. *preferenceO número inteiro de preferência para registros de recurso
MX. Presente apenas para registros do tipoMX. *exchangeO nome de domínio de troca para registros de recurso
MX. Presente apenas para registros do tipoMX. *nsdnameUm nome de domínio que especifica um host que deve ser autoritativo para a classe e domínio especificados. Geralmente presente para registros do tipo
NS. *rdataOs dados de recurso brutos (RDATA) para registros de recurso que não são reconhecidos. *
txtO valor do registro para registros
TXT. Quando há apenas uma string de caractere neste registro, este campo assume uma única string Lua. Caso contrário, este campo assume uma tabela Lua contendo todas as strings. *ptrdnameO valor do registro para registros
PTR.
Este método também aceita uma tabela de argumento options opcional, que aceita os seguintes campos:
-
qtypeO tipo da pergunta. Os valores possíveis são
1(TYPE_A),5(TYPE_CNAME),28(TYPE_AAAA), ou qualquer outro valor QTYPE especificado pela RFC 1035 e RFC 3596. O padrão é1(TYPE_A). *authority_sectionQuando definido como um valor verdadeiro, o valor de retorno
answersinclui a seçãoAuthorityda resposta DNS. O padrão éfalse. *additional_sectionQuando definido como um valor verdadeiro, o valor de retorno
answersinclui a seçãoAdditionalda resposta DNS. O padrão éfalse.
O parâmetro opcional tries pode ser fornecido como uma tabela vazia e será retornado como um terceiro resultado. A tabela será um array com a mensagem de erro para cada tentativa falha (se houver).
Quando ocorre truncamento de dados, o resolvedor tentará automaticamente usar o modo de transporte TCP para consultar o nameserver atual. Todas as conexões TCP são de curta duração.
tcp_query
syntax: answers, err = r:tcp_query(name, options?)
Assim como o método query, mas força o modo de transporte TCP em vez de UDP.
Todas as conexões TCP são de curta duração.
Aqui está um exemplo:
local resolver = require "resty.dns.resolver"
local r, err = resolver:new{
nameservers = { "8.8.8.8" }
}
if not r then
ngx.say("falha ao instanciar o resolvedor: ", err)
return
end
local ans, err = r:tcp_query("www.google.com", { qtype = r.TYPE_A })
if not ans then
ngx.say("falha ao consultar: ", err)
return
end
local cjson = require "cjson"
ngx.say("registros: ", cjson.encode(ans))
set_timeout
syntax: r:set_timeout(time)
Substitui a configuração atual de timeout pelo argumento time em milissegundos para todos os peers de nameserver.
compress_ipv6_addr
syntax: compressed = resty.dns.resolver.compress_ipv6_addr(address)
Comprime os grupos sucessivos de zeros de 16 bits na representação textual do endereço IPv6.
Por exemplo,
local resolver = require "resty.dns.resolver"
local compress = resolver.compress_ipv6_addr
local new_addr = compress("FF01:0:0:0:0:0:0:101")
resultará em FF01::101 no valor de retorno new_addr.
expand_ipv6_addr
syntax: expanded = resty.dns.resolver.expand_ipv6_addr(address)
Expande os grupos sucessivos de zeros de 16 bits na representação textual do endereço IPv6.
Por exemplo,
local resolver = require "resty.dns.resolver"
local expand = resolver.expand_ipv6_addr
local new_addr = expand("FF01::101")
resultará em FF01:0:0:0:0:0:0:101 no valor de retorno new_addr.
arpa_str
syntax: arpa_record = resty.dns.resolver.arpa_str(address)
Gera o nome de domínio reverso para consultas PTR para endereços IPv4 e IPv6. Endereços IPv6 comprimidos serão automaticamente expandidos.
Por exemplo,
local resolver = require "resty.dns.resolver"
local ptr4 = resolver.arpa_str("1.2.3.4")
local ptr6 = resolver.arpa_str("FF01::101")
resultará em 4.3.2.1.in-addr.arpa para ptr4 e 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 uma consulta PTR para endereços IPv4 e IPv6. Esta função é basicamente um wrapper para o comando query que usa o comando arpa_str para converter o endereço IP em tempo real.
Constantes
TYPE_A
O tipo de registro de recurso A, igual ao número decimal 1.
TYPE_NS
O tipo de registro de recurso NS, igual ao número decimal 2.
TYPE_CNAME
O tipo de registro de recurso CNAME, igual ao número decimal 5.
TYPE_SOA
O tipo de registro de recurso SOA, igual ao número decimal 6.
TYPE_PTR
O tipo de registro de recurso PTR, igual ao número decimal 12.
TYPE_MX
O tipo de registro de recurso MX, igual ao número decimal 15.
TYPE_TXT
O tipo de registro de recurso TXT, igual ao número decimal 16.
TYPE_AAAA
syntax: typ = r.TYPE_AAAA
O tipo de registro de recurso AAAA, igual ao número decimal 28.
TYPE_SRV
syntax: typ = r.TYPE_SRV
O tipo de registro de recurso SRV, igual ao número decimal 33.
Veja a RFC 2782 para detalhes.
TYPE_SPF
syntax: typ = r.TYPE_SPF
O tipo de registro de recurso SPF, igual ao número decimal 99.
Veja a RFC 4408 para detalhes.
CLASS_IN
syntax: class = r.CLASS_IN
O tipo de registro de recurso Internet, igual ao número decimal 1.
SECTION_AN
syntax: stype = r.SECTION_AN
Identificador da seção Answer na resposta DNS. Igual ao número decimal 1.
SECTION_NS
syntax: stype = r.SECTION_NS
Identificador da seção Authority na resposta DNS. Igual ao número decimal 2.
SECTION_AR
syntax: stype = r.SECTION_AR
Identificador da seção Additional na resposta DNS. Igual ao número decimal 3.
Registro Automático de Erros
Por padrão, o módulo subjacente ngx_lua faz registro de erros quando ocorrem erros de socket. Se você já estiver fazendo o tratamento de erros adequados em seu próprio código Lua, recomenda-se desabilitar este registro automático de erros desligando a diretiva lua_socket_log_errors do ngx_lua, ou seja,
lua_socket_log_errors off;
Limitações
- Esta biblioteca não pode ser usada em contextos de código como
set_by_lua*,log_by_lua*, eheader_filter_by_lua*, onde a API de cosocket do ngx_lua não está disponível. - A instância do objeto
resty.dns.resolvernão pode ser armazenada em uma variável Lua no nível do módulo Lua, porque ela será compartilhada por todas as solicitações concorrentes tratadas pelo mesmo processo trabalhador do nginx (veja https://github.com/openresty/lua-nginx-module/#data-sharing-within-an-nginx-worker) e resultará em más condições de corrida quando solicitações concorrentes tentarem usar a mesma instância deresty.dns.resolver. Você deve sempre iniciar objetosresty.dns.resolverem variáveis locais de função ou na tabelangx.ctx. Esses locais têm suas próprias cópias de dados para cada solicitação.
Veja Também
- o módulo ngx_lua: https://github.com/openresty/lua-nginx-module/#readme
- a biblioteca lua-resty-memcached.
- a biblioteca lua-resty-redis.
- a biblioteca lua-resty-mysql.
GitHub
Você pode encontrar dicas adicionais de configuração e documentação para este módulo no repositório GitHub do nginx-module-dns.