Pular para o conteúdo
OmniRoute source

MITM TPROXY Transparent Decrypt (Português (Brasil))

Cada um dos outros quatro modos de captura tem uma limitação:

Modo Como o tráfego é direcionado Limitação
AgentBridge Falsificação de DNS via /etc/hosts para um conjunto fixo de hosts somente os hosts de agentes de IDE registrados
Hosts personalizados Falsificação de DNS via /etc/hosts por host uma entrada por host; sudo para editar os hosts
HTTP_PROXY Variáveis de ambiente HTTP_PROXY/HTTPS_PROXY somente aplicativos que respeitam a variável de ambiente
Proxy para todo o sistema Configurações de proxy do sistema operacional altera o estado global; exige reversão

A descriptografia transparente TPROXY direciona o tráfego na camada do kernel. Ela marca novas conexões TCP locais de saída para uma porta de destino (por padrão, 443) na cadeia mangle OUTPUT; uma ip rule redireciona os pacotes marcados para entrega local e, ao reentrarem, o destino TPROXY de mangle PREROUTING os encaminha para um listener IP_TRANSPARENT — que então encerra o TLS e captura o texto simples.

Use-a quando quiser capturar e descriptografar o tráfego de um processo que:

  • se comunica com um host que o AgentBridge não registra, e
  • não respeita HTTP_PROXY, e
  • você não quer afetar com uma alteração de proxy para todo o sistema.

Como a interceptação ocorre no kernel, o processo de origem não precisa de nenhuma alteração de configuração — mas deve confiar na CA dinâmica instalada pelo OmniRoute (consulte §4).


Requisito Detalhes
SO Apenas Linux — IP_TRANSPARENT é uma opção de socket exclusiva do Linux. O carregador retorna “unavailable” em todas as outras plataformas.
Privilégio A capacidade CAP_NET_ADMIN para criar o socket transparente e aplicar regras de iptables/ip — na prática, execute como root.
Addon nativo Um pequeno addon N-API (src/mitm/tproxy/native/transparent.c) deve ser compilado ou fornecido como prebuild. Consulte §3.
Módulos do kernel iptables com suporte a TPROXY, mangle e correspondência de mark (validado com o kernel 6.8.0).

Degradação elegante: se algum requisito estiver ausente (sistema não Linux, sem toolchain, addon não compilado), o carregador do addon (src/mitm/tproxy/transparentSocket.ts::loadTransparentAddon) retornará null em vez de lançar uma exceção. O status do modo de captura então informa available: false, a opção no painel fica desabilitada com a dica “A descriptografia TPROXY requer Linux + root + o addon nativo”, e o restante do OmniRoute continua funcionando.


O módulo net do Node não consegue executar setsockopt(IP_TRANSPARENT) antes de bind(), como o TPROXY exige (caso contrário, o kernel descarta os pacotes redirecionados). O addon (src/mitm/tproxy/native/transparent.c, compilado por meio de binding.gyp) é um pequeno módulo N-API que expõe três funções, utilizadas por meio de transparentSocket.ts:

Função do addon Operação no socket Utilizada para
createTransparentListener(ip, port) socket() + SO_REUSEADDR + IP_TRANSPARENT + bind() + listen(), retorna o fd bruto o listener de captura transparente (o Node adota o fd por meio de server.listen({ fd }))
setSocketMark(fd, mark) setsockopt SO_MARK em um fd existente prevenção de loop (marca os próprios sockets do proxy)
connectMarked(ip, port, mark) socket() + SO_MARK antes de um connect() não bloqueante, retorna o fd o encaminhamento upstream recriptografado (o SYN carrega a marca)

O destino original é lido de socket.localAddress/localPort — o TPROXY o preserva, portanto não há consulta SO_ORIGINAL_DST/NAT.

Janela do terminal
npm run build:native:tproxy # cd src/mitm/tproxy/native && node-gyp rebuild
# -> native/build/Release/transparent.node
  • Durante npm run build, scripts/build/build-tproxy-native.mjs executa node-gyp rebuild. Isso é exclusivo do Linux e não fatal — a ausência de uma toolchain apenas deixa o modo de captura indisponível.
  • assembleStandalone.mjs copia build/Release/transparent.node para o pacote standalone; transparentSocket.ts o resolve tanto em relação ao módulo quanto em relação ao cwd (<cwd>/src/mitm/tproxy/native/...).
  • build/ e prebuilds/ são ignorados pelo git — o binário é compilado, nunca commitado.

O carregador verifica, em ordem de prioridade: native/build/Release/transparent.node e, em seguida, native/prebuilds/transparent.node (ambos em relação ao módulo e em <cwd>/src/mitm/tproxy/).


§4 A CA dinâmica por SNI e o instalador do repositório de confiança

Seção intitulada “§4 A CA dinâmica por SNI e o instalador do repositório de confiança”

Atualização #6684: o servidor estático do AgentBridge (src/mitm/server.cjs) agora compartilha esse mesmo padrão de arquitetura de CA/certificado de folha, em vez de usar um único certificado de folha autoassinado estático. Ele usa uma instância de CA distinta (src/mitm/cert/rootCa.ts, persistida em <DATA_DIR>/mitm/ca.key/ca.crt) e é instalado no slot preexistente omniroute-mitm.crt do repositório de confiança (substituindo ali o antigo certificado de folha único — sem necessidade de limpeza de confiança dupla), mantido totalmente separado do slot omniroute-tproxy-ca.crt do próprio TPROXY, descrito abaixo. Novas instalações do AgentBridge recebem automaticamente o modelo de CA; uma instalação que já confiava no antigo certificado de folha estático continua usando-o até que o operador aceite explicitamente a mudança por meio de MITM_ROOT_CA_ENABLED=true (consulte src/mitm/cert/migration.ts) — uma CA MITM confiável capaz de assinar um certificado de folha para qualquer host é substancialmente mais poderosa que o antigo certificado de folha com SANs fixos, portanto a mudança nunca ocorre silenciosamente em uma instalação que já tenha estabelecido essa confiança.

Historicamente, o certificado MITM estático do AgentBridge funcionava apenas porque o AgentBridge falsifica o DNS de um conjunto fixo de hosts (agora unificado com o modelo abaixo). O TPROXY intercepta hosts arbitrários, portanto seu listener deve apresentar um certificado de folha válido para qualquer SNI solicitado pelo cliente — o mesmo requisito que o AgentBridge agora tem para o conjunto completo de MITM_TOOL_HOSTS (9 entradas de ferramentas), em vez de apenas os 4 hosts do antigravity.

DynamicCertStore executa uma CA local (criada com base na dependência selfsigned) que:

  • Gera uma CA de longa duração por meio de generateMitmCa() (CN "OmniRoute MITM CA", validade de 10 anos, basicConstraints CA=true + keyUsage keyCertSign,cRLSign, RSA de 2048 bits / SHA-256).
  • Emite um certificado de folha por nome de host SNI sob demanda por meio de issueLeafCert() (validade de 1 ano, subjectAltName = o host SNI) e armazena em cache um tls.SecureContext por nome de host.
  • Expõe createSNICallback() para o servidor que encerra o TLS (consulte §5).
  • Pode ser construído com uma existingCa para manter a CA estável entre reinicializações (assim, o repositório de confiança não precisa ser reinstalado).

A chave privada da CA nunca sai da máquina.

Instalador do repositório de confiança (src/mitm/tproxy/caTrust.ts)

Seção intitulada “Instalador do repositório de confiança (src/mitm/tproxy/caTrust.ts)”

O cliente interceptado deve confiar na CA dinâmica; portanto, iniciar o modo de captura instala o certificado da CA no repositório de confiança do sistema operacional, em um slot dedicado — omniroute-tproxy-ca.crt (constante TPROXY_CA_CERT_NAME) — mantido separado do slot do certificado MITM estático (omniroute-mitm.crt), para que os dois nunca sobrescrevam um ao outro.

installTproxyCa(caPem, sudoPassword?) detecta o diretório de âncoras da distribuição (na ordem: primeiro no estilo Debian) e executa o comando de atualização correspondente:

Diretório de âncoras Comando de atualização
/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

A instalação coloca temporariamente o PEM em um arquivo temporário e, em seguida, executa (com privilégios) mkdir -p no diretório de âncoras, usa cp para copiar o arquivo temporário para ele e executa o comando de atualização. uninstallTproxyCa() remove apenas o slot dedicado (deixando intacto o certificado MITM estático) e atualiza o repositório — uma operação sem efeito em sistemas que não sejam Linux.

Todos os comandos privilegiados são executados por meio de execFileWithPassword (src/mitm/systemCommands.ts) — spawn com arrays de argumentos, sem shell e sem interpolação de strings (Regra Rígida nº 13). Quando o processo está sendo executado como root (por exemplo, no VPS), o destino é executado diretamente e nenhuma senha é necessária; em um desktop sem root, o sudoPassword é passado via sudo -S pela entrada padrão.

