Маскировка:
- 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>