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

210 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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) имеет более новую сборку.
**Как обновить сервер вручную:**
```bash
# Найти последнюю версию
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 начал ломаться](https://habr.com/ru/articles/1018672/) (s4q, 2 апр 2026) описывает похожую ситуацию — TLS fingerprint DPI на уровне ClientHello. Хотя статья про MTProxy, механизм детекта тот же: DPI анализирует ClientHello и принимает решение до установки TLS-сессии. Разница: MTProxy использует Fake-TLS (поддельный ServerHello), Reality использует настоящий сервер. Но fingerprinter самого ClientHello может быть распознан независимо от того, что происходит после.
#### Решение: WebSocket plain (без TLS)
**Шаг 1 — Добавить WS inbound на сервере:**
```json
{
"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 до версии >= клиентской.