Опыт противодействия DDoS

Первые впечатления
Определение наличия DDoS
Настройка linux для работы при большой сетевой нагрузке
Обнаружение участвующих в атаке ip адресов
Блокировка атакующих ip адресов
Параметры сервера, оценка ресурсов
Выводы
Приложения

Первые впечатления

Когда ddos начался, с непривычки было непонятно, что происходит:
в access_log веб-сервера понемногу добавлялись новые запросы,
но при попытке открыть сайт браузером я получал отказ.

Поскольку с ddos раньше не сталкивался, то первые несколько часов
думал, что какие-то сетевые проблемы (провайдер, где стоит
проксирующий http-сервер, иногда имеет проблемы со связностью
до провайдера, где стоит главный http-сервер). 

Когда стало ясно, что проблема более серьезная,
запустил tcpdump и увидел большую сетевую активность.

Определение наличия DDoS

Как выяснилось позже, наличие DDoS проще всего определить,
установив скорость поступающих по внешнему интерфейсу пакетов,
в linux это проще всего сделать чтением из /proc/net/dev:

root@laylah ~ #cat /proc/net/dev Inter-| Receive | Transmit face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed lo:59286813 278952 0 0 0 0 0 0 59286813 278952 0 0 0 0 0 0 eth0:38433436 575324 0 0 0 0 0 0 64005875 101279 0 0 0 0 0 0 eth1: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 eth2: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 eth3: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 eth4:160842629303 1968355293 0 9282641 689519 0 0 0 175815679927 464227952 0 0 0 0 0 0

На основании поля "Receive packets" можно рассчитать скорость поступающих на интерфейсов пакетов, для моего сервера в нормальных условиях эта цифра колеблется от 100 до 1000 пакетов в секунду. Во время же DDoS цифра за несколько десятков секунд достигает значений в десятки тысяч пакетов в секунду и продолжает держаться, пока продолжается атака.

Пример изменения скорости входящих пакетов при начале DDoS атаки

Sat Dec 12 13:31:58 2009: 202
Sat Dec 12 13:31:59 2009: 261
Sat Dec 12 13:32:00 2009: 206
Sat Dec 12 13:32:01 2009: 180
Sat Dec 12 13:32:02 2009: 188
Sat Dec 12 13:32:03 2009: 184
Sat Dec 12 13:32:04 2009: 190
Sat Dec 12 13:32:05 2009: 112
Sat Dec 12 13:32:06 2009: 284
Sat Dec 12 13:32:07 2009: 284
Sat Dec 12 13:32:08 2009: 250
Sat Dec 12 13:32:09 2009: 264
Sat Dec 12 13:32:10 2009: 246
Sat Dec 12 13:32:11 2009: 192
Sat Dec 12 13:32:13 2009: 118
Sat Dec 12 13:32:14 2009: 464
Sat Dec 12 13:32:15 2009: 1173
Sat Dec 12 13:32:16 2009: 1252
Sat Dec 12 13:32:17 2009: 3446
Sat Dec 12 13:32:18 2009: 2466
Sat Dec 12 13:32:19 2009: 3693
Sat Dec 12 13:32:20 2009: 5035
Sat Dec 12 13:32:21 2009: 3761
Sat Dec 12 13:32:22 2009: 2685
Sat Dec 12 13:32:23 2009: 3439
Sat Dec 12 13:32:24 2009: 6848
Sat Dec 12 13:32:25 2009: 12287
Sat Dec 12 13:32:26 2009: 12611
Sat Dec 12 13:32:27 2009: 13721
Sat Dec 12 13:32:28 2009: 12845
Sat Dec 12 13:32:29 2009: 12156
Sat Dec 12 13:32:30 2009: 11656
Sat Dec 12 13:32:31 2009: 12708
Sat Dec 12 13:32:32 2009: 13890
Sat Dec 12 13:32:33 2009: 16133
Sat Dec 12 13:32:34 2009: 19523
Sat Dec 12 13:32:35 2009: 19457
Sat Dec 12 13:32:36 2009: 12814
Sat Dec 12 13:32:37 2009: 12699
Sat Dec 12 13:32:38 2009: 11544
Sat Dec 12 13:32:39 2009: 11999
Sat Dec 12 13:32:40 2009: 11553
Sat Dec 12 13:32:41 2009: 12775
Sat Dec 12 13:32:42 2009: 12510
Sat Dec 12 13:32:43 2009: 13831
Sat Dec 12 13:32:44 2009: 15287
Sat Dec 12 13:32:45 2009: 14523
Sat Dec 12 13:32:46 2009: 14575
Sat Dec 12 13:32:47 2009: 13435
Sat Dec 12 13:32:48 2009: 13682
Sat Dec 12 13:32:49 2009: 14495
Sat Dec 12 13:32:50 2009: 17310
Sat Dec 12 13:32:51 2009: 18253
Sat Dec 12 13:32:52 2009: 16196
Sat Dec 12 13:32:53 2009: 15136
Sat Dec 12 13:32:54 2009: 15249
Sat Dec 12 13:32:55 2009: 15542
Sat Dec 12 13:32:56 2009: 15279
Sat Dec 12 13:32:57 2009: 14835
Sat Dec 12 13:32:58 2009: 14478
Sat Dec 12 13:32:59 2009: 15961
Sat Dec 12 13:33:00 2009: 16179
Sat Dec 12 13:33:01 2009: 18295
Sat Dec 12 13:33:02 2009: 21040
Sat Dec 12 13:33:03 2009: 19100
Sat Dec 12 13:33:04 2009: 17054
Sat Dec 12 13:33:05 2009: 19715

