Aller au contenu

dns: Résolveur DNS pour nginx-module-lua

Installation

Si vous n'avez pas configuré l'abonnement au dépôt RPM, inscrivez-vous. Ensuite, vous pouvez procéder avec les étapes suivantes.

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

Pour utiliser cette bibliothèque Lua avec NGINX, assurez-vous que nginx-module-lua est installé.

Ce document décrit lua-resty-dns v0.23 publié le 06 août 2023.


Cette bibliothèque Lua fournit un résolveur DNS pour le module ngx_lua de NGINX :

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

Cette bibliothèque Lua tire parti de l'API cosocket de ngx_lua, qui garantit un comportement 100 % non-bloquant.

Notez qu'au moins ngx_lua 0.5.12 ou OpenResty 1.2.1.11 est requis.

De plus, la bibliothèque bit est également requise. Si vous utilisez LuaJIT 2.0 avec ngx_lua, alors la bibliothèque bit est déjà disponible par défaut.

Notez que cette bibliothèque est regroupée et activée par défaut dans le bundle OpenResty.

IMPORTANT : pour pouvoir générer des identifiants uniques, le générateur aléatoire doit être correctement initialisé à l'aide de math.randomseed avant d'utiliser ce module.

Synopsis

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 retransmissions en cas de timeout de réception
                timeout = 2000,  -- 2 sec
                no_random = true, -- toujours commencer avec le premier serveur de noms
            }

            if not r then
                ngx.say("échec de l'instanciation du résolveur : ", err)
                return
            end

            local answers, err, tries = r:query("www.google.com", nil, {})
            if not answers then
                ngx.say("échec de la requête au serveur DNS : ", err)
                ngx.say("historique des tentatives :\n  ", table.concat(tries, "\n  "))
                return
            end

            if answers.errcode then
                ngx.say("le serveur a retourné le code d'erreur : ", answers.errcode,
                        ": ", answers.errstr)
            end

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

Méthodes

new

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

Crée un objet dns.resolver. Retourne nil et une chaîne de message en cas d'erreur.

Il accepte un argument de table opts. Les options suivantes sont prises en charge :

  • nameservers

    une liste de serveurs de noms à utiliser. Chaque entrée de serveur de noms peut être soit une chaîne de nom d'hôte unique, soit une table contenant à la fois la chaîne de nom d'hôte et le numéro de port. Le serveur de noms est choisi par un simple algorithme de round-robin pour chaque appel de méthode query. Cette option est requise. * retrans

    le nombre total de fois de retransmission de la requête DNS lorsque la réception d'une réponse DNS expire selon le paramètre timeout. Par défaut, cela est de 5 fois. Lors de la tentative de retransmission de la requête, le prochain serveur de noms selon l'algorithme de round-robin sera choisi. * timeout

    le temps en millisecondes d'attente pour la réponse lors d'une tentative unique de transmission de requête. Notez que ce n'est ''pas'' le temps d'attente maximal total avant d'abandonner, le temps d'attente maximal total peut être calculé par l'expression timeout x retrans. Le paramètre timeout peut également être modifié en appelant la méthode set_timeout. La valeur par défaut de timeout est de 2000 millisecondes, ou 2 secondes. * no_recurse

    un indicateur booléen qui contrôle s'il faut désactiver le drapeau "récursion désirée" (RD) dans la requête UDP. Par défaut, c'est false. * no_random

    un indicateur booléen qui contrôle s'il faut choisir aléatoirement le serveur de noms à interroger en premier, si true, il commencera toujours par le premier serveur de noms répertorié. Par défaut, c'est false.

destroy

syntax: r:destroy()

Détruit l'objet dns.resolver en libérant toutes les ressources internes occupées.

query

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

Effectue une requête DNS standard aux serveurs de noms spécifiés par la méthode new, et retourne tous les enregistrements de réponse dans une table Lua de type tableau. En cas d'erreurs, il retournera nil et une chaîne décrivant l'erreur à la place.

Si le serveur retourne un code d'erreur non nul, les champs errcode et errstr seront définis en conséquence dans la table Lua retournée.

