Автоматизация режима атаки CloudFlare

Привет, Discourse..

Несколько дней назад мой небольшой сервер подвергся DDoS-атаке.
Даже несмотря на то, что трафик можно пропускать через Cloudflare (CF), некоторые атаки всё же проникают на сервер. В результате нагрузка на процессор поднимается до 60–80%.
Это можно предотвратить, включив режим «Under Attack Mode».
Иногда я также отвлекаюсь, чтобы зайти в панель CF и вручную включить «Under Attack Mode».

После поиска в Google оказалось, что есть несколько статей, описывающих автоматическое включение режима «Under Attack Mode» на основе нагрузки на процессор.

CF AUTO

#!/bin/bash
# Cloudflare Auto Under Attack Mode = CF Auto UAM
# версия 0.99beta

# Перечисления уровней безопасности
SL_OFF=0
SL_ESSENTIALLY_OFF=1
SL_LOW=2
SL_MEDIUM=3
SL_HIGH=4
SL_UNDER_ATTACK=5

# Строки уровней безопасности
SL_OFF_S="off"
SL_ESSENTIALLY_OFF_S="essentially_off"
SL_LOW_S="low"
SL_MEDIUM_S="medium"
SL_HIGH_S="high"
SL_UNDER_ATTACK_S="under_attack"

# Конфигурация
debug_mode=0 # 1 = true, 0 = false, добавляет больше логов и позволяет редактировать переменные для тестирования скрипта
install_parent_path="/home"
cf_email=""
cf_apikey=""
cf_zoneid=""
upper_cpu_limit=35 # 10 = 10% нагрузки, 20 = 20% нагрузки. Общая нагрузка с учётом количества ядер
lower_cpu_limit=5
regular_status=$SL_HIGH
regular_status_s=$SL_HIGH_S
time_limit_before_revert=$((60 * 5)) # по умолчанию 5 минут
# конец конфигурации

# Функции

install() {
  mkdir $install_parent_path"/cfautouam" &>/dev/null

  cat >$install_parent_path"/cfautouam/cfautouam.service" <<EOF
[Unit]
Description=Автоматизация режима Cloudflare Under Attack Mode
[Service]
ExecStart=$install_parent_path/cfautouam/cfautouam.sh
EOF

  cat >$install_parent_path"/cfautouam/cfautouam.timer" <<EOF
[Unit]
Description=Автоматизация режима Cloudflare Under Attack Mode
[Timer]
OnBootSec=60
OnUnitActiveSec=5
AccuracySec=1
[Install]
WantedBy=timers.target
EOF

  chmod +x $install_parent_path"/cfautouam/cfautouam.service"
  systemctl enable $install_parent_path"/cfautouam/cfautouam.timer"
  systemctl enable $install_parent_path"/cfautouam/cfautouam.service"
  systemctl start cfautouam.timer
  echo "$(date) - cfautouam - Установлено" >>$install_parent_path"/cfautouam/cfautouam.log"
  exit
}

uninstall() {
  systemctl stop cfautouam.timer
  systemctl stop cfautouam.service
  systemctl disable cfautouam.timer
  systemctl disable cfautouam.service
  rm $install_parent_path"/cfautouam/cfstatus" &>/dev/null
  rm $install_parent_path"/cfautouam/uamdisabledtime" &>/dev/null
  rm $install_parent_path"/cfautouam/uamenabledtime" &>/dev/null
  rm $install_parent_path"/cfautouam/cfautouam.timer"
  rm $install_parent_path"/cfautouam/cfautouam.service"
  echo "$(date) - cfautouam - Удалено" >>$install_parent_path"/cfautouam/cfautouam.log"
  exit
}

disable_uam() {
  curl -X PATCH "https://api.cloudflare.com/client/v4/zones/$cf_zoneid/settings/security_level" \
    -H "X-Auth-Email: $cf_email" \
    -H "X-Auth-Key: $cf_apikey" \
    -H "Content-Type: application/json" \
    --data "{\"value\":\"$regular_status_s\"}" &>/dev/null

  # Запись времени
  date +%s >$install_parent_path"/cfautouam/uamdisabledtime"

  echo "$(date) - cfautouam - Нагрузка CPU: $curr_load - UAM отключён" >>$install_parent_path"/cfautouam/cfautouam.log"
}

enable_uam() {
  curl -X PATCH "https://api.cloudflare.com/client/v4/zones/$cf_zoneid/settings/security_level" \
    -H "X-Auth-Email: $cf_email" \
    -H "X-Auth-Key: $cf_apikey" \
    -H "Content-Type: application/json" \
    --data '{"value":"under_attack"}' &>/dev/null

  # Запись времени
  date +%s >$install_parent_path"/cfautouam/uamenabledtime"

  echo "$(date) - cfautouam - Нагрузка CPU: $curr_load - UAM включён" >>$install_parent_path"/cfautouam/cfautouam.log"
}

get_current_load() {
  currload=$(top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print 100 - $1}')
  currload=$(echo "$currload/1" | bc)
  return $currload
}

get_security_level() {
  curl -X GET "https://api.cloudflare.com/client/v4/zones/$cf_zoneid/settings/security_level" \
    -H "X-Auth-Email: $cf_email" \
    -H "X-Auth-Key: $cf_apikey" \
    -H "Content-Type: application/json" 2>/dev/null |
    awk -F":" '{ print $4 }' | awk -F',' '{ print $1 }' | tr -d '"' >$install_parent_path"/cfautouam/cfstatus"

  security_level=$(cat $install_parent_path"/cfautouam/cfstatus")

  case $security_level in
  "off")
    return $SL_OFF
    ;;
  "essentially_off")
    return $SL_ESSENTIALLY_OFF
    ;;
  "low")
    return $SL_LOW
    ;;
  "medium")
    return $SL_MEDIUM
    ;;
  "high")
    return $SL_HIGH
    ;;
  "under_attack")
    return $SL_UNDER_ATTACK
    ;;
  *)
    return 100 # ошибка
    ;;
  esac
}

