Files
networking-proxy/references/xray-reality-diagnostic-patterns.md
T
2026-09-06 13:51:04 +00:00

14 KiB
Raw Blame History

Session reference — Диагностика XRay Reality 2026-07-14

Контекст сессии

  • Сервер: VPS vps03 (80.209.240.167), XRay Reality на портах 443 и 8443
  • Клиент: Android, v2rayNG (версия не уточнена)
  • Оператор: мобильный, IP клиента 46.159.34.212
  • Проблема: Яндекс.Диск якобы работает через VPN, браузер — нет
  • Реальное положение дел (выяснилось позже): Пользователь отключал VPN, когда проверял Яндекс.Диск. VPN был реально выключен. Классический самообман.

Хронология диагностики

Фаза 1 — Подтверждение работоспособности сервера

  1. systemctl status xray → active ✅
  2. ss -tlnp | grep 443/8443 → LISTEN ✅
  3. journalctl -u xray --since ... | grep <user-ip> → видно REALITY: invalid connection from 46.159.34.212: failed to read client hello
  4. curl -v https://google.com с сервера → работает ✅ (через IPv4, IPv6 недоступен)
  5. curl -v -4 https://google.com с сервера → TLS handshake OK ✅

Вывод: Сервер жив, конфиг валиден, outbound freedom работает.

Фаза 2 — Анализ логов

accepted udp:46.159.34.212:XXXXX:53 [direct]              ← DNS проходит
REALITY: invalid connection from 46.159.34.212:XXXXX: failed to read client hello  ← TCP не проходит

Ключевое наблюдение: DNS-запросы (UDP:53) видны в логах как accepted udp:...:53 [direct], а TCP-соединения — только как REALITY: failed to read client hello. Ни одного accepted tcp: из этого IP не было.

Полезные наблюдения

curl зависает, а не таймаутит

Обычно curl на Android при недоступности пишет curl: (28) Connection timed out after X ms. В этой сессии curl просто висел бесконечно — не было ни ответа, ни таймаута. Это нестандартное поведение, указывающее на то, что TCP SYN достигает сервера, но дальнейшее взаимодействие обрывается.

REALITY: failed to read client hello — что это значит

Ошибка означает:

  1. TCP трёхстороннее рукопожатие состоялось (SYN → SYN-ACK → ACK прошли)
  2. После ACK сервер ожидает TLS ClientHello для проверки REALITY
  3. Клиент отправляет что-то, что непохоже на ClientHello, или отправляет ClientHello с неверными параметрами

Возможные причины (по вероятности):

  1. Клиент использует V2Ray-core вместо XRay-core — REALITY — эксклюзив XRay. V2Ray-core не умеет REALITY, соединение приходит но TLS-рукопожатие неправильное.
  2. Неверный publicKey или shortId — REALITY проверяет их на уровне ClientHello.
  3. Профиль v2rayNG не выбран — телефон шлёт трафик на порт 443, но не через v2rayNG.

Стратегия доменов IPIfNonMatch

Пользователь заметил, что смена Domain Strategy с ASIS на IPIfNonMatch один раз помогла, но потом перестала.

IPIfNonMatch — v2rayNG резолвит домен в IP, и только потом применяет правила маршрутизации. Если DNS на телефоне возвращает корректный IP, это может помочь обойти проблемы с DNS-перехватом.

Почему могло помочь один раз:

  • DNS-кеш на телефоне был пуст, первый запрос вернул правильный IP
  • После кеширования — запросы пошли по старому пути

IPv4 vs IPv6

При проверке curl https://google.com на сервере:

  • IPv6 адреса google.com резолвятся, но connect fails: Network is unreachable
  • IPv4 (-4) работают нормально

Это значит, что на VPS нет IPv6-связности. Если клиент резолвит google.com в IPv6 и пытается идти через XRay — соединение может не установиться.

Продолжение сессии — Обновление сервера и новая ошибка

Фаза 3 — proxy/vless/encoding: invalid request version

После обновления сервера с 26.3.27 до 26.7.11 (клиент: Xray-core 26.6.27) ошибка REALITY: failed to read client hello сменилась на:

rejected  proxy/vless/encoding: invalid request version

Значение: REALITY-рукопожатие прошло успешно, но VLESS-протокол на клиенте использует другой формат запроса, чем сервер.

Корень: версия Xray на сервере (26.3.27) и на клиенте (26.6.27) различаются. Между ними изменился VLESS-протокол. При этом install-release.sh не обновляет сервер, потому что проверяет только GitHub latest релиз (который всё ещё 26.3.27), а клиент (v2rayNG) имеет более новую сборку.

Как обновить сервер вручную:

# Найти последнюю версию
curl -sL https://api.github.com/repos/XTLS/Xray-core/releases | grep -E '"tag_name"' | head -5

# Скачать и распаковать
curl -sL -o /tmp/xray.zip "https://github.com/XTLS/Xray-core/releases/download/v26.7.11/Xray-linux-64.zip"
unzip -o /tmp/xray.zip xray -d /usr/local/bin/
chmod +x /usr/local/bin/xray
systemctl restart xray

Важно: unzip кладёт файлы рядом с xray (geoip.dat, geosite.dat). Если извлекать всё (unzip -o целиком) — перезаписываются актуальные geoip/geosite свежими из архива (что может быть ок, но лучше извлекать только бинарник).