O sudoPassword do desktop é fornecido no corpo da solicitação POST para autorizar a instalação no repositório de confiança; ele é totalmente ignorado quando o processo está sendo executado como root.


O pipeline (todo em src/mitm/tproxy/):

aplicativo local ──TCP/443──▶ mangle OUTPUT marca a conexão (fwmark)
ip rule → tabela de rotas local → lo
mangle PREROUTING TPROXY → listener IP_TRANSPARENT (porta 8443)
│ captureMode.ts: lê o destino original de socket.localAddress
▼
tlsCapture.ts:
1. Encerra o TLS do CLIENTE com um certificado leaf por SNI (dynamicCert)
2. http.Server interno analisa o texto simples descriptografado
3. captura → globalTrafficBuffer.push() com source: "tproxy"
(sanitizeHeaders + maskSecret aplicados)
4. encaminha RECRIPTOGRAFADO ao destino original
por um socket com marca de bypass (connectMarked, antirloop)
│
▼
upstream original (api.example.com)
  • Encerramento TLS (createTlsCaptureServer): encapsula o socket bruto interceptado em um tls.TLSSocket no lado do servidor usando o callback de SNI da CA dinâmica e, em seguida, entrega o fluxo descriptografado a um http.Server interno (o truque padrão de encerramento MITM). Os tempos de vida dos sockets são limitados por MITM_IDLE_TIMEOUT_MS, para que um túnel travado não esgote os descritores de arquivo.
  • Captura (handleDecryptedRequest): adiciona um InterceptedRequest com source: "tproxy", status inicial "in-flight", cabeçalhos processados por sanitizeHeaders() e corpos processados por maskSecret() antes de entrarem no buffer. Depois, a entrada é atualizada com a resposta, os tamanhos e a latência.
  • Encaminhamento recriptografado (createForward / realForward): recriptografa para o destino original. Por padrão, rejectUnauthorized é true (seguro por padrão) — o certificado upstream é verificado em relação ao SNI/Host solicitado pelo cliente, portanto o proxy rejeita exatamente o que o cliente original rejeitaria.

Como as regras marcam novas conexões locais de saída, o encaminhamento recriptografado do próprio proxy normalmente seria interceptado novamente — um loop infinito. O caminho de encaminhamento evita isso com uma marca de bypass no socket (SO_MARK):

  • realForward abre seu socket upstream por meio de connectMarked(ip, port, DEFAULT_BYPASS_MARK) — DEFAULT_BYPASS_MARK = 0x539 — que define a SO_MARK antes de connect(), fazendo com que o SYN do encaminhamento carregue a marca de bypass.
  • A regra mangle OUTPUT exclui conexões que já carregam a marca de bypass (-m mark ! --mark &lt;bypassMark&gt;), portanto o encaminhamento do proxy não é marcado novamente e não volta a entrar no TPROXY.

Observação de implementação: o socket com a marca de bypass deve ser instalado no createConnection do agente (https.request({ createConnection }) é silenciosamente ignorado quando há um agente presente); caso contrário, o encaminhamento abriria um socket sem marca e o loop retornaria. Essa foi a correção antirloop validada de ponta a ponta.


Controle Detalhe
API somente para loopback /api/tools/agent-bridge/tproxy é abrangida pelo prefixo /api/tools/agent-bridge/ em LOCAL_ONLY_API_PREFIXES (src/server/authz/routeGuard.ts). A restrição a loopback é aplicada antes da autenticação (Regras Rígidas nº 15 + nº 17) — um JWT vazado por meio de um túnel não pode iniciar a captura TPROXY, que aplica regras de iptables e instala uma CA no repositório de confiança por meio de processos filhos.
Slot de CA dedicado A CA dinâmica é instalada em omniroute-tproxy-ca.crt, sem jamais sobrescrever o certificado MITM estático.
A chave da CA nunca sai do host DynamicCertStore mantém a chave da CA na memória; ela não é exportada.
Mascaramento de segredos maskSecret() nos corpos de requisições/respostas e sanitizeHeaders() nos cabeçalhos são executados antes de globalTrafficBuffer.push().
Sem interpolação de shell Todos os comandos iptables/ip/do repositório de confiança são executados por meio de execFile/execFileWithPassword com arrays de argumentos (Regra Rígida nº 13).
Verificação do certificado upstream O encaminhamento com nova criptografia verifica o certificado upstream por padrão (rejectUnauthorized: true).
Sanitização de erros As respostas de erro da rota passam por sanitizeErrorMessage() (Regra Rígida nº 12).

A CA de MITM é um recurso poderoso. Uma CA considerada confiável pelo sistema operacional e capaz de assinar qualquer host significa que tudo o que o OmniRoute interceptar pode ser descriptografado. Ela é protegida pelo modo explícito de captura TPROXY, disponível somente localmente, desativado por padrão, e a entrada do repositório de confiança é removida quando você interrompe o modo.


§7 Aplicação / reversão transacional do firewall

Seção intitulada “§7 Aplicação / reversão transacional do firewall”

Uma falha nunca deve deixar uma regra mangle ou uma rota obsoleta para trás. O construtor de comandos (src/mitm/tproxy/commands.ts) e o executor (src/mitm/tproxy/setup.ts) garantem que a reversão seja o inverso exato da aplicação, em ordem inversa.

applyTproxy(cfg) executa os comandos de aplicação em ordem; em caso de qualquer falha, ele executa uma revertTproxy(cfg) completa de melhor esforço e relança o erro — assim, o firewall fica totalmente aplicado ou totalmente revertido, nunca parcialmente aplicado. revertTproxy(cfg) executa os comandos inversos em ordem inversa e ignora falhas (idempotente — pode ser chamada incondicionalmente, por exemplo, pela limpeza repairMitm() do AgentBridge).

validateTproxyConfig(cfg) é executada antes de qualquer comando: as portas devem estar entre 1–65535, mark/routeTable/bypassMark devem ser inteiros positivos, e bypassMark deve ser diferente de mark (prevenção de loop).

Janela do 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;

A reversão os exclui na ordem inversa: PREROUTING -D, OUTPUT -D, ip route del, ip rule del.

A receita é baseada em OUTPUT porque o caso de uso de MITM é o tráfego de saída local (aplicativos no mesmo host), que o TPROXY somente em PREROUTING não detecta — PREROUTING detecta apenas tráfego encaminhado. A cadeia OUTPUT marca novas conexões locais, a ip rule as redireciona para entrega local (lo), e PREROUTING então as encaminha para o listener transparente.


A solicitação de inicialização (POST /api/tools/agent-bridge/tproxy) aceita os seguintes campos, validados por StartTproxyBodySchema (tproxy/route.ts). Todos são opcionais e usam seus valores padrão quando omitidos:

Campo Tipo Padrão Observações
dport int (1–65535) 443 Porta TCP de destino a ser interceptada de forma transparente
mark int (≥1) 0x2333 Marca do firewall definida em OUTPUT, correspondida pela ip rule + PREROUTING
onPort int (1–65535) 8443 Porta à qual o listener transparente (IP_TRANSPARENT) se vincula
routeTable int (≥1) 233 ID da tabela de roteamento baseado em políticas que contém a rota local 0.0.0.0/0
bypassMark int (≥1, ≠ mark) 0x539 A marca de bypass do socket (SO_MARK) que o proxy define em suas próprias conexões upstream; excluída em OUTPUT (prevenção de loop)
sudoPassword string — Somente para desktops sem root: autoriza a instalação no armazenamento de confiança; ignorada quando executado como root

Não há variáveis de ambiente para TPROXY — toda a configuração é feita pelo corpo da solicitação POST ou pelos valores padrão acima.


  1. Abra o Inspetor de Tráfego (/dashboard/tools/traffic-inspector).
  2. Na barra de ferramentas dos modos de captura, localize o botão ⚠ “Descriptografar TPROXY” (src/app/(dashboard)/dashboard/tools/traffic-inspector/components/CaptureModesToolbar.tsx).
    • Se ele estiver desabilitado com a dica “A descriptografia TPROXY requer Linux + root + o addon nativo”, o addon nativo não está disponível neste host (não é Linux, não há toolchain ou o addon não foi compilado). Consulte §2 e §3.
  3. Clique no botão. Ele chama POST /api/tools/agent-bridge/tproxy por meio de startTproxyCaptureMode() (src/lib/inspector/tproxyCaptureApi.ts), que: cria a CA dinâmica, abre o listener transparente, aplica as regras de firewall e instala a CA no repositório de confiança do sistema operacional.
  4. Durante a execução, o botão de alternância fica âmbar e mostra a contagem de interceptações em tempo real (· &lt;interceptCount&gt;). As solicitações interceptadas aparecem na lista de solicitações com source: "tproxy".
  5. Clique novamente para interromper — DELETE /api/tools/agent-bridge/tproxy por meio de stopTproxyCaptureMode() fecha o listener, desinstala a CA e reverte as regras de firewall.

