Pular para conteúdo

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:

  • nameservers

    uma 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. * retrans

    o 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 é 5 vezes. Ao tentar retransmitir a consulta, o próximo nameserver de acordo com o algoritmo de round-robin será escolhido. * timeout

    o 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 de timeout também pode ser alterada chamando o método set_timeout. A configuração padrão de timeout é 2000 milissegundos, ou 2 segundos. * no_recurse

    um flag booleano que controla se deve desabilitar a flag "recursão desejada" (RD) na solicitação UDP. O padrão é false. * no_random

    um 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:

  • name

    O nome do registro de recurso. * type

    O 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. * address

    O endereço IPv4 ou IPv6 em suas representações textuais quando o tipo do registro de recurso é 1 (TYPE_A) ou 28 (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ático compress_ipv6_addr. * section

    O identificador da seção à qual o registro de resposta atual pertence. Os valores possíveis são 1 (SECTION_AN), 2 (SECTION_NS), e 3 (SECTION_AR). * cname

    O valor de dados do registro (decodificado) para registros de recurso CNAME. Presente apenas para registros CNAME. * ttl

    O valor de tempo de vida (TTL) em segundos para o registro de recurso atual. * class

    A classe atual do registro de recurso, os valores possíveis são 1 (CLASS_IN) ou quaisquer outros valores permitidos pela RFC 1035. * preference

    O número inteiro de preferência para registros de recurso MX. Presente apenas para registros do tipo MX. * exchange

    O nome de domínio de troca para registros de recurso MX. Presente apenas para registros do tipo MX. * nsdname

    Um 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. * rdata

    Os dados de recurso brutos (RDATA) para registros de recurso que não são reconhecidos. * txt

    O 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. * ptrdname

    O valor do registro para registros PTR.

Este método também aceita uma tabela de argumento options opcional, que aceita os seguintes campos:

  • qtype

    O 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_section

    Quando definido como um valor verdadeiro, o valor de retorno answers inclui a seção Authority da resposta DNS. O padrão é false. * additional_section

    Quando definido como um valor verdadeiro, o valor de retorno answers inclui a seção Additional da 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*, e header_filter_by_lua*, onde a API de cosocket do ngx_lua não está disponível.
  • A instância do objeto resty.dns.resolver nã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 de resty.dns.resolver. Você deve sempre iniciar objetos resty.dns.resolver em variáveis locais de função ou na tabela ngx.ctx. Esses locais têm suas próprias cópias de dados para cada solicitação.

Veja Também

GitHub

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