Chaque entrée dans la table retournée answers est également une table Lua de type hash qui prend généralement certains des champs suivants :

  • name

    Le nom de l'enregistrement de ressource. * type

    Le type d'enregistrement de ressource actuel, les valeurs possibles sont 1 (TYPE_A), 5 (TYPE_CNAME), 28 (TYPE_AAAA), et toutes les autres valeurs autorisées par la RFC 1035. * address

    L'adresse IPv4 ou IPv6 dans leurs représentations textuelles lorsque le type d'enregistrement de ressource est soit 1 (TYPE_A) soit 28 (TYPE_AAAA), respectivement. Les groupes de zéros successifs de 16 bits dans les adresses IPv6 ne seront pas compressés par défaut, si vous le souhaitez, vous devez appeler la méthode statique compress_ipv6_addr. * section

    L'identifiant de la section à laquelle appartient l'enregistrement de réponse actuel. Les valeurs possibles sont 1 (SECTION_AN), 2 (SECTION_NS), et 3 (SECTION_AR). * cname

    La valeur de données d'enregistrement (décodée) pour les enregistrements de ressource CNAME. Présente uniquement pour les enregistrements CNAME. * ttl

    La valeur de temps de vie (TTL) en secondes pour l'enregistrement de ressource actuel. * class

    La classe d'enregistrement de ressource actuelle, les valeurs possibles sont 1 (CLASS_IN) ou toutes les autres valeurs autorisées par la RFC 1035. * preference

    Le nombre entier de préférence pour les enregistrements de ressource MX. Présente uniquement pour les enregistrements de type MX. * exchange

    Le nom de domaine d'échange pour les enregistrements de ressource MX. Présente uniquement pour les enregistrements de type MX. * nsdname

    Un nom de domaine qui spécifie un hôte qui devrait être autoritaire pour la classe et le domaine spécifiés. Présente généralement pour les enregistrements de type NS. * rdata

    Les données de ressource brutes (RDATA) pour les enregistrements de ressource qui ne sont pas reconnus. * txt

    La valeur d'enregistrement pour les enregistrements TXT. Lorsqu'il n'y a qu'une seule chaîne de caractères dans cet enregistrement, alors ce champ prend une seule chaîne Lua. Sinon, ce champ prend une table Lua contenant toutes les chaînes. * ptrdname

    La valeur d'enregistrement pour les enregistrements PTR.

Cette méthode prend également un argument optionnel options, qui prend les champs suivants :

  • qtype

    Le type de la question. Les valeurs possibles sont 1 (TYPE_A), 5 (TYPE_CNAME), 28 (TYPE_AAAA), ou toute autre valeur QTYPE spécifiée par la RFC 1035 et la RFC 3596. Par défaut, c'est 1 (TYPE_A). * authority_section

    Lorsqu'il est défini sur une valeur vraie, la valeur de retour answers inclut la section Authority de la réponse DNS. Par défaut, c'est false. * additional_section

    Lorsqu'il est défini sur une valeur vraie, la valeur de retour answers inclut la section Additional de la réponse DNS. Par défaut, c'est false.

Le paramètre optionnel tries peut être fourni comme une table vide, et sera retourné comme un troisième résultat. La table sera un tableau avec le message d'erreur pour chaque tentative échouée (le cas échéant).

Lorsque la troncature des données se produit, le résolveur essaiera automatiquement de nouveau en utilisant le mode de transport TCP pour interroger le serveur de noms actuel. Toutes les connexions TCP sont de courte durée.

tcp_query

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

Tout comme la méthode query, mais impose le mode de transport TCP au lieu de l'UDP.

Toutes les connexions TCP sont de courte durée.

Voici un exemple :

    local resolver = require "resty.dns.resolver"

    local r, err = resolver:new{
        nameservers = { "8.8.8.8" }
    }
    if not r then
        ngx.say("échec de l'instanciation du résolveur : ", err)
        return
    end

    local ans, err = r:tcp_query("www.google.com", { qtype = r.TYPE_A })
    if not ans then
        ngx.say("échec de la requête : ", err)
        return
    end

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

set_timeout

syntax: r:set_timeout(time)

Remplace le paramètre timeout actuel par l'argument time en millisecondes pour tous les pairs de serveurs de noms.

compress_ipv6_addr

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

