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

推荐订阅源

Stack Overflow Blog
Stack Overflow Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
爱范儿
爱范儿
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
有赞技术团队
有赞技术团队
罗磊的独立博客
博客园 - 三生石上(FineUI控件)
小众软件
小众软件
L
LINUX DO - 最新话题
T
Troy Hunt's Blog
博客园_首页
量子位
Jina AI
Jina AI
S
SegmentFault 最新的问题
IT之家
IT之家
Hacker News - Newest:
Hacker News - Newest: "LLM"
大猫的无限游戏
大猫的无限游戏
N
News | PayPal Newsroom
P
Proofpoint News Feed
Cyberwarzone
Cyberwarzone
S
Securelist
Google Online Security Blog
Google Online Security Blog
P
Privacy International News Feed
博客园 - Franky
美团技术团队
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
NISL@THU
NISL@THU
C
Cisco Blogs
V
Vulnerabilities – Threatpost
腾讯CDC
The Hacker News
The Hacker News
K
Kaspersky official blog
C
Cyber Attacks, Cyber Crime and Cyber Security
雷峰网
雷峰网
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Security Archives - TechRepublic
Security Archives - TechRepublic
A
About on SuperTechFans
Webroot Blog
Webroot Blog
The Register - Security
The Register - Security
Scott Helme
Scott Helme
B
Blog
Security Latest
Security Latest
Last Week in AI
Last Week in AI
Google DeepMind News
Google DeepMind News
W
WeLiveSecurity
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
T
Tenable Blog
Blog — PlanetScale
Blog — PlanetScale
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
S
Schneier on Security

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

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Обход блокировок внутри iOS-приложения: VLESS + Reality через sing-box, и грабли по дороге
rcq · 2026-05-23 · via Все публикации подряд на Хабре

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

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

Охват и читатели0

Кейс

Мы делаем мессенджер. Весной 2026 наш бэкенд начал отваливаться у части пользователей из России: HTTPS-запросы к API таймаутятся, WebSocket не поднимается. Картина знакомая всем, кто держит сервис с одним доменом и одним IP.

Для мессенджера это приговор. Не «неудобно», а именно приговор: приложение, которое не может даже подключиться, бесполезно. И вариант «попросите пользователя сначала включить VPN» нас не устраивал совсем. Ниже разберу, почему мы в итоге встроили обход прямо в приложение, на чём он работает и на какие грабли мы наступили. Без маркетинга, по делу.

Почему не «просто VPN»

Первая мысль у всех одинаковая: пусть юзер поставит VPN. Но если посмотреть на это глазами продукта, всё разваливается.

Во-первых, это убивает воронку. Каждый дополнительный шаг до «приложение заработало» стоит вам части аудитории, а «установите и настройте отдельное приложение» это не шаг, это пропасть. Особенно болезненно, что отваливается ровно та часть пользователей, которой продукт нужнее всего.

Во-вторых, сам VPN это движущаяся мишень. Популярные протоколы (OpenVPN, WireGuard, IKEv2) давно и неплохо детектируются по сигнатурам, диапазоны известных VPN-провайдеров режут пачками. То есть вы делегируете критичную для вас функцию стороннему приложению, которое завтра само перестанет работать.

Нам хотелось другого поведения: пользователь открывает мессенджер, и он работает. Без отдельного приложения, без подписки на чужой сервис, без VPN-профиля в системных настройках iOS. Обход должен быть деталью реализации, а не задачей пользователя.

Что именно блокируется

Чтобы понимать, что чинить, надо понимать, что ломается. У нас довольно типичная схема: HTTPS API и WebSocket на одном домене. Заблокировать такое соединение можно несколькими способами, и обычно их комбинируют:

  • по IP-адресу сервера;

  • по имени домена в ClientHello (поле SNI отправляется открытым текстом даже в TLS);

  • по DPI, который смотрит на саму структуру трафика и опознаёт протокол.

