Ir al contenido
OmniRoute source

MITM TPROXY Transparent Decrypt (Español)

Los otros cuatro modos de captura tienen una limitación cada uno:

Modo Cómo se dirige el tráfico Limitación
AgentBridge Falsificación DNS mediante /etc/hosts para un conjunto fijo de hosts solo los hosts registrados del agente del IDE
Hosts personalizados Falsificación DNS mediante /etc/hosts por host una entrada por host; se necesita sudo para editar hosts
HTTP_PROXY Variables de entorno HTTP_PROXY/HTTPS_PROXY solo las aplicaciones que respetan la variable de entorno
Proxy de todo el sistema Configuración del proxy del sistema operativo modifica el estado global; es necesario revertirlo

En cambio, el descifrado transparente TPROXY dirige el tráfico en la capa del kernel. Marca las nuevas conexiones TCP salientes locales a un puerto objetivo (de forma predeterminada, 443) en la cadena mangle OUTPUT; una regla ip rule redirige los paquetes marcados a la entrega local y, al volver a entrar, el objetivo TPROXY de mangle PREROUTING los entrega a un listener IP_TRANSPARENT, que termina TLS y captura el texto sin cifrar.

Úselo cuando quiera capturar y descifrar el tráfico de un proceso que:

  • se comunica con un host que AgentBridge no registra,
  • no respeta HTTP_PROXY, y
  • no desea alterar con un cambio del proxy de todo el sistema.

Como la interceptación se produce en el kernel, el proceso de origen no necesita ningún cambio de configuración, pero debe confiar en la CA dinámica que instala OmniRoute (consulte §4).


Requisito Detalle
SO Solo Linux — IP_TRANSPARENT es una opción de socket exclusiva de Linux. El cargador devuelve “no disponible” en cualquier otra plataforma.
Privilegios La capacidad CAP_NET_ADMIN para crear el socket transparente y aplicar reglas de iptables/ip; en la práctica, debe ejecutarse como root.
Addon nativo Debe compilarse un pequeño addon de N-API (src/mitm/tproxy/native/transparent.c) o distribuirse como binario precompilado. Consulte §3.
Módulos del kernel iptables con compatibilidad para TPROXY, mangle y la coincidencia mark (validado con el kernel 6.8.0).

Degradación controlada: si falta algún requisito (sistema distinto de Linux, ausencia de cadena de herramientas, addon sin compilar), el cargador del addon (src/mitm/tproxy/transparentSocket.ts::loadTransparentAddon) devuelve null en lugar de generar una excepción. El estado del modo de captura indica entonces available: false, el selector del panel de control queda deshabilitado con la información sobre herramientas “El descifrado TPROXY requiere Linux + root + el addon nativo”, y el resto de OmniRoute continúa funcionando.


El módulo net de Node no puede ejecutar setsockopt(IP_TRANSPARENT) antes de bind(), lo cual es necesario para TPROXY (de lo contrario, el kernel descarta los paquetes redirigidos). El addon (src/mitm/tproxy/native/transparent.c, compilado mediante binding.gyp) es un pequeño módulo N-API que expone tres funciones, utilizadas a través de transparentSocket.ts:

Función del addon Operación del socket Uso
createTransparentListener(ip, port) socket() + SO_REUSEADDR + IP_TRANSPARENT + bind() + listen(); devuelve el fd sin procesar el listener de captura transparente (Node adopta el fd mediante server.listen({ fd }))
setSocketMark(fd, mark) setsockopt SO_MARK en un fd existente prevención de bucles (marca los sockets propios del proxy)
connectMarked(ip, port, mark) socket() + SO_MARK antes de un connect() no bloqueante; devuelve el fd el reenvío ascendente cifrado de nuevo (el SYN lleva la marca)

El destino original se obtiene de socket.localAddress/localPort; TPROXY lo conserva, por lo que no es necesaria ninguna consulta de SO_ORIGINAL_DST/NAT.

