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

推荐订阅源

Google DeepMind News
Google DeepMind News
S
Security Affairs
阮一峰的网络日志
阮一峰的网络日志
L
LangChain Blog
Microsoft Azure Blog
Microsoft Azure Blog
雷峰网
雷峰网
Recent Announcements
Recent Announcements
WordPress大学
WordPress大学
The GitHub Blog
The GitHub Blog
博客园_首页
The Cloudflare Blog
M
MIT News - Artificial intelligence
博客园 - 【当耐特】
MyScale Blog
MyScale Blog
S
SegmentFault 最新的问题
P
Proofpoint News Feed
Y
Y Combinator Blog
Jina AI
Jina AI
博客园 - 聂微东
A
About on SuperTechFans
Blog — PlanetScale
Blog — PlanetScale
博客园 - 司徒正美
G
Google Developers Blog
云风的 BLOG
云风的 BLOG
F
Full Disclosure
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Microsoft Security Blog
Microsoft Security Blog
爱范儿
爱范儿
T
Tailwind CSS Blog
J
Java Code Geeks
Vercel News
Vercel News
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Stack Overflow Blog
Stack Overflow Blog
罗磊的独立博客
小众软件
小众软件
酷 壳 – CoolShell
酷 壳 – CoolShell
T
The Blog of Author Tim Ferriss
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
博客园 - 三生石上(FineUI控件)
W
WeLiveSecurity
PCI Perspectives
PCI Perspectives
Attack and Defense Labs
Attack and Defense Labs
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
宝玉的分享
宝玉的分享
IT之家
IT之家
Hacker News: Ask HN
Hacker News: Ask HN
The Register - Security
The Register - Security
T
The Exploit Database - CXSecurity.com
T
Threat Research - Cisco Blogs

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

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет 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 миллионов точек без потерь
L×Box: диагностика per-app трафика, посмотрим кто куда ходит
developer · 2026-05-09 · via Все публикации подряд на Хабре

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

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

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

Кейс

Недавно я дебажил проблему «Tinkoff Investments не открывается через VPN» — стандартная, split-routing проблема. Симптом: app запускается, картинки грузятся, но авторизационная сессия не поднимается, login висит. Через 30 минут возни со снапшотами /state, /connections, /logs и ручного matching'а conn_id'ов между ними картина прояснилась.

Часть доменов вида *.t-bank-app.ru корректно матчится в мой ru-domains rule_set и идёт напрямую. Но другая часть резолвится через CNAME на *.trbcdn.net (TLD .net!) — этот target в ru-domains уже не попадает, и sing-box честно отправляет трафик через bypass-VPN в Польшу. Получается split: часть запросов уходит с моего домашнего IP, часть — с польского. Bank-backend, который привязывает session к source-IP / fingerprint'у, видит непоследовательного клиента и просто отказывается поднимать auth state.

Симптом — «login завис», корень — domain-level split routing внутри одного приложения.

Стало ясно: такая диагностика не должна занимать 30 минут. Поэтому в L×Box (мой Android-клиент на sing-box, open source) появилась фича — Per-app traffic profiler.

Pick app → ▶ Record → 30 секунд работы приложения → читаешь Domains tab. Видно по строкам, что часть *.t-bank-app.ru уходит через direct-out, а другая — через vpn-1. Cовершенно очевидно где провал в правилах. Никакого packet capture, root'а, внешних tools.

Зачем это вообще

Сейчас индивидуальная и корпоративная безопасность более востребована чем ранее, пользователи хотят знать куда смотрит какое их приложение — это простая точка диагностики.

Три класса задач, которые регулярно возникают у любого пользователя split-routing VPN'а:

Дебаг роутинга. «X не работает через VPN» — flagship-кейс. Корень обычно один: домен выглядит RU, но CNAME уводит на CDN с иностранным TLD, и rule_set не отрабатывает. Без видимости CNAME-chain'а — это всегда детектив.

Privacy audit. «Куда стучит это приложение?» — список доменов отсортированный по объёму трафика. Замечаешь незнакомое — копаешься в outbound'е, при необходимости блокируешь.

Дебаг производительности. «Почему этот сервис тормозит?» — смотришь в каком outbound'е лежит трафик, есть ли DNS retry'и, не висит ли всё на одном медленном IP.

На рынке Android-VPN-клиентов никто не показывает routing chain per-app в реальном времени. PCAPDroid делает packet capture (но не видит роутинг). NetGuard блокирует (но не диагностирует). AdGuard видит DNS (но не outbound). Wireshark — это десктоп.

Преимущество в том, что у меня под капотом sing-box — единственный VPN-engine, который пишет в логи структурированные DNS-резолвы с CNAME chain'ом, package_name по conn_id и outbound chain. Эту информацию надо просто вытащить и разложить по полкам.

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

Singleton-сервис TrafficProfiler (Dart, ChangeNotifier) держит одну active session + ring-buffer на 5 завершённых. Всё in-memory — на kill приложения сессии стираются. Persist принципиально не делается: фича diagnostic-only, не аналитическая, цена schema/migration'ов превышает value.

Два независимых источника данных:

              ┌────────────────────────────────────────┐
              │  TrafficProfiler (singleton)            │
              │   _active: Session?  _completed: Q[5]   │
              └────────────────────────────────────────┘
                     ▲                    ▲
        ┌────────────┴────────┐  ┌────────┴──────────────────┐
        │ A: log stream       │  │ B: /connections poll       │
        │  AppLog (core src)  │  │  Clash API every 2s        │
        │  ts-diff drain      │  │  diff vs prev snapshot     │
        └─────────────────────┘  └────────────────────────────┘

Источник A — sing-box log stream через native EventChannel lxbox/coreLogAppLog (мой per-source ring-buffer для логов) → TrafficProfiler listener. Парсятся три типа строк:

INFO[N] [<conn_id> <Nms>] router: found package name: <pkg>
INFO[N] [<conn_id> <Nms>] dns: exchanged|cached <type> <name>. <ttl> IN <type> <data>
INFO[N] [<conn_id> <Nms>] dns: exchange failed for <host>: <reason>

Per-conn-id accumulator (_DnsAccumulator) собирает CNAME chain и эмитит финальное dnsResolve event'ы с исходным запрошенным domain'ом, списком CNAME hops и финальным IP.

Источник B — Clash API polling /connections каждые 2 секунды (только пока session active — battery-friendly). Diff с предыдущим snapshot'ом даёт tcpOpen / tcpClose события с bytes, chains, host, IP, port. Фильтрация по metadata.process или metadata.processPath после strip'а UID-суффикса (там приходит "ru.tinkoff.investing (10364)", picker отдаёт чистый package).

Aggregates byDomain / byIp рассчитываются on-demand при чтении (sub-tab views). Connection-issue классификатор отмечает ⚠ на event'ах — два locale-агностичных типа:

Issue

Условие

DNS timeout

sing-box лог dns: exchange failed ... — прямой engine-сигнал, не heuristic

TCP RST early

conn closed в течение 1с, ↑0 ↓0 байт — heuristic для блокировки/RST/unreachable

Memory bounds — 50000 events на session или 3-часовое sliding window (что раньше). _completed ring-buffer FIFO-evict'ится при добавлении нового. Conn-id maps GC'атся по 30-секундному TTL когда map > 256 entries.

UI — 4 sub-tab'а:

  • Live — newest-first стрим events с цветными kind-метками (DNS/TCP/UDP) и inline-показом CNAME chain + outbound chain под каждым event'ом

  • Domains — aggregated по domain'у, sorted by bytes, с search-фильтром по domain || ip || cname-target. Раскрытие row'а показывает CNAME chain, все IPs, outbound, issues (⚠)

  • IPs — аналогично по IP. Полезен для hostless конн'ов (без SNI sniffing) и подозрительных IP из threat-feed'а

  • Connections — per-conn timeline с inline-expand: CNAME, all IPs, issues (⚠), кнопка [View in Domains →] с автопереходом + autofill search'а

Recording продолжается даже когда я вышел из этого экрана (singleton). На HomeScreen в traffic bar появляется красный ⚡-chip с короткой версией package name'а — индикатор; tap → возврат в Per-app tab. Останавливается только manual STOP, force-stop приложения или start новой сессии.

Грабли, на которые наступил

Из всего, что было запланировано в spec'е, больше всего времени ушло на штуки, которые не были предсказуемы из дизайна. Перечислю короткой сводкой — кто захочет деталей, найдёт в [lib/services/traffic_profiler.dart](https://github.com/Leadaxe/LxBox/blob/main/app/lib/services/traffic_profiler.dart) и §044 spec:

  • UID-суффикс в metadata.process. Sing-box возвращает не "ru.tinkoff.investing", а "ru.tinkoff.investing (10364)" — pid в скобках. Без strip'а ни одна conn'ция не атрибутируется к target session'у. Заодно сюрприз — metadata.process чаще null, актуальный package живёт в metadata.processPath.

  • Ring-buffer overflow ломает length-diff scan. Изначальный drain логов сравнивал entries.length с прошлым snapshot'ом. Мой AppLog имеет cap=500 для core source — когда busy traffic заполняет буфер, новые entries вытесняют старые, длина стабилизируется на 500, length-diff навсегда нулевой. DNS-events переставали приходить через 1-2 минуты после старта recording'а. Фикс — timestamp-diff (хранить ts последнего обработанного entry, а не его индекс).

  • dns: cached нужно ловить наравне с dns: exchanged. Sing-box логирует обе формы; у занятых apps большинство резолвов — cache-hit'ы. Изначальная regex-маска ловила только exchanged — DNS-уровень был разорванный (TCP-conn'ы есть, DNS-events нет).

  • Domain attribution на исходный domain, не на CNAME-target. Sing-box логирует CNAME chain последовательно: CNAME a → b, потом A b → ip. Если эмитить event с domain = именем из A-записи — в Domains tab появится b (CDN-хост), а изначально-запрошенный a исчезнет. Фикс — per-conn-id accumulator хранит первое имя, CNAME-targets копятся отдельно как chain.

  • Connections click target — только header. Inline-expand на conn row делал весь tile (включая раскрытую секцию) кликабельным. Тапаешь на кнопку «View in Domains» или хочешь скопировать CNAME — row схлопывается. Завернул InkWell только в header'ную часть, expanded section живёт отдельно.

Debug API — для глаз и для агентов

Все controls UI доступны через localhost HTTP API (Bearer-token authenticated, bind на 127.0.0.1, anti-rebinding host-check). Эндпоинты — /profiler/start, /profiler/stop, /profiler/active, /profiler/session/<id>?include=domains,ips,events, /profiler/sessions, плюс SSE-стрим /profiler/stream для real-time event feed'а. Полная reference — в [docs/api/debug-api-reference.md](https://github.com/Leadaxe/LxBox/blob/main/docs/api/debug-api-reference.md).

Это про себя круто, но настоящая ценность — другая. Это API не для меня, это API для агента.

Сценарий который реально работает у меня сейчас:

  1. Втыкаю телефон в кабель, поднимаю adb forward tcp:9270 tcp:9269 (один раз — потом скрипт scripts/ensure-wifi-adb.sh восстанавливает соединение через wifi автоматически)

  2. Открываю Cursor / Claude Code / любой coding agent в проекте L×Box

  3. Пишу: «Tinkoff Investments login виснет, разберись» — и кидаю токен Debug API из app'а

  4. Иду пить чай

