Сайты и Боты — забанить нельзя открыть.

Продолжая «цикл» заметок (1, 2, 3) хочу рассказать о новом опыте взаимодействия с «ИТ-Гигантами», которые либо самостоятельно создают большую нагрузку на сервис, либо позволяют это делать своим клиентам.

В какой-то момент полумеры уже не помогают и приходит понимание того, что ALICLOUD или TENCENT, а может и META надо банить целиком. Бан по USERAGENT на самом деле работает, но во-первых не всегда, т.к. большое количество запросов «из Алибабы» являются клиентскими, а не запросами от самого сервиса, а во-вторых, если миллион запросов в час вернут 403, это всё равно будет миллион запросов в час.

Код предложенный ниже не претендует в общем ни на универсальность ни даже на работоспособность, написан без новомодного AI и предназначен скорее для иллюстрации возможностей и проблем. Список «ИТ-Гигантов», и списки их AS я стрил вручную, ну а далее всё в общем довольно прозрачно. Комментарии написаны больше для наглядности в рамках данной заметки:

#!/bin/bash

# Проверка прав суперпользователя
if [ "$EUID" -ne 0 ]; then
  echo "Запустите скрипт с правами root (sudo)"
  exit 1
fi

# Ассоциативный массив: Название компании -> Список ее ASN
declare -A GIANTS
GIANTS=(
  ["alicloud"]="AS37963 AS45102 AS135377"
  ["tencent"]="AS132203 AS133478 AS45090"
  ["huawei"]="AS136901 AS55990 AS136959"
  ["linode"]="AS63949 AS8075"
  ["amazon"]="AS16509 AS14618 AS8987 AS33070"
  ["meta"]="AS32934 AS63293"
)

for COMPANY in "${!GIANTS[@]}"; do
  ASN_LIST=${GIANTS[$COMPANY]}
  SET_NAME="bl_${COMPANY}"
  TMP_SET_NAME="${SET_NAME}_tmp"

  echo "=== Обработка $COMPANY ==="

  # 1. Создаем временный ipset
  ipset create $TMP_SET_NAME hash:net -exist
  ipset flush $TMP_SET_NAME

  # Флаг для отслеживания успешности наполнения подсетями
  HAS_NETWORKS=false

  # 2. Опрашиваем сервер WHOIS (RADB)
  for ASN in $ASN_LIST; do
    echo "Получение сетей для $ASN..."

    # Исправленный синтаксис запроса с передачей параметров через -- ""
    networks=$(whois -h whois.radb.net -- "-i origin $ASN" | awk '/^route:/ {print $2}')

    # Проверяем, удалось ли получить хоть одну сеть для этой AS
    if [ -n "$networks" ]; then
      for net in $networks; do
        ipset add $TMP_SET_NAME "$net" -exist 2>/dev/null
      done
      HAS_NETWORKS=true
    fi
    sleep 10 # <--- ДОБАВЬТЕ СЮДА ЭТУ СТРОКУ ДЛЯ ЗАЩИТЫ ОТ БАНА!
  done

  # Защита: если по всем AS компании получен пустой ответ (например, проблема с сетью)
  if [ "$HAS_NETWORKS" = false ]; then
    echo "Предупреждение: Не удалось получить данные из WHOIS для $COMPANY. Обновление отменено."
    ipset destroy $TMP_SET_NAME
    echo "------------------------------------------------"
    continue
  fi

  # 3. Создаем основной ipset, если его нет
  ipset create $SET_NAME hash:net -exist

  # 4. Атомарно обновляем список (подмена старого списка новым)
  ipset swap $TMP_SET_NAME $SET_NAME

  # 5. Удаляем временный ipset
  ipset destroy $TMP_SET_NAME

  # 6. Интеграция с iptables
  if ! iptables -C INPUT -m set --match-set $SET_NAME src -j DROP 2>/dev/null; then
    echo "Добавление правила блокировки в iptables для $SET_NAME..."
    iptables -I INPUT 1 -m set --match-set $SET_NAME src -j DROP
  else
    echo "Правило iptables для $SET_NAME уже активно."
  fi

  # --- ВЫВОД СПИСКА СЕТЕЙ И ТОЧНОГО СЧЕТЧИКА ---
  echo "Заблокированные сети для $COMPANY:"

  # Корректное вырезание служебного заголовка ipset
  net_list=$(ipset list $SET_NAME | sed -e '1,/Members:/d')

  if [ -n "$net_list" ]; then