Ventana de terminal
npm run build:native:tproxy # entra en src/mitm/tproxy/native y ejecuta node-gyp rebuild
# -> native/build/Release/transparent.node
  • Durante npm run build, scripts/build/build-tproxy-native.mjs ejecuta node-gyp rebuild. Es exclusivo de Linux y no fatal: si falta la cadena de herramientas, simplemente el modo de captura queda no disponible.
  • assembleStandalone.mjs copia build/Release/transparent.node en el paquete independiente; transparentSocket.ts lo resuelve tanto de forma relativa al módulo como relativa al cwd (<cwd>/src/mitm/tproxy/native/...).
  • build/ y prebuilds/ están ignorados por git: el binario se compila, nunca se incluye en commits.

El cargador comprueba, en orden de prioridad: native/build/Release/transparent.node y después native/prebuilds/transparent.node (tanto de forma relativa al módulo como bajo <cwd>/src/mitm/tproxy/).


§4 La CA dinámica por SNI y el instalador del almacén de confianza

Sección titulada «§4 La CA dinámica por SNI y el instalador del almacén de confianza»

Actualización de #6684: el servidor estático de AgentBridge (src/mitm/server.cjs) ahora comparte este mismo patrón de arquitectura de CA/certificado final en lugar de usar un único certificado final autofirmado estático. Utiliza una instancia de CA distinta (src/mitm/cert/rootCa.ts, persistida en <DATA_DIR>/mitm/ca.key/ca.crt) y se instala en la ubicación preexistente omniroute-mitm.crt del almacén de confianza (sustituyendo allí el antiguo certificado final único, sin necesidad de limpiar dos certificados de confianza), que se mantiene completamente separada de la ubicación omniroute-tproxy-ca.crt propia de TPROXY descrita más adelante. Las instalaciones nuevas de AgentBridge obtienen automáticamente el modelo de CA; una instalación que ya confiaba en el antiguo certificado final estático continúa utilizándolo hasta que el operador habilita explícitamente MITM_ROOT_CA_ENABLED=true (consulte src/mitm/cert/migration.ts): una CA MITM de confianza que puede firmar un certificado final para cualquier host es considerablemente más potente que el antiguo certificado final con SAN fijos, por lo que el cambio nunca se realiza de forma silenciosa en una instalación que ya confía en él.

Históricamente, el certificado MITM estático de AgentBridge solo funcionaba porque AgentBridge falsifica mediante DNS un conjunto fijo de hosts (ahora unificado con el modelo descrito a continuación). TPROXY intercepta hosts arbitrarios, por lo que su listener debe presentar un certificado final válido para cualquier SNI que solicite el cliente, el mismo requisito que ahora tiene AgentBridge para el conjunto completo MITM_TOOL_HOSTS (9 entradas de herramientas), en lugar de solo para los 4 hosts de antigravity.

CA dinámica (src/mitm/tproxy/dynamicCert.ts)

Sección titulada «CA dinámica (src/mitm/tproxy/dynamicCert.ts)»

DynamicCertStore ejecuta una CA local (creada sobre la dependencia selfsigned) que:

  • Genera una CA de larga duración mediante generateMitmCa() (CN "OmniRoute MITM CA", validez de 10 años, basicConstraints CA=true + keyUsage keyCertSign,cRLSign, RSA de 2048 bits/SHA-256).
  • Emite un certificado final por nombre de host SNI bajo demanda mediante issueLeafCert() (validez de 1 año, subjectAltName = el host SNI) y almacena en caché un tls.SecureContext por nombre de host.
  • Expone createSNICallback() para el servidor que termina TLS (consulte §5).
  • Puede construirse con una existingCa para mantener estable la CA entre reinicios (de modo que no sea necesario reinstalarla en el almacén de confianza).

La clave privada de la CA nunca sale de la máquina.

Instalador del almacén de confianza (src/mitm/tproxy/caTrust.ts)

Sección titulada «Instalador del almacén de confianza (src/mitm/tproxy/caTrust.ts)»

El cliente interceptado debe confiar en la CA dinámica, por lo que, al iniciar el modo de captura, el certificado de la CA se instala en el almacén de confianza del SO bajo una ubicación dedicada —omniroute-tproxy-ca.crt (constante TPROXY_CA_CERT_NAME)—, separada de la ubicación del certificado MITM estático (omniroute-mitm.crt) para que nunca se sobrescriban entre sí.

