惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

F
Full Disclosure
Recorded Future
Recorded Future
T
Tenable Blog
S
Securelist
C
CERT Recently Published Vulnerability Notes
T
Threatpost
S
Schneier on Security
A
Arctic Wolf
The Hacker News
The Hacker News
C
CXSECURITY Database RSS Feed - CXSecurity.com
Know Your Adversary
Know Your Adversary
P
Privacy International News Feed
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
The Register - Security
The Register - Security
Cisco Talos Blog
Cisco Talos Blog
AWS News Blog
AWS News Blog
K
Kaspersky official blog
T
True Tiger Recordings
T
Threat Research - Cisco Blogs
V
Vulnerabilities – Threatpost
P
Palo Alto Networks Blog
T
The Exploit Database - CXSecurity.com
小众软件
小众软件
B
Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Microsoft Azure Blog
Microsoft Azure Blog
Cyberwarzone
Cyberwarzone
C
Cybersecurity and Infrastructure Security Agency CISA
T
Tor Project blog
Spread Privacy
Spread Privacy
Malwarebytes
Malwarebytes
P
Proofpoint News Feed
F
Fox-IT International blog
F
Fortinet All Blogs
P
Privacy & Cybersecurity Law Blog
G
GRAHAM CLULEY
量子位
Latest news
Latest news
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 叶小钗
Project Zero
Project Zero
T
Tailwind CSS Blog
N
Netflix TechBlog - Medium
Martin Fowler
Martin Fowler
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
I
Intezer
博客园_首页
腾讯CDC
H
Hackread – Cybersecurity News, Data Breaches, AI and More
D
Darknet – Hacking Tools, Hacker News & Cyber Security

Все публикации подряд на Хабре