#    echo "$net_list"  # <--- ЭТА СТРОКА ВЫВОДИТ САМ СПИСОК ПОД СЕТЕЙ
    net_count=$(echo "$net_list" | wc -l)
  else
    echo "[Список пуст]"
    net_count=0
  fi

  echo "Итого заблокировано сетей для $COMPANY: $net_count"
  echo "------------------------------------------------"
done

Комментировать код выше едва ли нужно. Код ниже убирает всё то, что сделал код выше:

#!/bin/bash

# Проверка прав суперпользователя
if [ "$EUID" -ne 0 ]; then
  echo "Запустите скрипт с правами root (sudo)"
  exit 1
fi

# Названия ИТ-гигантов должны точно совпадать со списком из первого скрипта
GIANTS=("alicloud" "tencent" "huawei" "linode" "amazon" "meta")

echo "=== Запуск процесса удаления правил блокировки ==="

for COMPANY in "${GIANTS[@]}"; do
  SET_NAME="bl_${COMPANY}"
  TMP_SET_NAME="${SET_NAME}_tmp"

  echo "Обработка $COMPANY..."

  # 1. Удаление правил из iptables
  # Цикл while необходим на случай, если правило было случайно продублировано в INPUT несколько раз
  while iptables -C INPUT -m set --match-set $SET_NAME src -j DROP 2>/dev/null; do
    echo "Удаление правила iptables для $SET_NAME..."
    iptables -D INPUT -m set --match-set $SET_NAME src -j DROP
  done

  # 2. Удаление временного списка ipset (если он остался после аварийного завершения основного скрипта)
  if ipset list $TMP_SET_NAME &>/dev/null; then
    echo "Удаление временного списка $TMP_SET_NAME..."
    ipset destroy $TMP_SET_NAME
  fi

  # 2. Очистка и удаление основного списка ipset
  if ipset list $SET_NAME &>/dev/null; then
    echo "Очистка содержимого списка $SET_NAME..."
    ipset flush $SET_NAME

    echo "Уничтожение списка $SET_NAME..."
    ipset destroy $SET_NAME
  fi

  echo "Все правила и списки для $COMPANY успешно удалены."
  echo "------------------------------------------------"
done

echo "=== Очистка полностью завершена ==="

Если раскомментировать в первом скрипте одну строчку, то помимо количества сетей которые добавятся в ipset список, будет собственно еще и перечень, но там тысячи сетей, поэтому строчка закомментирована.

И казалось бы всё хорошо, но, в скрипте используется whois, что требует открытого 43 порта, а он много где закрыт. Это раз. Подобный опрос («А выдайте мне все айпишники фейсбука» — это сложный запрос) запросто может привести к 24 часам бана клиента, что тоже не очень здорово. И что же делать? А забыть об автоматизации и интерактивности и сделать как-нибудь так для бана:

#!/bin/bash

