Guillaume Fenollar DevOps et SysAdmin Freelance

Guillaume FENOLLAR

Ingénieur Linux/DevOps Indépendant

− Montpellier −

L'option ndots du fichier resolv.conf et les limites de musl libc

Le fichier /etc/resolv.conf est un standard Linux, utilisé par le resolver, nom commun donné à l'ensemble de routines de la librairie C pour l'utilisation du protocole DNS pour, dois-je le préciser, traduire les noms d'hôtes et de domaines en IPs.

Ce fichier est un incontournable, mais peu de gens finalement savent qu’il présente tout un tas d’options configurables. Un fichier type est le suivant :

nameserver 192.168.1.1
search exemple.nc exemple.com

L’option search ici est facultative. Si elle est omise, le système tentera de déterminer le domaine autrement. Mais si elle est présente, les requêtes du resolver seront différentes selon le nombre de points présents dans le nom voulu. Par exemple, à un point présent (pour une requête sur “fenollar.fr” par exemple), un système va tenter de résoudre le nom tel quel, comme s’il était terminé par un point. Sans ce point (par exemple sur “fenollar”), seront essayés tous les domaines figurant dans la ligne “search” avant d’essayer ce nom de façon globale (fenollar.) et d’échouer lamentablement.

Un point c’est (presque) tout

Maintenant, avec une configuration ndots différente, le comportement peut changer. Par exemple avec l’option suivante:

options ndots:3

Chaque nom qui aura un nombre de points inférieur à 3 se verra requêté autant de fois qu’il y a de domaines dans “search” jusqu’à trouver un résultat, ou jusqu’à fallback à une requête pour le nom de domaine global. Cela augmente le nombre de requêtes DNS ainsi que le temps total de résolution. Une façon simple de contrecarrer cela est de rajouter le point final au nom voulu (“guillaume.fenollar.fr.”), mais cela n’est pas toujours possible (notamment quand le nom de domaine recherché passe par une API qui ne prend pas ça en compte).

Le choix de Kubernetes

Dans Kubernetes, les pods ayant une configuration dnsPolicy par défaut ont cette option mise à “ClusterFirst”. Cela implique que le fichier resolv.conf soit généré par le cluster pour utiliser sa brique DNS, et également que la configuration ndots est mise à 5. Voici un exemple.

nameserver 192.168.16.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

Une option mise à 2 aurait suffi pour que les noms coredns.kube-system ou encore kafka-0.kafka.default (dans le cas d’un statefulset) soient résolus correctement. Le fait de la mettre à 5 rend le nom `kafka-0.kafka.coredns.kube-system.svc résolu en passant par les domaines du “search”. Par contre, encore une fois ça implique évidemment que tous les noms requêtés passent par le serveur DNS un par un, ce qui fait plus de traffic, et plus de charge serveur, notamment dans le cas de gros clusters.

À contrario, une valeur à 1 rend les noms à rallonge uniquement résolvable en passant les serveur DNS racines, ce qui est à éviter pour un usage local.

Le cas musl libc

L’immense majorité des systèmes utilisent GNU Library C (glibc), mais pas tous. Certaines distributions ont fait le choix d’alternatives, notamment Alpine Linux, distribution très présente dans les conteneurs car très légère et donc facile à construire. Cette distribution a fait le choix de musl libc comme remplaçant de glibc, et celle-ci fonctionne un peu différemment.

Dans le cas où un nom est considéré global (ayant donc un nombre de points supérieur ou égal à l’option ndots qui est je le rappelle de 1 par défaut), la requête DNS va échouer s’il s’avère que le nom est en fait relatif à un domaine de recherche. En effet, là où glibc fait le taff de tenter les domaines de recherche si un nom “absolu” n’est pas résolu, musl abandonne tout simplement la requête et renvoie une erreur type “domain not found”.

Par exemple, dans un pod kubernetes où l’option ndots serait définie à 1, la requête kafka-0.kafka renvoit une erreur si le système utilise la librairie musl, au lieu de tenter une recherche de domaines parents.

A noter qu’il est possible de configurer un nombre de ndots par pod, grâce au Pod spec spec.dnsConfig :

spec:
  dnsConfig:
    options:
      - name: ndots
        value: "3"

Pour savoir quelle librairie C son conteneur utilise, vous pouvez essayer de lancer la commande suivante :

ldd --version

Bonnes pratiques

Ainsi, une valeur de ndots bien configurée peut avoir son impact sur le long terme, notamment si l’application en question est très gourmande en requêtes DNS. Il est donc conseillé dans la mesure du possible d’utiliser des noms globaux (se terminant par un point ‘.’) et de garder une valeur ndots au plus près du besoin, mais de ne jamais utiliser 1 si des noms enfants (la partie “sous” de sous-domaine) sont utilisés.