Главная » Сеть и DNS » Ограничение рекурсивных запросов в BIND: allow-recursion, ACL и RRL

Ограничение рекурсивных запросов в BIND: allow-recursion, ACL и RRL

DNS-сервер на BIND с публичным IP, который отвечает на рекурсивные запросы всем подряд, — это «открытый резолвер». Его находят сканеры и используют в атаках DNS-amplification: злоумышленник шлёт короткие запросы с подменённым адресом жертвы, а ваш сервер отправляет ей ответы, которые в разы больше запроса. Канал сервера при этом тоже забивается.

Коротко: на чисто авторитативном сервере выключите рекурсию (recursion no;) и закройте доступ к кэшу (allow-query-cache { none; };). Если сервер одновременно служит резолвером для своей сети, разрешите рекурсию только своей ACL через allow-recursion и allow-query-cache. Для авторитативной части добавьте rate-limit (RRL). Примеры ниже проверены по документации BIND 9.18/9.20.

Рекурсивный и итеративный запрос

Рекурсивный запрос (в заголовке DNS стоит флаг RD) просит сервер выдать окончательный ответ: если данных нет, сервер сам опрашивает корневые серверы, серверы зоны первого уровня и так далее, кэширует результат и возвращает клиенту. Итеративный (нерекурсивный) запрос получает то, что сервер знает сам: данные зоны, за которую он отвечает, или ссылку (referral) на серверы, которые знают больше.

Авторитативному серверу ваших доменов рекурсия не нужна вообще: резолверы провайдеров и публичные DNS обращаются к нему итеративно. Рекурсия нужна только резолверу, которым пользуются ваши же серверы и офис.

Что BIND разрешает по умолчанию

Очень старые версии BIND отвечали рекурсивно кому угодно, отсюда и мода на эту настройку. Сейчас логика такая (раздел allow-recursion в справочнике BIND 9.20):

  • recursion по умолчанию yes;
  • allow-recursion, если не задан, берётся из allow-query-cache, затем из allow-query, а если нет ни того ни другого — { localnets; localhost; };
  • allow-query-cache при recursion no; по умолчанию { none; }, при recursion yes; — значение allow-recursion или { localnets; localhost; }.

Отсюда две частые ошибки. Первая: в options пишут allow-query { any; };, чтобы зоны были видны всем, и не задают allow-recursion — рекурсия по цепочке наследования тоже открывается всем. Вторая: localnets — это все сети, к которым подключены интерфейсы сервера. На VPS с адресом из общей /24 или /22 в «свои» попадают соседи по подсети. Поэтому я всегда задаю ACL явно.

Авторитативный сервер: выключить рекурсию полностью

Где лежит блок options:

  • Debian 12/13, Ubuntu 22.04/24.04 (пакет bind9) — /etc/bind/named.conf.options;
  • AlmaLinux / Rocky Linux 9–10 (пакет bind) — /etc/named.conf.

Добавьте строки внутрь существующего блока options, второй такой блок BIND не примет:

options {
    // ... существующие параметры ...
    recursion no;
    allow-recursion { none; };
    allow-query-cache { none; };
    allow-query { any; };
};

Последние три строки при recursion no; частично избыточны, но явная запись защищает от сюрпризов, если кто-то позже поменяет одну из них. Ключевое слово none пишется без кавычек.

Резолвер только для своих: ACL

Если сервер должен разрешать имена для своих машин, рекурсию не выключают, а ограничивают. Объявите ACL до блока options:

acl "trusted" {
    127.0.0.1;
    ::1;
    192.0.2.0/24;      // офисная сеть
    198.51.100.15;     // отдельный сервер
};

options {
    // ...
    recursion yes;
    allow-recursion { trusted; };
    allow-query-cache { trusted; };
};

Нужны обе директивы: allow-query-cache отвечает за выдачу данных из кэша, allow-recursion — за новые рекурсивные запросы. Для сервера с несколькими интерфейсами есть ещё allow-recursion-on и allow-query-cache-on — они ограничивают локальные адреса, на которых принимается рекурсия. Если вы разносите ответы по представлениям (view), как в статье про BIND и GeoIP2, эти директивы можно задавать и внутри view.