Compresse les groupes successifs de zéros de 16 bits dans le format textuel de l'adresse IPv6.

Par exemple,

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

produira FF01::101 dans la valeur de retour new_addr.

expand_ipv6_addr

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

Développe les groupes successifs de zéros de 16 bits dans le format textuel de l'adresse IPv6.

Par exemple,

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

produira FF01:0:0:0:0:0:0:101 dans la valeur de retour new_addr.

arpa_str

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

Génère le nom de domaine inverse pour les recherches PTR pour les adresses IPv4 et IPv6. Les adresses IPv6 compressées seront automatiquement développées.

Par exemple,

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

produira 4.3.2.1.in-addr.arpa pour ptr4 et 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 pour ptr6.

reverse_query

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

Effectue une recherche PTR pour les adresses IPv4 et IPv6. Cette fonction est essentiellement un wrapper pour la commande query qui utilise la commande arpa_str pour convertir l'adresse IP à la volée.

Constantes

TYPE_A

Le type d'enregistrement de ressource A, égal au nombre décimal 1.

TYPE_NS

Le type d'enregistrement de ressource NS, égal au nombre décimal 2.

TYPE_CNAME

Le type d'enregistrement de ressource CNAME, égal au nombre décimal 5.

TYPE_SOA

Le type d'enregistrement de ressource SOA, égal au nombre décimal 6.

TYPE_PTR

Le type d'enregistrement de ressource PTR, égal au nombre décimal 12.

TYPE_MX

Le type d'enregistrement de ressource MX, égal au nombre décimal 15.

TYPE_TXT

Le type d'enregistrement de ressource TXT, égal au nombre décimal 16.

TYPE_AAAA

syntax: typ = r.TYPE_AAAA

Le type d'enregistrement de ressource AAAA, égal au nombre décimal 28.

TYPE_SRV

syntax: typ = r.TYPE_SRV

Le type d'enregistrement de ressource SRV, égal au nombre décimal 33.

Voir la RFC 2782 pour plus de détails.

TYPE_SPF

syntax: typ = r.TYPE_SPF

Le type d'enregistrement de ressource SPF, égal au nombre décimal 99.

Voir la RFC 4408 pour plus de détails.

CLASS_IN

syntax: class = r.CLASS_IN

Le type d'enregistrement de ressource Internet, égal au nombre décimal 1.

SECTION_AN

syntax: stype = r.SECTION_AN

Identifiant de la section Answer dans la réponse DNS. Égal au nombre décimal 1.

SECTION_NS

syntax: stype = r.SECTION_NS

Identifiant de la section Authority dans la réponse DNS. Égal au nombre décimal 2.

SECTION_AR

syntax: stype = r.SECTION_AR

Identifiant de la section Additional dans la réponse DNS. Égal au nombre décimal 3.

Journalisation automatique des erreurs

Par défaut, le module sous-jacent ngx_lua effectue une journalisation des erreurs lorsque des erreurs de socket se produisent. Si vous gérez déjà correctement les erreurs dans votre propre code Lua, il est recommandé de désactiver cette journalisation automatique des erreurs en désactivant la directive lua_socket_log_errors de ngx_lua, c'est-à-dire,

    lua_socket_log_errors off;

Limitations

  • Cette bibliothèque ne peut pas être utilisée dans des contextes de code tels que set_by_lua*, log_by_lua*, et header_filter_by_lua* où l'API cosocket de ngx_lua n'est pas disponible.
  • L'instance de l'objet resty.dns.resolver ne peut pas être stockée dans une variable Lua au niveau du module Lua, car elle sera alors partagée par toutes les requêtes concurrentes traitées par le même processus de travail nginx (voir https://github.com/openresty/lua-nginx-module/#data-sharing-within-an-nginx-worker) et entraînera de mauvaises conditions de concurrence lorsque des requêtes concurrentes essaient d'utiliser la même instance de resty.dns.resolver. Vous devez toujours initier des objets resty.dns.resolver dans des variables locales de fonction ou dans la table ngx.ctx. Ces endroits ont tous leurs propres copies de données pour chaque requête.

Voir aussi

GitHub

Vous pouvez trouver des conseils de configuration supplémentaires et de la documentation pour ce module dans le dépôt GitHub pour nginx-module-dns.