Всем привет!
Как-то писал скрипт для защиты от ддоса при помощи сервиса CloudFlare.
Но CloudFlare обновился, теперь там три режима:
- Полностью отключена фильтрация подозрительного трафика.
 - Посетители не получают никаких челленджей от Cloudflare.
 - Ноль защиты от ботов и атак на уровне security features (всё идёт напрямую).
 
- Минимальный уровень защиты.
 - Только самые подозрительные IP/боты получат челлендж (JS или CAPTCHA).
 - Легальный трафик почти всегда проходит без препятствий.
 
- Самый высокий уровень.
 - Все пользователи (включая реальных) получают JavaScript-челлендж (5 секунд).
 - Защита активируется до выполнения JS-кода, предотвращает доступ ботам/сканерам/LOIC и пр.
 
Напомню, скрипт запускается как демон и начинает мониторить число соединений nginx, apache и нагрузку на сервер раз в 3 секунды.
Если эти параметры превышают лимиты, то включается защита under_attack на пять минут.
Скрипт желательно запускать с повышенным приоритетом, например /usr/bin/nice -n -10.
Также в скрипте добавлена проверка, на установку нужного софта (Лучше для первого запуска, запустить скрипт вручную).
Чтобы скрипт мог подсчитывать число активных соединений, нужно включить ngx_http_stub_status_module. Обычно он собран по умолчанию, достаточно задать соответствующий location.
Пример добавления локейшена в конфигурацию Nginx (файл /etc/nginx/conf.d/status.conf (Нужно создать файл с содержимым ниже)):
		Код:
	
	server {
    listen 127.0.0.1:80;
    server_name localhost;
    location /nginx_status {
        stub_status;        # Включаем модуль
        allow 127.0.0.1;    # Разрешаем доступ только с localhost
        deny all;           # Остальным запрещаем
    }
}
	Теперь метрика будет доступна по адресу http://127.0.0.1/nginx_status.
Для остановки скрипта, нужно создать файл /tmp/cloudflare_monitor_stop.
Сам скрипт:
		Bash:
	
	#!/bin/bash
# cloudflare_load_monitor_v4.sh  (rev-2025-05-07)
#
# Включает Under-Attack-mode Cloudflare ТОЛЬКО после
#   – 5-кратного подряд превышения CPU-порога
#   – 5-кратного подряд превышения порога активных коннектов Nginx
#   – 5-кратного подряд превышения порога активных коннектов Apache
# -------------------------------------------------------------
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
########################  Cloudflare  ##########################
CF_API_TOKEN="Insert_real token"   # TODO: real token
ZONE_ID="Insert_zone_id"                # TODO: real zone_id
DEFAULT_SECURITY_LEVEL="essentially_off"          # off | essentially_off
SECURITY_LEVEL_ATTACK="under_attack"  # включаем этот режим
########################  Пороги/интервалы  ####################
CPU_THRESHOLD=95
CONN_THRESHOLD=2000          # Nginx
APACHE_CONN_THRESHOLD=120    # Apache
CHECK_INTERVAL=5             # сек
ATTACK_DURATION=300          # сек Under-Attack
NGINX_STATUS_URL="http://127.0.0.1/nginx_status"
APACHE_PORT=8080
APACHE_PROCESS_NAME="apache2"
EMAIL_TO="Insert_email"
LOG_FILE="/var/log/cloudflare_load_monitor.log"
########################  Функции утилиты  #####################
log(){ echo "$(date '+%F %T') $1" | tee -a "$LOG_FILE" >&2; }
send_email(){ echo "$2" | mail -s "$1" "$EMAIL_TO"; }
set_cf_security_level(){
    local level="$1"
    log "Cloudflare → ${level}"
    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}\"}" | tee -a "$LOG_FILE"
}
get_cpu_usage(){
    local idle
    idle=$(LANG=C mpstat 1 1 | awk '/Average/ {print $(NF)}')
    printf "%.0f" "$(echo "${idle:-100}" | awk '{print 100-$1}')"
}
get_nginx_conn(){
    local st
    st=$(curl -s "$NGINX_STATUS_URL")
    [[ -z $st || $st != *"Active connections:"* ]] \
        && { log "Nginx status недоступен"; echo $((CONN_THRESHOLD+1)); } \
        || echo "$st" | awk '/Active connections/ {print $3}'
}
get_apache_conn(){
    ss -tanp | awk -v p="$APACHE_PORT" -v n="$APACHE_PROCESS_NAME" '
        $1=="ESTAB" && $4 ~ ":"p"$" && $0 ~ n {c++} END{print c+0}'
}
#####################  Проверка окружения  #####################
# полный список утилит, от которых зависит скрипт
REQUIRED_CMDS=(curl mpstat jq mail ss awk bc)
for cmd in "${REQUIRED_CMDS[@]}"; do
    if ! command -v "$cmd" >/dev/null 2>&1; then
        log "Ошибка: требуемая утилита '$cmd' не найдена. Установите её и перезапустите."
        exit 1
    fi
done
# Проверяем доступность status-страницы nginx
if ! curl -s "$NGINX_STATUS_URL" | grep -q "Active connections"; then
    log "Ошибка: Nginx status-страница недоступна по $NGINX_STATUS_URL"
    exit 1