Обычное TLS-соединение на ваш домен срезается по SNI на раз. Значит, задача формулируется так: соединение должно выглядеть как обращение к какому-то другому, ничем не примечательному и заведомо разрешённому хосту. Не «зашифровать сильнее», а именно «выглядеть иначе».

Почему VLESS и Reality

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

Reality решает это аккуратным трюком. Прокси-сервер не предъявляет собственный TLS-сертификат. Вместо этого во время рукопожатия он проксирует TLS-handshake на реальный, посторонний, популярный сайт. Для пассивного наблюдателя и для активного пробера ваш сервер неотличим от этого сайта: валидный сертификат, валидное рукопожатие, валидная цепочка. И только клиент, у которого есть правильный публичный ключ, после рукопожатия «переключается» на настоящий туннель. Постороннему пробингу переключиться не на что, он видит обычный крупный сайт.

Что это даёт на практике:

  • нет своего домена, который можно занести в блок по SNI;

  • нет своего сертификата, по которому можно опознать «вот это прокси»;

  • активный пробинг упирается в чужой легитимный сайт.

VLESS тут это лёгкий транспорт без собственного слоя шифрования (шифрование берёт на себя TLS), с маленьким и невыразительным отпечатком. А поток xtls-rprx-vision дополнительно сглаживает паттерн «TLS внутри TLS», который иначе виден по характерным размерам пакетов.

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

sing-box внутри приложения

sing-box это универсальная прокси-платформа на Go, и среди прочего она умеет быть клиентом VLESS + Reality. Обычно её запускают как отдельный процесс с конфигом. Нам отдельный процесс не нужен, нам нужно, чтобы это жило внутри iOS-приложения.

Здесь помогает gomobile. Через gomobile bind Go-код собирается в нативный .xcframework, который линкуется в приложение как обычная зависимость. То есть sing-box у нас не CLI рядом, а фреймворк внутри бинарника. Запускаем его прямо в процессе приложения.

Поднятый таким образом sing-box открывает локальный inbound (тип mixed, то есть SOCKS и HTTP сразу) на 127.0.0.1 на случайном порту. Дальше есть развилка, и она принципиальная.

Можно сделать системный VPN через NEPacketTunnelProvider: тогда через туннель пойдёт весь трафик устройства. Можно сделать прокси уровня приложения: тогда через туннель пойдёт только трафик вашего приложения. Мы выбрали второе, и вот почему. Нам не нужно гнать через relay весь телефон, нам нужно довести до сервера только трафик мессенджера. А раз так, то Network Extension с его архитектурой, отдельным процессом-расширением, лимитами по памяти и дополнительными вопросами на ревью в App Store нам просто не нужен. Меньше движущихся частей, меньше точек отказа.

На практике это выглядит так: sing-box крутит локальный прокси, а сетевой стек приложения (URLSession) мы заворачиваем на этот локальный адрес.

let config = URLSessionConfiguration.default
config.connectionProxyDictionary = [
    "SOCKSEnable": 1,
    "SOCKSProxy": "127.0.0.1",
    "SOCKSPort": localPort,
]

Весь остальной телефон при этом не затронут, никакого VPN-профиля в настройках не появляется. Минус подхода честный: это не универсальный VPN, через него не пойдут другие приложения. Но нам это и не требовалось.

Конфиг для sing-box генерируем в рантайме. Если убрать лишнее, ядро выглядит так:

{
  "inbounds": [{
    "type": "mixed",
    "listen": "127.0.0.1",
    "listen_port": 0
  }],
  "outbounds": [{
    "type": "vless",
    "server": "RELAY_ADDR",
    "server_port": 443,
    "uuid": "...",
    "flow": "xtls-rprx-vision",
    "tls": {
      "enabled": true,
      "server_name": "www.посторонний-крупный-сайт.com",
      "utls": { "enabled": true, "fingerprint": "chrome" },
      "reality": {
        "enabled": true,
        "public_key": "...",
        "short_id": "..."
      }
    }
  }]
}

Отдельно отмечу utls с отпечатком chrome. Стандартная Go-реализация TLS имеет свой узнаваемый ClientHello, и это само по себе сигнатура. uTLS подменяет ClientHello так, чтобы он совпадал с настоящим Chrome. Деталь мелкая, но из таких мелочей и складывается «выглядит как обычный браузер».