# Функция для создания, очистки и заполнения индивидуального ipset
deploy_ipset() {
    local set_name="$1"
    local cidr_block="$2"

    # Создаем персональный список, если он не существует
    ipset create "$set_name" hash:net -exist
    # Очищаем старые записи
    ipset flush "$set_name"

    # Построчно добавляем адреса
    while read -r cidr; do
        [[ -z "$cidr" || "$cidr" =~ ^# ]] && continue
        ipset add "$set_name" "$cidr" -exist 2>/dev/null
    done <<< "$cidr_block"

    # Добавляем индивидуальные правила в iptables (Вход и Выход), если их нет
    iptables -C INPUT -m set --match-set "$set_name" src -j DROP 2>/dev/null || iptables -I INPUT -m set --match-set "$set_name" src -j DROP
#    iptables -C OUTPUT -m set --match-set "$set_name" dst -j DROP 2>/dev/null || iptables -I OUTPUT -m set --match-set "$set_name" dst -j DROP

    # Считаем количество добавленных сетей
#    local count=$(ipset list "$set_name" | grep -E -c "([0-9]{1,3}\.){3}[0-9]{1,3}")
    local count=$(ipset list "$set_name" | grep "Number of entries" | awk '{print $4}')
    echo "Список [$set_name] обновлен. Заблокировано сетей: $count"
}

echo "Запуск раздельной блокировки ИТ-гигантов (Offline режим)..."
echo "--------------------------------------------------------"

# --- БАЗЫ ДАННЫХ ДЛЯ КАЖДОГО "ПАРТНЁРА" ---

ALIBABA="
8.208.0.0/12
47.74.0.0/15
47.76.0.0/14
47.80.0.0/12
47.235.0.0/16
47.236.0.0/14
47.240.0.0/13
47.248.0.0/13
106.11.0.0/16
140.205.0.0/16
161.117.0.0/16
170.106.0.0/15
205.204.0.0/16
"


TENCENT="
43.128.0.0/12
49.51.0.0/16
81.68.0.0/14
101.32.0.0/14
119.28.0.0/15
124.156.0.0/16
129.226.0.0/16
150.109.0.0/16
152.136.0.0/16
159.75.0.0/16
162.14.0.0/16
203.205.128.0/17
"


BAIDU="
100.95.0.0/16
106.12.0.0/14
111.206.0.0/16
112.80.248.0/22
119.63.192.0/21
180.76.0.0/16
182.61.0.0/16
220.181.0.0/16
"


BYTEDANCE="
5.181.24.0/22
14.1.28.0/22
47.251.0.0/16
114.134.112.0/21
117.114.248.0/21
130.45.0.0/16
150.242.120.0/21
154.212.0.0/16
161.117.140.0/22
205.220.224.0/22
"


META="
31.13.24.0/21
31.13.64.0/18
45.64.40.0/22
66.220.144.0/20
69.63.176.0/20
69.171.224.0/19
74.119.76.0/22
102.132.96.0/20
103.4.96.0/22
129.134.0.0/16
157.240.0.0/16
173.252.64.0/18
179.60.192.0/22
185.60.216.0/22
204.15.20.0/22
"


AMAZON="
3.0.0.0/9
13.32.0.0/15
13.34.0.0/15
13.248.0.0/14
15.177.0.0/16
18.154.0.0/15
18.164.0.0/14
18.208.0.0/13
34.192.0.0/12
35.153.0.0/16
35.160.0.0/13
44.192.0.0/11
52.0.0.0/10
54.64.0.0/11
54.144.0.0/12
54.192.0.0/12
54.240.0.0/12
99.77.0.0/16
99.80.0.0/13
108.128.0.0/13
143.204.0.0/16
176.32.96.0/21
176.34.0.0/16
204.246.160.0/19
205.251.192.0/18
"


LINODE="
23.239.0.0/18
45.33.0.0/17
45.56.64.0/18
45.79.0.0/16
50.116.0.0/17
66.175.208.0/20
66.228.32.0/19
69.164.192.0/19
72.14.176.0/20
74.207.224.0/19
96.126.96.0/19
97.107.128.0/18
104.200.16.0/20
104.237.128.0/19
109.74.192.0/19
139.162.0.0/16
151.236.216.0/21
172.104.0.0/15
173.230.128.0/19
173.255.192.0/18
178.79.128.0/19
192.155.192.0/18
192.168.0.0/16
198.58.96.0/19
213.219.36.0/22
"


# --- РАЗВЕРТЫВАНИЕ ---
deploy_ipset "block_alibaba"   "$ALIBABA"
deploy_ipset "block_tencent"   "$TENCENT"
deploy_ipset "block_baidu"     "$BAIDU"
deploy_ipset "block_bytedance" "$BYTEDANCE"
deploy_ipset "block_meta"      "$META"
deploy_ipset "block_amazon"    "$AMAZON"
deploy_ipset "block_linode"    "$LINODE"

echo "--------------------------------------------------------"
echo "Все списки успешно изолированы и заблокированы в iptables."

И вот так для разбана:

#!/bin/bash

# Функция, которая мгновенно открывает трафик, не ломая зависимости ядра
undeploy_ipset() {
    local set_name="$1"

    echo "Разблокировка трафика для [$set_name]..."

    if ! ipset list "$set_name" &>/dev/null; then
        echo "-> Список [$set_name] не найден. Пропускаем."
        return 0
    fi

    # 1. Очищаем все IP-адреса внутри списка.
    # Список остается пустым -> блокировка полностью снимается, трафик идет свободно.
    if ipset flush "$set_name" 2>/dev/null; then
        echo "-> Трафик разблокирован. Список [$set_name] очищен."
    else
        echo "[ОШИБКА] Не удалось очистить список [$set_name]."
    fi
}

echo "Запуск разблокировки ИТ-гигантов..."
echo "--------------------------------------------------------"

undeploy_ipset "block_alibaba"
undeploy_ipset "block_tencent"
undeploy_ipset "block_baidu"
undeploy_ipset "block_bytedance"
undeploy_ipset "block_meta"
undeploy_ipset "block_amazon"
undeploy_ipset "block_linode"

echo "--------------------------------------------------------"
echo "Все списки очищены. Блокировки сняты, трафик полностью разрешен."

Понятно что списки не полны и не стоит использовать их как эталонные справочники, это скорее иллюстрация, чем готовый инструмент.

Чтобы понять на сколько не полнон список подсетей, можно посмотреть на пример работы «первого» скрипта:

tomcat@elar:~# sudo /opt/ban.sh
=== Обработка meta ===
Получение сетей для AS32934...
Получение сетей для AS63293...
Добавление правила блокировки в iptables для bl_meta...
Заблокированные сети для meta:
Итого заблокировано сетей для meta: 539
------------------------------------------------
=== Обработка linode ===
Получение сетей для AS63949...
Получение сетей для AS8075...
Добавление правила блокировки в iptables для bl_linode...
Заблокированные сети для linode:
Итого заблокировано сетей для linode: 1922
------------------------------------------------
=== Обработка amazon ===
Получение сетей для AS16509...
Задержка.
Получение сетей для AS14618...
Получение сетей для AS8987...
Получение сетей для AS33070...
Добавление правила блокировки в iptables для bl_amazon...
Заблокированные сети для amazon:
Итого заблокировано сетей для amazon: 20497
------------------------------------------------
=== Обработка alicloud ===
Получение сетей для AS37963...
Задержка.
Получение сетей для AS45102...
Задержка.
Получение сетей для AS135377...
Добавление правила блокировки в iptables для bl_alicloud...
Заблокированные сети для alicloud:
Итого заблокировано сетей для alicloud: 47128
------------------------------------------------
=== Обработка huawei ===
Получение сетей для AS136901...
Получение сетей для AS55990...
Получение сетей для AS136959...
Добавление правила блокировки в iptables для bl_huawei...
Заблокированные сети для huawei:
Итого заблокировано сетей для huawei: 458
------------------------------------------------
=== Обработка tencent ===
Получение сетей для AS132203...
Получение сетей для AS133478...
Получение сетей для AS45090...
Добавление правила блокировки в iptables для bl_tencent...
Заблокированные сети для tencent:
Итого заблокировано сетей для tencent: 6213
------------------------------------------------
tomcat@elar:~#

По опыту администрируемых репозиториев могу сказать что пристальное внимание требуется за активностью MS BING, META и за китайскими товарищами. В случае с Amazon сам по себе Amazonbot тоже может доставлять неудобства, но заметном еньшие чем боты META, а вот клиенты AWS могут сильно нагружать сервис. Но, банить AWS целиком это наверное слишком круто.

[UPD]
Еще пара скриптов по бану и разбану автономками, но на системах с закрытым 43 портом

Бан:

#!/bin/bash

# Проверка прав суперпользователя
if [ "$EUID" -ne 0 ]; then
  echo "Запустите скрипт с правами root (sudo)"
  exit 1
fi

# Проверка наличия jq и wget
for cmd in jq wget; do
  if ! command -v $cmd &> /dev/null; then
    echo "Ошибка: утилита $cmd не установлена."
    exit 1
  fi
done

# Ассоциативный массив: Название компании -> Список ее ASN
declare -A GIANTS
GIANTS=(
  ["alicloud"]="AS37963 AS45102"
  ["ucloud"]="AS135377"
  ["tencent"]="AS132203 AS133478 AS45090"
  ["huawei"]="AS136901 AS55990 AS136959"
  ["linode"]="AS63949 AS8075"
  ["amazon"]="AS16509 AS14618 AS8987 AS33070"
  ["meta"]="AS32934 AS63293"
)

# Разбиваем базовый рабочий URL на независимые части
PROTO="https://"
DOMAIN="stat.ripe.net"
PATH_URI="/data/announced-prefixes/data.json"
PARAM="?resource="

for COMPANY in "${!GIANTS[@]}"; do
  ASN_LIST=${GIANTS[$COMPANY]}
  SET_NAME="bl_${COMPANY}"
  TMP_SET_NAME="${SET_NAME}_tmp"
 
  echo "=== Обработка $COMPANY ==="

  # 1. Создаем временный ipset
  ipset create "$TMP_SET_NAME" hash:net -exist
  ipset flush "$TMP_SET_NAME"

  HAS_NETWORKS=false

  # 2. Опрашиваем RIPE STAT API через wget и парсим jq
  for ASN in $ASN_LIST; do
    # Очищаем ASN, оставляя только цифры
    ASN_NUM=$(echo "$ASN" | tr -d -c '0-9')
   
    echo "Получение сетей для AS${ASN_NUM}..."
   
    # Собираем чистый, оригинальный URL из сегментов
    API_URL="${PROTO}${DOMAIN}${PATH_URI}${PARAM}${ASN_NUM}"
   
    # Запрос к API RIPE
    networks=$(wget -qO- "$API_URL" | jq -r '.data.prefixes[].prefix' 2>/dev/null)
   
    if [ -n "$networks" ] && [ "$networks" != "null" ]; then
      for net in $networks; do
        # Исключаем IPv6 (содержат двоеточие)
        if [[ ! "$net" =~ ":" ]]; then
          ipset add "$TMP_SET_NAME" "$net" -exist 2>/dev/null
        fi
      done
      HAS_NETWORKS=true
    fi
  done

  # Защита: если по всем AS компании получен пустой ответ
  if [ "$HAS_NETWORKS" = false ]; then
    echo "Предупреждение: Не удалось получить данные из RIPE API для $COMPANY. Обновление отменено."
    ipset destroy "$TMP_SET_NAME"
    echo "------------------------------------------------"
    continue
  fi

  # 3. Создаем основной ipset, если его нет
  ipset create "$SET_NAME" hash:net -exist

  # 4. Атомарно обновляем список
  ipset swap "$TMP_SET_NAME" "$SET_NAME"

  # 5. Удаляем временный ipset
  ipset destroy "$TMP_SET_NAME"

  # 6. Интеграция с iptables
  if ! iptables -C INPUT -m set --match-set "$SET_NAME" src -j DROP 2>/dev/null; then
    echo "Добавление правила блокировки в iptables для $SET_NAME..."
    iptables -I INPUT 1 -m set --match-set "$SET_NAME" src -j DROP
  else
    echo "Правило iptables для $SET_NAME уже активно."
  fi

  # --- ВЫВОД СПИСКА СЕТЕЙ И ТОЧНОГО СЧЕТЧИКА ---
  echo "Заблокированные сети для $COMPANY:"
  net_list=$(ipset list "$SET_NAME" | sed -e '1,/Members:/d')
 
  if [ -n "$net_list" ]; then
    net_count=$(echo "$net_list" | wc -l)
  else
    echo "[Список пуст]"
    net_count=0
  fi

  echo "Итого заблокировано сетей для $COMPANY: $net_count"
  echo "------------------------------------------------"
done

Разбан:

#!/bin/bash

# Проверка прав суперпользователя
if [ "$EUID" -ne 0 ]; then
  echo "Пожалуйста, запустите скрипт с правами root (sudo)."
  exit 1
fi

# Названия ИТ-гигантов должны точно совпадать со списком из первого скрипта
GIANTS=("alicloud" "ucloud" "tencent" "huawei" "linode" "amazon" "meta")

echo "=== Запуск процесса удаления правил блокировки ==="

for COMPANY in "${GIANTS[@]}"; do
  SET_NAME="bl_${COMPANY}"
  TMP_SET_NAME="${SET_NAME}_tmp"
 
  echo "Обработка $COMPANY..."

  # 1. Удаление правил из iptables
  # Цикл while необходим на случай, если правило было случайно продублировано в INPUT несколько раз
  while iptables -C INPUT -m set --match-set $SET_NAME src -j DROP 2>/dev/null; do
    echo "Удаление правила iptables для $SET_NAME..."
    iptables -D INPUT -m set --match-set $SET_NAME src -j DROP
  done

  # 2. Удаление временного списка ipset (если он остался после аварийного завершения основного скрипта)
  if ipset list $TMP_SET_NAME &>/dev/null; then
    echo "Удаление временного списка $TMP_SET_NAME..."
    ipset destroy $TMP_SET_NAME
  fi

  # 3. Очистка и удаление основного списка ipset
  if ipset list $SET_NAME &>/dev/null; then
    echo "Очистка содержимого списка $SET_NAME..."
    ipset flush $SET_NAME
   
    echo "Уничтожение списка $SET_NAME..."
    ipset destroy $SET_NAME
  fi

  echo "Все правила и списки для $COMPANY успешно удалены."
  echo "------------------------------------------------"
done

echo "=== Очистка полностью завершена ==="

Оно по логике не сильно отличается от варианта с whois, но вроде работает там, где 43 порт закрыт. Как всегда, решение не претендует на изащнсоть и является только иллюстрацией.

Запись опубликована в рубрике Библиотека с метками , , . Добавьте в закладки постоянную ссылку.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *