Оптимальная защита от DDoS с помощью netstat и iptables

  • Tutorial
Доброго времени суток!

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

Перекопав полчища статей и опробовав множество вариантов, так и не нашел, что помогло бы с защитой. Взяв за основу статьи Простой и эффективный метод отразить http DDoS от 50мбит с помощью nginx и iptables и (D)DoS Deflate решил написать свой скрипт. Ну вернее не решил, а методом тыка и исправлений он получился сам.

Должен заметить, что статья от Алексея Кузьмина не идеальна, т.к. в логах nginx`a не достаточно копаться, да и обработка логов может потребовать много ресурсов. А именно в моем случае создавались логи более 50 Гиг, плюс запросы шли не «GET / HTTP/1.1», а «GET / HTTP/1.0», плюс, как оказалось, мой сервер сам от себя получал редиректы (127.0.0.1), которые не отображались в логах, которые отображались в запросе
netstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -n


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

Я создавал отдельные файлы, чтобы прослеживать весь ход работы отдельно, и по своей некомпетентности, легко было обнаружить где и что не срабатывало.

Теперь к практике:
создаем каталог, где будет скрипт
mkdir /usr/local/ddos

в нем создаем файл ddos.sh и меняем его права:
chmod 0755 /usr/local/ddos/ddos.sh

записываем в него:

#!/bin/sh

# находим все соединения и записываем их во временный файл ddos.iplist в каталоге tmp
netstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -n > /tmp/ddos.iplist

# очищаем скрипт бана айпишников
cat /dev/null > /tmp/iptables_ban.sh

# создаем DROP правила для 50 самых агрессивных ботов
awk '{if ($1 > 20) {print "/sbin/iptables -I INPUT -p tcp --dport 80 -s " $2 " -j DROP" }}' /tmp/ddos.iplist >> /tmp/iptables_ban.sh

# следующая строка нужна только для того, чтобы создавался файл с просмотром всех правил iptables
echo "/sbin/iptables -L INPUT -v -n > /tmp/iptables.log" >> /tmp/iptables_ban.sh

# запускаем скрипт бана айпишников
bash /tmp/iptables_ban.sh

# делаем ротацию лога
cat /dev/null > /var/log/ddos/error.log
[ ! -f /var/run/nginx.pid ] || kill -USR1 `cat /var/run/nginx.pid`


Вот в принципе и все. Теперь запускаем кронтаб, предпочитаю команду:
EDITOR=mcedit crontab -e

ну или просто
crontab -e

и добавляем новую задачу в него, выполняющуюся каждые 10 минут:
*/10 * * * * /bin/sh /usr/local/ddos/ddos.sh


Также я изменил ротацию логов в файле /etc/logrotate.d/nginx от nginx`a, чтобы многогиговые файлы не создавались
/var/log/nginx/*.log {
	daily
	size 20M
	missingok
	rotate 150
	compress
	delaycompress
	notifempty
	create 640 root adm
	sharedscripts
	postrotate
		[ ! -f /var/run/nginx.pid ] || kill -USR1 `cat /var/run/nginx.pid`
	endscript
}


и записал еще задачу в крон, выполняющуюся каждый час
0 * * * * /usr/sbin/logrotate /etc/logrotate.conf


ну и для больше комфорта еще и раз в сутки решил перезагружать сервак, опять же через крон:
0 4 * * * /sbin/reboot


общий список заданий, выведенный через crontab -l:
*/10 * * * * /bin/sh /usr/local/ddos/ddos.sh
0 * * * * /usr/sbin/logrotate /etc/logrotate.conf
0 4 * * * /sbin/reboot


я записывал все под пользователем root, поэтому если вы не под этим пользователем, перед каждой командой стоит добавить root, типа:
*/10 * * * * root /bin/sh /usr/local/ddos/ddos.sh


Все пути делал абсолютными, т.к. не все команды без полного пути срабатывали.