Дневной график роутера

9-10 декабря 2009, нет DDoS (в конце графика начало атаки)


10-11 декабря 2009, всю ночь шла DDoS


Настройка linux для работы при большой сетевой нагрузке

Сетевые настройки по умолчанию не очень хорошо подходят
для работы при больших нагрузках, их нужно поправить
(ниже не окончательный вариант, как минимум
не решена проблема Out of socket memory,
возможно, поможет установка параметра
/proc/sys/net/ipv4/tcp_tw_recycle,
но это еще не проверено)
# увеличение буферов (ring buffer) интерфейса,
# включение tcp checksum offloading,
# по совету Влада Селиверстова:
ethtool -G eth4 rx 4096 tx 4096
ethtool -K eth4 tso on

sysctl -w net.ipv4.tcp_max_syn_backlog=65536

# Увеличение памяти, доступной tcp стеку, по мотивам
# http://wwwx.cs.unc.edu/~sparkst/howto/network_tuning.php
# !!! memory pages; page size: getconf PAGESIZE
#sysctl -w net.ipv4.tcp_mem="32768 32768 32768" # 128MB
sysctl -w net.ipv4.tcp_mem="131072 131072 131072" # 512MB

# http://www.susegeek.com/networking/network-performance-fine-tuning-in-opensuse-suse/
sysctl -w net.ipv4.tcp_no_metrics_save=1

# чтобы установленные выше параметры
# вступили в силу как можно скорее
sysctl -w net.ipv4.route.flush=1

# включение tcp syn cookies (против syn flood)
echo 1 > /proc/sys/net/ipv4/tcp_syncookies

# увеличение таблицы conntrack
echo 224000 > /proc/sys/net/ipv4/netfilter/ip_conntrack_max

# уменьшение timeout-ов
echo 1 > /proc/sys/net/ipv4/tcp_synack_retries
echo 1 > /proc/sys/net/ipv4/tcp_syn_retries
echo 1 > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_timeout_last_ack
echo 1 > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_timeout_syn_recv
echo 1 > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_timeout_fin_wait

Обнаружение участвующих в атаке ip адресов

Обнаружение атакующих ip адресов я делал на основании access_log
проксируюющего веб-сервера, получая в режиме реального информацию
об обращающихся к серверу ip адресах. С момента начала атаки
(резкого роста скорости входящего трафика) до момента стабилизации
ситуации (когда пользователи могли в половине случаев
получить доступ к контенту веб-сервера) обычно проходило
от 1 до 2 часов.

Выявление атакующих ip адресов я делал на основании следующих алгоритмов.

Обнаружение атакуемого URL