256 зелёных тестов на нерабочем коде. Так выглядит «услужливый клерк» внутри нейросети Бизнес-аналитика для сети из 300 аптек: прогноз продаж и другие показатели Impact Analysis в дизайн-системе: как мы сделали CI осмысленнее, а review понятнее Топ-5 лучших нейросетей 2026 года: полный список на любой случай в SpeShu.AI Что делает сотрудников по-настоящему эффективными: процессы, знания или технологии Как за один вечер я написал сервис инвентаризации оргтехники для филиальной сети из 16 локаций Склад нанимает — и не может остановиться. Дефицит складских работников в 2026 году: причины и решения Шёл за утечкой памяти, нашёл утечку диска: SXSSFWorkbook без dispose() в Apache POI Штраф в размере 155 000 рублей получил владелец сайта по заявлению Роскомнадзора Индивидуальный план развития: от формальной процедуры к инструменту управления экспертизой команды Как понять, что вы не управляете финансами, а просто смотрите на цифры Водоросли и микропластик Масштабирование LLM: от одного чипа до ЦОДа. Глава 3. Траснформеры Бомба замедленного действия взорвалась: эпоха ИИ «бери сколько унесёшь» закончилась Стимпанк как часть жизни. История паровых двигателей и место, которое они занимали в мире в XIX-XX веках. Часть 2 288-ядерный Xeon 6+ и другие серверные CPU От OCR к смыслу: как мы научили модель понимать, кто кому отец, мать, жених и свидетель Насколько плох был Intel iAPX 432 — проверяем на практике Приручаем железо: внедряем DevOps в промышленной разработке Разработчики не экстрасенсы: как мы перестали приносить туман вместо ТЗ Дайджест C++: новости, полезные материалы и “свой язык” на десерт Ещё один репозиторий моделей для Archi 10 простых шагов, чтобы создать позиционирование для продукта Загадочная поэма древнего Китая, работающая как компьютер CLOUD Act, GDPR и ваш DNS: что на самом деле может ваш провайдер Ускоряем и оптимизируем numpy, pandas, scipy и sklearn Idempotency keys: 5 граблей, которые мы поймали на проде Gamedev. Парсинг данных из Google Sheets и Excel в json без привлечения программистов Nano Banana Google AI: как использовать Нано Банана для генерации и редактирования изображений Два игрока на весь российский рынок ИИ: что показал ЦИПР-2026 Менеджер ресурсов ЯНДЕКС 360 (YANDEX 360) промокоды июнь 2026: промокод Yandex 360 скидка 40% на годовые тарифы Open-Source инструмент для автоматического перевода книг Ищу ранних тестировщиков для Android-версии agent harnesses Не используйте LLM для текста Увеличиваем продажи без слез аналитика Оптимизация запросов к PostgreSQL: 5 неочевидных настроек для продакшена 45 лет тюрьмы за DROP TABLE и переход Карпатого в Anthropic Планирование движения для ровера на ходовой Ackerman'а Революция в изучении языков Java — быстрая. Ваш код может таким не быть Как я опоздал на конкурс OpenAi с новой архитектурой нейросети Быстрые интеграции в 1С: прощайте, бесконечные переделки Как получить субсидию 300 миллионов от Минпромторга? preIPO Anthropic, OpenAI, SpaceX. Разбираемся — стоит ли участвовать? Entaxy ION + OPC UA: два способа получить данные с промышленного оборудования Память на миллион, а толку ноль: как мы спасали ИИ-агента от «тупости» РСЯ, AdSense или myTarget: что на самом деле в 2026 приносит больше денег сайту и причем тут монетизаторы Практическое построение сервисов на Go под реальный трафик PostgreSQL и аналитика: что меняется, когда хранилище становится общим Codex за 5 месяцев 2026: мой топ-5 релизов, что не зашло и где OpenAI обогнал Anthropic Как создать короткое видео с помощью нейросетей: Полный гайд по Veo 3.1, Kling 3.0 и Happy Horse 1.0 Алгоритм проверок физлиц от экс сотрудника ФНС Как ИИ портит резюме студентам Системные вызовы в сфере ИТ в 2026: стратегический взгляд для ИТ-руководителей Вайбкодинг заканчивается на localhost: как я строю SaaS для цифровизации коттеджных поселков с Codex Производственные риски в небольшом кастомном производстве. С чем я сталкивалась и как научилась это учитывать Подключаем ИИ органы чувств: bash-демон, пайка и самосознание на Raspberry Pi Я хотел повторить Growing Neural CA за вечер. Ушёл месяц Промт для генерации текста без ИИ следа — как писать уникальные тексты через нейросеть От capabilities к AppArmor: что реально остановит атакующего в контейнере CactOS Вектора интересов: как находить настоящую мотивацию и усиливать команды Цена безопасности [Перевод] Цена безопасности “Рубик” от пет-проекта до прода или ITIL 4 для строительно-торговых центров Чего ждать (и не ждать) от ремейка AC4 Black Flag Архитектурный тупик корпоративного хранения: почему смена модели не снимает ограничений и что с этим делать Атаки через подрядчиков, дефицит кадров и квест с импортозамещением: главные вызовы ИБ в 2026 году Я не оставлю детям наследства Почему порты стали «дверями» в сервер, и кто решил, что SSH будет 22 Почему зарубежные разработчики чипов возвращаются на китайские фабрики Как у меня НЕ получился торговый бот на Polymarket Проектирование архитектуры в нотации ArchiMate с использованием ИИ. Часть 2 Как превратить домашнюю файлопомойку в умную AI-галерею на основе сборки из x99+Xeon и видеокарты за 2 тыс рублей Перспективы заселения нашей галактики Кризис менеджмент в ИТ Reactive Programming не спасёт вас. Если вы не решили эти 5 проблем — у вас просто медленный монолит с Flux Как я делаю DIY-контроллер для ПК: громкость, приложения, MIDI, OBS Миграция микросервисов на Python с помощью LLM: экономим месяцы для разработчиков Программирование микросхем GAL и им подобных Почему таск-трекер не заменяет ИСУП: из чего состоит полноценный контур управления проектами Всё об информационной безопасности. Кибербезопасность. DevOps, CI/CD. Хакеры. Алексей Федулаев Как импортировать базу клиентов в amoCRM и навести порядок в контактах Как мы четыре раза переписали Outbox Google предлагает единый «водяной знак» для изображений, видео и текста, созданных ИИ Сексизм в IT: данные вместо домыслов Один фронтенд, чтоб править всеми, один фронтенд, чтоб всех найти: 1 точка входа, разные BI ИИ в тестировании: зачем мы пошли в пилот и почему начали с чата, а не с агентов Как я научила Telegram-бота наводить порядок в чате с мемами: пересылка по хештегам в соответствующую тему Как мы сделали внутреннюю CRM для управления студией – опыт Doubletapp Десятипальцевый метод — как печатать цифру " Шесть "? Партнерская программа по нейросетям: зарабатывай на ИИ, приводя клиентов в AI-сервис Как я сделал «клик по элементу → открыть в VS Code» за один вечер Эволюция Telegram‑бота на C++: от «лапши» в main() до ООП, in‑memory кэша и мутов по Фибоначчи Как я (внезапно) стал адвокатом вайб‑кодинга в корпорации Дизайн за 5 минут. Дайджест мая 2026 Только 17% всех 64-битных целых чисел можно разложить на два 32-битных 0,000000001% × ∞ = 100%. Вы осознаёте что любое событие неизбежно? «Вы либо трусы наденьте, либо крестик снимите». Как мы выиграли еще один суд против PR-агентства PRslon
Когда Reality не хватает: добавляем Hysteria2 + Salamander в iOS-мессенджер, и как всегда грабли по дороге (ч.2)
rcq · 2026-05-27 · via Все публикации подряд на Хабре

Уровень сложностиСредний

Время на прочтение9 мин

Охват и читатели3K

Кейс

Эту статью я готовил с прошлой недели, и пока готовил, ТСПУ выкатил новые правила фильтрации, целящиеся именно в Reality-handshake, о котором тут речь. То есть статья стала актуальнее, чем когда я её начинал.

В прошлой статье я рассказывал, как мы встроили VLESS + Reality прямо в наше iOS-приложение через sing-box, чтобы обход блокировок был не задачей пользователя, а деталью реализации. Если коротко: TLS-рукопожатие проксируется на посторонний крупный сайт, активный пробинг упирается в этот сайт, IP относимся как к расходнику, конфиг доставляется отдельно от сборки. Подход работает, и для подавляющего большинства соединений из России работает прямо сейчас.

Кроме одного класса сетей, в которых не работал.

Внутри этого класса оказались, в том числе, корпоративные подсети, гостевой Wi-Fi в некоторых аэропортах и часть регионального покрытия одного из операторов. Картина в логах одна и та же. Туннель поднимается, TCP-соединение на relay открывается, TLS-рукопожатие начинается, и через секунду sing-box на сервере пишет в журнал: REALITY: processed invalid connection. Сразу обрыв, нет ретраев которые что-то меняют.

Эта статья про то, что мы увидели в этих сетях, почему Reality в одиночку их не пробивает, и что мы поставили рядом, чтобы пробивал. Если читали предыдущую часть, продолжайте отсюда. Если не читали, важен один тезис: туннель у нас живёт внутри приложения, через sing-box, скомпилированный в нативный фреймворк, без системного VPN.

Что такое «белый список» в DPI

Привычная модель цензуры это чёрный список: оператор знает плохие хосты, режет их, остальное идёт. Обычные методы обхода (Reality, прокси с маскировкой) хорошо работают против чёрного списка, потому что выглядят как «остальное».

Белый список это инверсия. Оператор разрешает трафик к небольшому набору заранее одобренных доменов и IP, а всё, что не подходит под этот список, обрубается с разной степенью аккуратности. На уровне SNI это тривиально: ClientHello открытый, в нём видно куда вы идёте, и если этого домена нет в разрешённом наборе, соединение режется. На уровне IP то же самое: пакеты на адреса вне разрешённого подмножества просто не доходят. Это та же DPI-инфраструктура, что обычно, только настроенная по противоположному принципу.

Тут возникает интересный вопрос. Если белый список идёт по SNI, то Reality, который во время TLS-рукопожатия проксирует трафик на постороний крупный сайт (например, microsoft.com), формально должен пройти: цензор видит в SNI разрешённый домен. Но не проходит. И вот тут начинается интересное.

Что именно ловит белый список DPI

Reality прячет содержимое прокси-туннеля внутри валидного TLS-рукопожатия к разрешённому домену. ClientHello идёт настоящий, отпечатки TLS совпадают с настоящим Chrome (через uTLS), сертификат и цепочка валидные. Пассивный наблюдатель и активный пробер не отличат это от обычного браузера, ходящего на microsoft.com.

Но «не отличит» это про обычный DPI, который смотрит набор сигнатур и принимает решение «разрешать или нет». Белый список DPI ведёт себя иначе. Он не пытается опознать плохой трафик, он пытается убедиться в хорошем. И это другой алгоритм.

Тут оговорюсь сразу: я не ковырял исходники конкретного DPI-вендора, и формулировка ниже это моя реконструкция по поведению, а не подтверждённая истина. Параллельно я задавал вопросы знакомым из тель-авивского инженерного слоя (на стороне коммерческих DPI-вендоров там исторически плотно), и формулировка, которую от них слышал чаще всего, звучит примерно как «современные продукты не опознают плохое по одной сигнатуре, они собирают уверенность что трафик хороший из нескольких признаков сразу». Это согласуется с тем, что мы наблюдали, но это всё равно эвристический вывод снаружи.

Дальше я просто перечислю, что в этой картине, по моим наблюдениям, влияет. Если ваше «обращение к microsoft.com» отличается от настоящего по любому набору микро-параметров, эвристика поднимает руку. И таких параметров много: порядок TLS-расширений в ClientHello, паттерн GREASE-значений, тайминги ответов после рукопожатия, размеры записей. uTLS аккуратно копирует ClientHello Chrome версии N, но реальный Chrome ещё и ходит по сети как Chrome: подключение из обычного браузера тянет за собой TCP fast open, HTTP/2 PING-фреймы в характерные моменты, OCSP-staple проверки, ALPN-переговоры, и десятки других мелочей.

Reality всё это не воспроизводит. Reality безупречно вышел из TLS-рукопожатия и переключился на туннель, и дальше через канал идёт ваш прокси-трафик. С точки зрения DPI это TLS на microsoft.com, в котором сразу после рукопожатия начинается странный поток данных, не похожий на HTTPS. Для классического чёрного списка это пройдёт, потому что сигнатуры «странности» в нём не прописаны явно. Для белого списка, где эвристика по умолчанию говорит «не разрешать, если не убедился», этого хватает чтобы зарезать.

Мы попробовали несколько настроек: разные SNI для маскировки, разные uTLS-отпечатки (chrome 120, chrome 131, firefox), разное поведение xtls-rprx-vision. Картина не менялась. Это не вопрос подобрать правильный отпечаток, это вопрос самой парадигмы.

Значит, в этих сетях нужен другой подход.

Почему UDP, и почему именно Hysteria2

В TCP-мире DPI на белый список это решённая задача с понятным ответом: смотрим на TLS, сравниваем со списком, режем неподходящее. В UDP-мире у DPI задача сложнее, потому что UDP сам по себе разный. QUIC к youtube.com и QUIC к Cloudflare и игровой трафик и видеозвонки и просто чей-то самописный протокол выглядят достаточно по-разному, чтобы единая эвристика «это разрешённый UDP» работала плохо. И слишком агрессивно резать UDP опасно для самой сети: половина мобильного трафика сейчас идёт по QUIC, ломать QUIC к крупным сайтам это себе же в ногу.

Поэтому UDP-транспорты в практическом обходе блокировок ведут себя лучше. Но просто QUIC «как есть» это тоже не решение: QUIC-handshake тоже маркируется, и DPI, который натренирован на конкретных публичных версиях QUIC (Chrome, Cloudflare, Google QUIC v1), может опознать и его.

Hysteria2 это собственный протокол поверх QUIC, ориентированный именно на обход блокировок. И главное, что нас интересовало, это его obfs-плагин по имени Salamander. Salamander накладывает на каждый UDP-пакет внешний XOR-слой с ключом, который выводится из пароля. То есть DPI, который попытается посмотреть в первый пакет hy2 и опознать QUIC-handshake по байтам, видит просто бессмысленный поток. Никаких узнаваемых сигнатур внутри пакета не выживает.

Стороны (клиент и сервер) знают пароль, выводят из него ключ, и пишут/читают одинаково. Снаружи не видно ничего полезного.

Этого вполне достаточно для того DPI, что нам нужно было обойти. Подчеркну «вполне достаточно для того» сознательно: я не знаю, как этот же подход поведёт себя на других реализациях DPI, и предположение «UDP+obfs всегда лучше» в общем виде наверняка неверно. Конкретно в наших сценариях оно сработало.

Как это устроено

Архитектура простая. Тот же relay-сервер, на котором уже жил VLESS + Reality на TCP/443, теперь дополнительно слушает hy2 на UDP/443. Один порт, разные транспорты, никакого конфликта (TCP и UDP это разные стеки в ядре).

