С неделю тому назад я обратил внимание на то, что через маршрутизатор у нас открывалось огромное количество подключений к одному из резервных серверов. Я бы охотно поверил в то, что так и нужно, если бы не статистика, которая говорила о том, что прежде нагрузка на эту машину не составляла и десятка одновременных подключений, теперь же одновременно открывалось более полутора тысяч подключений. Конечно же, это DDoS, но какой именно?
Маршрутизатор показывал, что подключения открываются на порты 80 и 443, что меня порадовало, так как по этим портам у нас доступна расширенная статистика. Посмотрел на самом сервере и увидел все эти подключения в статусе «SYN-RECV». Бесспорно, это атака типа «SYN-Flood», целью которой является открытие максимального количества подключений, чтобы для легитимных запросов их просто не оставалось. Сама по себе эта атака со стороны атакующего очень не ресурсоемкая и требует всего-то 100Кбит/сек канала доступа в сеть Интернет. Интересным нам показалось другое: DDoS'ить третий по уровню резервный сервер — бессмысленно, так как он вообще может быть отключен из сети до востребования и никто ничего от этого не потеряет. Мы стали думать в сторону того, что эта атака имеет своей целью не вывод из строя нашего оборудования, а нарушение работы того узла, от имени которого идет атака. Аргументами в пользу этой версии послужило и то, что чаще всего адреса, с которых якобы велась атака, вообще не подавали никаких признаков жизни. В основном, это были сервера имен, среди которых был даже сервер имен государственного домена Азербайджана gov.az. Очень уж с трудом мне верится, что наш резервный сервер чем-то не угодил государственным органам Азербайджана.
Сначала мы настроили сервер так, чтобы все эти запросы разворачивались обратно, однако со временем поняли, что наш сервер таким образом может быть использован для атак на другие сервера, поэтому перешли на фильтрацию запросов. Мы увеличили лимит одновременных соединений и уменьшили время жизни полуоткрытых подключений. В результате, такие меры сократили количество висящих запросов с 1600 до пяти. По сути дела, даже 1600 подключений никак не влияло на работу нашего сервера, однако мы решили пойти еще дальше, так как меня немного напрягала непонятная активность. Но для начала давайте рассмотрим метод, которым можно существенно сократить влияние SYN-Flood атаки на ваш сервер.
Для начала проверим, есть ли у нас признаки SYN-Flood. Исполняем в консоли команду подсчета полуоткрытых соединений:
netstat -n | grep SYN_RECV | wc -l
iptables -L
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
После того, как файл отредактирован, сбрасываем текущие правила iptables (на случай, если у вас там в данный момент стоит правило пропускать весь трафик с высоким приоритетом), вызываем rc.local и смотрим, записались ли наши правила.
# iptables -F
# sh /etc/rc.local
# iptables -L
iptables -L -v -n
Если вы поклонник monstrous firewall, то для вас есть вариант с поголовной блокировкой всех, кто будет уличен в атаке SYN-Flood. Такой метод я не рекомендую использовать на тех серверах, где существует большое количество легитимного трафика, так как высока вероятность того, что и некоторые легитимные подключения повиснут в статусе SYN_RECV и будут точно также как и вредоносные заблокированы жестким способом. Блокировать IP-адреса через iptables — дурной тон, так как при большом количестве правил iptables будет воздействовать на ваш сервер еще хуже, чем сама SYN-Flood атака. Для этих ситуаций существует ipset, который нам и нужно будет установить:
# apt-get install ipset
ipset -N blacklist iphash
#!/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
whereis ifconfig/grep/awk/cut/netstat/ipset/iptables/sort/uniq
chmod +x /путь_к_скрипту
Следующим этапом нам нужно добавить этот скрипт в список заданий cron. Частоту запуска определять вам, я рекомендую не чаще раза в пять минут, так как правила из первой части заметки итак не позволят уложить сервер за пять минут, а все, что будет делаться чаще — пустая нагрузка на сервер (хотя все, конечно же, зависит от интенсивности атаки и количества атакующих адресов).
crontab -e
*/5 * * * * /bin/sh /путь_к_скрипту
ipset -L | head
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
Наслаждайтесь спокойствием, пока атакующие вас роботы получают в ответ ICMP port unreachable.