URL считается атакуемым, если на протяжении последних 30 секунд к нему обращались хотя бы раз в 26 или более секунд (при этом абсолютное число обращений на этом этапе не учиывается). Понятно, что для разных серверов этот параметр будет плавающим -- ключевым является проведение черты между нормальной нагрузкой и нагрузкой в условиях атаки. После обнаружения атакуемого URL полезно "закрывать" его на веб-сервере так, чтобы он отдавался максимально быстро, я делал это путем возврата HTTP 503. Можно также делать статическую копию атакуемого URL (если она будет небольшой по размеру) или "переименовывать" на время оригинальную страницу, а по атакуемом адресу возвращать объяснение ситуации и ссылку на переименованную страницу. URL перестает считаться атакуемым, если вышеописанное условие не выполнялось для него ни разу за последние 30 минут -- после этого его можно "открывать" для всех.

Обнаружение вяло атакующих ip адресов

Для каждого ip адреса включается счетчик обращений к атакуемым URL; если ip адрес обратится более 15 раз к атакуемым url, то я считал его атакующим. При этом если до достижения порогового значения ip адрес обращался к неатакуемому url, то он вносился на 300 секунд в whitelist, по истечении 300 секунд алгоритм подсчета обращений к атакуемому URL запускался заново (счетчик обнулялся). При большом количестве атакующих машин этот процесс, растягивается на продолжительное время (чем больше атакующих машин, тем больше времени понадобится, чтобы их выявить).

Обнаружение агрессивно атакующих ip адресов

Критерием агрессивной атаки я выбрал X > 4 * Y, где X -- количество обращений от ip адреса к серверу за предыдущую секунду, а Y -- количество разных url, запрошенных этим же ip адресом с сервера за ту же секунду. То есть, если ip адрес запросил за последнюю секунду один URL пять или более раз, то он атакует, если меньше пяти раз, то не атакует. Если ip адрес запросил за последнюю секунду два разных URL суммарно девять или более раз, то он атакует, если меньше девяти раз, то не атакует. И так далее. Таким образом учитывается обычный режим работы пользователя с сайтом: чаще всего пользователь в одну секунду запрашивает html файл и используемые в нем картинки, css, javascript -- это может быть до 15 запросов в секунду, но все 15 url будут разными (а атакующий ip адрес в моем случае 15 раз запрашивал один и тот же url).

Обнаружение умеренно атакующих ip адресов

Возможен также некий средний по отношению к двум предыдущим способ поведения атакующего, когда он не считается вяло атакующим, потому что иногда запрашивает "неатакуемые" url, и не считается агрессивно атакующим, потому что не делает более 2-3 запросов в секунду. Для обнаружения таких ip адресов я использовал формулу X > 2 * Y, где X -- суммарное количество обращений ip адреса к серверу за последние 30 секунд, а Y -- количество секунд за последние 30 секунд, в которые данный ip адрес обращался к серверу хотя бы раз. При этом формула применялась только для тех ip адресов, для которых Y был больше 26. Важным моментом в этом алгоритме является то, что под него попадает поведение большинства поисковых роботов, поэтому для дополнительной надежности я делал проверку PTR записи и не считал атакующими ip адреса, принадлежащие большим поисковым компаниями (типа yandex, google, yahoo, mail.ru).

Блокировка атакующих ip адресов

ip адреса, определенные как атакующие,
по любому из вышеперечисленных алгоритмов,
заносились в iptables как адреса, от которых
не принимается никакой трафик.

Линейный перебор

Сначала я заносил все атакующие адреса в таблицу INPUT, что приводило к такого вида таблице:

-A INPUT -s 70.45.85.229/32 -j DROP -A INPUT -s 200.138.47.57/32 -j DROP -A INPUT -s 189.4.53.142/32 -j DROP -A INPUT -s 201.66.199.79/32 -j DROP -A INPUT -s 62.139.191.250/32 -j DROP -A INPUT -s 187.2.255.74/32 -j DROP -A INPUT -s 190.166.157.186/32 -j DROP -A INPUT -s 69.79.164.210/32 -j DROP -A INPUT -s 59.94.250.211/32 -j DROP -A INPUT -s 189.220.189.73/32 -j DROP -A INPUT -s 200.163.52.146/32 -j DROP -A INPUT -s 190.48.174.83/32 -j DROP -A INPUT -s 41.210.9.67/32 -j DROP -A INPUT -s 117.18.229.8/32 -j DROP -A INPUT -s 41.214.205.171/32 -j DROP -A INPUT -s 41.189.39.240/32 -j DROP -A INPUT -s 41.239.22.225/32 -j DROP -A INPUT -s 190.59.53.242/32 -j DROP -A INPUT -s 122.161.58.106/32 -j DROP -A INPUT -s 85.9.119.10/32 -j DROP -A INPUT -s 189.74.118.168/32 -j DROP -A INPUT -s 122.173.219.232/32 -j DROP -A INPUT -s 41.202.76.107/32 -j DROP -A INPUT -s 95.84.101.89/32 -j DROP -A INPUT -s 222.252.120.15/32 -j DROP -A INPUT -s 187.22.95.128/32 -j DROP -A INPUT -s 189.195.73.181/32 -j DROP -A INPUT -s 201.153.129.252/32 -j DROP -A INPUT -s 122.162.80.113/32 -j DROP -A INPUT -s 200.141.94.236/32 -j DROP -A INPUT -s 188.54.122.155/32 -j DROP

