Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 

Repository files navigation

VLESS и майская эскалация блокировок (РФ, 2026)

Гайд: как блокировки мая 2026 повлияли на VLESS и как адаптировать конфигурацию, чтобы он снова работал.

Контекст: разбор te-st.org «MTProxy» (№9, 02.06.2026) + технические первоисточники. Состояние на июнь 2026. Тема меняется буквально по неделям — проверяй актуальность.


TL;DR

Включи flow: "xtls-rprx-vision" + живой высоконагруженный донор SNI + при необходимости смени fingerprint на firefox/qq. Это закрывает слои сигнатуры / поведения / отпечатка. Но если сервер «заморожен» по объёму трафика или его подсеть в блоке — никакой конфиг не спасёт: нужно менять IP/хостинг или уходить за CDN.


Как майская эскалация повлияла на VLESS

VLESS как протокол не «вскрыли». Поймали то, что вокруг него — три независимых слоя:

1. Поведенческий / статистический анализ

DPI смотрит не на сигнатуру, а на форму трафика: VLESS-туннель даёт «постоянный двунаправленный поток без пауз», нетипичные размеры пакетов, отсутствие реальных API-запросов к сервису-донору. Ловит VLESS+TLS+REALITY без Vision (паттерн TLS-in-TLS виден по длинам пакетов).

2. TCP-«заморозка» по объёму данных (самое неприятное)

ТСПУ берёт подозрительные зарубежные IP из дата-центровых ASN (Hetzner, DigitalOcean, Vultr…) и подмораживает соединение после ~15–20 КБ / ~25 пакетов сервер→клиент. RST не шлют — просто таймаут. Протоколо-независимо: REALITY/Vision/маскировка не помогают. Это и есть «блокировка целыми ASN-подсетями». Конфигом не лечится.

3. Fingerprint (JA3/JA4)

Fingerprint Go-клиента давно в базах детекции. В мае ряду пользователей помогала смена fingerprint с chrome на firefox/qq.

К концу мая у большинства абонентов МТС/Билайн/Мегафон/Tele2 VLESS+REALITY+Vision снова заработал — но только в правильной конфигурации и на «живом» IP.


Шаг 0 — диагностика (не пропускать)

Симптом Причина Чинится конфигом?
Сервер недоступен (ping/SSH не идут) IP/подсеть в блоке ❌ менять IP/хостинг
Хендшейк проходит, грузит пару секунд → виснет/таймаут TCP-заморозка по объёму или TLS-in-TLS частично
Падает сразу на хендшейке fingerprint / SNI / active probing ✅ да

Пингуется и SSH идёт, а VLESS виснет после короткой загрузки → работаем с конфигом. Сервер мёртв целиком → сразу к разделу про IP.


Шаг 1 — рабочий конфиг: VLESS + REALITY + Vision

Главное — flow: "xtls-rprx-vision". Vision прокидывает внутренний TLS-record напрямую, без повторной обёртки, и убирает сигнатуру TLS-in-TLS. Если сейчас VLESS+WS+TLS или REALITY без flow — это и есть причина.

Сервер

{
  "inbounds": [{
    "listen": "0.0.0.0",
    "port": 443,
    "protocol": "vless",
    "settings": {
      "clients": [{ "id": "ВАШ-UUID", "flow": "xtls-rprx-vision" }],
      "decryption": "none"
    },
    "streamSettings": {
      "network": "tcp",
      "security": "reality",
      "realitySettings": {
        "show": false,
        "dest": "github.com:443",
        "xver": 0,
        "serverNames": ["github.com"],
        "privateKey": "PRIVATE-KEY",
        "shortIds": ["", "0123abcd"]
      }
    }
  }],
  "outbounds": [{ "protocol": "freedom" }]
}

Клиент

{
  "outbounds": [{
    "protocol": "vless",
    "settings": {
      "vnext": [{
        "address": "IP-СЕРВЕРА",
        "port": 443,
        "users": [{
          "id": "ВАШ-UUID",
          "encryption": "none",
          "flow": "xtls-rprx-vision"
        }]
      }]
    },
    "streamSettings": {
      "network": "tcp",
      "security": "reality",
      "realitySettings": {
        "serverName": "github.com",
        "fingerprint": "chrome",
        "publicKey": "PUBLIC-KEY",
        "shortId": "0123abcd",
        "spiderX": ""
      }
    }
  }]
}

Ключи: xray x25519 (выдаёт private для сервера + public для клиента).


Шаг 2 — донор SNI/dest (подбор и автопроверка)

REALITY «прикидывается» этим сайтом (serverName), а dest/target — это куда сервер зеркалит TLS-рукопожатие при active-probing.

Критерии хорошего донора

  • Реальный высоконагруженный, «скучный», нейтральный сайт (не политика/не экзотика — такие сами под прицелом).
  • TLS 1.3 + HTTP/2 (h2), валидный сертификат, ответ 200 (без редиректа 301/302).
  • НЕ за Cloudflare и не за шаренным CDN, которым ты не управляешь (Cloudflare в РФ сам душат).
  • Гео/ASN плаузибельны относительно сервера (хостинг донора рядом с VPS, не в другой стране).
  • Не дефолтные SNI из гайдов (google/microsoft/amazon/cloudflare — заезжены).
  • Избегай Apple (apple.com/icloud.com) — несовпадение ASN выдаёт.

Два способа задать dest

  • Сосед-IP того же ДЦ как dest (лучший вариант): dest=<IP-соседа>:443, serverNames=["<его cert-domain>"]. IP топологически рядом с твоим VPS — максимально правдоподобно. Это основной кейс RealiTLScanner: он сам подтверждает tls=TLS1.3 alpn=h2 для соседних IP.
  • Публичный домен как dest: dest=<домен>:443. Проще, но домен резолвится в свой реальный хост (часто Cloudflare!) — обязательно вет через donorcheck.sh.

Ручная проверка одного домена

H=example.com
echo | openssl s_client -connect $H:443 -servername $H -tls1_3 -alpn h2 2>/dev/null \
  | grep -E "Protocol|ALPN|Verify return"
curl -4 -sI -o /dev/null -w "%{http_code} h%{http_version} %{remote_ip}\n" https://$H

Годен если: TLSv1.3 + ALPN protocol: h2 + Verify return code: 0 + 200 + IP не Cloudflare. (Без -servername Cloudflare-сайты отдают «No ALPN negotiated» — тест будет неточным.)

Отсев Cloudflare по IP (такие — мимо)

104.16.0.0–104.31.255.255, 172.64.0.0–172.71.255.255, 188.114.96.0/20, 162.158.x, 173.245.x, 141.101.x, 131.0.72.0/22.

Автоматизация (RealiTLScanner + скрипты)

Скрипты — в ~/Dev/Projects/vpn/utils/RealiTLScanner/ (исходники в конце раздела):

  • donorcheck.sh — вет списка/лога сканера: TLS1.3 + h2 + cert + HTTP + Cloudflare, метит << GOOD.
  • findonor.sh — скан подсети сервера + автовет в одну команду.
# найти доноров для сервера (скан его /24 + автовет):
./findonor.sh <IP-сервера>                 # нужен gtimeout: brew install coreutils
./findonor.sh <IP-сервера> | grep GOOD     # только победители

# или вручную в два шага (gtimeout не нужен):
./RealiTLScanner -addr <IP-сервера>/24 | tee scan.log   # Ctrl-C когда хватит
./donorcheck.sh scan.log | grep GOOD

Правило: сканируй подсеть того сервера, который будет использовать донор. Между серверами в разных ДЦ доноры не переиспользуются (иначе гео-мисматч). Реальный анонсируемый префикс: whois <IP> | grep -iE 'route|cidr|netrange|inetnum'.

Исходники скриптов

donorcheck.sh:

#!/usr/bin/env bash
# donorcheck.sh — auto-vet REALITY SNI/dest candidates from RealiTLScanner output.
# Per domain (resolved by hostname): TLS1.3, ALPN h2, valid cert, HTTP code, Cloudflare.
# Marks "<< GOOD" when: cert ok + h2 + HTTP 200 + NOT Cloudflare.
#   ./donorcheck.sh scan.log              # vet a scanner log
#   ./donorcheck.sh scan.log | grep GOOD  # only winners
#   ./donorcheck.sh domains.txt           # also accepts a plain domain list
set -u
PAR=12; TIMEOUT=8
TO=""
command -v gtimeout >/dev/null 2>&1 && TO="gtimeout $TIMEOUT"
[ -z "$TO" ] && command -v timeout >/dev/null 2>&1 && TO="timeout $TIMEOUT"
is_cf() {
  echo "${1:-}" | grep -qE '^(104\.(1[6-9]|2[0-9]|3[01])|172\.(6[4-9]|7[01])|188\.114|162\.158|173\.245|141\.101|131\.0\.7[0-3])\.' \
    && echo CF || echo -
}
vet_one() {
  local d="$1" out vok h2 res code ver ip cf good
  out=$(printf '' | $TO openssl s_client -connect "$d:443" -servername "$d" -tls1_3 -alpn h2 2>/dev/null)
  if [ -z "$out" ]; then printf '%-40s TLS1.3=NO  (no TLS1.3/h2 or unreachable)\n' "$d"; return; fi
  echo "$out" | grep -q 'Verify return code: 0' && vok=ok || vok=BADCERT
  echo "$out" | grep -q 'ALPN protocol: h2'      && h2=h2  || h2=--
  res=$($TO curl -4 -sI -o /dev/null --max-time "$TIMEOUT" \
        -w '%{http_code} %{http_version} %{remote_ip}' "https://$d" 2>/dev/null)
  code=${res%% *}; res=${res#* }; ver=${res%% *}; ip=${res##* }
  cf=$(is_cf "$ip"); good=""
  [ "$vok" = ok ] && [ "$h2" = h2 ] && [ "$code" = 200 ] && [ "$cf" = - ] && good="<< GOOD"
  printf '%-40s cert=%-7s alpn=%-2s http=%s/%s ip=%-15s %s %s\n' \
         "$d" "$vok" "$h2" "${code:-?}" "${ver:-?}" "${ip:-?}" "$cf" "$good"
}
if [ "${1:-}" = "--one" ]; then vet_one "$2"; exit; fi
DATA=$(cat "${1:-/dev/stdin}")
cands=$(printf '%s\n' "$DATA" | grep -oE 'cert-domain=[^ "]+' | sed -E 's/cert-domain=//')
[ -z "$cands" ] && cands="$DATA"
printf '%s\n' "$cands" \
  | sed -E 's/^\*\.//' \
  | grep -E '^[A-Za-z0-9._-]+\.[A-Za-z]{2,}$' \
  | grep -viE 'duckdns|staging|\.local$|example\.|androidapplications|game-solutions' \
  | sort -u \
  | xargs -P "$PAR" -I{} "$0" --one {}

findonor.sh:

#!/usr/bin/env bash
# findonor.sh — find & vet REALITY donors for ANY server in ONE command.
#   ./findonor.sh 203.0.113.7          # single IP -> scans its /24
#   ./findonor.sh 203.0.113.0/24       # explicit CIDR
#   ./findonor.sh 203.0.113.7 120      # scan 120s (default 90)
# Needs gtimeout (brew install coreutils) to bound a bare-IP scan.
set -u
HERE="$(cd "$(dirname "$0")" && pwd)"
TARGET="${1:?usage: findonor.sh <ip|cidr> [scan-seconds=90]}"
SECS="${2:-90}"
case "$TARGET" in
  */*) CIDR="$TARGET" ;;
  *)   CIDR="${TARGET%.*}.0/24" ;;
esac
TO=""
command -v gtimeout >/dev/null 2>&1 && TO="gtimeout ${SECS}s"
[ -z "$TO" ] && command -v timeout >/dev/null 2>&1 && TO="timeout ${SECS}s"
[ -z "$TO" ] && echo "[!] no gtimeout/timeout — pass a CIDR (not a bare IP) or it won't stop" >&2
LOG="/tmp/scan_$(echo "$CIDR" | tr -c 'A-Za-z0-9' _).log"
echo "[*] scanning $CIDR for ${SECS}s -> $LOG" >&2
$TO "$HERE/RealiTLScanner" -addr "$CIDR" 2>/dev/null | tee "$LOG"
echo "[*] vetting $(grep -c 'cert-domain' "$LOG" 2>/dev/null || echo 0) hits ..." >&2
"$HERE/donorcheck.sh" "$LOG"

Шаг 3 — смена fingerprint

Если chrome не помогает, в realitySettings.fingerprint пробуй по очереди: firefox, randomized, safari, qq. В мае возвращала доступ именно смена chrome → firefox/qq. Меняется только на клиенте, сервер не трогать.


Шаг 4 — транспорт XHTTP (если Vision всё равно ловят)

«Следующий шаг» против поведенческого анализа — VLESS + XHTTP + REALITY вместо TCP/Vision. XHTTP разносит upload/download в отдельные HTTP-транзакции и добавляет паддинг → больше похоже на браузер, можно завести за CDN. Ключевое в streamSettings:

"network": "xhttp",
"xhttpSettings": { "mode": "auto", "path": "/random-path", "xPaddingBytes": "100-1000" }

Vision и XHTTP взаимоисключающие: либо TCP+Vision, либо XHTTP без flow.


Шаг 5 — IP / ASN и TCP-заморозка (самое трудное)

Если бьёт механизм №2 (заморозка по объёму) или подсеть в блоке — конфиг бесполезен:

  • Сменить хостинг/ASN на менее засвеченный (Hetzner/DO/Vultr под прицелом). Ближе к РФ и «респектабельнее» ISP — лучше.
  • Завести VLESS за CDN (Cloudflare/Gcore через XHTTP/WS) — наружу торчит общий IP CDN. Учти: Cloudflare в РФ сам периодически душат.
  • IPv6, если оператор его нормально маршрутизирует.
  • Сплит соединений по 15–20 КБ технически обходит заморозку, но убивает скорость (50 МБ ≈ 2500 соединений) — нежизнеспособно.

Когда менять IP, а когда нет (флап vs жёсткий блок)

Не всякий обрыв = сожжённый IP. Это два разных диагноза:

Жёсткий блок Флап
Поведение лежит часами/сутками, сам не оживает пропадает на ~10 мин и возвращается сам
SSH / порт 22 тоже мёртв наглухо восстанавливается вместе со всем
Причина IP/подсеть в блок-листе эвристика ТСПУ / троттл оператора / объёмная заморозка / сам сервер
Лечится только новым IP новый IP НЕ поможет — переедешь с проблемой

Если IP сам оживает — он НЕ в чёрном списке, менять его смысла нет.

Диагностика флапа (2 шага):

  1. Серверная сторона или путь из РФ? В момент провала проверь сервер не из России (аптайм-монитор на 443 / пинг с другого зарубежного хоста):
    • доступен извне, мёртв только из РФ → путь РФ→IP (DPI/оператор);
    • мёртв отовсюду → сам сервер/хостер (смотри systemctl status xray, логи 3x-ui, CPU/RAM) — IP ни при чём.
  2. Если путь из РФ — с чем совпадает: время суток/часы пик → троттл оператора; тяжёлая загрузка → объёмная заморозка; одновременно у всех серверов → upstream.

Вывод: меняй IP только при жёстком блоке (лежит сам по себе долго). Для флапа — failover (ниже). Звоночек: если окна растут (10 мин → часы) — вот тогда IP «догорает», пора менять.


Резервирование и устойчивость

Массовый отвал 5 июня 2026 был IP/destination- и хостинг-уровня, не конфигом: ТСПУ выборочно дропали пакеты к конкретным зарубежным IP (потеря 30–90% SYN, «флап»), плюс ложились целые хостинги (Timeweb и др.). Вывод из их же анализа: «сменой порта не лечится — нужен новый зарубежный IP». Поэтому durable-защита — не твик REALITY, а 2–3 резервных метода:

  1. Второй выход — другой IP / ASN / страна / протокол. Чтобы поверхность детекта отличалась, бери не второй VLESS, а другой протокол: Hysteria 2 (UDP/QUIC), Shadowsocks-2022, NaiveProxy, из экзотики — ShadowTLS v3 / TUIC v5 / Trojan+gRPC.
  2. Запасной IP + быстрый редеплой (снапшот/скрипт). Раз блок по destination IP — спейр-адрес и есть главный фолбэк.
  3. Клиент Karing + авторизация Mixed. Почти все клиенты (v2rayNG, NekoBox, Hiddify, v2RayTun, Happ…) поднимают localhost SOCKS5 на 127.0.0.1 без пароля — любое приложение узнаёт реальный exit-IP твоего сервера → IP в блок-лист → сервер сгорает быстрее. Karing закрыл: Settings → Mixed → логин/пароль. Бонус — per-app split (банки/Госуслуги/такси напрямую, через туннель только нужное).
  4. Авто-failover в клиенте. Собери группу url-test из 2+ узлов (основной + резерв) — клиент сам переключится при провале и вернётся, когда основной оживёт. Закрывает 10-минутные флапы без смены IP.
  5. Порт + fingerprint наготове (запасные порты; fp=randomized уже стоит) и диверсификация хостеров (Timeweb лёг целиком — не держи всё на одном).

(по разборам массового отвала, Ilya Rublev / Boosty)


Что НЕ помогает (частые заблуждения)

  • WARP в 3x-ui — это egress (как сервер выходит в интернет), а твой блок на ingress (RU-клиент → твой IP). Флапы он не лечит. Включай WARP только если отдельные сайты блокируют IP твоего VPS на своей стороне (ChatGPT, Google-капчи, стриминги).
  • WARP ≠ Cloudflare CDN-фронтинг. Фронтинг (домен с оранжевым облаком + WS/XHTTP) меняет вход, но ломает REALITY (Cloudflare сам терминирует TLS), требует домен и сам душится в РФ.
  • Не уводи REALITY с порта 443. Фраза «VLESS на стандартных портах уже не вариант» — про голый VLESS без REALITY. Для REALITY 443 правильный (маскировка под HTTPS).
  • «Russian Trusted Root / расшифровка по ключам» — про сайты на российском хостинге. Твой зарубежный REALITY так не вскрывается: он не использует CA-сертификат, который РКН мог бы перехватить.

Гигиена (частые причины «не работает»)

  • Одинаковая версия Xray на клиенте и сервере (Vision/XHTTP несовместимы между версиями).
  • mux выключить при использовании Vision.
  • Порт 443, TCP.
  • Держать Xray-core свежим — улучшения Vision/XHTTP выходят часто.

Источники

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors