How to Set Up an MITM Proxy with TPROXY for Decrypting CLI Traffic in OmniRoute

OmniRoute uses a Linux TPROXY-based subsystem to transparently intercept outbound TCP connections from local CLI tools without modifying system-wide proxy settings, configured through the TproxyConfig interface and managed via src/mitm/tproxy/setup.ts.

OmniRoute is an open-source traffic management tool that includes a sophisticated Man-in-the-Middle (MITM) subsystem for decrypting CLI traffic. By leveraging the Linux TPROXY mechanism, it can capture outbound TCP connections from processes running on the same host—something traditional NAT-based redirection cannot handle. This guide explains how to configure and deploy the TPROXY capture using OmniRoute's type-safe configuration model and transactional command execution.

Understanding the TPROXY Architecture

The implementation resides in src/mitm/tproxy/ and operates through three coordinated layers that ensure safe, reversible network modifications.

The Configuration Model

At the core is the TproxyConfig interface defined in src/mitm/tproxy/commands.ts (lines 35-46). This TypeScript interface describes the capture parameters: target port (dport), firewall mark (mark), listener port (onPort), routing table ID (routeTable), and optional bypass mark (bypassMark) to prevent interception loops.

Command Generation

The buildTproxyApplyCommands and buildTproxyRevertCommands functions translate configurations into exact iptables and ip command arrays. According to the logic in commands.ts (lines 98-122), the revert list is constructed as the precise inverse of the apply list, ensuring that system crashes or failures never leave orphaned firewall rules.

Transactional Execution

The applyTproxy and revertTproxy functions in src/mitm/tproxy/setup.ts execute these commands using safe execFile calls without shell string interpolation. If errors occur during the apply phase (lines 40-52), the system triggers an automatic best-effort cleanup to maintain idempotency.

Configuring the TPROXY Capture

To intercept HTTPS traffic from local CLI tools, create a TproxyConfig instance and invoke the apply function.

import { applyTproxy, revertTproxy } from "./src/mitm/tproxy/setup";
import type { TproxyConfig } from "./src/mitm/tproxy/commands";

const cfg: TproxyConfig = {
  dport: 443,          // Target TLS connections
  mark: 0x2333,        // Firewall mark for OUTPUT rule
  onPort: 8443,       // OmniRoute listener receiving intercepted traffic
  routeTable: 233,     // Policy-routing table ID
  bypassMark: 0x9999,  // Prevents proxy loopbacks (optional)
};

// Apply TPROXY rules - modifies host iptables and routing tables
await applyTproxy(cfg);

The bypassMark field is critical—it marks the proxy's own upstream connections to avoid infinite interception loops.

Enabling MITM via the HTTP API

OmniRoute exposes a REST endpoint at src/app/api/settings/mitm/route.ts for dashboard integration. You can programmatically enable MITM using a standard HTTP POST:

curl -X POST https://<omniroute-host>/api/settings/mitm \
  -H "Content-Type: application/json" \
  -d '{
    "dport": 443,
    "mark": 9001,
    "onPort": 8443,
    "routeTable": 9001,
    "bypassMark": 1234
  }'

The endpoint validates the payload using validateTproxyConfig (found in commands.ts lines 62-75) before invoking applyTproxy. Send a DELETE request to the same endpoint to trigger revertTproxy and disable interception.

Monitoring and Reverting the Capture

To check the current MITM state or disable capture programmatically:

import { listActiveTproxy } from "./src/mitm/manager.runtime";

// Returns persisted config or null if MITM is disabled
const active = await listActiveTproxy();
console.log(active);

// Later, to remove rules:
await revertTproxy(cfg);

The src/mitm/manager.runtime.ts module persists configurations and coordinates between the UI/API layer and the underlying TPROXY implementation.

Why TPROXY Matters for Local Traffic

Standard NAT-based redirection fails for locally generated traffic because packets from CLI tools never traverse the PREROUTING chain. As documented in commands.ts (lines 8-15), OmniRoute's TPROXY implementation marks new local outbound connections in the mangle OUTPUT chain, routes them back to the loopback device via an ip rule, and captures them in mangle PREROUTING using the TPROXY target. This hands packets to OmniRoute's IP-TRANSPARENT listener while the bypassMark prevents the proxy from intercepting its own connections.

Summary

  • OmniRoute's MITM subsystem uses Linux TPROXY to intercept local CLI traffic that bypasses traditional NAT redirection
  • Configuration is type-safe through the TproxyConfig interface in src/mitm/tproxy/commands.ts
  • Command generation produces reversible iptables/ip sequences via buildTproxyApplyCommands and buildTproxyRevertCommands
  • Transactional execution in src/mitm/tproxy/setup.ts ensures automatic rollback on failure
  • The HTTP API at src/app/api/settings/mitm/route.ts enables dashboard-driven configuration

Frequently Asked Questions

What makes TPROXY different from standard NAT redirection for MITM?

TPROXY operates on the mangle table and can intercept traffic originating from the local host, whereas NAT redirection only catches forwarded traffic traversing PREROUTING. This allows OmniRoute to decrypt connections from CLI tools running on the same machine without requiring global proxy environment variables.

How does OmniRoute prevent infinite interception loops?

The bypassMark field in TproxyConfig assigns a specific firewall mark to the proxy's own upstream connections. The iptables rules skip processing for packets bearing this mark, ensuring that OmniRoute's traffic exits normally while other local processes remain intercepted.

What happens if the applyTproxy command fails mid-execution?

The transactional runner in src/mitm/tproxy/setup.ts (lines 40-52) implements automatic rollback. If any command in the apply sequence fails, the system executes the corresponding revert commands to restore the original firewall and routing state, preventing partial configuration that could break connectivity.

Can I intercept multiple ports simultaneously?

Yes. Create multiple TproxyConfig instances with different dport values (e.g., 80 for HTTP and 443 for HTTPS) and unique mark/routeTable combinations. Invoke applyTproxy for each configuration, or make separate API calls to /api/settings/mitm for each target port.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →