Всем привет!
Для начала краткая теория-шпаргалка:
Какие бывают DDoS атаки?По типу объекта, который выводится из строя можно выделить четыре основных класса атак, осуществляемых на разных уровнях согласно модели OSI:
Первый класс (L2) — «забивание» канала. Эти атаки направлены на лишение доступа сервера к внешней сети вследствие исчерпания ширины его канала. Чаще всего в таких случаях используются массированные, с точки зрения количества трафика, атаки типа Amplification (NTP-, DNS-, RIP-… Amplification может быть любой). Основная задача состоит в том, чтобы канал шириной, например, 1 гигабит/с залить хотя бы 1,1 гигабит/с. Этого будет достаточно для прекращения доступа к серверу.
К данному классу атак также относятся различные flood’ы:
- SYN-флуд (TCP/SYN) устанавливает полуоткрытые соединения с узлом. Когда жертва принимает SYN-пакет через открытый порт, она должна послать в ответ SYN-ACK пакет и установить соединение. После этого инициатор посылает получателю ответ с ACK-пакетом. Данный процесс условно называется рукопожатием. Однако, во время атаки SYN-флудом рукопожатие не может быть завершено, т.к. злоумышленник не отвечает на SYN-ACK сервера-жертвы. Такие соединения остаются полуоткрытыми до истечения тайм-аута, очередь на подключение переполняется и новые клиенты не могут подключиться к серверу
- UDP-флуд чаще всего используются для широкополосных DDoS-атак в силу их бессеансовости, а также простоты создания сообщений протокола 17 (UDP) различными языками программирования.
- ICMP-флуд. Протокол межсетевых управляющих сообщений (ICMP) используется в первую очередь для передачи сообщений об ошибках и не используется для передачи данных. ICMP-пакеты могут сопровождать TCP-пакеты при соединении с сервером.
Третий класс (L4) — эксплуатация слабых мест TCP-стека, то есть атаки на транспортном уровне. Этот транспортный протокол, лежащий в основе HTTP и ряда других протоколов, довольно сложно устроен. Например, в нём используется большая таблица открытых соединений, каждое из которых является, фактически, конечным автоматом. И именно атаки на этот автомат составляют третий класс DDoS-нападений.
Четвёртый класс (L7) — деградация Web-приложения. Сюда относятся всевозможные «кастомные» атаки, начиная от типичного GET/POST/HTTP Flood до нападений, нацеленных на многократно повторяющиеся поиск и извлечение конкретной информации из БД, памяти или с диска, пока у сервера просто не закончатся ресурсы.
Если отражение атак класса L2-L4 это прерогатива хостинга обычно, достаточно заказать сервер с защитой от ддос, также от таких атак спасают облачные сервисы такие как CloudFlare, DDOSGUARD и т.д.
А вот с атаками уровня приложения, тут проблема, очень тяжело фильтровать такой трафик, ведь система защиты ничего незнает, как фильтровать что-бы не поафектить ваш сайт.
К сожалению тот-же CloudFlare даже на платных тарифах не защищает от таких типов атак.
Да, можно сделать режим "Я под атакой", который будет постоянно проверять HTTP трафик, показывая посетителям капчу, но тогда перестанут работать различные API вашего сайта, мониторинги и т.д. + посетителям не всегда приятно лицезреть капчу.
Короче постоянно держать капчу не вариант.
На дорогих тарифах появилась интелектуальная фильтрация, но стоит такая защита нет менее 200$, да и тоже пропучкает...:(
Поэтому предлагаю разработать мониторинг, который будет ну скажем раз в 10 секунд мониторить нашу систему (Такие метрики как загрузка CPU и число соединений веб-сервера) и в случае обнаружения аномалий включать защиту, я взял CloudFlare и её API, т.к. это можно сделать на бесплатном тарифе.
Наш скрипт должен запускать с максимальным приоритетом nice -n -10, далее в бесконечном цикле раз в 10 секунд делать метрики.
Также в кроне мы сделаем проверку работоспособности нашей защиты, т.к. нам важно что-бы при краши, защита перезапустилась.
Также скрипт при обнаружении атаки должен отправлять уведомление об атаке.
Вот примерная схема работы:
Код:
┌─────────────────────┐
│ Сервер │
│ (Nginx + система) │
└─────────┬──────────┘
│
(1) Сбор метрик (CPU, Nginx)
│
v
┌───────────────────────────────┐
│ Скрипт cloudflare_load_monitor.sh │
│ (мониторинг + логика включения │
│ "Я под атакой" в Cloudflare) │
└─────────────────┬───────────────────┘
│
(2) Проверка порогов CPU/Conn
│
│ Порог превышен?
│
┌────── Нет ──┴───────────────────┐
│ │
│Да v
v (3) Включаем under_attack
┌─────────────────┐ через Cloudflare API
│ Скрипт ждёт │─────────────────────────────────────────┐
│ ATTACK_DURATION│ │
└─────────────────┘ │
(4) По истечении времени │
переводим режим обратно │
в DEFAULT_SECURITY_LEVEL │
│
v
┌─────────────────────────┐
(5) Отправка email ─────▶│ Уведомления на почту │
(начало/конец атак) └─────────────────────────┘
Объяснение схемы
- Сервер (Nginx + система):
- Здесь работают веб-сервер Nginx и система, где проверяется загрузка CPU.
- (1) Сбор метрик:
- Скрипт обращается к mpstat для чтения % загрузки CPU.
- Делаает запрос к
Вы должны зарегистрироваться, чтобы увидеть внешние ссылки, чтобы узнать число активных соединений (Active connections).
- (2) Проверка порогов:
- Скрипт сверяет полученные значения (CPU и Conn) с заданными порогами (CPU_THRESHOLD и CONN_THRESHOLD).
- (3) Включение режима "Я под атакой":
- Если порог(и) превышены, скрипт делает PATCH-запрос к Cloudflare API, переводя уровень безопасности в under_attack.
- (4) Ожидание ATTACK_DURATION:
- Скрипт удерживает режим "Я под атакой" заданное время (например, 300 секунд), периодически проверяя, не нужно ли завершить работу (файл /tmp/cloudflare_monitor_stop).
- (5) Возврат в DEFAULT_SECURITY_LEVEL + уведомление:
- По завершении периода ожидания скрипт возвращает Cloudflare в стандартный режим (например, medium) и рассылает email-уведомление об окончании режима «Я под атакой».
Итак начнем разработку:
Шаг 1. Подготовка окружения
1. Установка необходимых пакетов
- sysstat – для утилиты mpstat, которая нужна для мониторинга CPU.
- mailutils (или mailx) – для отправки email-уведомлений.
- bc – в некоторых системах требуется для арифметических операций (echo "100 - idle" | bc -l).
- jq – для форматирования и обработки JSON-ответов от Cloudflare (не обязательно, но полезно).
Код:
sudo apt update
sudo apt install -y sysstat mailutils bc jq
Для CentOS/RHEL:
Код:
sudo yum install -y sysstat mailx bc jq
2. Настройка Nginx для получения метрик
Чтобы скрипт мог подсчитывать число активных соединений, нужно включить ngx_http_stub_status_module. Обычно он собран по умолчанию, достаточно задать соответствующий location.Пример добавления локейшена в конфигурацию Nginx (файл /etc/nginx/conf.d/status.conf (Нужно создать файл с содержимым ниже) или nginx.conf):
Код:
server {
listen 127.0.0.1:80;
server_name localhost;
location /nginx_status {
stub_status; # Включаем модуль
allow 127.0.0.1; # Разрешаем доступ только с localhost
deny all; # Остальным запрещаем
}
}
Затем перезагрузите Nginx:
Код:
sudo systemctl reload nginx
Теперь метрика будет доступна по адресу
Вы должны зарегистрироваться, чтобы увидеть внешние ссылки
.Шаг 2. Получение Cloudflare API Token
- Зайдите в свою учётную запись на
Вы должны зарегистрироваться, чтобы увидеть внешние ссылки
- Нажмите Create Custom Token.
- Укажите необходимые Permissions:
- Zone: Zone Settings: Edit
- Выберите нужные Zone Resources:
- Include > Specific zone (или All zones, если нужно для всех доменов).
- Сохраните настройки и создайте токен.
- Скопируйте полученный токен и сохраните в безопасном месте (после создания полностью скопировать его можно будет только один раз!).
- Таже нужен будет Account ID вашего домена, его можно увидеть в правой панели тут на вкладке Overview.
Шаг 3. Создание скрипта мониторинга
Ниже приведён пример bash-скрипта, который:- Мониторит загрузку CPU (через mpstat) и число активных соединений (через Nginx stub_status).
- Если превышен порог по загрузке CPU или по числу активных соединений, переводит Cloudflare в режим «Я под атакой» на 5 минут (300 секунд).
- Отправляет email-уведомления при входе в режим атаки и при возврате в нормальный режим.
- Ведёт лог в файл /var/log/cloudflare_load_monitor.log.
- Завершается, если обнаружит файл /tmp/cloudflare_monitor_stop.
Bash:
#!/bin/bash
# cloudflare_load_monitor.sh
#
# Скрипт мониторинга CPU и активных соединений Nginx. При превышении порогов
# включается режим "Я под атакой" в Cloudflare, затем через 5 минут
# режим возвращается в исходный.
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
#### Настройки Cloudflare ####
CF_API_TOKEN="ВСТАВЬТЕ_СВОЙ_API_TOKEN"
ZONE_ID="ВСТАВЬТЕ_СВОЙ_ZONE_ID"
DEFAULT_SECURITY_LEVEL="medium"
SECURITY_LEVEL_ATTACK="under_attack"
#### Настройки мониторинга ####
CPU_THRESHOLD=90 # Порог CPU (в %)
CONN_THRESHOLD=2000 # Порог активных соединений Nginx
CHECK_INTERVAL=10 # Интервал проверки (сек)
ATTACK_DURATION=300 # Длительность режима "Я под атакой" (сек)
NGINX_STATUS_URL="http://127.0.0.1/nginx_status"
#### Настройки уведомлений ####
EMAIL_TO="[email protected]" # Куда отправлять уведомления
#### Логирование ####
LOG_FILE="/var/log/cloudflare_load_monitor.log"
# Функция логирования
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') $1" | tee -a "$LOG_FILE" >&2
}
# Функция отправки email
send_email() {
local subject="$1"
local message="$2"
echo "$message" | mail -s "$subject" "$EMAIL_TO"
}
# Установка security_level в Cloudflare
set_cf_security_level() {
local level="$1"
log "Меняем режим безопасности Cloudflare на: ${level}"
response=$(curl -s -X PATCH "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/settings/security_level" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
-H "Content-Type: application/json" \
--data "{\"value\":\"${level}\"}")
log "Ответ Cloudflare: $response"
}
# Получение загрузки CPU
get_cpu_usage() {
local idle
idle=$(LANG=C mpstat 1 1 | awk '/Average/ {print $(NF)}')
if [ -z "$idle" ]; then
echo 0
else
printf "%.0f" "$(echo "100 - $idle" | bc -l)"
fi
}
# Function to get the number of active Nginx connections.
# Assumes that Nginx is configured with ngx_http_stub_status_module.
# If the status page is unavailable (e.g. due to exhausted worker_connections),
# the function returns CONN_THRESHOLD+1 to trigger protection.
get_nginx_conn() {
local status
status=$(curl -s "$NGINX_STATUS_URL")
if [ -z "$status" ] || [[ "$status" != *"Active connections:"* ]]; then
log "Unable to retrieve Nginx status. Possibly worker_connections are exhausted."
echo $((CONN_THRESHOLD + 1))
else
echo "$status" | awk '/Active connections/ {print $3}'
fi
}
# Проверка наличия необходимых утилит
if ! command -v mpstat >/dev/null 2>&1; then
log "mpstat не найден. Установите пакет sysstat."
exit 1
fi
if ! command -v mail >/dev/null 2>&1; then
log "Утилита mail не найдена. Установите пакет mailutils (или mailx)."
exit 1
fi
log "Запуск скрипта мониторинга Cloudflare. PID: $$"
while true; do
# Если обнаружен /tmp/cloudflare_monitor_stop — завершаемся
if [ -f /tmp/cloudflare_monitor_stop ]; then
log "Найден /tmp/cloudflare_monitor_stop, завершаем работу."
rm -f /tmp/cloudflare_monitor_stop
exit 0
fi
CPU_USAGE=$(get_cpu_usage)
NGINX_CONN=$(get_nginx_conn)
log "Текущая загрузка CPU: ${CPU_USAGE}%"
log "Текущее число активных соединений: ${NGINX_CONN}"
TRIGGER_CPU=0
TRIGGER_CONN=0
[ "$CPU_USAGE" -gt "$CPU_THRESHOLD" ] && TRIGGER_CPU=1
# Если NGINX_CONN — это число и оно больше заданного порога
if [[ "$NGINX_CONN" =~ ^[0-9]+$ ]] && [ "$NGINX_CONN" -gt "$CONN_THRESHOLD" ]; then
TRIGGER_CONN=1
fi
if [ "$TRIGGER_CPU" -eq 1 ] || [ "$TRIGGER_CONN" -eq 1 ]; then
REASON=""
[ "$TRIGGER_CPU" -eq 1 ] && REASON+="CPU usage выше порога (${CPU_USAGE}%). "
[ "$TRIGGER_CONN" -eq 1 ] && REASON+="Число соединений выше порога (${NGINX_CONN})."
log "Обнаружены условия перегрузки: $REASON"
send_email "Включение режима 'Я под атакой'" "Включён режим 'under_attack' в Cloudflare. Причина: $REASON"
set_cf_security_level "$SECURITY_LEVEL_ATTACK"
log "Удерживаем режим '${SECURITY_LEVEL_ATTACK}' в течение ${ATTACK_DURATION} секунд..."
SECONDS_PASSED=0
while [ $SECONDS_PASSED -lt $ATTACK_DURATION ]; do
sleep 10
SECONDS_PASSED=$((SECONDS_PASSED+10))
if [ -f /tmp/cloudflare_monitor_stop ]; then
log "Файл /tmp/cloudflare_monitor_stop найден во время режима атаки, завершаем работу."
exit 0
fi
done
log "Возвращаем режим безопасности на '${DEFAULT_SECURITY_LEVEL}'"
set_cf_security_level "$DEFAULT_SECURITY_LEVEL"
send_email "Возврат нормального режима" "Снятие режима 'under_attack'. Возврат к '${DEFAULT_SECURITY_LEVEL}'."
fi
sleep "$CHECK_INTERVAL"
done
Важны эти настройки:
Код:
#### Настройки Cloudflare ####
CF_API_TOKEN="ВСТАВЬТЕ_СВОЙ_API_TOKEN"
ZONE_ID="ВСТАВЬТЕ_СВОЙ_ZONE_ID"
DEFAULT_SECURITY_LEVEL="medium"
SECURITY_LEVEL_ATTACK="under_attack"
#### Настройки мониторинга ####
CPU_THRESHOLD=90 # Порог CPU (в %)
CONN_THRESHOLD=2000 # Порог активных соединений Nginx
CHECK_INTERVAL=10 # Интервал проверки (сек)
ATTACK_DURATION=300 # Длительность режима "Я под атакой" (сек)
NGINX_STATUS_URL="http://127.0.0.1/nginx_status"
Укажите свои настройки, например CONN_THRESHOLD=2000 # Порог активных соединений Nginx, я указал такой, т.к. у меня в конфиге:
worker_processes 3; worker_connections 768 итого максимально может-быть 2 304 коннектов, сделал немного меньше для мониторинга, также и CPU_THRESHOLD=90.
Шаг 4. Создание скрипта контроля мониторинга
Нам важно что-бы скрипт мониторинга всегда работал, даже после крашей, для этого сделаем мониторинг нашего мониторинга.)Который будет проверять что наш скрипт мониторинга запущен и в случае чего перезапускать его.
Bash:
#!/bin/bash
# monitor_cloudflare_monitor.sh
#
# Этот скрипт проверяет, запущен ли основной скрипт мониторинга (cloudflare_load_monitor.sh).
# Если монитор не запущен, он запускает его с повышенным приоритетом и логирует событие.
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
LOG_FILE="/var/log/cloudflare_monitor_supervisor.log"
MONITOR_SCRIPT="/var/local/cloudflare_load_monitor.sh"
# Функция логирования
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') $1" | tee -a "$LOG_FILE"
}
# Имя процесса: можно искать по имени скрипта
PROCESS_COUNT=$(pgrep -fc "$(basename "$MONITOR_SCRIPT")")
if [ "$PROCESS_COUNT" -eq 0 ]; then
log "Основной скрипт мониторинга не найден. Запускаем его с повышенным приоритетом."
# Запускаем с повышенным приоритетом. Здесь используется nice с значением -10.
# Запускаем в фоне и перенаправляем вывод в лог.
nohup nice -n -10 "$MONITOR_SCRIPT" >/dev/null 2>&1 &
sleep 5 # Подождем немного, чтобы процесс стартовал.
log "Скрипт мониторинга запущен. PID: $!"
else
log "Скрипт мониторинга уже запущен (процессов: ${PROCESS_COUNT})."
fi
И добавим его в крон (Prio -10), проверять раз в минуту (Не забудьте установить права на исполнение скрипта):
Код:
crontab -e
* * * * * /usr/bin/nice -n -10 /bin/bash /var/local/monitor_cloudflare_monitor.sh
Да и все скрипты надо выполнять с правами рута.
Шаг 5. Настройка ротации логов
Чтобы лог-файл /var/log/cloudflare_load_monitor.log не разрастался бесконечно, нужно настроить ротацию через logrotate.Создайте файл /etc/logrotate.d/cloudflare_load_monitor со следующим содержимым:
Код:
/var/log/cloudflare_load_monitor.log {
daily
rotate 7
compress
missingok
notifempty
create 644 root root
sharedscripts
postrotate
endscript
}
Теперь система будет автоматически ротировать лог раз в день, храня до 7 сжатых архивов.
Итог
Таким образом, у вас будет готовая система, которая автоматически включает защитный режим Cloudflare при перегрузке CPU или избыточном количестве подключений, затем возвращает сайт в нормальный режим по истечении заданного времени.Статья на английском языке в моем гитхабе:
Вы должны зарегистрироваться, чтобы увидеть внешние ссылки
Последнее редактирование: