Files
netrunner-proxy/core/src/nrxp/README.MD
T
nineap 99a9eee908 Улучшить TLS-маскировку, ввести версионирование протокола, добавить тесты и нагрузочный инструмент
Маскировка:
- ChangeCipherSpec middlebox-compat запись после ClientHello/ServerHello —
  без неё последовательность типов TLS-записей отличала нас от настоящего
  браузера даже при идеальном JA3/JA4-отпечатке.
- SNI-aware stealth fallback: невалидный клиент проксируется на запрошенный
  им хост, а не всегда на один и тот же decoy — иначе активное зондирование
  с разными SNI на одном IP получало одинаковый ответ. Резолв только в
  публичные IP (защита от SSRF на внутреннюю сеть ноды через подставной SNI).
- Профиль браузера привязан к session_id, а не к номеру попытки реконнекта:
  раньше при нескольких быстрых ретраях с одного IP летели ClientHello
  Chrome→Edge→Firefox подряд — сама по себе аномалия для DPI.
- Бакетный паддинг Data/UdpData кадров вместо полного отсутствия паддинга;
  джиттер перед ServerHello вместо мгновенного детерминированного ответа.

Версионирование:
- PROTOCOL_VERSION в session_id ClientHello — сервер узнаёт версию клиента
  до отправки чего-либо и может включать версионно-зависимое поведение
  (сейчас — CCS) только для тех, кто его понимает. Позволяет катить будущие
  несовместимые изменения протокола без синхронного flag-day релиза.

Тесты (core, было 0):
- Round-trip тесты TLS-записей/hello-сообщений/кадров/кодека.
- Полный цикл хендшейка с реальным выводом ключей на обеих сторонах.
- Вся SSRF-защита (hostname/IP валидация, v4/v6).
- Интеграционные тесты по настоящему TCP: легитимный хендшейк и
  stealth-fallback на мусорный ClientHello.

tools/loadtest — нагрузочный тест поверх настоящего скомпилированного
netrunner-server (реальный процесс, метрики RSS/CPU из /proc) и настоящего
публичного клиентского API. Попутно найден и обойдён гоночный баг:
Connect-кадр в ClientHandler::connect() уходит через try_send ещё до того,
как поднимется хоть одна нога тунеля, и молча теряется при синтетической
нагрузке.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-06 18:05:10 +07:00

3.7 KiB

Блок nrxp — протокол Netrunner eXchange Protocol

Прикладной протокол, который ездит внутри замаскированного TLS-канала. Снаружи трафик выглядит как обычные TLS-записи ApplicationData (0x17), а внутри каждой записи — один зашифрованный кадр NRXP с мультиплексированием потоков.

Блок отвечает только за «упаковку/распаковку» и ничего не знает о сети — он превращает (stream_id, тип, payload) в байты и обратно.

Детали — в rustdoc, модуль nrxp.

Файлы

Файл Что внутри
frame.rs Структура кадра, (де)сериализация, паддинг.
codec.rs Шифрующий слой: TxCodec/RxCodec (кадр ⇄ зашифрованный TLS).
bridge.rs TLS-обёртка TlsBridge: хендшейк и ApplicationData.
errors.rs TlsError + стратегия реакции ErrorAction (Wait/Redirect/Drop).

Формат кадра (25-байтовый заголовок)

┌──────────┬───────────┬──────┬─────────────┬─────────────┬─────────┬─────────┐
│ Auth Tag │ Stream ID │ Type │ Payload Len │ Padding Len │ Payload │ Padding │
│ 16 байт  │ 4 байта   │ 1 б. │ 2 байта     │ 2 байта     │ N байт  │ 0..255  │
└──────────┴───────────┴──────┴─────────────┴─────────────┴─────────┴─────────┘
  └────────────────── FRAME_HEADER_SIZE = 25 ──────────────────┘
  • Auth Tag — TOTP-подобный HMAC-тег (см. crypto), анти-replay.
  • Stream ID — id логического потока (нечётные у клиента, чётные у сервера).
  • TypeConnect/Data/Close/Heartbeat/UdpConnect/UdpData.
  • Padding — против анализа длин. Управляющие кадры получают 0..255 случайных байт; Data/UdpData выравниваются до ближайшего бакета (256/512/1024/2048/4096/8192) — кроме кадров, уже близких к максимальному размеру (крупные закачки), где паддинг только бил бы по throughput без выигрыша в приватности.

Конвейер

 TX:  Frame::new → into_bytes(tag) → AEAD encrypt in-place → TlsBridge::pack_app_data
 RX:  TlsBridge::unpack_app_data → AEAD decrypt in-place (staging) → Frame::parse

Инвариант: одна TLS-запись ApplicationData = ровно один кадр NRXP. На нём держится потоковая расшифровка в codec.rs (буфер staging).

Связи

  • Шифрование — crypto (AeadPacker, SessionAuth).
  • TLS-маскировка и хендшейк — tlseng.
  • Кадры передаёт/принимает движок туннеля net.