51 votes

Comment désactiver les recherches AAAA?

... pour compenser les serveurs DNS défaillants qui sont hors de notre contrôle.

Notre problème : nous déployons des appareils embarqués qui collectent des données de capteurs sur divers sites, principalement en IPv4. Certains sites ont des réseaux mal entretenus, par exemple des caches DNS mal configurés ou cassés et/ou des pare-feu qui ignorent complètement les requêtes AAAA, ou y répondent par des réponses erronées (par exemple, mauvaise adresse IP source!). En tant que fournisseur externe au département des installations, nous n'avons presque aucun impact sur les (parfois réticents) départements informatiques. Les chances qu'ils corrigent leurs serveurs DNS/pare-feu dans un avenir proche sont minuscules.

L'effet sur notre appareil est qu'à chaque appel à gethostbyname(), les processus doivent attendre que les requêtes AAAA expirent, à ce moment-là certains processus ont déjà dépassé le délai de leurs tentatives de connexion.

Je recherche des solutions qui sont ...

  • à l'échelle du système. Je ne peux pas reconfigurer des dizaines d'applications individuellement
  • non permanente et configurable. Nous devons (ré)activer l'IPv6 là où/quand il est réparé/déployé. Un redémarrage est acceptable.
  • Si une solution nécessite le remplacement d'une bibliothèque de base comme glibc, le package de bibliothèque de remplacement doit être disponible depuis un référentiel bien entretenu (par exemple, Debian Testing, Ubuntu universe, EPEL). L'autoconstruction n'est pas une option pour de nombreuses raisons que je ne sais même pas par où commencer, donc je ne les énumère même pas...

La solution la plus évidente serait de configurer la bibliothèque de résolution par exemple via /etc/{resolv,nsswitch,gai}.conf pour ne pas interroger les enregistrements AAAA. Une option resolv.conf no-inet6 comme suggéré ici serait exactement ce que je recherche. Malheureusement, elle n'est pas implémentée, du moins pas sur nos systèmes (libc6-2.13-38+deb7u4 sur Debian 7; libc6-2.19-0ubuntu6.3 sur Ubuntu 14.04)

Alors, comment faire alors ? Voici les méthodes suggérées sur SF et ailleurs, mais aucune d'entre elles ne fonctionne :

  • Désactiver complètement l'IPv6, par exemple en mettant l'ipv6 LKM sur liste noire dans /etc/modprobe.d/, ou sysctl -w net.ipv6.conf.all.disable_ipv6=1. (Par curiosité : Pourquoi le résolveur demande-t-il des AAAA alors que l'IPv6 est désactivé ?)
  • Supprimer options inet6 de /etc/resolv.conf. Il n'était pas là en premier lieu, inet6 est simplement activé par défaut de nos jours.
  • Définir options single-request dans /etc/resolv.conf. Cela garantit uniquement que les requêtes A et AAAA sont effectuées séquentiellement plutôt qu'en parallèle
  • Changer precedence dans /etc/gai.conf. Cela n'affecte pas les requêtes DNS, seulement comment les réponses multiples sont traitées.
  • Utiliser des résolveurs externes (ou exécuter un démon de résolution local qui contourne les serveurs DNS défaillants) serait utile, mais est généralement interdit par les politiques de pare-feu de l'entreprise. Et cela peut rendre les ressources internes inaccessibles.

Idées alternatives moches :

  • Exécuter un cache DNS en local. Le configurer pour transférer toutes les requêtes non-AAAA, mais pour répondre aux requêtes AAAA avec NOERROR ou NXDOMAIN (en fonction du résultat de la requête A correspondante). Je ne connais aucun cache DNS capable de faire cela cependant.
  • Utiliser une correspondance iptables u32 intelligente, ou le module DNS iptables d'Ondrej Caletka iptables DNS module pour correspondre aux requêtes AAAA, afin de les refuser en ICMP (comment la bibliothèque résolveur réagirait-elle à cela ?), ou pour les rediriger vers un serveur DNS local qui répond à tout par un NOERROR vide.

Remarquez qu'il existe des questions similaires et connexes sur SE. Ma question diffère en ce sens qu'elle développe le problème réel que j'essaie de résoudre, qu'elle énumère des exigences explicites, qu'elle met en liste noire certaines solutions souvent suggérées qui ne fonctionnent pas, et qu'elle n'est pas spécifique à une seule application. Suivant cette discussion, j'ai posté ma question.

12voto

Michael Hampton Points 232226

Arrêtez d'utiliser gethostbyname(). Vous devriez utiliser getaddrinfo() à la place, cela fait des années que vous auriez dû le faire. La page de manuel vous met même en garde à ce sujet.

Les fonctions gethostbyname*(), gethostbyaddr*(), herror() et hstrerror() sont obsolètes. Les applications devraient utiliser getaddrinfo(3), getnameinfo(3) et gai_strerror(3) à la place.

Voici un programme d'exemple rapide en C qui démontre la recherche uniquement des enregistrements A pour un nom, ainsi qu'une capture de Wireshark montrant que seuls les recherches d'enregistrements A ont été effectuées sur le réseau.

En particulier, vous devez définir ai_family sur AF_INET si vous voulez uniquement que des recherches d'enregistrements A soient effectuées. Ce programme d'exemple affiche uniquement les adresses IP retournées. Consultez la page de manuel de getaddrinfo() pour un exemple plus complet de comment établir des connexions sortantes.

Dans la capture Wireshark, 172.25.50.3 est le résolveur DNS local; la capture a été réalisée là-bas, vous pouvez donc également voir ses requêtes sortantes et ses réponses. Notez que seul un enregistrement A a été demandé. Aucune recherche AAAA n'a été effectuée.

#include 
#include 
#include 
#include 
#include 
#include 

int main(void) {
    struct addrinfo hints;
    struct addrinfo *result, *rp;
    int s;
    char host[256];

    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = 0;

    s = getaddrinfo("www.facebook.com", NULL, &hints, &result);
    if (s != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
        exit(EXIT_FAILURE);
    }

    for (rp = result; rp != NULL; rp = rp->ai_next) {
        getnameinfo(rp->ai_addr, rp->ai_addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST);
        printf("%s\n", host);
    }
    freeaddrinfo(result);
}

5voto

BMDan Points 7059

Quand on doute, il suffit de se rendre au code source! Alors, voyons... gethostbyname() semble intéressant; cela décrit exactement ce que nous observons : essayer d'abord IPv6, puis passer à IPv4 si vous n'obtenez pas une réponse satisfaisante. Qu'est-ce que ce drapeau RES_USE_INET6 ? En remontant la piste, cela vient de res_setoptions(). C'est ici que le fichier resolv.conf est lu.

Et.... c'est le point final de mes idées. Je ne comprends pas du tout comment le drapeau RES_USE_INET6 est défini si ce n'est pas dans resolv.conf.

3voto

Robert Kerr Points 76

Vous pourriez utiliser BIND en tant que résolveur local, il dispose d'une option pour filtrer AAAA:

https://kb.isc.org/article/AA-00576/0/Filter-AAAA-option-in-BIND-9-.html

0voto

Glueon Points 3474

Avez-vous essayé de configurer PDNS-recursor, de le définir dans votre /etc/resolv.conf et de refuser les recherches "AAAA" dedans? Utilisez quelque chose comme query-local-address6=

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X