installTproxyCa(caPem, sudoPassword?) detecta el directorio de anclajes de la distribución (en orden: primero el estilo Debian) y ejecuta el comando de actualización correspondiente:

Directorio de anclajes Comando de actualización
/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

La instalación coloca primero el PEM en un archivo temporal y, a continuación, ejecuta (con privilegios) mkdir -p para crear el directorio de anclajes, usa cp para copiar allí el archivo preparado y ejecuta el comando de actualización. uninstallTproxyCa() elimina únicamente la ubicación dedicada (sin modificar el certificado MITM estático) y actualiza el almacén; en sistemas que no sean Linux, no realiza ninguna operación.

Todos los comandos con privilegios se ejecutan mediante execFileWithPassword (src/mitm/systemCommands.ts): spawn con matrices de argumentos, sin shell ni interpolación de cadenas (Regla estricta n.º 13). Cuando el proceso se ejecuta como root (por ejemplo, en el VPS), el comando de destino se ejecuta directamente y no se necesita contraseña; en un equipo de escritorio sin acceso root, sudoPassword se pasa mediante sudo -S por stdin.

El valor sudoPassword del equipo de escritorio se proporciona en el cuerpo de la solicitud POST para autorizar la instalación en el almacén de confianza; se ignora por completo cuando el proceso se ejecuta como root.


§5 Cómo funcionan el descifrado y la captura

Sección titulada «§5 Cómo funcionan el descifrado y la captura»

La canalización (todo bajo src/mitm/tproxy/):

aplicación local ──TCP/443──▶ mangle OUTPUT marca la conexión (fwmark)
ip rule → tabla de rutas local → lo
mangle PREROUTING TPROXY → listener IP_TRANSPARENT (puerto 8443)
│ captureMode.ts: lee el destino original de socket.localAddress
▼
tlsCapture.ts:
1. finaliza TLS para el CLIENTE con un certificado hoja por SNI (dynamicCert)
2. un http.Server interno analiza el texto plano descifrado
3. captura → globalTrafficBuffer.push() con source: "tproxy"
(se aplican sanitizeHeaders + maskSecret)
4. reenvía, cifrándolo de nuevo, al destino original
mediante un socket marcado para omisión (connectMarked, antirrecursión)
│
▼
servidor ascendente original (api.example.com)
  • Terminación TLS (createTlsCaptureServer): envuelve el socket interceptado sin procesar en un tls.TLSSocket del lado del servidor mediante el callback SNI de la CA dinámica y, a continuación, entrega el flujo descifrado a un http.Server interno (la técnica estándar de terminación MITM). La duración de los sockets está limitada por MITM_IDLE_TIMEOUT_MS, de modo que un túnel bloqueado no pueda agotar los descriptores de archivo.
  • Captura (handleDecryptedRequest): inserta un InterceptedRequest con source: "tproxy", un estado inicial de "in-flight", las cabeceras procesadas mediante sanitizeHeaders() y los cuerpos mediante maskSecret() antes de que entren en el búfer. A continuación, la entrada se actualiza con la respuesta, los tamaños y la latencia.
  • Reenvío cifrado de nuevo (createForward / realForward): vuelve a cifrar el tráfico hacia el destino original. El valor predeterminado de rejectUnauthorized es true (seguro de forma predeterminada): el certificado del servidor ascendente se verifica con respecto al SNI/Host solicitado por el cliente, por lo que el proxy rechaza exactamente lo que rechazaría el cliente original.

Como las reglas marcan las nuevas conexiones salientes locales, el reenvío cifrado de nuevo del propio proxy normalmente volvería a interceptarse, lo que produciría un bucle infinito. La ruta de reenvío se protege frente a esto mediante una marca de socket de omisión (SO_MARK):

  • realForward abre su socket ascendente mediante connectMarked(ip, port, DEFAULT_BYPASS_MARK) — DEFAULT_BYPASS_MARK = 0x539 —, que establece SO_MARK antes de connect(), de modo que el SYN del reenvío incluye la marca de omisión.
  • La regla mangle OUTPUT excluye las conexiones que ya tengan la marca de omisión (-m mark ! --mark &lt;bypassMark&gt;), por lo que el reenvío del proxy no vuelve a marcarse ni vuelve a entrar en TPROXY.

Nota de implementación: el socket marcado para omisión debe instalarse en createConnection del agente (https.request({ createConnection }) se ignora silenciosamente cuando hay un agente presente); de lo contrario, el reenvío abriría un socket sin marcar y el bucle reaparecería. Esta fue la corrección antirrecursión validada mediante pruebas e2e.


Control Detalle
API exclusiva de loopback /api/tools/agent-bridge/tproxy está cubierta por el prefijo /api/tools/agent-bridge/ en LOCAL_ONLY_API_PREFIXES (src/server/authz/routeGuard.ts). La restricción de loopback se aplica antes de la autenticación (Reglas estrictas n.º 15 y n.º 17): un JWT filtrado a través de un túnel no puede iniciar la captura TPROXY, que aplica reglas de iptables e instala una CA en el almacén de confianza mediante procesos secundarios.
Ubicación dedicada para la CA La CA dinámica se instala en omniroute-tproxy-ca.crt, sin sobrescribir nunca el certificado MITM estático.
La clave de la CA nunca sale del host DynamicCertStore mantiene la clave de la CA en memoria; no se exporta.
Enmascaramiento de secretos maskSecret() en los cuerpos de las solicitudes/respuestas y sanitizeHeaders() en los encabezados se ejecutan antes de globalTrafficBuffer.push().
Sin interpolación de shell Todos los comandos de iptables/ip/almacén de confianza se ejecutan mediante execFile/execFileWithPassword con matrices de argumentos (Regla estricta n.º 13).
Verificación del certificado de origen El reenvío recifrado verifica de forma predeterminada el certificado del servidor de origen (rejectUnauthorized: true).
Saneamiento de errores Las respuestas de error de la ruta pasan por sanitizeErrorMessage() (Regla estricta n.º 12).

La CA MITM es una capacidad muy potente. Una CA en la que confía el sistema operativo y que puede firmar para cualquier host implica que se puede descifrar todo lo que OmniRoute intercepte. Está protegida mediante el modo explícito de captura TPROXY, exclusivo para conexiones locales y desactivado de forma predeterminada; además, la entrada del almacén de confianza se elimina al detener el modo.


§7 Aplicación / reversión transaccional del firewall

Sección titulada «§7 Aplicación / reversión transaccional del firewall»

Un fallo nunca debe dejar una regla mangle ni una ruta obsoleta. El generador de comandos (src/mitm/tproxy/commands.ts) y el ejecutor (src/mitm/tproxy/setup.ts) garantizan que la reversión sea exactamente la operación inversa de la aplicación, en orden inverso.

applyTproxy(cfg) ejecuta los comandos de aplicación en orden; ante cualquier fallo, ejecuta una operación completa de revertTproxy(cfg) mediante el mejor esfuerzo y vuelve a lanzar el error, de modo que el firewall queda completamente aplicado o completamente revertido, nunca aplicado a medias. revertTproxy(cfg) ejecuta los comandos inversos en orden inverso e ignora los fallos (es idempotente y se puede invocar incondicionalmente, por ejemplo, desde la limpieza repairMitm() de AgentBridge).

validateTproxyConfig(cfg) se ejecuta antes de cualquier comando: los puertos deben estar entre 1–65535, mark/routeTable/bypassMark deben ser enteros positivos y bypassMark debe ser diferente de mark (antibucle).

Ventana de terminal
ip rule add fwmark &lt;mark&gt; lookup &lt;routeTable&gt;
ip route add local 0.0.0.0/0 dev lo table &lt;routeTable&gt;
iptables -t mangle -A OUTPUT -p tcp --dport &lt;dport&gt; -m mark ! --mark &lt;bypassMark&gt; -j MARK --set-mark &lt;mark&gt;
iptables -t mangle -A PREROUTING -p tcp --dport &lt;dport&gt; -m mark --mark &lt;mark&gt; -j TPROXY --on-port &lt;onPort&gt; --tproxy-mark &lt;mark&gt;

La reversión los elimina en orden inverso: PREROUTING -D, OUTPUT -D, ip route del, ip rule del.

La receta se basa en OUTPUT porque el caso de uso de MITM es el tráfico saliente local (aplicaciones en el mismo host), que TPROXY en PREROUTING por sí solo no ve, ya que PREROUTING solo ve el tráfico reenviado. La cadena OUTPUT marca las conexiones locales nuevas, la regla ip rule las redirige a la entrega local (lo) y, a continuación, PREROUTING las asigna al listener transparente.


La solicitud de inicio (POST /api/tools/agent-bridge/tproxy) acepta los siguientes campos, validados mediante StartTproxyBodySchema (tproxy/route.ts). Todos son opcionales y, si no se especifican, adoptan sus valores predeterminados:

Campo Tipo Valor predeterminado Notas
dport int (1–65535) 443 Puerto TCP de destino que se interceptará de forma transparente
mark int (≥1) 0x2333 Marca del firewall establecida en OUTPUT, con la que coinciden ip rule + PREROUTING
onPort int (1–65535) 8443 Puerto al que se vincula el listener transparente (IP_TRANSPARENT)
routeTable int (≥1) 233 Id. de la tabla de enrutamiento por políticas que contiene la ruta local 0.0.0.0/0
bypassMark int (≥1, ≠ mark) 0x539 Marca de socket de omisión (SO_MARK) que el proxy establece en sus propias conexiones ascendentes; se excluye en OUTPUT (antibucle)
sudoPassword string — Solo para escritorios sin acceso root: autoriza la instalación en el almacén de confianza; se ignora con acceso root

No hay variables de entorno para TPROXY: toda la configuración se proporciona mediante el cuerpo de la solicitud POST o los valores predeterminados anteriores.


§9 Habilitación desde el Inspector de tráfico

Sección titulada «§9 Habilitación desde el Inspector de tráfico»
  1. Abra el Inspector de tráfico (/dashboard/tools/traffic-inspector).
  2. En la barra de herramientas de modos de captura, busque el botón ⚠ “Descifrado TPROXY” (src/app/(dashboard)/dashboard/tools/traffic-inspector/components/CaptureModesToolbar.tsx).
    • Si está deshabilitado y muestra la información sobre herramientas “El descifrado TPROXY requiere Linux + root + el complemento nativo”, el complemento nativo no está disponible en este host (no es Linux, no hay cadena de herramientas o el complemento no se ha compilado). Consulte §2 y §3.
  3. Haga clic en el botón. Este llama a POST /api/tools/agent-bridge/tproxy mediante startTproxyCaptureMode() (src/lib/inspector/tproxyCaptureApi.ts), que: crea la CA dinámica, abre el listener transparente, aplica las reglas del firewall e instala la CA en el almacén de confianza del sistema operativo.
  4. Cuando está en ejecución, el interruptor cambia a ámbar y muestra el número de interceptaciones en tiempo real (· &lt;interceptCount&gt;). Las solicitudes interceptadas aparecen en la lista de solicitudes con source: "tproxy".
  5. Haga clic de nuevo para detenerlo: DELETE /api/tools/agent-bridge/tproxy mediante stopTproxyCaptureMode() cierra el listener, desinstala la CA y revierte las reglas del firewall.

El estado del modo de captura (en ejecución / disponible / número de interceptaciones / puerto del listener) se obtiene de GET /api/tools/agent-bridge/tproxy (getCaptureStatus() en src/mitm/tproxy/captureManager.ts). Solo se ejecuta una sesión TPROXY a la vez; al intentar iniciar una segunda, se rechaza con “El modo de captura TPROXY ya está en ejecución”.