Грабли с relay

Теперь то, ради чего стоило писать эту статью. С протоколом всё оказалось ровно. По граблям мы прошлись на инфраструктуре.

Первый relay мы подняли на дешёвом VPS у типичного хостера. Адрес сгорел быстро. Диапазоны датацентров и хостингов, на которых исторически много прокси, отслеживаются и режутся либо превентивно, либо очень оперативно. Reality прекрасно прячет протокол, но он никак не прячет сам факт «трафик идёт на IP из подозрительного диапазона».

Мы перенесли relay на адрес в диапазоне крупного облачного провайдера. И вот он живёт. Логика простая: вырезать оптом адресное пространство большого облака дорого по сопутствующему ущербу, на тех же адресах висит масса легитимных сервисов, которые ломать никто не хочет.

Вывод, который мы для себя записали: с Reality слабое место это не протокол, а IP. Горит именно адрес. Значит, relay по определению расходник, и относиться к нему надо как к расходнику.

Адрес relay не должен быть зашит в бинарник

Прямое следствие предыдущего пункта. Если адрес relay захардкожен в приложении, то сгоревший IP означает новый релиз в App Store и ожидание ревью. Для расходника, который может сгореть в любой день, это неприемлемо.

Поэтому параметры relay (адрес, ключи, SNI) приложение должно получать отдельно от своей сборки. Тогда смена relay это смена конфига, а не релизный цикл. Конкретный механизм доставки конфига можно выбрать разный, важен сам принцип: то, что горит часто, не живёт в бинарнике.

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

Туннель это не магия, и продавать его как магию нечестно.

Оператор relay (в нашем случае мы сами) видит ваш трафик к нашим серверам ровно так же, как его видел бы ваш провайдер при прямом подключении. Туннель меняет то, как соединение выглядит для цензора по дороге, и не меняет того, кто стоит на концах. Содержимое переписки у нас в любом случае защищено сквозным шифрованием на уровне приложения (libsignal), и туннель к этому ничего не добавляет и ничего не отнимает. Это важно разделять: обход блокировок и приватность переписки это две разные задачи, которые решаются разными слоями.

Reality хорошо держит активный пробинг, но «навсегда» в этой области не существует. IP горят, сигнатуры накапливаются, методы детекта развиваются. Это гонка, и относиться к ней надо как к гонке: иметь запас адресов, уметь их менять быстро, мониторить, что именно отвалилось.

У нас обход по умолчанию выключен. Приложение сначала пробует подключиться напрямую, и только если прямое соединение не проходит, поднимает туннель. Никакого смысла гонять трафик через relay там, где сеть и так открыта, нет.

Что стоит забрать из этого текста

Если вы решаете похожую задачу, три вещи, которые сэкономят вам время:

Прокси уровня приложения часто лучше системного VPN. Если вам надо довести до сервера только свой трафик, NEPacketTunnelProvider это лишняя сложность. Локальный inbound плюс connectionProxyDictionary закрывают задачу меньшими силами.

Протокол это лёгкая часть. VLESS + Reality + sing-box это решённая задача, всё уже написано до вас. Тяжёлая часть это инфраструктура: какие адреса вы используете, как быстро вы их меняете, как вы это всё мониторите.

Планируйте ротацию с первого дня. Не «когда-нибудь вынесем relay в конфиг», а сразу. Потому что первый же сгоревший IP покажет вам, есть у вас ротация или нет, и узнавать это в проде неприятно.


Всё описанное живёт в нашем мессенджере RCQ, сейчас он в открытой бете на iOS. Клиент под iOS у нас с открытым исходным кодом, так что при желании можно посмотреть, как именно устроена работа с транспортом, а не верить на слово:

github.com/rcq-messenger/rcq-ios

Если занимаетесь чем-то похожим и набили свои шишки на инфраструктуре, расскажите в комментариях, особенно интересно про практику ротации адресов.