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 :
-
nameserversune 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. *retransle 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 de5fois. Lors de la tentative de retransmission de la requête, le prochain serveur de noms selon l'algorithme de round-robin sera choisi. *timeoutle 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ètretimeoutpeut également être modifié en appelant la méthodeset_timeout. La valeur par défaut detimeoutest de 2000 millisecondes, ou 2 secondes. *no_recurseun 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_randomun 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'estfalse.
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 :
-
nameLe nom de l'enregistrement de ressource. *
typeLe 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. *addressL'adresse IPv4 ou IPv6 dans leurs représentations textuelles lorsque le type d'enregistrement de ressource est soit
1(TYPE_A) soit28(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 statiquecompress_ipv6_addr. *sectionL'identifiant de la section à laquelle appartient l'enregistrement de réponse actuel. Les valeurs possibles sont
1(SECTION_AN),2(SECTION_NS), et3(SECTION_AR). *cnameLa valeur de données d'enregistrement (décodée) pour les enregistrements de ressource
CNAME. Présente uniquement pour les enregistrementsCNAME. *ttlLa valeur de temps de vie (TTL) en secondes pour l'enregistrement de ressource actuel. *
classLa classe d'enregistrement de ressource actuelle, les valeurs possibles sont
1(CLASS_IN) ou toutes les autres valeurs autorisées par la RFC 1035. *preferenceLe nombre entier de préférence pour les enregistrements de ressource
MX. Présente uniquement pour les enregistrements de typeMX. *exchangeLe nom de domaine d'échange pour les enregistrements de ressource
MX. Présente uniquement pour les enregistrements de typeMX. *nsdnameUn 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. *rdataLes données de ressource brutes (RDATA) pour les enregistrements de ressource qui ne sont pas reconnus. *
txtLa 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. *ptrdnameLa valeur d'enregistrement pour les enregistrements
PTR.
Cette méthode prend également un argument optionnel options, qui prend les champs suivants :
-
qtypeLe 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'est1(TYPE_A). *authority_sectionLorsqu'il est défini sur une valeur vraie, la valeur de retour
answersinclut la sectionAuthorityde la réponse DNS. Par défaut, c'estfalse. *additional_sectionLorsqu'il est défini sur une valeur vraie, la valeur de retour
answersinclut la sectionAdditionalde la réponse DNS. Par défaut, c'estfalse.
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*, etheader_filter_by_lua*où l'API cosocket de ngx_lua n'est pas disponible. - L'instance de l'objet
resty.dns.resolverne 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 deresty.dns.resolver. Vous devez toujours initier des objetsresty.dns.resolverdans des variables locales de fonction ou dans la tablengx.ctx. Ces endroits ont tous leurs propres copies de données pour chaque requête.
Voir aussi
- le module ngx_lua : https://github.com/openresty/lua-nginx-module/#readme
- la bibliothèque lua-resty-memcached.
- la bibliothèque lua-resty-redis.
- la bibliothèque lua-resty-mysql.
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.