1736961407332.png


Всем привет!

Как-то писал скрипт для защиты от ддоса при помощи сервиса CloudFlare.

Но CloudFlare обновился, теперь там три режима:

🔓off
  • Полностью отключена фильтрация подозрительного трафика.
  • Посетители не получают никаких челленджей от Cloudflare.
  • Ноль защиты от ботов и атак на уровне security features (всё идёт напрямую).
➡️ Подходит только для внутреннего трафика или когда защита мешает бизнесу.

🟡essentially_off
  • Минимальный уровень защиты.
  • Только самые подозрительные IP/боты получат челлендж (JS или CAPTCHA).
  • Легальный трафик почти всегда проходит без препятствий.
➡️ Подходит для пассивного наблюдения, когда защита нужна, но важно не мешать пользователям.

🔥 under_attack
  • Самый высокий уровень.
  • Все пользователи (включая реальных) получают 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;           # Остальным запрещаем
    }
}

Теперь метрика будет доступна по адресу
Чтобы увидеть нужно авторизоваться или зарегистрироваться.
.

Для остановки скрипта, нужно создать файл /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​

  1. Зайдите в свою учётную запись на
    Чтобы увидеть нужно авторизоваться или зарегистрироваться.
  2. Нажмите Create Custom Token.
  3. Укажите необходимые Permissions:
    • Zone: Zone Settings: Edit
  4. Выберите нужные Zone Resources:
    • Include > Specific zone (или All zones, если нужно для всех доменов).
  5. Сохраните настройки и создайте токен.
  6. Скопируйте полученный токен и сохраните в безопасном месте (после создания полностью скопировать его можно будет только один раз!).
  7. Таже нужен будет Account ID вашего домена, его можно увидеть в правой панели тут на вкладке Overview.
Не забудьте также поменять емаил и другие настройки, если нужно.

Удачи!)
  • Like
Реакции: x64dbg