SYN-Flood: фильтруем базар

С неделю тому назад я обратил внимание на то, что через маршрутизатор у нас открывалось огромное количество подключений к одному из резервных серверов. Я бы охотно поверил в то, что так и нужно, если бы не статистика, которая говорила о том, что прежде нагрузка на эту машину не составляла и десятка одновременных подключений, теперь же одновременно открывалось более полутора тысяч подключений. Конечно же, это DDoS, но какой именно?


Маршрутизатор показывал, что подключения открываются на порты 80 и 443, что меня порадовало, так как по этим портам у нас доступна расширенная статистика. Посмотрел на самом сервере и увидел все эти подключения в статусе «SYN-RECV». Бесспорно, это атака типа «SYN-Flood», целью которой является открытие максимального количества подключений, чтобы для легитимных запросов их просто не оставалось. Сама по себе эта атака со стороны атакующего очень не ресурсоемкая и требует всего-то 100Кбит/сек канала доступа в сеть Интернет. Интересным нам показалось другое: DDoS'ить третий по уровню резервный сервер — бессмысленно, так как он вообще может быть отключен из сети до востребования и никто ничего от этого не потеряет. Мы стали думать в сторону того, что эта атака имеет своей целью не вывод из строя нашего оборудования, а нарушение работы того узла, от имени которого идет атака. Аргументами в пользу этой версии послужило и то, что чаще всего адреса, с которых якобы велась атака, вообще не подавали никаких признаков жизни. В основном, это были сервера имен, среди которых был даже сервер имен государственного домена Азербайджана gov.az. Очень уж с трудом мне верится, что наш резервный сервер чем-то не угодил государственным органам Азербайджана.

Сначала мы настроили сервер так, чтобы все эти запросы разворачивались обратно, однако со временем поняли, что наш сервер таким образом может быть использован для атак на другие сервера, поэтому перешли на фильтрацию запросов. Мы увеличили лимит одновременных соединений и уменьшили время жизни полуоткрытых подключений. В результате, такие меры сократили количество висящих запросов с 1600 до пяти. По сути дела, даже 1600 подключений никак не влияло на работу нашего сервера, однако мы решили пойти еще дальше, так как меня немного напрягала непонятная активность. Но для начала давайте рассмотрим метод, которым можно существенно сократить влияние SYN-Flood атаки на ваш сервер.

Для начала проверим, есть ли у нас признаки SYN-Flood. Исполняем в консоли команду подсчета полуоткрытых соединений:

netstat -n | grep SYN_RECV | wc -l
Если выводится число, равное или большее десяти, то это признаки SYN-Flood атаки. Прежде, чем чем-то заниматься, просмотрите и запомните все существующие на данный момент правила iptables, так как после проведения описанных в данной заметке манипуляций вы рискуете нарушить работу тех правил, которые настроены сейчас.
iptables -L
Теперь открываем /etc/rc.local и добавляем туда следующие строки:
echo "20000" > /proc/sys/net/ipv4/tcp_max_syn_backlog
echo "1" > /proc/sys/net/ipv4/tcp_synack_retries
echo "30" > /proc/sys/net/ipv4/tcp_fin_timeout
echo "5" > /proc/sys/net/ipv4/tcp_keepalive_probes
echo "15" > /proc/sys/net/ipv4/tcp_keepalive_intvl
echo "20000" > /proc/sys/net/core/netdev_max_backlog
echo "20000" > /proc/sys/net/core/somaxconn
iptables -N syn_flood
iptables -A INPUT -p tcp --syn -j syn_flood
iptables -A syn_flood -m limit --limit 500/s --limit-burst 1500 -j RETURN
iptables -A syn_flood -j DROP
iptables -A INPUT -p tcp -m state --state NEW -m recent --update --seconds 60 --hitcount 20 -j DROP
iptables -A INPUT -p tcp -m state --state NEW -m recent --set -j ACCEPT
iptables -t mangle -I PREROUTING -p tcp -m tcp --dport 80 -m state --state NEW -m tcpmss ! --mss 536:65535 -j DROP
iptables -t mangle -I PREROUTING -p tcp -m tcp --dport 443 -m state --state NEW -m tcpmss ! --mss 536:65535 -j DROP
Эти строки обеспечивают увеличение количества доступных соединений, уменьшаю время жизни полуоткрытых соединений, фильтруют откровенные SYN-Flood атаки и сбрасывают все неестественные запросы. Ключевое слово тут «фильтруют», то есть в полной мере доступ атакующим не будет закрыт, они только будут ограничены. Оптимальный вариант для production, обеспечивающий бесперебойный доступ для легитимных запросов.

После того, как файл отредактирован, сбрасываем текущие правила iptables (на случай, если у вас там в данный момент стоит правило пропускать весь трафик с высоким приоритетом), вызываем rc.local и смотрим, записались ли наши правила.