Результат

После обновления сервера до 26.7.11 (новее клиента 26.6.27) ошибка invalid request version должна исчезнуть. Если остаётся — обновить и клиент (v2rayNG → последняя версия).

Продолжение сессии 2026-07-15 — Порто-независимая блокировка XRay Reality на Ростелеком

Контекст

  • Провайдер: Ростелеком (домашний WiFi)
  • Клиент: Android, v2rayNG 2.2.6 (Xray-core 26.6.27, Lib v38)
  • Сервер: XRay 26.7.11, Reality, VLESS+XTLS+Vision
  • Параметры: flow=xtls-rprx-vision, fingerprint=chrome (потом random), encryption=none

Симптом

Вечером предыдущего дня соединение работало. На следующее утро/день:

  • "Internet check failed" в v2rayNG
  • curl зависает без ответа (не таймаут, не connection refused)
  • В логах сервера: failed to read client hello с IP клиента

Попробованные меры

Мера Что изменили Результат
Порт 443 → 8443 → 50000 failed to read client hello на всех
dest reddit.com → microsoft.com → discord.com failed to read client hello на всех
serverNames reddit.com → microsoft.com → discord.com failed to read client hello (сначала server name mismatch, после исправления — failed to read client hello)
fingerprint chrome → random Без изменений
Всё вместе 50000 + discord.com + random Без изменений

Ни одно изменение не дало ни одного успешного accepted tcp: в логах. Все соединения обрывались на failed to read client hello.

Вывод

DPI на Ростелекоме научился распознавать ClientHello от XRay-core независимо от:

  • Порта назначения (443, 8443, 50000 — все под фильтром)
  • SNI (reddit.com, microsoft.com, discord.com — все под фильтром)
  • Настройки fingerprint (chrome и random — одинаково под фильтром)

Эскалация: В такой ситуации менять параметры Reality бесполезно. Нужно менять транспорт/протокол:

  1. WebSocket + TLS через nginx reverse proxy
  2. Trojan protocol
  3. AmneziaWG
  4. Shadowsocks + v2ray-plugin (WebSocket)

Ссылка на статью

Статья Конец удобства? Почему MTProxy начал ломаться (s4q, 2 апр 2026) описывает похожую ситуацию — TLS fingerprint DPI на уровне ClientHello. Хотя статья про MTProxy, механизм детекта тот же: DPI анализирует ClientHello и принимает решение до установки TLS-сессии. Разница: MTProxy использует Fake-TLS (поддельный ServerHello), Reality использует настоящий сервер. Но fingerprinter самого ClientHello может быть распознан независимо от того, что происходит после.

Решение: WebSocket plain (без TLS)

Шаг 1 — Добавить WS inbound на сервере:

{
  "port": 50002,
  "protocol": "vless",
  "settings": {
    "clients": [
      { "id": "cf4d32e5-9c19-460e-a5de-f881b01dcfe1" }
    ],
    "decryption": "none"
  },
  "streamSettings": {
    "network": "ws",
    "security": "none",
    "wsSettings": {
      "path": "/",
      "headers": { "Host": "discord.com" }
    }
  },
  "sniffing": { "enabled": false }
}

Ключевые моменты:

  • security: "none" — нет TLS. WS идёт через HTTP Upgrade, ClientHello не отправляется.
  • flow не указывается — для WS без TLS flow не нужен.
  • sniffing: false — для WS без TLS сниффинг не работает (нечего сниффать).
  • Host: "discord.com" в заголовках — для маскировки HTTP-запроса.

Шаг 2 — Проверить что порт слушается:

ss -tlnp | grep 50002

Шаг 3 — Настроить клиент (v2rayNG):

Поле Значение
address IP сервера
port 50002
id как на сервере
flow (пусто)
encryption none
network ws
security none (не TLS!)
ws path /
ws Host discord.com

Результат: Соединение установилось, 2ip.io показывает IP сервера (80.209.240.167). ✅

Почему это работает: WS без TLS не отправляет ClientHello. DPI не с чем сравнивать fingerprint. HTTP Upgrade-запрос не имеет характерных признаков прокси-трафика. VLESS шифрует содержимое внутри WS, так что конфиденциальность сохраняется.

Trade-off: WS без TLS виден как HTTP-трафик (не TLS), поэтому не маскируется под HTTPS. Пройдёт DPI, но может быть замечен провайдером как не-TLS трафик на нестандартном порту. Для дополнительной защиты можно использовать TLS-версию WS через nginx reverse proxy.

Выводы по сессии

  1. Самое вероятное объяснение (первоначальное): v2rayNG на телефоне использует V2Ray-core (старый), а не XRay-core. REALITY не поддерживается старым core. Решение: обновить v2rayNG до 1.8.13+ (там встроен XRay-core).
  2. Фактическая проблема: версионное несоответствие Xray между сервером (26.3.27) и клиентом (26.6.27). install-release.sh не обновляет до не-latest версий, требуется ручное обновление.
  3. Самое тривиальное объяснение: VPN был выключен при проверке Яндекс.Диска (подтверждено пользователем).
  4. Для будущей диагностики: Всегда проверять реальное состояние VPN на клиенте, не полагаться на слова пользователя. Лучший индикатор — логи сервера.
  5. Новый паттерн: proxy/vless/encoding: invalid request version → обновить сервер Xray до версии >= клиентской.