MITM TPROXY Transparent Decrypt (日本語)
§1 概要と使用場面
Section titled “§1 概要と使用場面”他の4つのキャプチャモードには、それぞれ制限があります。
| モード | トラフィックの誘導方法 | 制限 |
|---|---|---|
| AgentBridge | 固定されたホストセットを /etc/hosts で DNS 偽装 |
登録済みの IDE エージェントホストのみ |
| カスタムホスト | ホストごとに /etc/hosts で DNS 偽装 |
ホストごとに1エントリが必要。hosts の編集には sudo が必要 |
| HTTP_PROXY | HTTP_PROXY/HTTPS_PROXY 環境変数 |
環境変数に対応するアプリのみ |
| システム全体のプロキシ | OS のプロキシ設定 | グローバル状態を変更するため、元に戻す必要がある |
代わりに、TPROXY 透過復号はカーネルレイヤーでトラフィックを誘導します。mangle OUTPUT チェーンでターゲットポート(デフォルトは 443)への新しいローカル送信 TCP 接続にマークを付け、ip rule がマークされたパケットをローカル配信へ再ルーティングします。再進入時には、mangle PREROUTING の TPROXY ターゲットがパケットを IP_TRANSPARENT リスナーに渡し、そこで TLS を終端して平文をキャプチャします。
次のようなプロセスのトラフィックをキャプチャして復号したい場合に使用します。
- AgentBridge に登録されていないホストと通信し、
HTTP_PROXYに対応しておらず、- システム全体のプロキシ変更による影響を与えたくない場合。
傍受はカーネルで行われるため、送信元プロセスに設定変更は一切不要です。ただし、そのプロセスは OmniRoute がインストールする動的 CA を信頼する必要があります(§4を参照)。
| 要件 | 詳細 |
|---|---|
| OS | Linux のみ — IP_TRANSPARENT は Linux 専用のソケットオプションです。それ以外のすべてのプラットフォームでは、ローダーは「利用不可」を返します。 |
| 権限 | 透過ソケットを作成し、iptables/ip ルールを適用するための CAP_NET_ADMIN ケーパビリティ — 実際には root として実行します。 |
| ネイティブアドオン | 小さな N-API アドオン(src/mitm/tproxy/native/transparent.c)をビルドするか、事前ビルド済みバイナリとして同梱する必要があります。§3 を参照してください。 |
| カーネルモジュール | TPROXY、mangle、および mark マッチをサポートする iptables(kernel 6.8.0 で検証済み)。 |
グレースフルデグラデーション: いずれかの要件が満たされていない場合(Linux 以外、ツールチェーンなし、
アドオンが未ビルド)、アドオンローダー(src/mitm/tproxy/transparentSocket.ts::loadTransparentAddon)は
例外をスローせずに null を返します。その後、キャプチャモードのステータスは
available: false と報告され、ダッシュボードのトグルは
「TPROXY 復号には Linux + root + ネイティブアドオンが必要です」というツールチップとともに無効化されますが、
OmniRoute のその他の機能は引き続き動作します。
§3 ネイティブ IP_TRANSPARENT アドオン
Section titled “§3 ネイティブ IP_TRANSPARENT アドオン”Node の net モジュールでは、TPROXY に必要な、bind() の_前_の
setsockopt(IP_TRANSPARENT) を実行できません(実行しない場合、カーネルはリダイレクトされたパケットを破棄します)。このアドオン
(src/mitm/tproxy/native/transparent.c、binding.gyp を介してビルド)は、3 つの関数を公開する小さな N-API
モジュールであり、transparentSocket.ts を通じて使用されます。
| アドオン関数 | ソケット処理 | 用途 |
|---|---|---|
createTransparentListener(ip, port) |
socket() + SO_REUSEADDR + IP_TRANSPARENT + bind() + listen() を実行し、生の fd を返す |
透過キャプチャリスナー(Node は server.listen({ fd }) を介して fd を引き継ぐ) |
setSocketMark(fd, mark) |
既存の fd に対して setsockopt SO_MARK を実行 |
ループ防止(プロキシ自身のソケットにマークを付ける) |
connectMarked(ip, port, mark) |
ノンブロッキング connect() の前に socket() + SO_MARK を実行し、fd を返す |
再暗号化されたアップストリーム転送(SYN がマークを保持する) |
元の宛先は socket.localAddress/localPort から読み取られます — TPROXY は
これを保持するため、SO_ORIGINAL_DST/NAT ルックアップは不要です。
アドオンのビルド
Section titled “アドオンのビルド”npm run build:native:tproxy # src/mitm/tproxy/native に移動して node-gyp rebuild を実行 # -> native/build/Release/transparent.nodenpm 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 SNI ごとの動的 CA とトラストストアインストーラー
Section titled “§4 SNI ごとの動的 CA とトラストストアインストーラー”#6684 更新: AgentBridge の静的サーバー(
src/mitm/server.cjs)は、 単一の静的な自己署名リーフではなく、同じ CA/リーフアーキテクチャパターンを 共有するようになりました。これは独立した CA インスタンス (src/mitm/cert/rootCa.ts、<DATA_DIR>/mitm/ca.key/ca.crtに永続化) を使用し、既存のomniroute-mitm.crtトラストストアスロットへ インストールされます(そこにあった旧単一リーフを置き換えるため、 二重信頼のクリーンアップは不要です)。また、後述する TPROXY 固有のomniroute-tproxy-ca.crtスロットとは完全に分離されています。 AgentBridge の新規インストールでは CA モデルが自動的に適用されます。 旧静的リーフをすでに信頼しているインストールでは、オペレーターがMITM_ROOT_CA_ENABLED=trueによって明示的に移行するまで、そのリーフを 引き続き使用します(src/mitm/cert/migration.tsを参照)。任意の ホスト向けリーフに署名できる、信頼された MITM CA は、固定 SAN の旧リーフより 実質的にはるかに強力であるため、すでに信頼済みのインストールでは 暗黙的に切り替わることは決してありません。
従来、静的な AgentBridge MITM 証明書が機能していたのは、AgentBridge が
固定されたホストセットに対して DNS スプーフィングを行っていたために
すぎません(現在は後述のモデルに統合されています)。TPROXY は任意の
ホストをインターセプトするため、そのリスナーはクライアントが要求する
どの SNI に対しても有効なリーフを提示する必要があります。これは現在、
4 つの antigravity ホストだけでなく、MITM_TOOL_HOSTS の全セット
(9 個のツールエントリ)に対して AgentBridge にも求められる要件です。
動的 CA(src/mitm/tproxy/dynamicCert.ts)
Section titled “動的 CA(src/mitm/tproxy/dynamicCert.ts)”DynamicCertStore は、selfsigned 依存関係上に構築されたローカル CA を実行し、次の処理を行います。
generateMitmCa()を使用して長期間有効な CA を生成します(CN"OmniRoute MITM CA"、 有効期間 10 年、basicConstraints CA=true+keyUsage keyCertSign,cRLSign、 2048 ビット RSA/SHA-256)。issueLeafCert()を使用し、SNI ホスト名ごとにオンデマンドでリーフを発行 します(有効期間 1 年、subjectAltName= SNI ホスト)。また、ホスト名ごとに 1 つのtls.SecureContextをキャッシュします。- TLS を終端するサーバー向けに
createSNICallback()を公開します (§5 を参照)。 - 再起動後も CA を維持するため、
existingCaを指定して構築できます (これにより、トラストストアを再インストールする必要がありません)。
CA 秘密鍵は決してマシンの外部に出ません。
トラストストアインストーラー(src/mitm/tproxy/caTrust.ts)
Section titled “トラストストアインストーラー(src/mitm/tproxy/caTrust.ts)”インターセプト対象のクライアントは動的 CA を信頼する必要があるため、
キャプチャモードを開始すると、CA 証明書が OS のトラストストア内の
専用スロット — 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 復号とキャプチャの仕組み
Section titled “§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. SNI ごとのリーフ証明書(dynamicCert)でクライアントの TLS を終端 2. 内部の http.Server が復号された平文を解析 3. キャプチャ → source: "tproxy" を指定して globalTrafficBuffer.push() (sanitizeHeaders + maskSecret を適用) 4. バイパスマーク付きソケット(connectMarked、ループ防止)を介して 元の宛先へ再暗号化して転送 │ ▼ 元のアップストリーム(api.example.com)- TLS 終端(
createTlsCaptureServer): 動的 CA の SNI コールバックを使用し、インターセプトした未加工のソケットをサーバー側のtls.TLSSocketでラップした後、復号されたストリームを内部のhttp.Serverに渡します(標準的な MITM 終端手法)。ハングしたトンネルによってファイルディスクリプタが枯渇しないよう、ソケットの生存期間はMITM_IDLE_TIMEOUT_MSによって制限されます。 - キャプチャ(
handleDecryptedRequest):source: "tproxy"、初期ステータス"in-flight"のInterceptedRequestを追加します。バッファに格納される前に、ヘッダーにはsanitizeHeaders()、ボディにはmaskSecret()が適用されます。その後、エントリはレスポンス、サイズ、レイテンシーの情報で更新されます。 - 再暗号化転送(
createForward/realForward): 元の宛先に向けて再暗号化します。rejectUnauthorizedのデフォルトはtrue(デフォルトで安全)です。アップストリーム証明書はクライアントが要求した SNI/Host に照らして検証されるため、プロキシは元のクライアントが拒否するものとまったく同じ証明書を拒否します。
ループ防止(SO_MARK)
Section titled “ループ防止(SO_MARK)”ルールは新しいローカル送信接続をマークするため、通常であれば、プロキシ自身による再暗号化転送も再びインターセプトされ、無限ループが発生します。転送パスでは、バイパス用のソケットマーク(SO_MARK)を使用してこれを防ぎます。
realForwardはconnectMarked(ip, port, DEFAULT_BYPASS_MARK)を介してアップストリームソケットを開きます。DEFAULT_BYPASS_MARK = 0x539であり、connect()の前に SO_MARK を設定するため、転送の SYN にはバイパスマークが付与されます。mangle OUTPUTルールは、すでにバイパスマークが付いている接続を除外します(-m mark ! --mark <bypassMark>)。そのため、プロキシの転送は再度マークされず、TPROXY に再進入しません。
実装上の注意: バイパスマーク付きソケットは、エージェントの
createConnectionに設定する必要があります(エージェントが存在する場合、https.request({ createConnection })は通知なく無視されます)。そうしないと、転送時にマークなしのソケットが開かれ、ループが再発します。これは e2e で検証済みのループ防止修正です。
§6 セキュリティ
Section titled “§6 セキュリティ”| 制御 | 詳細 |
|---|---|
| ループバック専用 API | /api/tools/agent-bridge/tproxy は、LOCAL_ONLY_API_PREFIXES(src/server/authz/routeGuard.ts)内の /api/tools/agent-bridge/ プレフィックスの対象です。ループバック制限は認証の前に実行されます(ハードルール #15 + #17)。そのため、トンネル経由で JWT が漏洩しても、iptables ルールを適用し、子プロセスを介してトラストストア CA をインストールする TPROXY キャプチャを開始することはできません。 |
| 専用 CA スロット | 動的 CA は omniroute-tproxy-ca.crt にインストールされ、静的 MITM 証明書を上書きすることはありません。 |
| CA キーをホスト外に出さない | DynamicCertStore は CA キーをメモリ内に保持し、エクスポートしません。 |
| シークレットのマスキング | リクエスト/レスポンス本文には maskSecret()、ヘッダーには sanitizeHeaders() が、globalTrafficBuffer.push() の前に実行されます。 |
| シェル補間なし | すべての iptables/ip/トラストストアコマンドは、引数配列を使用して execFile/execFileWithPassword 経由で実行されます(ハードルール #13)。 |
| アップストリーム証明書の検証 | 再暗号化された転送では、デフォルトでアップストリーム証明書が検証されます(rejectUnauthorized: true)。 |
| エラーのサニタイズ | ルートのエラーレスポンスは sanitizeErrorMessage() を経由します(ハードルール #12)。 |
MITM CA は強力な機能です。 任意のホストの証明書に署名できる CA が OS によって信頼されている場合、OmniRoute が傍受するすべての通信を復号できます。この機能は、明示的かつローカル専用の TPROXY キャプチャモードによって制限され、デフォルトでは無効です。また、このモードを停止すると、トラストストアのエントリは削除されます。
§7 トランザクショナルなファイアウォールの適用 / 復元
Section titled “§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 と異なる必要があります(ループ防止)。
適用コマンド(実行順)
Section titled “適用コマンド(実行順)”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 のユースケースが同一ホスト上のアプリによる ローカルな アウトバウンドトラフィックであり、
PREROUTINGの TPROXY だけでは それを検出できないためです。PREROUTINGが検出するのは転送トラフィックのみです。OUTPUTチェーンが新しいローカル接続にマークを付け、ip ruleがそれらをローカル配信 (lo)へ再ルーティングし、その後PREROUTINGが透過リスナーへ割り当てます。
開始リクエスト(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 ルートを保持するポリシールーティングテーブル ID |
| bypassMark | int (≥1, ≠ mark) |
0x539 |
プロキシが自身のアップストリーム接続に設定するバイパスソケットマーク(SO_MARK)。OUTPUT では除外されます(ループ防止) |
| sudoPassword | string | — | root 以外のデスクトップ環境のみ:トラストストアへのインストールを承認します。root の場合は無視されます |
TPROXY 用の環境変数はありません。すべての設定は POST ボディまたは上記のデフォルト値を介して行います。
§9 Traffic Inspector からの有効化
Section titled “§9 Traffic Inspector からの有効化”- Traffic Inspector (
/dashboard/tools/traffic-inspector) を開きます。 - キャプチャモードのツールバーで、「TPROXY Decrypt」 ⚠ ボタンを探します
(
src/app/(dashboard)/dashboard/tools/traffic-inspector/components/CaptureModesToolbar.tsx)。 - ボタンをクリックします。
startTproxyCaptureMode()(src/lib/inspector/tproxyCaptureApi.ts) を介してPOST /api/tools/agent-bridge/tproxyが呼び出され、動的 CA の構築、 透過リスナーのオープン、ファイアウォールルールの適用、および OS の信頼ストアへの CA のインストールが行われます。 - 実行中はトグルがアンバー色になり、リアルタイムのインターセプト数
(
· <interceptCount>) が表示されます。インターセプトされたリクエストは、source: "tproxy"としてリクエスト一覧に表示されます。 - もう一度クリックすると停止します。
stopTproxyCaptureMode()を介してDELETE /api/tools/agent-bridge/tproxyが呼び出され、リスナーが閉じられ、 CA がアンインストールされ、ファイアウォールルールが元に戻されます。
キャプチャモードのステータス(実行中 / 利用可能 / インターセプト数 / リスナーポート)は、
GET /api/tools/agent-bridge/tproxy(src/mitm/tproxy/captureManager.ts の
getCaptureStatus())から取得されます。同時に実行できる TPROXY セッションは1 つだけです。
2 つ目を開始しようとすると、「TPROXY キャプチャモードはすでに実行中です」というエラーで拒否されます。
§10 トラブルシューティング
Section titled “§10 トラブルシューティング”トグルが無効になっている
Section titled “トグルが無効になっている”ネイティブアドオンをロードできません。Linux 上で実行していること、アドオンを
ビルド済みであること (npm run build:native:tproxy)、およびプロセスが
transparent.node をロードできることを確認してください。
isTransparentSocketAvailable() によってトグルの有効性が制御されます。
アドオンが見つからない場合、GET /api/tools/agent-bridge/tproxy は
available: false を返します。
何もキャプチャされない
Section titled “何もキャプチャされない”- インターセプト対象のプロセスが、設定された
dport(デフォルトは443)へ 実際に接続していることを確認してください。 - プロセスが動的 CA を信頼していることを確認してください。CA は
omniroute-tproxy-ca.crtとしてインストールされます。独自の信頼ストアを持つ アプリ(Firefox/Chrome NSS)では、そこにも証明書を追加する必要がある場合があります。 - 証明書の信頼状態およびサーバーの健全性を確認するには、AgentBridge の
Diagnose セルフテストを実行してください
(
AGENTBRIDGE.mdを参照)。
クラッシュ後にファイアウォールルールが残っている
Section titled “クラッシュ後にファイアウォールルールが残っている”revertTproxy() は apply と正確に逆の処理を行い、冪等です。モードを停止すると
ルールが元に戻されます。OmniRoute がセッション途中で強制終了された場合は、
AgentBridge の Repair アクション (POST /api/tools/agent-bridge/repair) を使用して、
孤立したシステム状態(DNS スプーフィング、ルート CA、システムプロキシ)を元に戻してください。
TPROXY の mangle ルールとルートも、再起動時に自動的に消去されます。
無限ループ / プロキシが自身の転送をインターセプトする
Section titled “無限ループ / プロキシが自身の転送をインターセプトする”これはアンチループのケースです。bypassMark が mark と異なること
(バリデーションによって強制されます)、および転送で connectMarked が使用されていること
(realForward では使用されています)を確認してください。
§5 アンチループ を参照してください。
§11 ソースマップ
Section titled “§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 ごとの動的 CA とリーフ証明書キャッシュ |
src/mitm/tproxy/caTrust.ts |
OS の信頼ストアへのインストール/アンインストール (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 により長時間のコーディングを視覚的で協力的な体験にします。