ServerAdmin.ru
31.6K subscribers
851 photos
57 videos
23 files
3K links
Авторская информация о системном администрировании.

Информация о рекламе: @srv_admin_reklama_bot
Автор: @zeroxzed

Второй канал: @srv_admin_live
Сайт: serveradmin.ru

Ресурс включён в перечень Роскомнадзора
Download Telegram
На днях случился необычный для меня инцидент. Расскажу, как его преодолел. Сразу скажу, что это будет не инструкция, как надо делать, а то, как поступил я в конкретной ситуации. У меня есть в управлении одиночный сервер, где одна из виртуальных машин выполняет роль Nginx Proxy для других сервисов. Кто-то эту прокси решил ддоснуть. Немного растерялся от ситуации, так как сталкиваешься с ней нечасто и выверенного алгоритма действий нет.

Я не знаю, кем и для чего это делалось. Виртуалка проксировала доступ на неприметный публичный сайт, который давно не обновлялся и работал для галочки. Также проксировались соединения на мониторинг, 1С и некоторые другие внутренние сервисы для доступа к ним извне. На виртуалке внутри не было никакой защиты от ddos, внешняя, соответственно, тоже подключена не была. В этом не было необходимости. Ничего критичного там не стояло.

От мониторинга прилетел алерт на недоступность некоторых ресурсов, и на высокий LA виртуалки. По SSH хоть и небыстро, но подключился к ней. Заодно сразу зашёл на гипервизор. Он нормально переваривал нагрузку. Увидел, что на виртуалке CPU на четырёх ядрах в потолке. И все ядра нагрузил Nginx. Голым Nginx нагрузить практически пустыми соединениями на 100% 4 CPU не так просто. Я думал, что сейчас провайдер просто отрубит сервак и на этом всё закончится. Но нет, он переваривал, так что я стал разбираться на сервере локально.

Глянул лог Nginx и увидел массовые обращения на один конкретный URL публичного сайта с разных IP адресов. Вообще не понял, причём тут этот URL и почему в него долбят. Первое, что сделал, вообще отключил этот сайт. Nginx стал отдавать 502 ошибку, но серверу это слабо помогло.

Потом добавил параметр

limit_conn_zone $binary_remote_addr zone=perip:10m;

в nginx.conf и

limit_conn perip 50;

в настройки виртуального хоста. Хотел собирать айпишники тех, кто открывает более 50 соединений к сайту, чтобы потом их банить. Но там особо не собиралось, плюс лог ошибок рос с огромной скоростью, трудно было из него что-то выбирать. Виртуалка и так сильно тормозила. IP постоянно менялись, одновременных запросов, которые бы ловились Nginx, не было.

Решил мимо Nginx напрямую с iptables работать. У меня есть в закладках команда, которая сортирует и считает соединения с каждого IP адреса:

# netstat -ntu | awk '{print $5}' | grep -vE "(Address|servers|127.0.0.1)" | cut -d: -f1 | sort | uniq -c | sort -n| sed 's/^[ \t]*//'

Тут уже было хорошо видно адреса, которые открыли много соединений. Немного изменил скрипт, чтобы выводить чистые списки IP адресов, открывших более 50-ти соединений и сохранять их в файл:

# netstat -ntu | awk '{print $5}' | grep -vE "(Address|servers|127.0.0.1)" | cut -d: -f1 | sort | uniq -c | sort -n| sed 's/^[ \t]*//' | awk '{if ($1 > 50 ) print$2}' > ~/top_connect.txt

Дальше создал список ipset для использования списка IP адресов в правилах iptables. Напрямую большие списки в iptables направлять не надо, он их может не переварить.

# ipset -N top_connect nethash

Закинул туда список пойманных ранее IP адресов:

# cat ~/top_connect.txt | xargs -n1 ipset -A top_connect

И сделал правило в iptables для блокировки из этого списка:

# iptables -I INPUT 1 -m set --match-set top_connect src -j DROP

Блокировка заработала, счётчики правила пошли вверх, но помогло это слабо. Адреса постоянно менялись. Собрал всё это в скрипт и запускал по крону раз в минуту, тоже не помогло.

Решил забанить всех, кто обращается к урлу, который ддосят. Забирал IP ардеса, чтобы сильно не грузить сервак, так:

# tail -1000 /var/log/nginx/access.log | egrep "GET /url-for-ddos/" | awk '{print $1}' | sort -n | uniq -c | sort -n | tail -n100 | awk '{if ($1 > 2 ) print $2}' > ~/top_get.txt