O status do modo de captura (em execução / disponível / contagem de interceptações / porta do listener) vem de GET /api/tools/agent-bridge/tproxy (getCaptureStatus() em src/mitm/tproxy/captureManager.ts). Apenas uma sessão TPROXY é executada por vez — a tentativa de iniciar uma segunda sessão é rejeitada com “O modo de captura TPROXY já está em execução”.


O addon nativo não pode ser carregado. Confirme se você está usando Linux, compilou o addon (npm run build:native:tproxy) e o processo consegue carregar transparent.node. isTransparentSocketAvailable() controla o botão de alternância; GET /api/tools/agent-bridge/tproxy retorna available: false quando o addon está ausente.

  • Confirme se o processo interceptado realmente se conecta à dport configurada (o padrão é 443).
  • Confirme se o processo confia na CA dinâmica. A CA é instalada como omniroute-tproxy-ca.crt; aplicativos com seu próprio repositório de confiança (Firefox/Chrome NSS) também podem exigir que o certificado seja adicionado a esse repositório.
  • Execute o autoteste Diagnosticar do AgentBridge (consulte AGENTBRIDGE.md) para verificar a confiança no certificado / a integridade do servidor.

revertTproxy() é o inverso exato da aplicação e é idempotente. A interrupção do modo reverte as regras; se o OmniRoute tiver sido encerrado durante uma sessão, use a ação Reparar do AgentBridge (POST /api/tools/agent-bridge/repair) para desfazer estados órfãos do sistema (falsificação de DNS, CA raiz, proxy do sistema). As regras mangle e a rota do TPROXY também são limpas automaticamente na reinicialização.

Loop infinito / o proxy intercepta seu próprio encaminhamento

Seção intitulada “Loop infinito / o proxy intercepta seu próprio encaminhamento”

Este é o caso de prevenção de loop. Confirme se bypassMark é diferente de mark (a validação garante isso) e se o encaminhamento usa connectMarked (ele usa em realForward). Consulte §5 Prevenção de loop.


Arquivo Responsabilidade
src/mitm/tproxy/commands.ts Criador puro de comandos de aplicação + reversão de iptables/ip; validateTproxyConfig
src/mitm/tproxy/setup.ts Executor transacional de applyTproxy / revertTproxy (reversão em caso de falha)
src/mitm/tproxy/transparentSocket.ts Carregador do addon nativo (loadTransparentAddon), createTransparentListenerFd, connectMarked, setSocketMark, isTransparentSocketAvailable
src/mitm/tproxy/native/transparent.c Addon N-API: createTransparentListener (IP_TRANSPARENT), setSocketMark, connectMarked
src/mitm/tproxy/native/binding.gyp Manifesto de compilação do node-gyp
src/mitm/tproxy/dynamicCert.ts DynamicCertStore — CA dinâmica por SNI + cache de certificados finais
src/mitm/tproxy/caTrust.ts Instalação/desinstalação no repositório de confiança do SO (installTproxyCa / uninstallTproxyCa, slot dedicado)
src/mitm/tproxy/tlsCapture.ts Mecanismo de descriptografia com terminação TLS + encaminhamento recriptografado com prevenção de loop
src/mitm/tproxy/captureMode.ts Orquestração do listener transparente; lê o destino original de socket.localAddress
src/mitm/tproxy/captureManager.ts Ciclo de vida singleton: startCaptureMode / stopCaptureMode / getCaptureStatus
src/app/api/tools/agent-bridge/tproxy/route.ts Rota GET / POST / DELETE (LOCAL_ONLY)
src/lib/inspector/tproxyCaptureApi.ts Funções auxiliares de fetch do cliente (fetchTproxyStatus / startTproxyCaptureMode / stopTproxyCaptureMode)

Código-fonte do OmniRoute (a58000c7685f)

HagiCode

HagiCode é um ambiente de programação com agentes, fluxos estruturados, execução multiagente e visualizações Hero Dungeon.

Transforme ideias em software útil com um fluxo de trabalho com agentes mais inteligente, rápido e agradável.

Interface principal do HagiCode no tema claro
  • SmartFluxos estruturados transformam intenções em um caminho executável da ideia à entrega.
  • EfficientFluxos multiagente mantêm pesquisa, implementação e revisão em andamento simultaneamente.
  • FunO Hero Dungeon torna longas sessões de programação mais visuais e colaborativas.
Acessar HagiCode