Серверный конфиг sing-box, ядро:

{ "inbounds": [{ "type": "hysteria2", "listen": "::", "listen_port": 443, "users": [{ "password": "..." }], "obfs": { "type": "salamander", "password": "..." }, "tls": { "enabled": true, "server_name": "www.apple.com", "certificate_path": "/etc/sing-box/hy2_cert.pem", "key_path": "/etc/sing-box/hy2_key.pem" } }] }

Два пароля, не один. Первый это аутентификация пользователя на hy2. Второй это пароль для Salamander, отдельный, потому что obfs работает на слое до того, как мы вообще разбираем hy2-пакет. Если obfs-пароль не совпал, сервер вообще не поймёт что к нему пришло.

Сертификат self-signed, на тот же CN, что и SNI разрешённого домена (мы используем тот же домен, что и в Reality, по соображениям связности логов и по тому, что cert CN внутри TLS виден только клиенту, для DPI он внутри obfs-слоя и не имеет значения). Клиент ходит с insecure: true, потому что аутентификация выносится на пароли, не на PKI.

Клиентский outbound (тот же sing-box, тот же фреймворк, что и в первой статье):

{ "outbounds": [{ "type": "hysteria2", "server": "RELAY_ADDR", "server_port": 443, "password": "...", "obfs": { "type": "salamander", "password": "..." }, "tls": { "enabled": true, "server_name": "www.apple.com", "insecure": true } }] }

Один нюанс по сборке gomobile. Если вы собирали sing-box для прошлой статьи, у вас уже есть тег with_utls. Под hy2 + obfs понадобится дополнительно with_quic, иначе нужные модули просто не попадут во фреймворк. На размер бинарника это влияет ощутимо (десятки мегабайт), но это плата за UDP-транспорт, ничего не поделать.

Как клиент выбирает между Reality и hy2

В sing-box есть outbound типа urltest, который параллельно поднимает несколько подоблочных outbound и выбирает самый быстрый по результатам HTTP-проб. Мы сложили в urltest оба варианта, Reality и hy2, для каждого relay. На сетях, где Reality проходит, побеждает он (на TCP мы получаем чуть меньшую латентность). На сетях с белым списком Reality проба тихо не проходит, hy2 проходит, urltest это видит и переключает основной поток на hy2.

Пользователь ничего не настраивает. Приложение в первый раз делает прямое подключение, если не получается, поднимает sing-box, sing-box внутри себя выбирает рабочий транспорт.

Грабли по дороге

С самим протоколом и с конфигами всё было аккуратно, документация sing-box достаточная. Грабли вылезли в инфраструктуре.

Облачные файрволы на UDP. На этой грабле я честно потерял часть вечера, и пишу про неё подробно, потому что это та категория ошибок, в которой ты долго винишь свой конфиг. На двух хостингах из четырёх iptables на самой машине настроили правильно (UDP/443 ACCEPT), сертификаты разложили, sing-box стартанул и слушает, всё хорошо. Снаружи трафика на машину не приходит вообще. Сидишь с tcpdump на хосте, видишь ноль пакетов, и думаешь «что я сломал в iptables», хотя в iptables всё в порядке. Это не баг конфига, это облачный firewall на уровне провайдера, который по умолчанию открывает только привычные порты, а UDP/443 закрыт. У одного из них, к слову, у API-роли инстанса не оказалось права открывать порты программно, пришлось руками заходить в UI. У второго подобная история была с named security group. Это пятиминутная задача, когда вы знаете, что её надо сделать, и часовое расследование, когда не знаете.

Урок: после того, как вы подняли listener на новом порту, обязательно проверяйте c посторонней машины, что пакеты доходят. Никогда не верить «iptables говорит open», верить tcpdump на машине, в котором вы видите входящие пакеты. И если tcpdump молчит, искать причину не в правилах самой машины, а слоем выше.

Один общий SNI для пары транспортов на одном relay. Внутри Reality TLS-рукопожатие настоящее, проксируется на microsoft.com (например), и cert CN должен соответствовать. Внутри hy2 TLS зашифрован obfs-слоем и снаружи не виден, но cert CN тоже microsoft.com, чтобы внутренние логи и панель администрирования не выглядели разнокалиберно. Это не функциональная необходимость, это операционная гигиена, чтобы через полгода смотреть в логи и не теряться.