Надеюсь кому-нибудь пригодится данная статейка. Прошу строго не судить по самому коду, т.к. я вообще впервые сам что-то делал на серваке )
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 26

    +9
    ну и для больше комфорта еще и раз в сутки решил перезагружать сервак, опять же через крон

    Зачем?!
      +7
      Написали же: для еще большего комфорта.
        –2
        Ну до того, как написал свой скрипт, готовые решения не всегда срабатывали и перезагрузка помогала выйти на минуточку из зависания от ддоса и запускать новые вариации скриптов. Ну и здесь, если все-таки система ляжет, то перезагрузка поможет снова запуститься. Мне сложно объяснить зачем это, но вот два логичных объяснения:
        1) перезапуск правил iptables, например если айпишник не от ддоса, а просто глюк какой-нить, тогда перезагрузка поможет пользователю на следующий день попасть на сайт.
        2) если скрипт блокировки айпишников не смог запуститься через 10 минут, т.к. система уже легла от новой атаки, тогда перезагрузка может оживить сервер
        ну и так, для профилактики на одну минутку мона выключать сервер) в 4 часа утра все равно мало кто полезет на сайт ))
        • UFO just landed and posted this here
            0
            это скорее старое обычное действие при зависании системы «а вы пробовали выключить и включить?» ;)
            а вообще он и не нужен, так уж, для переперестарховки (пере — это не опечатка, а усиление акцента)
        0
        А какие на сегодняшний день есть HTTP 1.0 клиенты? Даже links и curl по умолчанию используют 1.1. Можно ли в таких случаях блокировать всех, кто обращается с HTTP 1.0?
          +1
          На самом деле я не знаю что это )
          Но логи соединения именно такие были:
          2013/11/26 13:58:15 [error] 737#0: *16080 limiting connections by zone "perip", client: 91.191.234.194, server: mysite.ru, request: "GET / HTTP/1.0", host: "mysite.ru", referrer: "http://mysite.ru/"
          2013/11/26 13:58:15 [error] 737#0: *16077 limiting connections by zone "perip", client: 91.191.234.194, server: mysite.ru, request: "GET / HTTP/1.0", host: "mysite.ru", referrer: "http://mysite.ru/"
          2013/11/26 13:58:15 [error] 737#0: *16081 limiting connections by zone "perip", client: 91.191.234.194, server: mysite.ru, request: "GET / HTTP/1.0", host: "mysite.ru", referrer: "http://mysite.ru/"
          2013/11/26 13:58:15 [error] 737#0: *16038 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: 91.191.234.194, server: mysite.ru, request: "GET / HTTP/1.0", upstream: "http://127.0.0.1:3000/", host: "mysite.ru", referrer: "http://mysite.ru/"
          2013/11/26 13:58:15 [error] 737#0: *16035 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: 91.191.234.194, server: mysite.ru, request: "GET / HTTP/1.0", upstream: "http://127.0.0.1:3000/", host: "mysite.ru", referrer: "http://mysite.ru/"
          2013/11/26 13:58:15 [error] 737#0: *16023 connect() failed (110: Connection timed out) while connecting to upstream, client: 91.191.234.194, server: mysite.ru, request: "GET / HTTP/1.0", upstream: "http://127.0.0.1:3000/", host: "mysite.ru", referrer: "http://mysite.ru/"
          2013/11/26 13:58:17 [error] 737#0: *16082 limiting connections by zone "perip", client: 91.191.234.194, server: mysite.ru, request: "GET / HTTP/1.0", host: "mysite.ru", referrer: "http://mysite.ru/"
          2013/11/26 13:58:19 [error] 737#0: *16051 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: 91.191.234.194, server: mysite.ru, request: "GET / HTTP/1.0", upstream: "http://127.0.0.1:3000/", host: "mysite.ru", referrer: "http://mysite.ru/"
          2013/11/26 13:58:19 [error] 737#0: *16090 limiting connections by zone "perip", client: 91.191.234.194, server: mysite.ru, request: "GET / HTTP/1.0", host: "mysite.ru", referrer: "http://mysite.ru/"
          
          0
          Под бан попадёт гейт любого провайдера, за которым сидит хотя бы несколько десятков интересующихся вашим сервисом честных пользователей. Низачот, на пересдачу.
            0
            Ну тогда просто поднять планку не 20, а 50 или 100 в строке
            # создаем DROP правила для 50 самых агрессивных ботов
            awk '{if ($1 > 20) {print "/sbin/iptables -I INPUT -p tcp --dport 80 -s " $2 " -j DROP" }}' /tmp/ddos.iplist >> /tmp/iptables_ban.sh
            
              0
              Ну и еще как вариант, скрипт активировать только во время атак, а у меня лично со всего мира редко бывают случаи, когда с одного айпишника идут одновременно больше 10 человек ;)
              +1
              Критерий оптимальности можно услышать?
                0
                Да, конечно, нажми на «Say It» на сайте text-to-speech.imtranslator.net

                А вообще отличие от других скриптов — это захват всех айпишников, которые контачат с сервером, без какой-либо дополнительной нагрузки, типа открывания логов ошибок…
                +2
                При количестве коннектов over 5000 netstat будет тупить от минуты и более.
                  +1
                  Правило iptables на каждый IP тоже не вариант. Для этого был придуман ipset.
                    0
                    у мне было 10К соединений и очень быстро работал нетстат
                      0
                      будет 100К и Линукс устанет считать. Фряха не против, линуксу эта нагрузка в часы пик может боком выйти. Пользуйте ss вместо netstat. Считаю им коннекты для заббикса раз в 30сек практически без оверхеда
                    0
                    Буду рад, если внесете исправление ;)
                    я за наиоптимальнейший способ отражение атаки!
                      0
                      Я извиняюсь, но зачем «awk '{print $5}' | cut -d: -f1», если можно обойтись одним awk или двумя cut?

                      netstat -ntu | awk '{split($5, a, ":"); print a[2];}'
                      
                        0
                        Ну просто такая запись была общепринятой в инете )
                        Везде она в таком виде использовалась
                        0
                        Смотрели в сторону github.com/kyprizel/testcookie-nginx-module?
                          0
                          Я могу ошибаться, но всякие куки, скрипты редиректов, по-моему, отсеивают роботов поискоиковиков…
                            0
                            Тут можно использовать скрипт, переключающий конфиг nginx под нагрузкой. И плюс у можуля есть вайтлист.
                        –1
                        SniFFeR:
                        netstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -n | grep -v "Ваш IP сервера" > /tmp/ddos.iplist
                        — с помощью данной команды можно исключить из проверки на количество подключение IP адрес самого сервера
                          0
                          полезная статья — спасибо
                          немного поправил под себя следующее:

                          # находим все соединения и записываем их во временный файл ddos.iplist в каталоге tmp
                          netstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -n > /tmp/ddos.iplist

                          заменил на
                          # находим все соединения и записываем их во временный файл ddos.iplist в каталоге tmp
                          netstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -nr > /tmp/ddos.iplist

                          это чтобы в файле сначала шли IP с большим количеством обращений — просто для наглядности

                          еще закрывал доступ не просто по 80 порту, а по всем портам и протоколам, так как атака может идти на тот же подбор паролей по SSH или FTP

                          то есть блокировку выполнял так:
                          iptables -A INPUT -s 192.230.77.217 -j DROP
                          iptables -A INPUT -d 192.230.77.217 -j DROP
                            0
                            Дельное замечание! Спасибо!

                          Only users with full accounts can post comments. Log in, please.