Собрал их в ipset, добавил правило блокировки:

# ipset -N top_get nethash
# iptables -I INPUT 1 -m set --match-set top_get src -j DROP


Продолжение в следующей заметке 👇👇👇👇

#ddos #nginx #iptables
1👍107👎2
После прошлой истории с ddos захотелось проработать какое-то готовое универсальное решение, чтобы можно было его быстро применить, а не придумывать разные способы. В комментариях люди писали, что надо было установить fail2ban, какой-нибудь WAF или что-то в этом роде. Это всё слабо поможет, если идёт большая нагрузка. Fail2ban сразу мимо, забываем про него. А WAF или какой-то фильтрующий сервер надо настраивать заранее, а не когда у вас единственную машину ддосят.

Я взял наиболее простой и быстрый с точки зрения производительности вариант: ipset + iptables. Если ваш канал и сервер в целом вывозят нагрузку и вас не блокирует провайдер, указанная связка будет наиболее лёгкой и эффективной с точки зрения потребления ресурсов.

Сначала хотел что-то простое набросать, но потом всё расширял и расширял возможности. В итоге родился довольно большой bash скрипт с некоторыми проверками и логированием. Рассказываю, что он делает:

1️⃣ Проверяет установку ipset, создаёт списки.
2️⃣ Проверяет установку rsyslog, чтобы логировать заблокированные подключения. Это нужно на момент отладки и в обычное время, когда нет ддоса. Если начали ддосить, логирование надо отключить.
3️⃣ Создаёт конфигурацию rsyslog для хранения логов в отдельных файлах. Настраивает ротацию через logrotate.
4️⃣ Дальше идут переменные, касающиеся правил iptables: имя и ip адрес WAN интерфейса, белый список IP, с которых разрешено всё, разрешённые TCP и UDP порты, на которые мы принимаем соединения. Всё остальное блокируем.
5️⃣ Дальше идут наборы правил:
- очистка всех правил
- запрет всего, что не разрешено явно
- разрешаем установленные соединения
- разрешаем localhost и icmp
- разрешаем исходящие соединения сервера
- разрешаем все соединения от белого списка
- блокируем неопознанные и нулевые пакеты
- блокируем новые соединения без статуса NEW
- блокируем списки ipset, которые формируются ниже
- добавляем в список тех, кто открывает более 10-ти соединений с сервером в течении секунды, логируем таких товарищей
- разрешаем доступ к открытым портам, в данном примере это порты 80, 443 TCP и 443 UDP
- тех, кто обращается к другим портам, добавляем в список ipset
- сохраняю все правила в файл

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

Для проверки работы надо установить на сервере iptables, ipset, rsyslog:

# apt install iptables ipset rsyslog

Скопировать и заполнить переменные в bash скрипте. Особое внимание уделить переменным WAN, WAN_IP, WLIST, WPORT_TCP, WPORT_UDP. На основе них будут сформированы правила iptables. Отдельного правила для SSH я не делал. Подразумевается, что подключаться куда-либо кроме открытых 80 и 443 портов можно только из списка WLIST. Туда можно добавлять IP адреса или подсети через запятую.

После выполнения скрипта, смотрим правила iptables:

# iptables -L -v -n

Проверял работу так. Установил веб сервер. Попробовал с другой машины подключиться к серверу через telnet на любой отличный от HTTP порт:

# telnet 85.143.173.182 23

Получил бан с занесением в список BANPORT. Смотреть этот список так:

# ipset -L BANPORT

Далее установил ab и попробовал спам запросов в 50 потоков к веб серверу:

# ab -c 50 -n 30000 "http://85.143.173.182/"

Получил бан с занесением в список BANDDOS. Оба бана были отражены в лог файлах ipset_portscan.log и ipset_ddos.log. При этом если ничего из описанного выше не делать, а просто ходить на веб сайт, доступ открыт, всё работает нормально.

⚡️На работающем сервере запускать такие скрипты не рекомендую, особенно если нет доступа к консоли. Сначала проверьте и отладьте на тестовой виртуалке. Убедитесь, что не отрубаете себе доступ. В целом, тут небольшой и относительно простой набор правил.

Забрать скрипт iptables.sh

❗️Если заметка вам полезна, не забудьте 👍 и забрать в закладки.

#iptables #ipset #ddos
Please open Telegram to view this post
VIEW IN TELEGRAM
3👍183👎2