# iptables -F
# sh /etc/rc.local
# iptables -L
Через пару минут проверьте, имеют ли эти правила какой-то эффект. Для этого запускаем команду, которая указана ниже, и смотрим, сколько пакетов и байт трафика осело на конкретных правилах.
iptables -L -v -n
Если указанные нами в /etc/rc.local правила ничего на себя не перехватывают, значит там что-то не подходит под вашу конкретную ситуацию, так как у нас все эти правила через минуту имели уже по тысяче пакетов на DROP.

Если вы поклонник monstrous firewall, то для вас есть вариант с поголовной блокировкой всех, кто будет уличен в атаке SYN-Flood. Такой метод я не рекомендую использовать на тех серверах, где существует большое количество легитимного трафика, так как высока вероятность того, что и некоторые легитимные подключения повиснут в статусе SYN_RECV и будут точно также как и вредоносные заблокированы жестким способом. Блокировать IP-адреса через iptables — дурной тон, так как при большом количестве правил iptables будет воздействовать на ваш сервер еще хуже, чем сама SYN-Flood атака. Для этих ситуаций существует ipset, который нам и нужно будет установить:

# apt-get install ipset
Прежде всего, создаем таблицу ipset:
ipset -N blacklist iphash
Наша таблица будет называться «blacklist», но фактически ее можно будет назвать как душа пожелает, главное потом менять имя во всех командах дальше по тексту. Вторым делом пишем скрипт, который будет запрашивать статистику по SYN_RECV соединениям и блокировать провинившихся. Скрипт можно сохранить куда угодно, но обязательно отдельным скриптом, то есть, внесение в rc.local нам не подходит.
#!/bin/bash
IFCONFIG=/sbin/ifconfig
GREP=/bin/grep
AWK=/usr/bin/awk
CUT=/usr/bin/cut
NETSTAT=/bin/netstat
IPSET=/usr/sbin/ipset
IPTABLES=/sbin/iptables
SORT=/usr/bin/sort
UNIQ=/usr/bin/uniq

srvIP=`$IFCONFIG eth0 | $GREP 'inet addr' | $AWK '{print $2}' | $CUT -f2 -d ":"` for i in `$NETSTAT -ntu | $GREP SYN_RECV | $AWK '{print $5}' | $CUT -f1 -d ":" | $SORT | $UNIQ | $GREP -v ${srvIP}`
do
$IPSET -A blacklist $i
done
$IPSET -S > /var/log/ipset.log
Переменные в первой части подогнаны под Debian, так что если у вас другой дистрибутив, то лучше проверить местоположение пакетов при помощи
whereis ifconfig/grep/awk/cut/netstat/ipset/iptables/sort/uniq
естественно, по очереди, а не вместе, как написал я для наглядности. Теперь делаем скрипт исполняемым при помощи chmod:
chmod +x /путь_к_скрипту

Следующим этапом нам нужно добавить этот скрипт в список заданий cron. Частоту запуска определять вам, я рекомендую не чаще раза в пять минут, так как правила из первой части заметки итак не позволят уложить сервер за пять минут, а все, что будет делаться чаще — пустая нагрузка на сервер (хотя все, конечно же, зависит от интенсивности атаки и количества атакующих адресов).
crontab -e
*/5 * * * * /bin/sh /путь_к_скрипту
Теперь раз в пять минут скрипт будет собирать статистику и блокировать новые IP-адреса. Узнать, какие адреса были заблокированы, вы можете либо в файле /var/log/ipset.log, либо при помощи следующей команды:
ipset -L | head
Последнее, что нам осталось сделать — это направить трафик на определенные порты таким образом, чтобы он проходил фильтрацию через созданный нами черный список. Прямо таки все порты туда направлять не стоит, так как это лишняя нагрузка и лишний риск вам самому остаться без доступа к серверу. Определяем интересующие атакующих порты (у меня это 80/443) и добавляем в rc.local соответствующее правило:
iptables -I INPUT -m set --match-set blacklist src -p TCP --destination-port 80 -j REJECT
iptables -I INPUT -m set --match-set blacklist src -p TCP --destination-port 443 -j REJECT
Далее уже известным нам по первой части статьи способом сбрасываем iptables, а затем исполняем /etc/rc.local. Через несколько минут (в зависимости от того, с какой частотой вы поставили запуск скрипта фильтрации) проверяем, заблокировались ли какие-то IP-адреса и пошел ли на соответствующие правила iptables какой-то трафик.

Наслаждайтесь спокойствием, пока атакующие вас роботы получают в ответ ICMP port unreachable.


18.01.2015, 15:27
  Linux, администрирование, отражение атак.
Просмотров: 3695.