El complemento nativo no se puede cargar. Confirme que está en Linux, que ha compilado el complemento (npm run build:native:tproxy) y que el proceso puede cargar transparent.node. isTransparentSocketAvailable() controla el interruptor; GET /api/tools/agent-bridge/tproxy devuelve available: false cuando falta el complemento.

  • Confirme que el proceso interceptado se conecta realmente al dport configurado (de forma predeterminada, 443).
  • Confirme que el proceso confía en la CA dinámica. La CA se instala como omniroute-tproxy-ca.crt; es posible que las aplicaciones con su propio almacén de confianza (Firefox/Chrome NSS) también necesiten que se añada allí el certificado.
  • Ejecute la autoprueba Diagnose de AgentBridge (consulte AGENTBRIDGE.md) para realizar comprobaciones de confianza del certificado y del estado del servidor.

Reglas obsoletas del firewall después de un fallo

Sección titulada «Reglas obsoletas del firewall después de un fallo»

revertTproxy() es la operación inversa exacta de la aplicación de reglas y es idempotente. Al detener el modo, se revierten las reglas; si OmniRoute se cerró de forma abrupta durante una sesión, utilice la acción Repair de AgentBridge (POST /api/tools/agent-bridge/repair) para deshacer el estado huérfano del sistema (suplantación de DNS, CA raíz y proxy del sistema). Las reglas mangle de TPROXY y la ruta también se eliminan automáticamente al reiniciar.

Bucle infinito / el proxy intercepta su propio reenvío

Sección titulada «Bucle infinito / el proxy intercepta su propio reenvío»

Este es el caso antibucle. Confirme que bypassMark sea distinto de mark (la validación lo exige) y que el reenvío utilice connectMarked (así ocurre en realForward). Consulte §5 Antibucle.


Archivo Responsabilidad
src/mitm/tproxy/commands.ts Generador puro de comandos de aplicación y reversión de iptables/ip; validateTproxyConfig
src/mitm/tproxy/setup.ts Ejecutor transaccional de applyTproxy / revertTproxy (reversión en caso de fallo)
src/mitm/tproxy/transparentSocket.ts Cargador del complemento nativo (loadTransparentAddon), createTransparentListenerFd, connectMarked, setSocketMark, isTransparentSocketAvailable
src/mitm/tproxy/native/transparent.c Complemento N-API: createTransparentListener (IP_TRANSPARENT), setSocketMark, connectMarked
src/mitm/tproxy/native/binding.gyp Manifiesto de compilación de node-gyp
src/mitm/tproxy/dynamicCert.ts DynamicCertStore: CA dinámica por SNI y caché de certificados hoja
src/mitm/tproxy/caTrust.ts Instalación/desinstalación en el almacén de confianza del SO (installTproxyCa / uninstallTproxyCa, ranura dedicada)
src/mitm/tproxy/tlsCapture.ts Motor de descifrado con terminación TLS y reenvío antibucle recifrado
src/mitm/tproxy/captureMode.ts Orquestación del listener transparente; lee el destino original desde socket.localAddress
src/mitm/tproxy/captureManager.ts Ciclo de vida singleton: startCaptureMode / stopCaptureMode / getCaptureStatus
src/app/api/tools/agent-bridge/tproxy/route.ts Ruta GET / POST / DELETE (LOCAL_ONLY)
src/lib/inspector/tproxyCaptureApi.ts Funciones auxiliares de fetch del cliente (fetchTproxyStatus / startTproxyCaptureMode / stopTproxyCaptureMode)

Código fuente de OmniRoute (a58000c7685f)

HagiCode

HagiCode es un espacio de trabajo de programación con agentes, flujos estructurados, ejecución multiagente y vistas de Hero Dungeon.

Convierte ideas en software útil con un flujo de trabajo con agentes más inteligente, rápido y ameno.

Interfaz principal de HagiCode con tema claro
  • SmartLos flujos estructurados convierten la intención en un itinerario ejecutable desde la idea hasta la entrega.
  • EfficientLos flujos multiagente permiten avanzar en paralelo con la investigación, implementación y revisión.
  • FunHero Dungeon hace que las largas sesiones de programación sean visuales y colaborativas.
Visitar HagiCode