Self-signed cert и insecure: true. Это нормально для hy2 с паролями, но при первом запуске рука сама тянется проверять «а валидный ли cert». Не валидный, и это нормально. Вся аутентификация на паролях. Если вы для какого-то будущего этапа захотите выпустить настоящий cert (через DNS-01 challenge на поддомен relay), это не сделает hy2 лучше с точки зрения обхода, это просто уберёт строку с insecure из конфига клиента.

Hosting diversity сильнее, чем кажется. Reality горит по IP, и hy2 горит по IP примерно так же. Имеет смысл, чтобы пара (Reality, hy2) у вас была на разных провайдерах, в разных регионах. Если cidr одного провайдера попадает под массовый блок, у вас остаются другие. Мы держим набор из нескольких сочетаний (один провайдер, второй провайдер, третий) и в конфиге, который доставляется отдельно от сборки, прописаны они все. Подробнее об этом подходе писал в первой статье.

Честно про границы

Уже писал в первой статье, повторю кратко: туннель меняет то, как соединение выглядит для цензора по дороге, и не меняет того, кто стоит на концах. Содержимое переписки защищается отдельно, на уровне приложения (мы используем libsignal). Hy2 + Salamander это про другой слой: про то, чтобы пакеты вообще доходили до сервера.

Salamander обфускация на пароле, не на полноценной криптографии. Если пароль вытащить (например, из вашей же скомпрометированной сборки), obfs снимается тривиально и DPI снова видит QUIC-handshake внутри. Поэтому пароли мы (как и всё, что должно быстро меняться) храним не в бинарнике, а в подписанном конфиге, который доставляется в рантайме. Сменили relay, сменили пароли, перевыпустили подпись, клиент подтянул.

Ещё одно важное наблюдение, которое мы видим по логам и обратной связи. Даже с hy2 в дополнение к Reality у части пользователей всё равно не поднимается транспорт. Это меньшинство, но устойчивое. По косвенным признакам (геолокация, оператор, время суток когда проблема обостряется) картина довольно последовательная: чем ближе сеть пользователя к зонам с особо чувствительной для государства активностью, тем плотнее настроены белые списки и тем агрессивнее режется всё, что не вписывается. То есть DPI это не однородный слой по стране, это градиент с локальными ужесточениями. Я не могу это доказать строго, у меня нет доступа к настройкам ни одного оператора, но картина совпадает у достаточного числа пользователей, чтобы упоминать это как наблюдение, а не совпадение.

И отдельно: всё это гонка. Сегодня белый список DPI не умеет хорошо резать обфускированный UDP, через год может научиться. Это нормальный процесс, к нему просто надо относиться как к процессу: иметь запас инструментов, мониторить, что отвалилось, и не считать ни один из них «навсегда».

Что забрать

Если вы делаете похожую задачу:

В сетях с белым списком DPI один Reality поверх TCP может не сработать, потому что DPI работает не на «опознать плохое», а на «убедиться в хорошем». Это другая парадигма, и Reality в неё не попадает по построению.

UDP + obfs (в нашем случае Hysteria2 + Salamander) в этих сетях работает лучше, потому что UDP-DPI в принципе слабее, а Salamander дополнительно убирает любые сигнатуры внутри пакета.

Не выбирайте один транспорт, держите оба. Reality экономит ресурс на сетях без белого списка, hy2 пробивает то, что Reality не пробивает. urltest в sing-box решает это автоматически.

Облачные firewall на UDP это отдельная тема и почти всегда отдельная боль. Закладывайте время на «открыть нужный порт в UI провайдера», особенно если у вас несколько разных провайдеров.

И сразу планируйте, что и hy2, и Reality будут гореть по IP. Один из них может прожить дольше, но оба расходники. Конфиг доставляется отдельно от сборки, иначе каждый горящий IP это новый релиз в App Store.


Всё, что описано, живёт в нашем мессенджере RCQ, сейчас он в открытой бете на iOS. Клиент с открытым исходным кодом, можно посмотреть, как именно устроен транспорт и переключение между ним: github.com/rcq-messenger/rcq-ios.

Если занимаетесь похожим и видели у себя поведение белого списка DPI или работали с hy2 в проде, расскажите в комментариях. Особенно интересно про сети, в которых не сработал ни Reality, ни hy2: с такими мы пока не встречались, но это не значит, что их нет.