Правильнее всего вообще разнести роли: авторитативный сервер отдельно, резолвер отдельно и слушает только внутренние адреса (listen-on). ISC рекомендует именно так.

Rate limiting (RRL) против DNS-amplification

Даже закрытый от рекурсии авторитативный сервер можно использовать для отражённой атаки: запросы к вашей зоне с подменённым адресом тоже порождают ответы. Для этого в BIND есть Response Rate Limiting — блок rate-limit в options или view:

options {
    // ...
    rate-limit {
        responses-per-second 10;
        window 5;
        log-only yes;
    };
};

Что важно знать по документации:

  • responses-per-second — лимит одинаковых непустых ответов на один адресный блок клиентов; по умолчанию 0, то есть без ограничения. Клиенты группируются по ipv4-prefix-length (по умолчанию 24) и ipv6-prefix-length (по умолчанию 56).
  • window — окно учёта, по умолчанию 15 секунд, допустимо 1–3600.
  • slip (по умолчанию 2) — каждый второй ограниченный ответ отправляется усечённым (TC=1), чтобы настоящий клиент повторил запрос по TCP. Значение 0 — все ограниченные ответы отбрасываются.
  • exempt-clients — адреса, на которые лимит не действует; log-only yes — только писать в лог, ничего не отбрасывая.
  • Ограничиваются только ответы по UDP. ISC указывает, что RRL рассчитан на авторитативные серверы; на резолвере он может замедлить почтовые и веб-серверы, которые часто спрашивают одни и те же имена.

Я включаю RRL сначала с log-only yes, смотрю журнал (journalctl -u named, сообщения категории rate-limit) и только потом убираю эту строку.

Проверка конфигурации и применение

named-checkconf            # Debian/Ubuntu проверяет /etc/bind/named.conf, AlmaLinux — /etc/named.conf
named-checkconf -z         # дополнительно загрузить и проверить зоны

Если команда ничего не вывела — синтаксис в порядке. Применить изменения:

rndc reconfig
# или
systemctl reload named

Служба называется named и в Debian/Ubuntu, и в AlmaLinux (в Debian есть алиас bind9). Если на AlmaLinux используется chroot, служба — named-chroot.

Как проверить результат через dig

Проверять нужно с внешнего адреса, которого нет в ACL. Рекурсивный запрос чужого имени (203.0.113.10 — адрес вашего сервера):

dig @203.0.113.10 example.com A

Правильный результат — status: REFUSED, в флагах нет ra, dig пишет ;; WARNING: recursion requested but not available. BIND 9.18/9.20 добавляет расширенный код ошибки EDE: 18 (Prohibited), а в журнал пишет строку вида query (cache) 'example.com/A/IN' denied (allow-query-cache did not match).

Своя зона при этом должна отвечать нормально, с флагом aa:

dig @203.0.113.10 example.ru SOA +norec

С доверенного адреса тот же запрос example.com должен вернуть NOERROR и флаг ra.

СимптомПричинаРешение
Внешний dig получает ответ на чужое имя с флагом raallow-query { any; } без явного allow-recursion, или клиент попал в localnetsЗадать allow-recursion и allow-query-cache с явной ACL
Свои серверы перестали резолвить именаИх адресов нет в ACL или на этом сервере выключена рекурсияДобавить адреса в ACL или указать им другой резолвер
named не перезапускается после правкиСинтаксическая ошибка, второй блок options, "none" в кавычкахЗапустить named-checkconf и исправить указанную строку
Исходящий трафик DNS резко вырос, рекурсия закрытаОтражённая атака через вашу авторитативную зонуВключить rate-limit

Частые вопросы

Хватит ли одной строки recursion no?

Для авторитативного сервера — да: при recursion no; доступ к кэшу по умолчанию закрыт, и запросы чужих имён получают REFUSED. Явные allow-recursion { none; } и allow-query-cache { none; } я добавляю для наглядности.

Нужен ли RRL, если рекурсия уже закрыта?

Желателен. Закрытие рекурсии убирает самый опасный сценарий, но ответы по вашим зонам, особенно с DNSSEC, тоже можно отражать на жертву. RRL ограничивает именно такие повторяющиеся ответы.