С ростом базы блокируемых ip адресов таблица увеличивалась, из-за чего увеличивалось время проверки каждого поступающего пакета. При превышении определенного количества блокируемых ip адресов и превышении определенной скорости поступающих пакетов машина переставала справляться с нагрузкой. Это выглядело, как очень длинные задержки при работе по ssh вплоть до невозможности подключения, а также очень сильные задержки при работе с консолью, кроме того в top висел процесс ksoftirqd/0 (или ksoftirqd/1, ksoftirqd/2, ksoftirqd/3). По совету Владимира Неверова я установил irqbalance, но это ситуацию не исправило. Озвучивалась также версия, что данное поведение проявляется, когда сетевая карта перестает справляться с входящим потоком, однако версия не оправдалась, потому что внесение первым пунктом в таблицу INPUT iptables правила

-A INPUT -p tcp -m tcp --dport 80 -j DROP

моментально исправляло положение.

Линейный перебор по октетам

Дмитрий Тейблюм посоветовал изменить порядок блокировки атакующих ip адресов следующим образом. Для блокировки ip адреса A.B.C.D в iptables создаются: 1) в таблице INPUT правило -s A.0.0.0/8 -j A 2) таблица A с правилом -s A.B.0.0/16 -j A_B 3) таблица A_B с правилом -s A.B.C.0/24 -j A_B_C 4) таблица A_B_C с правилом -s A.B.C.D/32 -j DROP При таком способе блокировки ip адресов при любом размере базы скорость проверки ip адреса не превышает 4 выборов из таблицы размером 255 элементов. Это оказывается заметно эффективней, чем линейный перебор таблицы в 10,000 ip адресов.

Параметры сервера, оценка ресурсов

Для работы с DDoS использовался сервер на базе
Intel Xeon E5310 quad-core с 8 гигабайтами оперативной памяти,
встроенной сетевой картой на чипсете Intel 80003ES2LAN (linux pci id 8086:1096),
PCI Express сетевой картой на чипсете Intel 82576 (linux pci id 8086:10e8),
linux 2.6.31-gentoo-r5.

Сервер адекватно работал на скоростях до 60,000
входящих пакетов в секунду и базой блокируемых
ip адресов порядка 10,000 (на больших цифрах
не удалось проверить, но есть предположение,
что при использовании схемы Тейблюма
потолок заметно выше).

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

Например, для подавления атаки со 100,000 ip адресов
скоростью в 600,000 пакетов в секунду потребуется
10 серверов описанного типа и один высокопроизводительный балансер,
поддерживающий скорость 600,000 пакетов в секунду на одном интерфейсе.

Выводы

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

При ограниченности доступных на подавление атаки ресурсов
(ширина канала подключения, стоимость входящего трафика,
количество отражающих атаку серверов) крайне полезным
оказывается ограничение на скорость входящих TCP SYN пакетов,
впрочем это ограничение пропорционально уменьшает
как количество паразитного трафика, так и количество
полезного трафика.

Приложения

* список атаковавших ip адресов, ~5000 атакующих адресов, 4 декабря 2009
* iptables-save, ~9000 атакующих адресов, линейный перебор, 11 декабря 2009
* iptables -n -L -v, ~9000 атакующих адресов, схема Тейблюма, 14 декабря 2009
* iptables-save, ~7000 атакующих адресов, схема Тейблюма, 19 декабря 2009
* realtime_ddos.pl, программа обнаружения и подавления ddos (по логам apache и nginx)

Обсуждение

* LJR
* LJ

--
petya@kohts.ru, 2009