MITM TPROXY Transparent Decrypt (Русский)
§1 Что это такое и когда это использовать
Заголовок раздела «§1 Что это такое и когда это использовать»Каждый из остальных четырёх режимов перехвата имеет свои ограничения:
| Режим | Как перенаправляется трафик | Ограничение |
|---|---|---|
| AgentBridge | Подмена DNS через /etc/hosts для фиксированного набора хостов |
только зарегистрированные хосты IDE-агентов |
| Пользовательские хосты | Подмена DNS через /etc/hosts для каждого хоста |
одна запись на хост; для редактирования требуется sudo |
| HTTP_PROXY | Переменные среды HTTP_PROXY/HTTPS_PROXY |
только приложения, учитывающие переменную среды |
| Общесистемный прокси | Настройки прокси ОС | изменяет глобальное состояние; требует отката |
Прозрачная расшифровка TPROXY перенаправляет трафик на уровне ядра. Она
помечает новые локальные исходящие TCP-соединения с целевым портом (по умолчанию 443)
в цепочке mangle OUTPUT, правило ip rule перенаправляет помеченные пакеты на локальную
доставку, а при повторном входе цель TPROXY в mangle PREROUTING передаёт их
прослушивателю IP_TRANSPARENT, который затем завершает TLS-соединение и захватывает
незашифрованные данные.
Используйте этот режим, если требуется перехватить и расшифровать трафик процесса, который:
- обращается к хосту, не зарегистрированному в AgentBridge, и
- не учитывает
HTTP_PROXY, и - не должен затрагиваться изменением общесистемных настроек прокси.
Поскольку перехват выполняется в ядре, исходному процессу не требуется изменять конфигурацию, однако процесс должен доверять динамическому центру сертификации, который устанавливает OmniRoute (см. §4).
§2 Требования
Заголовок раздела «§2 Требования»| Требование | Подробности |
|---|---|
| ОС | Только Linux — IP_TRANSPARENT является опцией сокета, доступной только в Linux. На всех остальных платформах загрузчик возвращает «недоступно». |
| Привилегии | Возможность CAP_NET_ADMIN для создания прозрачного сокета и применения правил iptables/ip — на практике следует запускать от имени root. |
| Нативный аддон | Небольшой аддон N-API (src/mitm/tproxy/native/transparent.c) должен быть собран или поставляться в виде предварительно собранного бинарного файла. См. §3. |
| Модули ядра | iptables с поддержкой TPROXY, mangle и сопоставления mark (проверено с ядром 6.8.0). |
Корректная деградация: если какое-либо требование не выполнено (система не на базе Linux, отсутствует набор инструментов,
аддон не собран), загрузчик аддона (src/mitm/tproxy/transparentSocket.ts::loadTransparentAddon)
возвращает null вместо выбрасывания исключения. После этого статус режима перехвата сообщает
available: false, переключатель на панели управления отключается, а во всплывающей подсказке
отображается текст «Для расшифровки TPROXY требуются Linux, права root и нативный аддон»; остальная часть
OmniRoute продолжает работать.
§3 Нативный аддон IP_TRANSPARENT
Заголовок раздела «§3 Нативный аддон IP_TRANSPARENT»Модуль Node net не может вызвать setsockopt(IP_TRANSPARENT) до bind(), как
того требует TPROXY (в противном случае ядро отбрасывает перенаправленные пакеты). Аддон
(src/mitm/tproxy/native/transparent.c, собираемый через binding.gyp) представляет собой небольшой модуль N-API,
предоставляющий три функции, которые используются через transparentSocket.ts:
| Функция аддона | Работа с сокетом | Назначение |
|---|---|---|
createTransparentListener(ip, port) |
socket() + SO_REUSEADDR + IP_TRANSPARENT + bind() + listen(), возвращает исходный fd |
прозрачный прослушиватель перехвата (Node принимает fd через server.listen({ fd })) |
setSocketMark(fd, mark) |
setsockopt SO_MARK для существующего fd |
предотвращение зацикливания (маркировка собственных сокетов прокси) |
connectMarked(ip, port, mark) |
socket() + SO_MARK до неблокирующего connect(), возвращает fd |
перенаправление повторно зашифрованного трафика вышестоящему серверу (SYN содержит метку) |
Исходный адрес назначения считывается из socket.localAddress/localPort — TPROXY
сохраняет его, поэтому поиск через SO_ORIGINAL_DST/NAT не требуется.
Сборка аддона
Заголовок раздела «Сборка аддона»npm run build:native:tproxy # cd src/mitm/tproxy/native && node-gyp rebuild # -> native/build/Release/transparent.node- Во время выполнения
npm run buildскриптscripts/build/build-tproxy-native.mjsзапускаетnode-gyp rebuild. Он предназначен только для Linux, а его сбой не является критическим — отсутствие набора инструментов просто делает режим перехвата недоступным. assembleStandalone.mjsкопируетbuild/Release/transparent.nodeв автономный пакет;transparentSocket.tsнаходит его как относительно модуля, так и относительно cwd (<cwd>/src/mitm/tproxy/native/...).build/иprebuilds/игнорируются Git — бинарный файл собирается, но никогда не добавляется в репозиторий.
Загрузчик проверяет в порядке приоритета:
native/build/Release/transparent.node, затем native/prebuilds/transparent.node
(как относительно модуля, так и в <cwd>/src/mitm/tproxy/).
§4 Динамический CA для каждого SNI и установщик хранилища доверия
Заголовок раздела «§4 Динамический CA для каждого SNI и установщик хранилища доверия»Обновление #6684: статический сервер AgentBridge (
src/mitm/server.cjs) теперь использует ту же архитектурную схему CA/конечного сертификата вместо одного статического самоподписанного конечного сертификата. Он использует отдельный экземпляр CA (src/mitm/cert/rootCa.ts, сохраняемый в<DATA_DIR>/mitm/ca.key/ca.crt) и устанавливает сертификат в ранее существовавший слот хранилища доверияomniroute-mitm.crt(заменяя в нём старый одиночный конечный сертификат — очистка двойного доверия не требуется), оставаясь полностью отделённым от собственного слота TPROXYomniroute-tproxy-ca.crt, описанного ниже. Новые установки AgentBridge автоматически используют модель CA; установка, которая уже доверяет старому статическому конечному сертификату, продолжает использовать его, пока оператор явно не включитMITM_ROOT_CA_ENABLED=true(см.src/mitm/cert/migration.ts) — доверенный MITM CA, способный подписать конечный сертификат для любого хоста, обладает существенно большими возможностями, чем старый сертификат с фиксированным SAN, поэтому для уже доверяющей ему установки переключение никогда не выполняется незаметно.
Исторически статический MITM-сертификат AgentBridge работал только потому, что
AgentBridge подменяет DNS для фиксированного набора хостов (теперь
унифицированного с приведённой ниже моделью). TPROXY перехватывает
произвольные хосты, поэтому его прослушиватель должен предъявлять
действительный конечный сертификат для любого SNI, запрошенного клиентом, —
то же требование теперь применяется к AgentBridge для полного набора
MITM_TOOL_HOSTS (9 записей инструментов), а не только для 4 хостов
antigravity.
Динамический CA (src/mitm/tproxy/dynamicCert.ts)
Заголовок раздела «Динамический CA (src/mitm/tproxy/dynamicCert.ts)»DynamicCertStore запускает локальный CA (созданный на основе зависимости
selfsigned), который:
- Создаёт долгоживущий CA с помощью
generateMitmCa()(CN"OmniRoute MITM CA", срок действия 10 лет,basicConstraints CA=true+keyUsage keyCertSign,cRLSign, 2048-битный RSA / SHA-256). - Выдаёт отдельный конечный сертификат для каждого имени хоста SNI по
требованию с помощью
issueLeafCert()(срок действия 1 год,subjectAltName= хост SNI) и кэширует по одномуtls.SecureContextдля каждого имени хоста. - Предоставляет
createSNICallback()для сервера, завершающего TLS-соединение (см. §5). - Может быть создан с
existingCa, чтобы CA оставался неизменным после перезапусков (и хранилище доверия не требовалось переустанавливать).
Закрытый ключ CA никогда не покидает машину.
Установщик хранилища доверия (src/mitm/tproxy/caTrust.ts)
Заголовок раздела «Установщик хранилища доверия (src/mitm/tproxy/caTrust.ts)»Перехватываемый клиент должен доверять динамическому CA, поэтому при запуске
режима перехвата сертификат CA устанавливается в хранилище доверия ОС в
выделенный слот — omniroute-tproxy-ca.crt (константа
TPROXY_CA_CERT_NAME), отдельный от слота статического MITM-сертификата
(omniroute-mitm.crt), чтобы они никогда не перезаписывали друг друга.
installTproxyCa(caPem, sudoPassword?) определяет каталог доверенных
сертификатов дистрибутива (сначала проверяется вариант Debian) и запускает
соответствующую команду обновления:
| Каталог доверенных сертификатов | Команда обновления |
|---|---|
/usr/local/share/ca-certificates |
update-ca-certificates |
/etc/ca-certificates/trust-source/anchors |
update-ca-trust |
/etc/pki/ca-trust/source/anchors |
update-ca-trust |
/etc/pki/trust/anchors |
update-ca-certificates |
При установке PEM помещается во временный файл, после чего с повышенными
привилегиями выполняется mkdir -p для каталога доверенных сертификатов,
командой cp подготовленный файл копируется в него, а затем запускается команда
обновления. uninstallTproxyCa() удаляет только выделенный слот (не затрагивая
статический MITM-сертификат) и обновляет хранилище; вне Linux операция ничего
не делает.
Все привилегированные команды выполняются через execFileWithPassword
(src/mitm/systemCommands.ts) — spawn с массивами аргументов, без оболочки
и без строковой интерполяции (жёсткое правило №13).
Когда процесс выполняется от имени root (например, на VPS), целевая команда
запускается напрямую и пароль не требуется; на настольной системе не от имени
root значение sudoPassword передаётся через стандартный ввод sudo -S.
Значение
sudoPasswordдля настольной системы передаётся в теле POST-запроса, чтобы авторизовать установку в хранилище доверия; оно полностью игнорируется, если процесс выполняется от имени root.
§5 Как работают расшифровка и перехват
Заголовок раздела «§5 Как работают расшифровка и перехват»Конвейер (всё находится в src/mitm/tproxy/):
локальное приложение ──TCP/443──▶ mangle OUTPUT помечает соединение (fwmark) ip rule → локальная таблица маршрутизации → lo mangle PREROUTING TPROXY → слушатель IP_TRANSPARENT (порт 8443) │ captureMode.ts: считывает исходный адрес назначения из socket.localAddress ▼ tlsCapture.ts: 1. Завершает TLS-соединение КЛИЕНТА с отдельным сертификатом для каждого SNI (dynamicCert) 2. внутренний http.Server разбирает расшифрованный открытый текст 3. перехват → globalTrafficBuffer.push() с source: "tproxy" (применяются sanitizeHeaders + maskSecret) 4. пересылает данные, ПОВТОРНО зашифровав их, исходному получателю через сокет с обходной меткой (connectMarked, защита от зацикливания) │ ▼ исходный вышестоящий сервер (api.example.com)- Завершение TLS (
createTlsCaptureServer): оборачивает перехваченный необработанный сокет в серверныйtls.TLSSocket, используя SNI-обработчик динамического центра сертификации, а затем передаёт расшифрованный поток внутреннемуhttp.Server(стандартный приём завершения соединения в MITM). Время жизни сокетов ограничено значениемMITM_IDLE_TIMEOUT_MS, чтобы зависший туннель не мог исчерпать файловые дескрипторы. - Перехват (
handleDecryptedRequest): помещаетInterceptedRequestсsource: "tproxy"и начальным статусом"in-flight"в буфер, предварительно обрабатывая заголовки с помощьюsanitizeHeaders(), а тела — с помощьюmaskSecret(). Затем запись дополняется ответом, размерами и задержкой. - Повторно зашифрованная пересылка (
createForward/realForward): повторно шифрует данные для исходного получателя. По умолчаниюrejectUnauthorizedимеет значениеtrue(безопасно по умолчанию) — сертификат вышестоящего сервера проверяется относительно SNI/Host, запрошенного клиентом, поэтому прокси отклоняет именно то, что отклонил бы исходный клиент.
Защита от зацикливания (SO_MARK)
Заголовок раздела «Защита от зацикливания (SO_MARK)»Поскольку правила помечают новые локальные исходящие соединения, собственная повторно зашифрованная пересылка прокси обычно перехватывалась бы повторно, что привело бы к бесконечному циклу. Путь пересылки защищён от этого с помощью обходной метки сокета (SO_MARK):
realForwardоткрывает сокет к вышестоящему серверу черезconnectMarked(ip, port, DEFAULT_BYPASS_MARK)—DEFAULT_BYPASS_MARK = 0x539— который устанавливает SO_MARK до вызоваconnect(), поэтому SYN-пакет пересылаемого соединения содержит обходную метку.- Правило
mangle OUTPUTисключает соединения, уже содержащие обходную метку (-m mark ! --mark <bypassMark>), поэтому пересылаемое прокси соединение не помечается повторно и не попадает снова в TPROXY.
Примечание о реализации: сокет с обходной меткой должен устанавливаться через
createConnectionагента (https.request({ createConnection })незаметно игнорируется при наличии агента), иначе для пересылки был бы открыт сокет без метки и зацикливание возобновилось бы. Это исправление защиты от зацикливания было проверено e2e-тестами.
§6 Безопасность
Заголовок раздела «§6 Безопасность»| Мера защиты | Подробности |
|---|---|
| API только для loopback-интерфейса | /api/tools/agent-bridge/tproxy охватывается префиксом /api/tools/agent-bridge/ в LOCAL_ONLY_API_PREFIXES (src/server/authz/routeGuard.ts). Ограничение loopback применяется до аутентификации (жёсткие правила №15 и №17): утёкший JWT, переданный через туннель, не позволит запустить перехват TPROXY, который применяет правила iptables и устанавливает CA в хранилище доверенных сертификатов посредством дочерних процессов. |
| Выделенное место для CA | Динамический CA устанавливается как omniroute-tproxy-ca.crt, не перезаписывая статический сертификат MITM. |
| Ключ CA не покидает хост | DynamicCertStore хранит ключ CA в памяти; он не экспортируется. |
| Маскирование секретов | maskSecret() для тел запросов и ответов и sanitizeHeaders() для заголовков выполняются до globalTrafficBuffer.push(). |
| Без интерполяции командной оболочки | Все команды iptables/ip/хранилища доверенных сертификатов выполняются через execFile/execFileWithPassword с массивами аргументов (жёсткое правило №13). |
| Проверка сертификата вышестоящего сервера | Повторно зашифрованное перенаправление по умолчанию проверяет сертификат вышестоящего сервера (rejectUnauthorized: true). |
| Санитизация ошибок | Ответы маршрута с ошибками обрабатываются через sanitizeErrorMessage() (жёсткое правило №12). |
CA для MITM — это мощная возможность. CA, которому доверяет ОС и который может подписывать сертификаты для любого хоста, означает, что всё перехватываемое OmniRoute содержимое может быть расшифровано. Эта возможность доступна только в явно включённом локальном режиме перехвата TPROXY, по умолчанию отключена, а запись в хранилище доверенных сертификатов удаляется при остановке режима.
§7 Транзакционное применение / откат правил брандмауэра
Заголовок раздела «§7 Транзакционное применение / откат правил брандмауэра»Сбой никогда не должен оставлять после себя правило mangle или устаревший маршрут. Построитель команд
(src/mitm/tproxy/commands.ts) и исполнитель (src/mitm/tproxy/setup.ts) гарантируют, что
откат является точной операцией, обратной применению, и выполняется в обратном порядке.
applyTproxy(cfg) выполняет команды применения по порядку; при любом сбое он выполняет
полный revertTproxy(cfg) в режиме максимальных усилий и повторно выбрасывает исключение — поэтому правила брандмауэра
либо применяются полностью, либо полностью откатываются, но никогда не остаются применёнными частично. revertTproxy(cfg) выполняет
обратные команды в обратном порядке и игнорирует сбои (операция идемпотентна — её можно безопасно вызывать
безусловно, например при очистке AgentBridge в repairMitm()).
validateTproxyConfig(cfg) выполняется до любой команды: порты должны находиться в диапазоне 1–65535,
mark/routeTable/bypassMark должны быть положительными целыми числами, а bypassMark должен
отличаться от mark (защита от зацикливания).
Команды применения (по порядку)
Заголовок раздела «Команды применения (по порядку)»ip rule add fwmark <mark> lookup <routeTable>ip route add local 0.0.0.0/0 dev lo table <routeTable>iptables -t mangle -A OUTPUT -p tcp --dport <dport> -m mark ! --mark <bypassMark> -j MARK --set-mark <mark>iptables -t mangle -A PREROUTING -p tcp --dport <dport> -m mark --mark <mark> -j TPROXY --on-port <onPort> --tproxy-mark <mark>При откате они удаляются в обратном порядке: PREROUTING -D, OUTPUT -D, ip route del, ip rule del.
Этот механизм основан на OUTPUT, поскольку в сценарии MITM перехватывается локальный исходящий трафик (приложения на том же хосте), который TPROXY только в
PREROUTINGне видит —PREROUTINGвидит только перенаправляемый трафик. ЦепочкаOUTPUTмаркирует новые локальные соединения,ip ruleперенаправляет их на локальную доставку (lo), после чегоPREROUTINGнаправляет их прозрачному слушателю.
§8 Конфигурация
Заголовок раздела «§8 Конфигурация»Запрос запуска (POST /api/tools/agent-bridge/tproxy) принимает следующие
поля, проверяемые с помощью StartTproxyBodySchema (tproxy/route.ts). Все они необязательны
и при отсутствии получают значения по умолчанию:
| Поле | Тип | По умолчанию | Примечания |
|---|---|---|---|
| dport | int (1–65535) | 443 |
TCP-порт назначения, который требуется прозрачно перехватывать |
| mark | int (≥1) | 0x2333 |
Метка брандмауэра, устанавливаемая в OUTPUT и сопоставляемая посредством ip rule + PREROUTING |
| onPort | int (1–65535) | 8443 |
Порт, к которому привязывается прозрачный слушатель (IP_TRANSPARENT) |
| routeTable | int (≥1) | 233 |
Идентификатор таблицы маршрутизации на основе политик, содержащей маршрут local 0.0.0.0/0 |
| bypassMark | int (≥1, ≠ mark) |
0x539 |
Обходная метка сокета (SO_MARK), которую прокси устанавливает для собственных исходящих соединений; исключается в OUTPUT (защита от зацикливания) |
| sudoPassword | string | — | Только для настольных систем без root-доступа: разрешает установку в хранилище доверия; игнорируется при наличии root-доступа |
Для TPROXY нет переменных окружения — вся конфигурация передаётся через тело POST-запроса либо используются указанные выше значения по умолчанию.
§9 Включение через Инспектор трафика
Заголовок раздела «§9 Включение через Инспектор трафика»- Откройте Инспектор трафика (
/dashboard/tools/traffic-inspector). - На панели режимов захвата найдите кнопку «Расшифровка TPROXY» ⚠
(
src/app/(dashboard)/dashboard/tools/traffic-inspector/components/CaptureModesToolbar.tsx). - Нажмите кнопку. Она вызывает
POST /api/tools/agent-bridge/tproxyчерезstartTproxyCaptureMode()(src/lib/inspector/tproxyCaptureApi.ts), который: создаёт динамический ЦС, открывает прозрачный слушатель, применяет правила межсетевого экрана и устанавливает ЦС в системное хранилище доверенных сертификатов. - Во время работы переключатель становится янтарным и показывает текущее количество
перехватов (
· <interceptCount>). Перехваченные запросы появляются в списке запросов сsource: "tproxy". - Нажмите ещё раз, чтобы остановить работу:
DELETE /api/tools/agent-bridge/tproxyчерезstopTproxyCaptureMode()закрывает слушатель, удаляет ЦС и отменяет правила межсетевого экрана.
Состояние режима захвата (работает / доступен / количество перехватов / порт слушателя)
получается из GET /api/tools/agent-bridge/tproxy (getCaptureStatus() в
src/mitm/tproxy/captureManager.ts). Одновременно может выполняться только один
сеанс TPROXY — попытка запуска второго завершается ошибкой «Режим захвата TPROXY уже запущен».
§10 Устранение неполадок
Заголовок раздела «§10 Устранение неполадок»Переключатель отключён
Заголовок раздела «Переключатель отключён»Нативный аддон невозможно загрузить. Убедитесь, что используется Linux, аддон собран
(npm run build:native:tproxy), а процесс может загрузить transparent.node.
isTransparentSocketAvailable() управляет доступностью переключателя;
GET /api/tools/agent-bridge/tproxy возвращает available: false, если аддон отсутствует.
Ничего не захватывается
Заголовок раздела «Ничего не захватывается»- Убедитесь, что перехватываемый процесс действительно подключается к настроенному
dport(по умолчанию443). - Убедитесь, что процесс доверяет динамическому ЦС. ЦС устанавливается как
omniroute-tproxy-ca.crt; в приложениях с собственным хранилищем доверенных сертификатов (Firefox/Chrome NSS), возможно, потребуется также добавить сертификат туда. - Запустите самопроверку Диагностика AgentBridge (см.
AGENTBRIDGE.md) для проверки доверия к сертификату и работоспособности сервера.
Устаревшие правила межсетевого экрана после сбоя
Заголовок раздела «Устаревшие правила межсетевого экрана после сбоя»revertTproxy() является точной обратной операцией для применения правил и идемпотентна.
Остановка режима отменяет правила; если OmniRoute был принудительно завершён во время
сеанса, используйте действие Восстановление AgentBridge
(POST /api/tools/agent-bridge/repair), чтобы отменить оставшееся системное состояние
(подмена DNS, корневой ЦС, системный прокси-сервер). Правила TPROXY mangle и маршрут
также автоматически очищаются при перезагрузке.
Бесконечный цикл / прокси-сервер перехватывает собственную пересылку
Заголовок раздела «Бесконечный цикл / прокси-сервер перехватывает собственную пересылку»Это случай защиты от зацикливания. Убедитесь, что bypassMark отличается от mark
(это обеспечивается валидацией) и что для пересылки используется connectMarked
(он используется в realForward). См. §5 Защита от зацикливания.
§11 Карта исходного кода
Заголовок раздела «§11 Карта исходного кода»| Файл | Назначение |
|---|---|
src/mitm/tproxy/commands.ts |
Чистый построитель команд применения и отмены iptables/ip; validateTproxyConfig |
src/mitm/tproxy/setup.ts |
Транзакционный исполнитель applyTproxy / revertTproxy (откат при сбое) |
src/mitm/tproxy/transparentSocket.ts |
Загрузчик нативного аддона (loadTransparentAddon), createTransparentListenerFd, connectMarked, setSocketMark, isTransparentSocketAvailable |
src/mitm/tproxy/native/transparent.c |
Аддон N-API: createTransparentListener (IP_TRANSPARENT), setSocketMark, connectMarked |
src/mitm/tproxy/native/binding.gyp |
Манифест сборки node-gyp |
src/mitm/tproxy/dynamicCert.ts |
DynamicCertStore — динамический ЦС для каждого SNI и кеш конечных сертификатов |
src/mitm/tproxy/caTrust.ts |
Установка/удаление в хранилище доверия ОС (installTproxyCa / uninstallTproxyCa, выделенный слот) |
src/mitm/tproxy/tlsCapture.ts |
Механизм расшифровки с терминированием TLS и повторно зашифрованной пересылкой с защитой от зацикливания |
src/mitm/tproxy/captureMode.ts |
Оркестрация прозрачного слушателя; исходный адрес назначения считывается из socket.localAddress |
src/mitm/tproxy/captureManager.ts |
Жизненный цикл синглтона: startCaptureMode / stopCaptureMode / getCaptureStatus |
src/app/api/tools/agent-bridge/tproxy/route.ts |
Маршрут GET / POST / DELETE (LOCAL_ONLY) |
src/lib/inspector/tproxyCaptureApi.ts |
Клиентские вспомогательные функции fetch (fetchTproxyStatus / startTproxyCaptureMode / stopTproxyCaptureMode) |
HagiCode
HagiCode — агентная среда разработки со структурированными процессами, параллельным выполнением несколькими агентами и интерфейсами Hero Dungeon.
Превращайте идеи в полезное ПО с более умным, быстрым и увлекательным агентным рабочим процессом.

- SmartСтруктурированные процессы превращают намерение в исполнимый путь от идеи до готового изменения.
- EfficientМультиагентные процессы параллельно продвигают исследование, реализацию и проверку.
- FunHero Dungeon делает длительную совместную разработку наглядной и увлекательной.