main() {
  # Получение текущего уровня защиты и нагрузки
  get_security_level
  curr_security_level=$?
  get_current_load
  curr_load=$?

  if [ $debug_mode == 1 ]; then
    debug_mode=1 # случайная строка, необходимая для скрытия глупой ошибки shellcheck
	# редактируйте переменные здесь для отладки скрипта
    #curr_load=5
    #time_limit_before_revert=15
  fi

  # Если UAM был недавно включён

  if [[ $curr_security_level == "$SL_UNDER_ATTACK" ]]; then
    uam_enabled_time=$(<$install_parent_path"/cfautouam/uamenabledtime")
    currenttime=$(date +%s)
    timediff=$((currenttime - uam_enabled_time))

    # Если лимит времени не истёк, ничего не делаем
    if [[ $timediff -lt $time_limit_before_revert ]]; then
        if [ $debug_mode == 1 ]; then
          echo "$(date) - cfautouam - Нагрузка CPU: $curr_load - лимит времени не истёк независимо от нагрузки CPU - ничего не делаем" >>$install_parent_path"/cfautouam/cfautouam.log"
        fi
        exit
    fi

    # Если лимит времени истёк и нагрузка CPU нормализовалась, отключаем UAM
    if [[ $timediff -gt $time_limit_before_revert && $curr_load -lt $lower_cpu_limit ]]; then
        if [ $debug_mode == 1 ]; then
          echo "$(date) - cfautouam - Нагрузка CPU: $curr_load - лимит времени истёк - нагрузка CPU ниже порога" >>$install_parent_path"/cfautouam/cfautouam.log"
        fi
        disable_uam
        exit
    fi

    # Если лимит времени истёк, но нагрузка CPU не нормализовалась, ждём
    if [[ $timediff -gt $time_limit_before_revert && $curr_load -gt $lower_cpu_limit ]]; then
      if [ $debug_mode == 1 ]; then
        echo "$(date) - cfautouam - Нагрузка CPU: $curr_load - лимит времени истёк, но нагрузка CPU выше порога, ждём окончания лимита" >>$install_parent_path"/cfautouam/cfautouam.log"
      fi
    fi
    exit
  fi

  # Если UAM не включён, продолжаем

  # Включение и отключение UAM на основе нагрузки

  # Если нагрузка выше предела
  if [[ $curr_load -gt $upper_cpu_limit && $curr_security_level == "$regular_status" ]]; then
    enable_uam
  # Иначе, если нагрузка ниже предела
  elif [[ $curr_load -lt $lower_cpu_limit && $curr_security_level == "$SL_UNDER_ATTACK" ]]; then
    disable_uam
  else
    if [ $debug_mode == 1 ]; then
      echo "$(date) - cfautouam - Нагрузка CPU: $curr_load - изменений не требуется" >>$install_parent_path"/cfautouam/cfautouam.log"
    fi
  fi
}

# Конец функций

# Main -> аргументы командной строки

if [ "$1" = '-install' ]; then
  install
  echo "$(date) - cfautouam - Установлено" >>$install_parent_path"/cfautouam/cfautouam.log"
  exit
elif [ "$1" = '-uninstall' ]; then
  uninstall
  echo "$(date) - cfautouam - Удалено" >>$install_parent_path"/cfautouam/cfautouam.log"
  exit
elif [ "$1" = '-enable_uam' ]; then
  echo "$(date) - cfautouam - UAM включён вручную" >>$install_parent_path"/cfautouam/cfautouam.log"
  enable_uam
  exit
elif [ "$1" = '-disable_uam' ]; then
  echo "$(date) - cfautouam - UAM отключён вручную" >>$install_parent_path"/cfautouam/cfautouam.log"
  disable_uam
  exit
elif [ -z "$1" ]; then
  main
  exit
else
  echo "cfautouam - Неверный аргумент"
  exit
fi

Я попробовал следовать инструкции, но это не сработало… Есть ли другой способ?

Использование метрики, связывающей загрузку процессора с фактом атаки на хост, может сработать для сайтов на PHP, но помните, что процессы перестроения и обновления требуют значительных ресурсов процессора и, скорее всего, также будут вызывать срабатывание этого механизма…

Да, это совершенно верно. Что касается процессора, то при каждом обновлении и улучшении чего-либо в Discourse показания нагрузки на процессор всегда становятся очень высокими. Это не причина, по которой обновление возможно каждый раз. Возможно, вы можете остановить этот скрипт, выполнить обновление, а затем снова запустить его.

Для первой DDoS-атаки мне нужно, чтобы этот скрипт работал на моём сервере, чтобы справиться со второй атакой. Возможно, я сразу заблокировал все ASN и IP-адреса ботов. Злоумышленники будут обновлять свои ASN и IP, поэтому мне нужно найти способ блокировать внезапные атаки.

Вы уверены, что они просто обходят Cloudflare через утекший IP-адрес?

Некоторые друзья из Discourse полагают, что сервер принимает только IP-адреса, а не просто Cloudflare, и что в WAF также есть определённые правила. Я не думаю, что смогу снова войти на сервер или подвергнуть его DDoS-атаке.