mirror of
https://gitverse.ru/kpa39l/networking-proxy.git
synced 2026-09-29 09:15:02 +00:00
Initial commit: Hermes skill networking-proxy
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
# SSH Tunnel: Telegram API via WireGuard + SOCKS5
|
||||
|
||||
## Контекст
|
||||
|
||||
Telegram Bot API заблокирован на сервере (Россия, DPI). Решение: SSH SOCKS5 туннель через VPS01 по WireGuard.
|
||||
|
||||
## Схема подключения
|
||||
|
||||
```
|
||||
bigbox (este сервер)
|
||||
├── wg0 — 10.8.0.2/24
|
||||
├── VPS01 — 10.8.0.1 (WireGuard IP), 5.129.217.146 (внешний)
|
||||
├── Остальные: VPS02 (87.242.100.206), VPS03 (80.209.240.167)
|
||||
└── SSH SOCKS5: 127.0.0.1:1080
|
||||
через root@10.8.0.1 (WireGuard адрес VPS01)
|
||||
```
|
||||
|
||||
## Текущая конфигурация (2026-07-15)
|
||||
|
||||
**Режим: SOCKS5 (-D).** Ранее был TCP forward (-L 127.0.0.1:8444:api.telegram.org:443), переключено на SOCKS5 для поддержки TELEGRAM_PROXY и удалённого DNS.
|
||||
|
||||
### WireGuard
|
||||
- Интерфейс: `wg0`
|
||||
- Адрес bigbox: `10.8.0.2/24`
|
||||
- Маска: `/24`
|
||||
- Сервис: `wg-quick@wg0.service` — автостарт после загрузки
|
||||
- Jump host: VPS01 (5.129.217.146, root, key timewebVPS)
|
||||
|
||||
### SSH SOCKS5 туннель
|
||||
|
||||
```ini
|
||||
ExecStart=/usr/bin/ssh \
|
||||
-i /mnt/yandex-disk/.ssh_box/timewebVPS \
|
||||
-D 127.0.0.1:1080 \
|
||||
-N \
|
||||
-o ServerAliveInterval=30 \
|
||||
-o ServerAliveCountMax=3 \
|
||||
-o ExitOnForwardFailure=yes \
|
||||
root@10.8.0.1
|
||||
```
|
||||
|
||||
- Сервис: `telegram-tunnel.service`
|
||||
- Файл: `/etc/systemd/system/telegram-tunnel.service`
|
||||
- Ключ: `/mnt/yandex-disk/.ssh_box/timewebVPS` (совпадает с `~/.ssh/timewebVPS`, хранится на Yandex.Disk davfs)
|
||||
- Автостарт: включён (`systemctl enable telegram-tunnel.service`)
|
||||
|
||||
### Hermes Gateway
|
||||
|
||||
- Сервис: `hermes-gateway.service`
|
||||
- В `.env` (`~/.hermes/.env`):
|
||||
- `TELEGRAM_BOT_TOKEN=8620910071:...`
|
||||
- `TELEGRAM_PROXY=socks5://127.0.0.1:1080`
|
||||
- Gateway читает `TELEGRAM_PROXY` через `resolve_proxy_url()` и использует socks5
|
||||
- В venv должен быть `aiohttp-socks` (установлен)
|
||||
- DNS резолвится на VPS01 (обход блокировки DNS)
|
||||
|
||||
### SSH конфиг (`~/.ssh/config`)
|
||||
```
|
||||
Host vps01
|
||||
Hostname 5.129.217.146
|
||||
User root
|
||||
IdentityFile ~/.ssh/timewebVPS
|
||||
IdentitiesOnly yes
|
||||
ForwardAgent yes
|
||||
```
|
||||
|
||||
### Проверка после перезагрузки
|
||||
|
||||
```bash
|
||||
# 1. WireGuard
|
||||
ip addr show wg0
|
||||
|
||||
# 2. SOCKS5 туннель
|
||||
systemctl status telegram-tunnel
|
||||
ss -tlnp | grep 1080
|
||||
|
||||
# 3. Gateway
|
||||
systemctl status hermes-gateway
|
||||
|
||||
# 4. Telegram API доступен через SOCKS5
|
||||
TOKEN=$(grep -a TELEGRAM_BOT_TOKEN ~/.hermes/.env | grep -v "^#" | cut -d= -f2-)
|
||||
curl -s --socks5 127.0.0.1:1080 "https://api.telegram.org/bot${TOKEN}/getMe"
|
||||
|
||||
# 5. IP выходит через VPS01
|
||||
curl -s --socks5 127.0.0.1:1080 https://httpbin.org/ip
|
||||
|
||||
# 6. Логи туннеля
|
||||
journalctl -u telegram-tunnel --since "1 hour ago"
|
||||
```
|
||||
|
||||
## Порядок старта после перезагрузки
|
||||
|
||||
1. Сеть → `network-online.target`
|
||||
2. Yandex.Disk davfs → `/etc/fstab` (ключи на FUSE)
|
||||
3. WireGuard → `wg-quick@wg0.service`
|
||||
4. SSH SOCKS5 туннель → `telegram-tunnel.service`
|
||||
5. Hermes Gateway → `hermes-gateway.service`
|
||||
|
||||
## Заметка в Obsidian
|
||||
|
||||
Создана: mozg → homelab/hermes/Gateway Telegram tunnel.md
|
||||
|
||||
## История изменений
|
||||
|
||||
- 2026-07-15: Переход с TCP forward (-L 8444) на SOCKS5 (-D 1080)
|
||||
- Причина: поддержка TELEGRAM_PROXY, удалённый DNS, единый порт для нескольких сервисов
|
||||
- Добавлено: `TELEGRAM_PROXY=socks5://127.0.0.1:1080` в `.env`
|
||||
@@ -0,0 +1,97 @@
|
||||
# Subscription Distribution for v2rayNG — Session Example
|
||||
|
||||
## Context
|
||||
|
||||
VPS (80.209.240.167, debian, `vps03.nixg.ru`), XRay с одним UUID на все протоколы. Рабочий протокол: VLESS+WebSocket без TLS на порту 50002. Reality порты (443, 8443, 50000) заблокированы DPI (TLS fingerprint). Shadowsocks нестабилен.
|
||||
|
||||
## Цель
|
||||
|
||||
Раздать конфиг WS 50002 на несколько Android-устройств членов семьи через subscription.
|
||||
|
||||
## Шаги
|
||||
|
||||
### 1. Установить Caddy
|
||||
|
||||
```bash
|
||||
apt-get update && apt-get install -y caddy
|
||||
systemctl status caddy # active (running)
|
||||
```
|
||||
|
||||
### 2. Создать файл subscription
|
||||
|
||||
```bash
|
||||
cat > /etc/caddy/subscription.txt << 'EOF'
|
||||
vless://cf4d32e5-9c19-460e-a5de-f881b01dcfe1@vps03.nixg.ru:50002?encryption=none&security=none&type=ws&host=discord.com&path=%2F#XRay+WS+50002
|
||||
EOF
|
||||
```
|
||||
|
||||
### 3. Скопировать в document root Caddy
|
||||
|
||||
```bash
|
||||
cp /etc/caddy/subscription.txt /usr/share/caddy/subscription
|
||||
```
|
||||
|
||||
### 4. Настроить Caddyfile (HTTP только, порт 80)
|
||||
|
||||
```caddyfile
|
||||
:80 {
|
||||
root * /usr/share/caddy
|
||||
file_server
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Перезагрузить и проверить
|
||||
|
||||
```bash
|
||||
systemctl stop caddy
|
||||
# правим Caddyfile
|
||||
systemctl start caddy
|
||||
curl -s http://localhost/subscription
|
||||
# → vless://... (должен вернуть ссылку)
|
||||
```
|
||||
|
||||
## Генерация share-ссылки
|
||||
|
||||
Формат для v2rayNG (VLESS+WS без TLS):
|
||||
|
||||
```
|
||||
vless://UUID@host:port?encryption=none&security=none&type=ws&host=discord.com&path=%2F#remark
|
||||
```
|
||||
|
||||
- `path=%2F` — это URL-encoded `/` (корневой путь)
|
||||
- `host=discord.com` — заголовок Host в HTTP Upgrade
|
||||
- `#remark` — имя профиля в v2rayNG
|
||||
|
||||
## Проблема: Caddy не может занять 443
|
||||
|
||||
XRay Reality уже слушает 443. Caddy пытается повесить HTTPS на 443 и падает:
|
||||
|
||||
```
|
||||
listen tcp :443: bind: address already in use
|
||||
```
|
||||
|
||||
**Решение:** раздавать subscription через HTTP (порт 80). v2rayNG не требует HTTPS для subscription.
|
||||
|
||||
## Добавление в v2rayNG
|
||||
|
||||
1. Открыть v2rayNG
|
||||
2. Меню (⋮) → **Subscription Group**
|
||||
3. `+` → ввести URL: `http://vps03.nixg.ru/subscription`
|
||||
4. ✅ → **Update** → появится профиль **XRay WS 50002**
|
||||
5. Тапнуть на профиль, чтобы подключиться
|
||||
|
||||
## Добавление нескольких конфигов
|
||||
|
||||
Просто дописать каждую share-ссылку с новой строки в файл `/usr/share/caddy/subscription`:
|
||||
|
||||
```
|
||||
vless://...@...:50002?...#WS+50002
|
||||
vless://...@...:443?...#Reality+443
|
||||
ss://...@...:50003?...#Shadowsocks
|
||||
```
|
||||
|
||||
## Важные детали
|
||||
|
||||
- **UUID общий для семьи** — если не нужно разграничение доступа, хватит одного UUID на всех
|
||||
- **Обновление конфигов** — отредактировать файл на сервере → v2rayNG обновит при следующем poll (или нажать Update вручную)
|
||||
- **HTTP vs HTTPS** — subscription не содержит секретов (ссылки уже есть у пользователя), HTTP безопасен для этой задачи
|
||||
@@ -0,0 +1,133 @@
|
||||
# TLS Fingerprint — DPI Detection Technique
|
||||
|
||||
## Источник
|
||||
|
||||
Статья на Хабре: [Конец удобства? Почему MTProxy начал ломаться](https://habr.com/ru/articles/1018672/) (2 апреля 2026, s4q)
|
||||
|
||||
## Суть техники
|
||||
|
||||
DPI (Deep Packet Inspection) анализирует TLS ClientHello на предмет узнаваемого отпечатка (fingerprint). Отпечаток складывается из:
|
||||
|
||||
- Порядка cipher suites
|
||||
- Списка TLS extensions
|
||||
- Supported groups (elliptic curves)
|
||||
- Signature algorithms
|
||||
- Версии TLS
|
||||
- Других параметров ClientHello
|
||||
|
||||
У каждого TLS-клиента (браузер, Telegram, curl) — уникальная комбинация этих параметров. DPI сравнивает её с базой известных fingerprint'ов и принимает решение: пропустить, деградировать (RST-инъекция) или дропнуть пакеты.
|
||||
|
||||
## Применимость к XRay Reality
|
||||
|
||||
XRay Reality принципиально отличается от MTProxy/Fake-TLS тем, что **не имитирует TLS, а использует настоящий TLS-сервер** (reddit.com, cloudflare.com и т.д.) как маскировку. Reality:
|
||||
|
||||
1. Принимает ClientHello от клиента
|
||||
2. Проверяет REALITY-подпись (publicKey + shortId + время)
|
||||
3. Если подпись невалидна — проксирует соединение на реальный сервер (dest), как обычный HTTPS
|
||||
4. Если подпись валидна — расшифровывает VLESS-трафик
|
||||
|
||||
**Разница с MTProxy:** MTProxy использует Fake-TLS — сам отправляет поддельный ServerHello, не обращаясь к реальному серверу. У Reality — настоящий TLS-сервер на backend'е, поэтому поведение трафика после рукопожатия неотличимо от браузерного.
|
||||
|
||||
## Почему это важно для диагностики
|
||||
|
||||
Когда в логах XRay появляется `REALITY: failed to read client hello`, это может быть следствием:
|
||||
|
||||
1. **Неверный publicKey/shortId в клиенте** — DPI не при чём, чисто конфигурационная проблема
|
||||
2. **DPI обрезает ClientHello на уровне сети** — пакет приходит повреждённым/обрезанным
|
||||
3. **Клиент отправляет ClientHello с нестандартными параметрами** — например, из-за ошибки в реализации TLS
|
||||
|
||||
### Случай из статьи: баг в encrypted_client_hello Telegram
|
||||
|
||||
В коде генерации `encrypted_client_hello` у Telegram нашли ошибку:
|
||||
- Формируется нестандартное TLS-расширение с **неверным ID** и **некорректной длиной**
|
||||
- Из-за этого ClientHello Telegram легко матчится ТСПУ
|
||||
- Патч подготовлен, но на момент статьи не смерджен в официальный tdesktop
|
||||
|
||||
**Этот случай — иллюстрация того, как даже небольшая ошибка в TLS-реализации клиента делает его узнаваемым для DPI.** Применительно к XRay: если клиент (v2rayNG, XRay-core на Android) отправляет ClientHello, который отличается от нормального браузерного, DPI может отфильтровать соединение ещё до того, как XRay сервер успеет проверить REALITY-подпись.
|
||||
|
||||
## Как DPI взаимодействует с XRay Reality
|
||||
|
||||
### Вариант A — Дроп после ClientHello
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
Client->>DPI: TCP SYN
|
||||
DPI->>Server: TCP SYN
|
||||
Server->>DPI: TCP SYN-ACK
|
||||
DPI->>Client: TCP SYN-ACK
|
||||
Client->>DPI: TCP ACK
|
||||
Client->>DPI: TLS ClientHello
|
||||
DPI->>DPI: Анализ fingerprint
|
||||
Note over DPI: ClientHello не похож на браузерный?<br/>Дропаем/инжектим RST
|
||||
DPI->>Client: RST (поддельный)
|
||||
```
|
||||
|
||||
**Проявление:** `failed to read client hello` в логах сервера, при этом TCP SYN успешно доходит.
|
||||
|
||||
### Вариант B — Деградация после установки соединения
|
||||
|
||||
DPI пропускает ClientHello, но после установки REALITY-сессии анализирует поведение трафика. Если трафик не похож на HTTPS — начинает дропать пакеты.
|
||||
|
||||
**Проявление:** соединение устанавливается, работает несколько секунд/минут, потом обрывается.
|
||||
|
||||
### Дополнительные механизмы DPI из статьи
|
||||
|
||||
Статья описывает несколько техник DPI, которые выходят за рамки простого fingerprint-матчинга:
|
||||
|
||||
1. **Поведенческий анализ после соединения:** DPI может пропустить ClientHello, установить TLS-сессию, но затем анализировать **паттерны трафика** (размеры пакетов, частоту, направление). Если трафик не похож на HTTPS-сёрфинг (например, идёт постоянный поток данных без видимых HTTP-запросов), DPI начинает дропать или деградировать соединение.
|
||||
|
||||
2. **Деградация (throttling) вместо блокировки:** Вместо RST или дропа пакетов DPI может **замедлять** соединение — искусственно вносить задержки, ограничивать пропускную способность. Пользователь видит «тормозит, но работает», что сложнее диагностировать, чем полный обрыв.
|
||||
|
||||
3. **Адаптивное обучение:** DPI не имеет статической базы fingerprint'ов. Он может **обучаться** новым сигнатурам в реальном времени. Если новый fingerprint появился и DPI его пропустил (например, потому что трафика мало), через некоторое время он может начать его блокировать. **Это объясняет паттерн «работало вчера, перестало сегодня»** — DPI потребовалось время, чтобы накопить статистику и обучить классификатор.
|
||||
|
||||
4. **Корреляция по нескольким признакам:** DPI может комбинировать fingerprint + SNI + IP + порт + поведение. Если хотя бы один признак совпадает с известной сигнатурой прокси — соединение помечается как подозрительное.
|
||||
|
||||
### Баг в encrypted_client_hello Telegram (из статьи)
|
||||
|
||||
В коде генерации `encrypted_client_hello` у Telegram нашли ошибку:
|
||||
- Формируется нестандартное TLS-расширение с **неверным ID** и **некорректной длиной**
|
||||
- Из-за этого ClientHello Telegram легко матчится ТСПУ
|
||||
- Патч подготовлен, но на момент статьи не смерджен в официальный tdesktop
|
||||
|
||||
**Этот случай — иллюстрация того, как даже небольшая ошибка в TLS-реализации клиента делает его узнаваемым для DPI.** Применительно к XRay: если клиент (v2rayNG, XRay-core на Android) отправляет ClientHello, который отличается от нормального браузерного, DPI может отфильтровать соединение ещё до того, как XRay сервер успеет проверить REALITY-подпись.
|
||||
|
||||
## Меры противодействия
|
||||
|
||||
1. **Использовать Reality вместо Fake-TLS/MTProxy** — Reality уже имеет встроенную маскировку под реальный HTTPS
|
||||
2. **Менять `fingerprint` в клиенте** — `chrome` / `firefox` / `random`. Если провайдер знает fingerprint конкретного браузера, смена маски может помочь
|
||||
3. **Менять `dest` на сервере** — если DPI блокирует fingerprint клиента для конкретного сайта, смена `reddit.com` на `cloudflare.com` или `microsoft.com` может обойти блокировку (потому что DPI может сверять fingerprint + dest)
|
||||
4. **Добавлять второй порт** — 8443, 2053, 2096 — на разных портах DPI может работать по-разному
|
||||
5. **Encrypted Client Hello (ECH)** — скрывает часть параметров TLS-рукопожатия, но пока не везде поддерживается и может блокироваться отдельно
|
||||
6. **Обновлять клиент** — fingerprint клиента может меняться с версиями v2rayNG/XRay-core
|
||||
|
||||
## Эмпирические наблюдения: когда меры не помогают (сессия 2026-07-15)
|
||||
|
||||
В ходе диагностики на Ростелеком (домашний WiFi) был задокументирован случай, когда **ни одна из стандартных мер не дала результата**:
|
||||
|
||||
| Мера | Что пробовали | Результат |
|
||||
|------|--------------|-----------|
|
||||
| Смена порта | 443 → 8443 → 50000 | `failed to read client hello` на всех |
|
||||
| Смена dest | reddit.com → microsoft.com → discord.com | `failed to read client hello` на всех |
|
||||
| Смена fingerprint | chrome → random | Без изменений |
|
||||
| Смена shortId | Не пробовали | — |
|
||||
|
||||
**Контекст:** пользователь Ростелеком, домашний WiFi. Вечером предыдущего дня соединение работало. На следующее утро/день — перестало на всех портах и всех маскирующих доменах. Сервер XRay 26.7.11, клиент Xray-core 26.6.27 (встроен в v2rayNG 2.2.6). Параметры: flow=xtls-rprx-vision, fingerprint=chrome (позже random), encryption=none.
|
||||
|
||||
**Вывод:** DPI на конкретном провайдере (Ростелеком) может детектить XRay Reality **независимо от порта и маскирующего домена**. Fingerprint клиента (v2rayNG отправляет ClientHello с характерными параметрами XRay-core) является достаточным признаком для блокировки. Это означает, что DPI обучился на fingerprint XRay-core, а не на конкретном сайте или порте.
|
||||
|
||||
**Ограничение эксперимента:** Не пробовались другие транспорты (gRPC, WebSocket, QUIC), не пробовался Trojan или Shadowsocks на тех же портах. Возможно, блокировка специфична именно для VLESS+Reality+Vision — следующая попытка должна сменить протокол.
|
||||
|
||||
### Что пробовать в такой ситуации (от простого к сложному)
|
||||
|
||||
1. **Сменить transport** — Reality → WebSocket + TLS (через nginx reverse proxy) → другая сигнатура ClientHello
|
||||
2. **Сменить протокол** — VLESS → Trojan → другой fingerprint XRay-core
|
||||
3. **Сменить `fingerprint` на `random`** в клиенте — тогда XRay-core будет генерировать случайный ClientHello каждый раз, что делает fingerprint-анализ бесполезным
|
||||
4. **Добавить CDN/Cloudflare перед сервером** — если IP провайдера попал в «серые списки», CDN скроет реальный IP
|
||||
5. **Try AmneziaWG** — использует другой механизм обфускации, не основанный на TLS fingerprint
|
||||
6. **Try Shadowsocks + v2ray-plugin (WebSocket)** — другая транспортировка, другой fingerprint
|
||||
|
||||
### Ключевой вывод
|
||||
|
||||
Статья про MTProxy — это не про MTProxy. Это про то, что **DPI перешёл на новый уровень анализа**. Fingerprint-фильтрация ClientHello стала реальностью. XRay Reality спроектирован с учётом этого (использует настоящий TLS-сервер), но не застрахован полностью: если ClientHello от клиента выглядит подозрительно, DPI может заблокировать его ещё до REALITY-проверки.
|
||||
|
||||
**Урок на будущее:** fingerprint клиента XRay-core (даже с `chrome`/`random`) может быть распознан DPI независимо от порта и dest. В такой ситуации нужно менять не параметры Reality, а транспорт/протокол целиком.
|
||||
@@ -0,0 +1,210 @@
|
||||
# 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 до версии >= клиентской.
|
||||
@@ -0,0 +1,251 @@
|
||||
# Session reference — XRay Reality настройка 2026-07-14
|
||||
|
||||
## Окружение
|
||||
- **US VPS:** vps03.nixg.ru (80.209.240.167), Debian 13 (trixie), root доступ
|
||||
- **RU VPS:** cloud.ru (vps02, 87.242.100.206), пользователь estorozhenko
|
||||
- **Клиент:** Android, v2rayNG (v2.2.6)
|
||||
- **SSH ключи:** /mnt/yandex-disk/.ssh_box/hostkeyVPS
|
||||
- **Маскировка:** reddit.com (SNI), fingerprint chrome
|
||||
- **Пинг US→RU:** ~130ms
|
||||
|
||||
## Баг heredoc — подробности
|
||||
|
||||
**Проблема:** При записи JSON-конфига через SSH с heredoc:
|
||||
```bash
|
||||
ssh root@host "cat > /path/config.json << 'EOF'
|
||||
{ \"key\": \"value\" }
|
||||
EOF\"
|
||||
```
|
||||
|
||||
Все кавычки в JSON теряются — на сервере оказывается:
|
||||
```json
|
||||
{
|
||||
key: value
|
||||
}
|
||||
```
|
||||
XRay выдаёт ошибку: `invalid character 'l' looking for beginning of object key string` (ожидал кавычку, получил 'l' от `log`).
|
||||
|
||||
**Причина:** Двойная обработка shell'ом: сначала локальный shell разбирает кавычки, потом передаёт через SSH, и quoted heredoc (`'EOF'`) не спасает — кавычки съедаются на одном из уровней.
|
||||
|
||||
**Проверенное решение — scp:**
|
||||
```bash
|
||||
# 1. Напиши локально
|
||||
write_file(path="/tmp/xray_config.json", content="...")
|
||||
|
||||
# 2. Скопируй через SCP
|
||||
scp -i /path/key -o StrictHostKeyChecking=no /tmp/xray_config.json root@host:/usr/local/etc/xray/config.json
|
||||
|
||||
# 3. Перезапусти
|
||||
ssh -i /path/key root@host "systemctl restart xray"
|
||||
```
|
||||
|
||||
**Альтернатива — Python json.dump() over SSH:**
|
||||
```bash
|
||||
ssh root@host "python3 -c '
|
||||
import json
|
||||
config = { ... }
|
||||
with open(\"/usr/local/etc/xray/config.json\", \"w\") as f:
|
||||
json.dump(config, f, indent=2)
|
||||
'"
|
||||
```
|
||||
Но с этим тоже были проблемы экранирования — scp надёжнее.
|
||||
|
||||
## Полный конфиг сервера (рабочий)
|
||||
|
||||
```json
|
||||
{
|
||||
"log": {
|
||||
"loglevel": "warning"
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"port": 443,
|
||||
"protocol": "vless",
|
||||
"settings": {
|
||||
"clients": [
|
||||
{
|
||||
"id": "<uuid>",
|
||||
"flow": "xtls-rprx-vision"
|
||||
}
|
||||
],
|
||||
"decryption": "none"
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "tcp",
|
||||
"security": "reality",
|
||||
"realitySettings": {
|
||||
"show": false,
|
||||
"dest": "reddit.com:443",
|
||||
"xver": 0,
|
||||
"serverNames": [
|
||||
"reddit.com",
|
||||
"www.reddit.com"
|
||||
],
|
||||
"privateKey": "<privateKey>",
|
||||
"shortIds": [
|
||||
"<shortId>"
|
||||
]
|
||||
}
|
||||
},
|
||||
"sniffing": {
|
||||
"enabled": true,
|
||||
"destOverride": ["http", "tls", "quic"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"protocol": "freedom",
|
||||
"tag": "direct"
|
||||
},
|
||||
{
|
||||
"protocol": "blackhole",
|
||||
"tag": "block"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Рабочая ссылка для импорта в v2rayNG
|
||||
|
||||
Порт 443 (основной):
|
||||
```
|
||||
vless://cf4d32e5-9c19-460e-a5de-f881b01dcfe1@80.209.240.167:443?encryption=none&flow=xtls-rprx-vision&security=reality&sni=reddit.com&fp=chrome&pbk=_ZuQgpScINJVuTRhifNX7cy9MikcNxQNq1vLOZa9ZBE&sid=f8701c8463b82974&type=tcp&headerType=none#XRay-Reality-US
|
||||
```
|
||||
|
||||
Порт 8443 (если 443 блокируется оператором):
|
||||
```
|
||||
vless://cf4d32e5-9c19-460e-a5de-f881b01dcfe1@80.209.240.167:8443?encryption=none&flow=xtls-rprx-vision&security=reality&sni=reddit.com&fp=chrome&pbk=_ZuQgpScINJVuTRhifNX7cy9MikcNxQNq1vLOZa9ZBE&sid=f8701c8463b82974&type=tcp&headerType=none#XRay-Reality-US-8443
|
||||
```
|
||||
|
||||
## Диагностика "не подключается"
|
||||
|
||||
Когда клиент пишет "connect deadline exceeded" / "Сбой проверки интернет-соединения":
|
||||
|
||||
```bash
|
||||
# 1. Сервер жив?
|
||||
systemctl status xray
|
||||
ss -tlnp | grep -E '443|8443'
|
||||
|
||||
# 2. Конфиг валиден?
|
||||
xray run -test -config /usr/local/etc/xray/config.json
|
||||
|
||||
# 3. Публичный ключ совпадает?
|
||||
xray x25519 -i <privateKey> # сравни с тем, что в клиенте
|
||||
|
||||
# 4. Сервер выходит в интернет?
|
||||
curl -s -o /dev/null -w '%{http_code}' https://reddit.com
|
||||
|
||||
# 5. Внешняя доступность порта?
|
||||
# с другого хоста:
|
||||
nc -w 5 80.209.240.167 443 && echo "OK" || echo "BLOCKED"
|
||||
|
||||
# 6. Логи xray — есть ли accepted?
|
||||
journalctl -u xray --since '30 min ago' --no-pager | grep 'accepted'
|
||||
```
|
||||
|
||||
Если всё выше ок — проблема **на стороне мобильного оператора** (блокировка порта, а не IP).
|
||||
|
||||
## Multi-port стратегия
|
||||
|
||||
При блокировке 443 порта мобильным оператором — добавить второй inbound на другой порт. Просто скопировать блок `inbounds[0]`, заменив `port`. Перезапустить xray.
|
||||
|
||||
Дополнить ссылку для клиента: заменить `@IP:443` на `@IP:8443`.
|
||||
|
||||
## ICMP не работает через VLESS+Reality (важно для диагностки)
|
||||
|
||||
XRay VLESS с flow=`xtls-rprx-vision` не проксирует **ICMP** (ping/traceroute).
|
||||
Проксируется только **TCP** и **UDP**.
|
||||
|
||||
Когда пользователь пишет "VPN подключился, но пинг не идёт / сайты не грузятся":
|
||||
|
||||
- **ping 8.8.8.8** → ❌ не работает через XRay (ICMP)
|
||||
- **curl https://google.com** → ✅ должно работать (TCP)
|
||||
- **curl -s ifconfig.me** → ✅ покажет внешний IP сервера
|
||||
- **nslookup google.com** → ✅ работает (UDP:53)
|
||||
- **Открыть сайт в браузере** → ✅ должно работать
|
||||
|
||||
**Сценарии:**
|
||||
1. ping не идёт, но curl работает → **VPN в порядке**, это норма
|
||||
2. ping не идёт и curl не работает → проблема с TCP-маршрутизацией (см. "Диагностика" ниже)
|
||||
3. tcpdump показывает трёхстороннее рукопожатие (SYN→SYN-ACK→ACK) → соединение с сервером есть, проблему искать в DNS или outbound-маршрутизации
|
||||
|
||||
**Live-диагностика через tcpdump (новые соединения):**
|
||||
```bash
|
||||
# На сервере — мониторинг порта 443
|
||||
tcpdump -i any -n port 443 -c 10 -t
|
||||
|
||||
# Пользователь в это время пробует открыть сайт
|
||||
# SYNs не видно → телефон не шлёт трафик (проблема с настройкой клиента)
|
||||
# Видно SYN→SYN-ACK→ACK → рукопожатие есть, проблема в XRay routing
|
||||
# Только FIN от старых сессий → старые сессии закрываются, новых нет
|
||||
```
|
||||
|
||||
## Ротация ключей Reality (при смене конфига или утечке)
|
||||
|
||||
Периодическая смена ключей Reality повышает безопасность. Полный цикл:
|
||||
|
||||
```bash
|
||||
# 1. Сгенерировать новую пару
|
||||
xray x25519
|
||||
# Вывод:
|
||||
# PrivateKey: SEnziJbX3vDlWEQeGHHYXWa5DP3WXJrydkUJbUCh2FQ
|
||||
# Password (PublicKey): 7PS-NrEqmF9_AcXrxjladd9zEAIxeE2LpfCAMsEQ_As
|
||||
|
||||
# 2. Сгенерировать новый shortId (8 байт hex)
|
||||
openssl rand -hex 8
|
||||
# Вывод: b11c31731d6d43d8
|
||||
|
||||
# 3. Обновить privateKey в serverNames и shortId в конфиге сервера
|
||||
# 4. Перезапустить xray
|
||||
systemctl restart xray
|
||||
|
||||
# 5. Передать клиенту:
|
||||
# - PublicKey из шага 1
|
||||
# - shortId из шага 2
|
||||
# (UUID и адрес не меняются)
|
||||
```
|
||||
|
||||
## Режим debug в логах XRay
|
||||
|
||||
При проблемах с соединением временно включить debug:
|
||||
|
||||
```json
|
||||
"log": {
|
||||
"loglevel": "debug"
|
||||
}
|
||||
```
|
||||
|
||||
После отладки вернуть `"warning"` — debug пишет очень много.
|
||||
|
||||
В debug видны:
|
||||
- `accepted tcp:...` — входящие соединения
|
||||
- `accepted udp:...:53 [direct]` — DNS-запросы (если есть → клиент умеет резолвить)
|
||||
- `drain...` — завершение сессий
|
||||
- Ошибки рукопожатия и таймауты
|
||||
|
||||
## Полезные команды
|
||||
|
||||
```bash
|
||||
# Сгенерировать UUID
|
||||
python3 -c 'import uuid; print(uuid.uuid4())'
|
||||
|
||||
# Сгенерировать ключевую пару Reality
|
||||
xray x25519
|
||||
|
||||
# Получить public key из private key
|
||||
xray x25519 -i <privateKey>
|
||||
|
||||
# Прослушиваемые порты
|
||||
ss -tlnp | grep xray
|
||||
|
||||
# Логи xray
|
||||
journalctl -u xray --since '1 hour ago' --no-pager
|
||||
```
|
||||
|
||||
## Возможные проблемы
|
||||
|
||||
1. **XRay запущен, но порт 443 не отображается** — сначала проверь ss. Если там только SSH (22) и exim (25), проверь, что XRay стартовал без ошибок. Причина часто — битый JSON (см. баг heredoc).
|
||||
2. **Connection refused при curl** — это нормально с локальной машины, если нет маршрута до сервера (там порт 443 открыт только на интерфейсе *:443). Проверять через `nc`.
|
||||
3. **uuidgen: command not found** — на минимальных Debian-образах uuidgen отсутствует. Использовать `python3 -c 'import uuid; print(uuid.uuid4())'`.
|
||||
4. **Client "connect deadline exceeded" при работающем сервере** — почти всегда блокировка порта оператором. Сменить порт или попробовать с Wi-Fi.
|
||||
Reference in New Issue
Block a user