сервер имен, FreeBSD и dummynet – так хорошо вместе!

Все началось с того, что кто-то попробовал провести против моего домашнего соединения DoS-атаку, целью которой был перерасход трафика. Неизвестные товарищи для достижения своих коварных замыслов флудили мой (публичный) DNS запросами к серверам имен зоны .zone.

Мои серверы имен отвергали такие запросы, однако использование CPU и канала (8mbit входящий и только 1mbit исходящий) возросло - «отвергнутые» послания кушали исходящий трафик, поэтому я решил поискать способы уменьшить это воздействие фильтрующими средствами.
Атака продолжалась около 10 часов. Сервер имен получил более 1 Gb запросов. Если быть точным, то это был один повторяющийся запрос, который приходил настолько быстро, насколько это позволяла другая сторона. Вся эта радость шла от одного клиента.

Если честно, я до сих пор не могу понять мотивы атаки сервера имен, обслуживающего всего-навсего несколько частных доменов и не содержащего каких-либо важных данных. Это на редкость бестолковое занятие, однако чего только не случается на просторах Cети. Поэтому лучше не задаваться вопросом о мотивах, а подумать о способах защиты.

фильтрация запросов

Самый очевидный способ избавиться от такой напасти – отсечь весь трафик, идущий от неблагонадежного клиента и на этом успокоиться. Ну и, при желании, сообщить куда следует о его безобразном поведении.

Я избрал более перспективный путь, который позволяет противостоять подобным атакам с любого IP. Его смысл заключается в определении для каждого клиента лимита на DNS-трафик. Это значит, что от него будет приниматься только определенное количество запросов, а все остальное будет отброшено.

Мой сервер доменных имен работает под FreeBSD 5.3. В ядре уже присутствуют ipfw и dummynet. Если у вас дела обстоят не так, то вам понадобится загрузить соответствующий модуль. Добавьте в /boot/loader.conf строку

dummynet_load="YES"

Не забывайте, что ipfw по умолчанию запретит любой трафик. Таким образом, вам потребуется задать для ipfw все необходимые правила и убедиться, что они выполняются при загрузке. Если вы не сильно дружите с ipfw, то перед началом работ убедитесь, что у вас в случае чего есть физический доступ к консоли.

После того, как вы внесете правки в loader.conf и удостоверитесь в работоспособности системы при загрузке ipfw, можете перезагрузить машину. Альтернативой вышеописанным действиям может выступать ручной запуск dummynet командой

kldload dummynet

Однако запуск при старте системы в любом случае предпочтительнее.
Я провел в своей локальной сети некоторые измерения и определил, что на моем сервере имен, который обслуживает 12 доменов, для того, чтобы в сети не возникало какого-либо заметного лага, на каждого клиента требуется выделить приблизительно по 4kbit. В системе под FreeBSD это можно настроить с помощью dummynet. Нам понадобится создать канал с нужной полосой, числом слотов и масками source/destination.

ipfw pipe 1 config bw 4kbit/s queue 15 mask src-ip 0xffffffff

Вам также может понадобиться настроить параметры выделяемой памяти (в сегментах). По умолчанию в настройках указано значение 64. Это подходит для большинства случаев, однако если у вас работает действительно масштабный сервер имен, то может потребоваться больше – по одному сегменту на каждого DNS-клиента. Как я уже говорил, я работал с 12 доменами. Если у вас их больше, то надо будет выделить несколько больше ресурсов, чем этого требуют подсчеты. Это необходимо для того чтобы быть уверенным в том, что мы сможем обработать все значимые запросы от клиентов. Нужное число слотов можно приблизительно подсчитать, умножив число обслуживаемых доменов на 1,2. Полоса рассчитывается так: 1024 * число доменов / 3. Не забудьте проверить маску src-ip – она работает с каждым клиентом.

Теперь направляем DNS-трафик в канал:

ipfw add pipe 1 udp from any to me 53 in

Все вышеперечисленное подразумевает, что вы не используете ipfw для каких либо иных целей, кроме описываемых в данной статье. Если это не так, обязательно выставьте вторую строку в нужное место списка правил.

Кроме этого, может потребоваться отключить на файрволле опцию one pass:

sysctl net.inet.ip.fw.one_pass=0

Приведу цитату из мануала на ipfw:
«Пропуск пакета по каналу dummynet(4) (c целью ограничения полосы, задержки и т.п.). Поиск прерывается в любом случае на выходе из канала и в случае, если переменная sysctl(8) net.inet.ip.fw.one_pass не установлена. Пакет снова обрабатывается фаерволлом, начиная со следующего правила». Другими словами, если не выключить one pass, то в том случае, когда пакет подходит под правила канала и никакие другие правила к этому пакету не применяются, то он пройдет файрволл. Вот этого-то нам совсем не надо.

Вместо использования команды sysctl вы можете попробовать применить с аналогичными целями «ipfw disable one_pass».

как это работает?

Это ограничивает полосу, которую может использовать клиент. Более того - ограничивает общее число ожидающих выполнения запросов от клиента. Если клиент отправляет чересчур много запросов и делает это слишком быстро, то излишние запросы будут элементарно проигнорированы.

насколько хорошо это работает?

Убиению подверглись порядка 95% всех входящих запросов от машины, с которой проводилась DoS-атака, ответ был дан только на 5% таких запросов. Ни один из тысяч нормальных запросов от других серверов имен за это время отвергнут не был. На мой взгляд, это можно считать довольно неплохим результатом – влияние DoS-атаки было уменьшено без потерь для нормального трафика. Кроме того, полученный результат не зависел от IP-адреса атакующего. Предлагаемый способ не позволит полностью защититься от масштабной распределенной DoS-атаки, но он, по крайней мере, способен уменьшить ее воздействие на сервер имен.
Если в ходе реализации предложенной схемы вы заметите, что отбрасываются нормальные запросы, подумайте над увеличением числа слотов и выделяемой части канала.

примечания

- Значения для выделяемой части канала и количества слотов основаны на моих личных наблюдениях, а не на серьезных вычислениях. Если у вас другие условия, то выбирайте данные значения исходя из собственной обстановки.

- Выделяемая полоса должна быть немного больше, чем требуется по расчетам. Это надо для того, чтобы быть уверенным в том, что сервер имен сможет обслужить нормальные запросы. Моей целью было отвечать менее чем на 1 запрос из 15 от клиента посылающего флуд на скорости 1 Mbit/s, при этом не затрагивая запросы от нормальных клиентов и не вызывая более чем десятипроцентный рост латентности для подключенных напрямую (через 100- мегабитный Ethernet) клиентов.

- Применять рассматриваемый способ имеет смысл только на публичных серверах имен, которые отвечают только за определенные зоны и не отвечают на запросы к другим доменам. Если ваш сервер имен работает в том числе и на поиск имен хостов для клиентов вашей локальной сети, то вам надо будет удостовериться, что они исключены из фильтра. От таких клиентов за короткое время может поступить значительное количество нормальных запросов, поэтому использование для них описанных ограничений может вызвать ненужные проблемы.

- Данный способ защищает ваш сервер имен и исходящий трафик, но не защищает ваш входящий трафик. Возможно, имеет смысл побеседовать со своим провайдером на предмет проведения соответствующей фильтрации его средствами.

- Все это позволит хорошо защититься от флуда со стороны одиночного клиента, однако возможности такой защиты в случае распределенных атак достаточно скромны.



bart, перевод Алексея Кутовенко.


Сетевые решения. Статья была опубликована в номере 05 за 2005 год в рубрике sysadmin

©1999-2021 Сетевые решения