fi
#######################  Счётчики превышений  ###################
CPU_EXCEED_COUNT=0
NGINX_EXCEED_COUNT=0
APACHE_EXCEED_COUNT=0
log "★ Старт мониторинга Cloudflare. PID $$"
########################  Главный цикл  ########################
while true; do
    [[ -f /tmp/cloudflare_monitor_stop ]] && {
        log "Stop-файл найден. Завершение."; rm -f /tmp/cloudflare_monitor_stop; exit 0; }
    CPU_USAGE=$(get_cpu_usage)
    NGINX_CONN=$(get_nginx_conn)
    APACHE_CONN=$(get_apache_conn)
    log "CPU: ${CPU_USAGE}%  |  Nginx: ${NGINX_CONN}  |  Apache: ${APACHE_CONN}"
    TRIGGER_CPU=0; TRIGGER_NGINX=0; TRIGGER_APACHE=0
    # --- CPU ---
    if (( CPU_USAGE > CPU_THRESHOLD )); then
        ((CPU_EXCEED_COUNT++))
        log "CPU порог превышен: ${CPU_EXCEED_COUNT}/5"
        [[ $CPU_EXCEED_COUNT -ge 5 ]] && { TRIGGER_CPU=1; CPU_EXCEED_COUNT=0; }
    else CPU_EXCEED_COUNT=0; fi
    # --- Nginx ---
    if [[ $NGINX_CONN =~ ^[0-9]+$ && $NGINX_CONN -gt $CONN_THRESHOLD ]]; then
        ((NGINX_EXCEED_COUNT++))
        log "Nginx порог превышен: ${NGINX_EXCEED_COUNT}/5"
        [[ $NGINX_EXCEED_COUNT -ge 5 ]] && { TRIGGER_NGINX=1; NGINX_EXCEED_COUNT=0; }
    else NGINX_EXCEED_COUNT=0; fi
    # --- Apache ---
    if [[ $APACHE_CONN =~ ^[0-9]+$ && $APACHE_CONN -gt $APACHE_CONN_THRESHOLD ]]; then
        ((APACHE_EXCEED_COUNT++))
        log "Apache порог превышен: ${APACHE_EXCEED_COUNT}/5"
        [[ $APACHE_EXCEED_COUNT -ge 5 ]] && { TRIGGER_APACHE=1; APACHE_EXCEED_COUNT=0; }
    else APACHE_EXCEED_COUNT=0; fi
    # --- триггер ---
    if (( TRIGGER_CPU || TRIGGER_NGINX || TRIGGER_APACHE )); then
        REASON=""
        ((TRIGGER_CPU))    && REASON+="CPU>${CPU_THRESHOLD}% ×5. "
        ((TRIGGER_NGINX))  && REASON+="Nginx>${CONN_THRESHOLD} ×5. "
        ((TRIGGER_APACHE)) && REASON+="Apache>${APACHE_CONN_THRESHOLD} ×5. "
        log "► UNDER_ATTACK: $REASON"
        send_email "Cloudflare Under-Attack ON" "$REASON"
        set_cf_security_level "$SECURITY_LEVEL_ATTACK"
        SECONDS_PASSED=0
        while (( SECONDS_PASSED < ATTACK_DURATION )); do
            sleep 10
            ((SECONDS_PASSED+=10))
            [[ -f /tmp/cloudflare_monitor_stop ]] && {
                log "Stop-файл в режиме атаки. Завершение."
                rm -f /tmp/cloudflare_monitor_stop; exit 0; }
        done
        log "◄ Возврат в нормальный режим (${DEFAULT_SECURITY_LEVEL})"
        set_cf_security_level "$DEFAULT_SECURITY_LEVEL"
        send_email "Cloudflare Under-Attack OFF" "Режим 'under_attack' снят."
    fi
    sleep "$CHECK_INTERVAL"
done
	После запуска в /var/log/cloudflare_load_monitor.log будут логи работы скрипта.
Рекомендую сделать ротацию через logrotate, иначе сильно разрастется, сделать можно так:
Чтобы лог-файл /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 сжатых архивов.
Также можно создать скрипт мониторинга работы нашего скрипта:
		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
	И добавить его в планировщик, раз в минуту:
		Код:
	
	crontab -e
* * * * * /usr/bin/nice -n -10 /bin/bash /var/local/monitor_cloudflare_monitor.sh
	Получение Cloudflare API Token
- Зайдите в свою учётную запись на https://dash.cloudflare.com/profile/api-tokens
 - Нажмите Create Custom Token.
 - Укажите необходимые Permissions:
- Zone: Zone Settings: Edit
 
 - Выберите нужные Zone Resources:
- Include > Specific zone (или All zones, если нужно для всех доменов).
 
 - Сохраните настройки и создайте токен.
 - Скопируйте полученный токен и сохраните в безопасном месте (после создания полностью скопировать его можно будет только один раз!).
 - Таже нужен будет Account ID вашего домена, его можно увидеть в правой панели тут на вкладке Overview.
 
Удачи!)
	