Дальше агент сам:

  • Дёргает /profiler/start с package=ru.tinkoff.investing

  • Просит меня сделать действие в app'е (тапнуть Login)

  • Пуллит /profiler/session/<id>?include=domains,events

  • Видит split: 80% доменов через direct-out, 3 — через vpn-1 (несимметричный routing внутри одного app'а — это ровно то, что ломает auth)

  • Раскапывает CNAME chain в самой session JSON'е

  • Пишет: «Корень — *.trbcdn.net уходит на .net TLD, в ru-domains rule_set его нет, добавь suffix»

  • Если попросить — сам делает PR с правкой template'а через Edit-tool, прогоняет тесты

Возвращаюсь — на экране summary проблемы, ссылка на git diff, иногда уже зелёный CI. Времени потрачено — на чай.

Это особенно работает потому что вся диагностика structured: agent читает не grep по логам, а typed JSON с domain / ip / cname_chain / outbound_chain / issues полями. SSE-стрим (fire-and-forget, без Last-Event-ID reconnect — overkill для in-app single-user use case'а) даёт live-feed для агентов которые хотят не пуллить, а реактивно ловить события.

Собственно фичу Per-app traffic profiler я во многом и ради этого сделал — раньше тот же flow был «дай мне 10 файлов logcat / state / connections, я разберу», теперь — «дай адрес API и подожди».

Что не делает (пока)

DNS-уровень с CNAME chain'ом — для каждого резолва записываю не только финальный IP, но и всю цепочку: cdn.t-bank-app.ru → cl-ead2c819.edgecdn.ru → 193.17.93.194. Это главное чего нет ни в одном другом Android роутере клиенте, и без чего сложные инциденты не отлавливаются.

Connections с outbound chain'ом — для каждого TCP/UDP conn'а видно через что роутится: direct-out или vless-server → 🇫🇮Финляндия (vpn-1), с детуром если он есть. Split-routing-диагностика без видимости outbound'а невозможна — это и есть та симметричная половина к domain'у, ради которой я фичу делал.

Connection-issue detection (DNS timeout + TCP RST early) — пассивная ⚠ подсветка на event'ах, чтобы не сканировать самому список из 50 доменов искать «что плохо». DNS timeout — прямой engine-сигнал из лога sing-box, TCP RST early — heuristic (closed <1с с 0 bytes).

Process inference fallback — когда sing-box find_process не атрибутировал conn к package'у (характерно для WebView и system services), пробую attribute по prior DNS resolved IPs в окне 10 секунд, помечается 〽. Без этого для WebView-based apps вроде того же Tinkoff'а часть трассы была бы дырявая.

JSON export через Share — один тап на иконку share — native Android Share intent с полным session JSON'ом (events + aggregates + meta). Закрывает workflow «записал → отправил себе/агенту/в Telegram → продолжил». Дешёвая ниточка, но без неё фича была бы dead-end.

Debug API + SSE — все UI-controls доступны через localhost HTTP API + Server-Sent Events стрим. Это foundation для agent-driven диагностики (про что я выше написал) — без него фича была бы просто-ещё-один-UI-tab. Самое ценное для меня лично.

HTTP-уровневые URL'ы и headers — сейчас видно только domain:port, а не GET /api/v3/auth/login, User-Agent, Authorization. Полезно было бы для full API-аудита (какой именно endpoint app дёргает, какие токены утекают). Но sing-box работает на уровне TUN/SOCKS — к нему трафик приходит уже зашифрованный TLS'ом, расшифровать без MITM-сертификата нельзя. Так что это принципиальная limit'ация архитектуры, не моя.

Per-domain latency / RTT — сейчас собираю только bytes и conn count. Добавить TCP handshake timings и DNS round-trip — было бы понятно, какой CDN-нод тормозит, а не «весь сервис тупит». Sing-box частично эту информацию пишет в dns: exchangedNms префиксе), TCP-handshake придётся probing'ом мерить отдельно, пока как идея чисто.

Inline создание routing rules[Block this domain] / [Add to ru-direct] кнопками прямо в Domains tab. Закрывает feedback loop: увидел проблему в trace → одним тапом сделал rule → проверил → не надо переключаться на Custom Rules screen и копировать имя домена руками. Это самое near-term — UI почти готов, осталось wire'нуть в существующий CustomRule API, но не ясно нужно ли, кстати агенты через API эту работу могут сделать без UI.

TLS fingerprinting (JA3/JA4) — хеш TLS ClientHello для каждой connection. Очень полезно для дебага DPI-проблем соединений: многие провайдеры в РФ блокируют не по домену, а по JA3 (распознают что вас sing-box а не Chrome). Sing-box внутри умеет fake'ить fingerprint через outbound/uTLS (мимикрия под Chrome/Firefox/Safari), но сам fingerprint наружу через libbox API пока не expose'ится. Жду в апстриме.

Где это работает

Релиз L×Box v1.7.0 (тема цикла — «Observability»). Android 8.0+ (minSdk 26), arm64-v8a / x86_64. Sing-box find_process: true нужен в config'е (по дефолту включён в моём wizard-template'е).

Open-source: github.com/Leadaxe/LxBox. Apk'и собираются исключительно через CI/CD на GitHub, телеметрии и сетевых обращений «налево» нет. Spec — [docs/spec/features/044](https://github.com/Leadaxe/LxBox/tree/main/docs/spec/features). User guide на per-app trace — [docs/features/per-app-trace.md](https://github.com/Leadaxe/LxBox/blob/main/docs/features/per-app-trace.md).

Если у вас есть кейсы где per-app трассировка реально пригодилась бы (или наоборот — где её всё ещё не хватает), идеи по issue rule'ам / locale-агностичной geo-проверке через geoip-lookup, или просто желание помочь с UI — буду рад issue'м и pull request'ам.

Sing-box внутри уже знает кто куда ходит и как роутится. L×Box — просто интерфейс, который наконец показывает это людям, не заставляя вручную matching'ать conn_id'ы между log-снапшотами.