Как отключить поиск AAAA?
... чтобы компенсировать сломанные DNS-серверы, которые находятся вне нашего контроля.
Наша проблема: мы внедряем встроенные устройства, которые собирают данные датчиков на различных сайтах, в основном только для IPv4. Некоторые сайты имеют плохо обслуживаемые сети, например, неправильно настроенные или иным образом поврежденные кэши DNS и / или брандмауэры, которые либо полностью игнорируют запросы AAAA, либо отвечают на них с поврежденными ответами (например, неверный IP-адрес источника!). Как внешний поставщик для отдела оборудования, мы почти не влияем на (иногда неохотно) ИТ-отделы. Вероятность того, что они исправят свои DNS-серверы / брандмауэры в ближайшее время, ничтожна.
Влияние на наше устройство состоит в том, что с каждым gethostbyname() процессы должны ждать, пока не истечет время ожидания запросов AAAA, после чего некоторые процессы уже полностью отключили свои попытки подключения.
Я ищу решения, которые...
- общесистемный. Я не могу перенастроить десятки приложений по отдельности
- непостоянный и настраиваемый. Нам нужно (повторно) включить IPv6, где / когда он будет исправлен / развернут. Перезагрузка в порядке.
- Если для решения требуется замена базовой библиотеки, такой как glibc, пакет библиотеки для замены должен быть доступен из хорошо поддерживаемого репозитория (например, Debian Testing, Ubuntu Universe, EPEL). Самостоятельное строительство не является вариантом по многим причинам, поэтому я даже не знаю, с чего начать, поэтому я просто не перечисляю их вообще...
Наиболее очевидным решением было бы настроить библиотеку распознавателя, например, через / etc / { resolv, nsswitch, gai }.conf, чтобы не запрашивать записи AAAA. Опция resolv.conf no-inet6
как предложено здесь будет именно то , что я ищу. К сожалению, это не реализовано, по крайней мере, в наших системах (libc6-2.13-38+deb7u4 в Debian 7; libc6-2.19-0ubuntu6.3 в Ubuntu 14.04)
Так как же тогда? Можно найти следующие методы, предложенные на SF и в других местах, но ни один из них не работает:
- Отключение IPv6 в целом, например путем внесения в черный список ipv6 LKM в /etc/modprobe.d/, или
sysctl -w net.ipv6.conf.all.disable_ipv6=1
, (Из любопытства: почему распознаватель запрашивает AAAA, где IPv6 отключен?) - Удаление
options inet6
из /etc/resolv.conf. Во-первых, его там не было,inet6
просто включен по умолчанию в эти дни. - настройка
options single-request
в /etc/resolv.conf. Это только гарантирует, что запросы A и AAAA выполняются последовательно, а не параллельно - изменения
precedence
в /etc/gai.conf. Это не влияет на DNS-запросы, только на то, как обрабатывается несколько ответов. - Использование внешних распознавателей (или запуск демона локального распознавателя, который обходит поврежденные DNS-серверы) может помочь, но обычно не разрешается политиками брандмауэра компании. И это может сделать внутренние ресурсы недоступными.
Альтернативные уродливые идеи:
- Запустите кеш DNS на локальном хосте. Сконфигурируйте его для пересылки всех не-AAAA-запросов, но для ответа на AAAA-запросы с помощью NOERROR или NXDOMAIN (в зависимости от результата соответствующего A-запроса). Я не знаю, кэш DNS, способный сделать это, хотя.
- Используйте какое-нибудь умное соответствие iptables u32 или модуль iptables DNS Ondrej Caletka для сопоставления запросов AAAA, чтобы либо отклонить их icmp (как отреагирует на это lib lib resolver), либо перенаправить их на локальный DNS-сервер, который отвечает на запросы. все с пустым NOERROR.
Обратите внимание, что есть похожие вопросы по SE. Мой вопрос отличается тем, что он раскрывает реальную проблему, которую я пытаюсь решить, поскольку в нем перечислены явные требования, так как в нем перечислены некоторые часто предлагаемые нерабочие решения, и поскольку он не характерен для отдельного приложения. После этого обсуждения я разместил свой вопрос.
5 ответов
Прекратить использование gethostbyname()
, Вы должны использовать getaddrinfo()
вместо этого и должно было быть в течение многих лет. Страница man даже предупреждает вас об этом.
Функции gethostbyname*(), gethostbyaddr*(), herror() и hstrerror() устарели. Приложения должны использовать getaddrinfo(3), getnameinfo(3) и gai_strerror(3).
Вот быстрый пример программы на C, который демонстрирует поиск только записей A по имени, и захват Wireshark, показывающий, что только поиск записей A проходил по сети.
В частности, вам нужно установить ai_family
в AF_INET
если вы только хотите, чтобы A сделал поиск записей. Этот пример программы печатает только возвращенные IP-адреса. Увидеть getaddrinfo()
Страница man для более полного примера того, как устанавливать исходящие соединения.
В перехвате Wireshark172.25.50.3 - локальный преобразователь DNS; захват был сделан там, так что вы также видите исходящие запросы и ответы. Обратите внимание, что была запрошена только запись А. Поиск AAAA никогда не проводился.
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <netdb.h>
#include <stdio.h>
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);
}
Если вы сомневаетесь, обратитесь к исходному коду! Итак, давайте посмотрим... gethostbyname () выглядит интересно; это точно описывает то, что мы видим: сначала попробуйте IPv6, а затем вернитесь к IPv4, если вы не получили ответ, который вам нравится. Что это RES_USE_INET6
флаг? Возвращаясь к нему, это происходит из res_setoptions (). Это где resolv.conf
читается в.
И.... это я вне идей. Мне совершенно непонятно, как это так RES_USE_INET6
устанавливается, если не в resolv.conf
,
Вы можете использовать BIND в качестве локального распознавателя, у него есть опция для фильтрации AAAA:
https://kb.isc.org/article/AA-00576/0/Filter-AAAA-option-in-BIND-9-.html
Вы можете использовать CoreDNS в качестве локального преобразователя с помощью плагина aaaa . Это специальный плагин, для которого необходимо изменить файл coredns plugin.cfg, а затем пересобрать его ( дополнительную информацию см. здесь ), или вы можете использовать этот форк , скомпилированный с плагином aaaa. (В ghcr есть образ докера ). В качестве альтернативы вы можете использовать официальный CoreDNS с плагином шаблона с этой конфигурацией:
template AAAA {
rcode NXDOMAIN
}
Однакоaaaa
У плагина меньше накладных расходов, если у вас огромная нагрузка на DNS.
Преимущество по сравнению с BIND заключается в том, что coreDNS занимает меньше места, а также вы можете отключить многие из его плагинов по умолчанию и пересобрать его, чтобы иметь еще меньший CoreDNS.
Вы пытались настроить PDNS-рекурсор, установить его в /etc/resolv.conf и запретить поиск в нем "AAAA"? Используя что-то вроде query-local-address6=