
信頼できないワークロードのためのegressファイアウォール。
CI ジョブ、AI コーディングエージェント、サンドボックス化されたコンテナは、任意のアウトバウンド リクエストを行うことができます。侵害された依存関係、プロンプトインジェクション、悪意のあるビルド ステップによって、シークレットの外部への持ち出し、ホームへのコールバック、リバースシェルの起動が行われる可能性があります。多くの チームは、ワークロードから何が出て行っているのかをまったく把握できておらず、ましてやそれを止める手段も ありません。
iron-proxy は、内蔵 DNS サーバーを備えた MITM エグレスプロキシであり、信頼できない ワークロードとインターネットの間に位置します。ネットワーク境界でデフォルト拒否を 強制するため、ワークロードは明示的に許可したドメインにのみ到達できます。 実際のシークレットはサンドボックスに入りません。ワークロードはプロキシトークンを使用し、 iron-proxy はエグレス時に実際の認証情報に置き換えます。つまり、侵害されたワークロードは プロキシの外では無価値なトークンを外部に持ち出せるだけです。
単一バイナリ。単一の YAML 設定。
169.254.169.254、
fd00:ec2::254、fd20:ce::254)とループバックはデフォルトで拒否されます。
proxy.upstream_deny_cidrs または
IRON_PROXY_UPSTREAM_DENY_CIDRS で上書きできます。HTTP_PROXY、HTTPS_PROXY、SOCKS5 設定による
プロキシ設定をネイティブにサポートするツール向けのオプションのトンネル
リスナー。SET ROLE を注入し、
クライアントがロールを変更しようとする試み(SET ROLE、
set_config('role', ...)、DO ブロックなど)を SQL AST ウォークで拒否するオプションのリスナー。
PostgreSQL の行レベルセキュリティと組み合わせることで、
アプリケーションが共有サービスアカウントユーザーとして接続する際に、テナントごとのデータ分離を実現します。要件:
PgBouncer(使用する場合)は pool_mode = session で実行する必要があります — トランザクションまたは
ステートメントプールモードでは、クエリ間でバックエンドが暗黙的に再バインドされ、
ポリシーが無効になります。詳細は docs.iron.sh を参照してください。CI パイプライン、GitHub Actions、AI エージェント(Claude Code、Cursor、 Codex)、および完全には信頼できないコードを実行するあらゆる環境向けに作られています。
Docker イメージは Docker Hub で入手でき、 Linux/macOS(amd64/arm64)向けのビルド済みバイナリは GitHub Releases にあります。
またはソースからビルド:```bash go build -o iron-proxy ./cmd/iron-proxy
## クイックスタート```bash
cd examples/docker-compose
docker compose up
これにより iron-proxy と、プロキシを通じて5つのリクエストを送信するデモクライアントが起動し ます。ログを確認して、許可されたリクエスト、ブロックされたリクエスト、シークレットが書き換えられたリクエストを確認してください:```bash docker compose logs proxy
すべてのリクエストは構造化されたJSON監査エントリを生成します:```json
{
"host": "httpbin.org",
"method": "GET",
"path": "/headers",
"action": "allow",
"status_code": 200,
"duration_ms": 142,
"request_transforms": [
{ "name": "allowlist", "action": "continue" },
{
"name": "secrets",
"action": "continue",
"annotations": { "swapped": [{ "secret": "OPENAI_API_KEY", "locations": ["header:Authorization"] }] }
}
]
}
拒否されたリクエストには rejected_by フィールドが含まれ、WARN レベルでログが記録されます。完全なスキーマについては
監査ログ形式 を参照してください。
iron-proxy は、提供された CA によって署名されたリーフ証明書をその場で生成して TLS を終端します。
クライアントコンテナはこの CA を信頼する必要があります。```bash
mkdir -p certs
openssl genrsa -out certs/ca.key 4096
openssl req -x509 -new -nodes
-key certs/ca.key
-sha256 -days 3650
-subj "/CN=iron-proxy CA"
-addext "basicConstraints=critical,CA:TRUE"
-addext "keyUsage=critical,keyCertSign"
-out certs/ca.crt
### 2. Dockerネットワークを作成する
iron-proxyは固定IPを必要とするため、コンテナがDNSをそのIPに向けられるようにします:```bash
docker network create --subnet=172.20.0.0/24 iron-proxy
シークレットを含む env ファイルを作成します(バージョン管理の対象外にしてください):```bash echo "OPENAI_API_KEY=sk-real-key" > .env
[No input provided]```bash
docker run -d --name iron-proxy \
--network iron-proxy --ip 172.20.0.2 \
-v $(pwd)/proxy.yaml:/etc/iron-proxy/proxy.yaml:ro \
-v $(pwd)/certs/ca.crt:/etc/iron-proxy/ca.crt:ro \
-v $(pwd)/certs/ca.key:/etc/iron-proxy/ca.key:ro \
--env-file .env \
ironsh/iron-proxy:latest -config /etc/iron-proxy/proxy.yaml
最も簡単な方法はDNSベースのルーティングです。コンテナのDNSをiron-proxyに向けると、すべてのホスト名ルックアップがプロキシIPに解決され、トラフィックが自動的にプロキシ経由でルーティングされます:```bash
docker run --rm
--network iron-proxy
--dns 172.20.0.2
-v $(pwd)/certs/ca.crt:/certs/ca.crt:ro
curlimages/curl --cacert /certs/ca.crt https://httpbin.org/get
より強力な適用を行うには、nftables ルールを重ねてプロキシを経由しない外部送信をブロックするか、または
カーネルレベルでの傍受には TPROXY を使用します。各アプローチの詳細は [プロキシへのトラフィックの
ルーティング](#routing-traffic-to-the-proxy) を参照してください。
## なぜ iron-proxy なのか?
| | iron-proxy | Squid | mitmproxy | Envoy |
| ------------------------ | ------------------------------ | --------------------------- | ------------------------- | ---------------------------------- |
| デフォルト拒否の外部送信 | 組み込み | 複雑なACL設定が必要 | カスタムスクリプトが必要 | RBAC/フィルター設定が必要 |
| シークレット注入 | 組み込み | なし | なし | なし |
| 構造化監査ログ | 組み込み、変換ごとのトレース | 基本的なアクセスログ | プラグインベース | 設定可能なアクセスログ |
| セットアップの複雑さ | 単一バイナリ + YAML | 広範な設定言語 | Pythonスクリプト | 複雑なYAMLまたはコントロールプレーン |
iron-proxy は、信頼できないワークロードからの外部送信を制御・監査するという単一の目的のために設計されています。Squid はデフォルト拒否を実行できますが、かなりの ACL 設定が必要であり、シークレット注入の概念はありません。mitmproxy は優れたデバッグツールですが、本番環境での強制適用を目的としていません。Envoy は汎用プロキシであり、この機能の一部を実行するように設定できますが、問題が必要とするよりもはるかに複雑です。
## 仕組み
iron-proxy は DNS サーバーと HTTP/HTTPS プロキシを実行します。コンテナの DNS を iron-proxy に向けると、すべてのホスト名のルックアップがプロキシ IP に解決され、トラフィックが自動的にプロキシを経由します。プロキシは TLS を終端し(提供する CA からリーフ証明書をその場で生成)、リクエストを順序付けられた変換パイプラインに通し、アップストリームに転送し、レスポンスを再度パイプラインに通します。```
Container → DNS lookup → iron-proxy IP → TLS termination → transforms → upstream
変換は順に実行されます。組み込み変換:
| 変換 | 説明 |
|---|---|
allowlist | 一致するドメイン/CIDRへのリクエストを許可し、それ以外はすべて拒否します (403)。 |
secrets | プロキシトークンがないかヘッダー(必要に応じてクエリ、パス、本文)をスキャンし、環境変数から実際のシークレットと置き換えます。 |
body_capture | 一致するホストのデコードされたリクエスト本文を request_body 監査フィールドとして記録します。観察専用であり、拒否はしません。 |
iron-proxy は単一のフラグ -config path/to/config.yaml を受け取ります。以下が
全体の構造です(iron-proxy.example.yaml はコピーして
使える出発点として参照してください):```yaml
dns:
listen: ":53"
proxy_ip: "10.16.0.1" # IP where iron-proxy is running (required)
passthrough: # Domains forwarded to OS resolver
- "*.internal.corp"
- "metadata.google.internal"
records: # Static DNS records (highest precedence)
- name: "internal.example.com"
type: A
value: "10.0.0.5"
proxy: http_listen: ":80" https_listen: ":443" tunnel_listen: ":8080" # Optional CONNECT/SOCKS5 listener max_request_body_bytes: 1048576 # 1 MiB (default) max_response_body_bytes: 0 # uncapped (default)
tls: ca_cert: "/etc/iron-proxy/ca.crt" # Required ca_key: "/etc/iron-proxy/ca.key" # Required cert_cache_size: 1000 # LRU cache for generated leaf certs leaf_cert_expiry_hours: 72
transforms:
name: allowlist config: domains: - "api.openai.com" - "*.anthropic.com" cidrs: - "10.0.0.0/8"
name: secrets config: secrets: - source: type: env var: OPENAI_API_KEY # Env var holding the real secret proxy_value: "proxy-token-123" # Token the sandbox sends match_headers: ["Authorization"] match_body: false require: true # Reject requests without the proxy token rules: - host: "api.openai.com"
log: level: "info" # debug, info, warn, error
### DNS
デフォルトではすべてが `proxy_ip` に解決されます。これによりトラフィックがプロキシ経由でルーティングされます。例外は以下のとおりです。
- **`passthrough`:** OSリゾルバに転送されるグロブパターン(例: `*.internal.corp`)。これらのホストへのトラフィックはプロキシを完全にバイパスします。
- **`records`:** 静的AレコードまたはCNAMEレコード。最も優先度が高くなります。
### レスポンス再試行ハンドラ
`IRON_RESPONSE_RETRY_HANDLER_URL`、`IRON_RESPONSE_RETRY_COMPLETE_URL`、`IRON_RESPONSE_RETRY_HANDLER_TOKEN`、`IRON_RESPONSE_RETRY_HANDLER_SANDBOX_ID`、およびカンマ区切りの `IRON_RESPONSE_RETRY_STATUSES` リストを設定すると、外部認証によるレスポンス再試行を有効にできます。認証ハンドラは、正確なアップストリームのスキーム、オーソリティ、メソッド、パス/クエリ、再試行可能性、レスポンスステータスとヘッダー、トレースコンテキスト、サンドボックスIDを受け取ります。ハンドラは、1回の正確な再試行のためのリクエストヘッダーと試行IDを返すことができます。完了ハンドラは、`IRON_RESPONSE_RETRY_COMPLETION_HEADERS` によって選択された再試行ステータスとレスポンスヘッダーを受け取ります。これはデフォルトで `Payment-Receipt` になります。
レスポンスボディはどちらのハンドラにも送信されず、宛先を変更することはできず、接続/フレーミングヘッダーは拒否されます。`proxy.max_request_body_bytes` を超えるリクエストは通常どおり処理されますが、再試行不可としてマークされます。チャレンジされた場合、元のレスポンスが返されます。ハンドラの障害でも元のレスポンスは保持されます。ハンドラURLは、ループバックまたは信頼された内部ネットワーク向けに `IRON_RESPONSE_RETRY_HANDLER_ALLOW_HTTP=true` が明示的に設定されている場合を除き、HTTPSを使用する必要があります。リダイレクトは拒否され、レスポンス再試行トークンはコントロールプレーントークンとは独立して設定する必要があります。WebSocket、gRPC、および長さ不明のストリーミングリクエストは、レスポンス再試行処理をバイパスします。
信頼されたハンドラが `proxy.upstream_deny_cidrs` 内で解決される場合、`IRON_RESPONSE_RETRY_HANDLER_ALLOW_CIDRS` に、ハンドラが使用できる狭いプライベートCIDRのカンマ区切りリストを設定します。この例外は、設定された正確な認証エンドポイントと完了エンドポイントにのみ適用されます。通常のプロキシトラフィックは、引き続き完全なアップストリーム拒否リストの対象となります。パブリック、ループバック、リンクローカル、およびクラウドメタデータのレンジは、この設定では追加できません。
### 許可リスト
デフォルトは拒否です。リクエストは、少なくとも1つのドメイングロブまたはCIDRに一致する必要があります。一致しないリクエストには `403 Forbidden` が返されます。
ドメインパターンはグロブマッチングを使用します。`*.example.com` は任意のサブドメインと `example.com` 自体に一致します。
**警告モード:** `warn: true` を設定すると、許可リストが実際に適用せずに何をブロックするかを確認できます。拒否されるはずのリクエストは通過できますが、変換トレースに `"action": "warn"` という注釈が付けられます。これは、新しい許可リストルールを段階的に導入したり、適用に切り替える前に既存のトラフィックを監査したりするのに役立ちます。
### 注釈 (Annotate)
ホスト/メソッド/パスルールに基づいて、HTTPリクエストヘッダーを監査ログの注釈に取り込みます。これは、プロキシコアを変更せずに、リクエストIDなどのリクエスト固有のコンテキストで監査ログを強化するのに役立ちます。
各注釈グループは、照合するルールと取得するヘッダーを指定します。リクエストがグループ内のいずれかのルールに一致すると、指定されたヘッダー値が変換トレースの注釈に `header:<Name>` エントリとして書き込まれます。一致しないリクエストは変更されずに通過します。この変換がリクエストを拒否することはありません。
> **警告:** ヘッダー値は監査ログに平文で出力されます。リクエストIDやプロキシのシークレットトークンを含むヘッダーなど、公開しても安全なヘッダーのみをログに記録してください。生のシークレットを含むヘッダーはログに記録しないでください。```yaml
transforms:
- name: annotate
config:
annotations:
- rules:
- host: "api.openai.com"
methods: ["POST"]
paths: ["/v1/*"]
headers: ["x-request-id"]
- rules:
- host: "*.anthropic.com"
headers: ["x-request-id"]
デフォルト拒否方式のリクエストヘッダーフィルターです。正規名が設定済みの headers リストに含まれないリクエストヘッダーは、リクエストが上流へ送信される前に除去されます。サンドボックスが付与する可能性のあるトラッキング、フィンガープリンティング、または偶発的な漏洩ヘッダー(クッキー、内部相関ID、X-Forwarded-* など)をブロックするのに役立ちます。
エントリは正規ヘッダー名に対して大文字小文字を区別せず照合されます。/.../ で区切られたパターン(例: /^X-Trace-.*$/)は大文字小文字を区別しない正規表現であり、secrets トランスフォームの match_headers 構文と同様です。
オプションの rules は、許可リストを特定のホスト/メソッド/パスに制限します。省略した場合、許可リストはこのトランスフォームに到達するすべてのリクエストに適用されます。
少なくとも1つのヘッダーが除去されると、トレースには除去された名前を列挙した stripped_headers が注釈として付与されます。
配置:
header_allowlistはsecretsの後(許可リストにない注入済み認証情報が除去されないよう、それらを列挙できます)およびannotateの後(注釈が元のヘッダーを読み取れるように)に配置してください。```yaml transforms:
### ボディキャプチャ
一致するリクエストのデコード済みリクエストボディを記録し、監査ログレコードの `body_capture` グループに `request_body` と `request_body_truncated` として表示します。プロキシを通過するペイロード(サンドボックスがLLMプロバイダーに送信するプロンプトなど)を、上流のトラフィックを変更せずに監査するのに役立ちます。
ホスト、メソッド、パスは、`allowlist` や `secrets` と同じ `rules` 構文で照合されます。`max_request_body_bytes` は、各ボディのうちどれだけをキャプチャするかを制限します。上限を超えるボディはプレフィックスに切り詰められ、`request_body_truncated` が `true` に設定されます。上限のデフォルトは16 KiBで、グローバルな `proxy.max_request_body_bytes` 制限とは独立しています。このトランスフォームは観察専用であり、リクエストを拒否することはありません。また、ボディの読み取りエラーはリクエストを失敗させるのではなく、トレースに注釈として記録されます。
キャプチャが成功すると、`request_transforms` 内のこのトランスフォームのエントリに `captured_bytes` と `truncated` が注釈として付与され、ボディ自体を複製することなく、ボディがキャプチャされたことがトレースに記録されます。
レスポンスボディはキャプチャされません。ストリーミングレスポンス(SSE)は、転送前にエンドツーエンドでバッファリングする必要があり、クライアントを停滞させることになります。
> **警告:** キャプチャされたボディは監査ログに平文で書き込まれます。`secrets` を `match_body: true` で実行する場合は、`body_capture` を `secrets` の *前* に配置して、監査ログが `secrets` がボディに置き換える実際の認証情報ではなく、サンドボックスのプロキシトークンを記録するようにしてください。```yaml
transforms:
- name: body_capture
config:
max_request_body_bytes: 16384
rules:
- host: "api.anthropic.com"
methods: ["POST"]
paths: ["/v1/messages"]
- host: "api.openai.com"
methods: ["POST"]
paths: ["/v1/chat/completions"]
サンドボックスは実際の認証情報を保持しません。その代わりに:
proxy-openai-abc123)。secrets 変換を構成して、プロキシトークンをそれらのソースにマッピングします。iron-proxy は送信リクエストをスキャンし、上流へ転送する前にプロキシトークンを実際の値に置き換えます。検索場所は次のように制御できます:
match_headers: スキャンするヘッダー名のリスト。空リスト = すべてのヘッダー。
リテラル名は大文字小文字を区別せずに一致しますが、記述した大文字小文字の表記は
ヘッダーが上流へ転送されるときに保持されます。/.../ で区切られたエントリは、
正規化されたヘッダー名に対して照合される大文字小文字を区別しない正規表現として
コンパイルされます(例:/^x-.*-key$/)。match_body: リクエストボディをスキャンします(max_request_body_bytes までバッファリング)。match_query: URL クエリ文字列をスキャンします。デフォルトは false。クエリパラメータで
シークレットを期待する上流のためにオプトインします。クエリ文字列はプロキシの両側の
アクセスログに頻繁に現れるため、デフォルトではオフになっています。match_path: URL パスをスキャンします。デフォルトは false。Telegram のように
シークレットをパスに埋め込む上流(例:/bot<TOKEN>/sendMessage)のためにオプトインします。
URL パスはプロキシの両側のアクセスログに頻繁に現れるため、デフォルトではオフになっています。require: true の場合、一致するホストへのリクエストで、プロキシトークンを
リクエストは 403 で拒否されます。これにより、侵害されたワークロードが代替認証情報で
シークレット交換メカニズムをバイパスするのを防ぎます。デフォルト: 。クエリパラメータは常にスキャンされます。
シークレットソース:
env: プロキシプロセスの環境から var を読み取ります。プロセス開始時に固定 —
実行中のプロキシで値をローテーションする必要がある場合は、代わりに file を使用します。file: ディスク上の path からシークレットを読み取ります。ファイルは構成の再読み込み
ごと(起動時および各 POST /v1/reload 時)、および ttl が設定されている場合は
キャッシュの有効期限ごとに再読み取りされます。そのため、実行中のプロキシのシークレットは、
ファイルを書き換え(原子的に: temp への書き込み + リネーム)して再読み込みすることで、
再起動なしにローテーションできます。値はファイルの正確な内容(トリミングなし)なので、
末尾の空白は書き込み側が制御します。オプションの ttl と failure_ttl がサポートされています。aws_sm: AWS Secrets Manager から secret_id を読み取ります。オプションの region、
ttl、failure_ttl がサポートされています。すべてのソースはオプションの json_key も受け入れます。設定されている場合、解決された値は
JSON オブジェクトとして解析され、そのキーにある単一のトップレベル文字列フィールドが
抽出されます。JSON シークレットから 1 つのフィールドを取り出すために使用します。
ttl は、正常に取得された値が更新前にキャッシュされる期間を制御します
(空の場合は無期限にキャッシュ)。failure_ttl は、取得エラーが再試行前にキャッシュされる
期間を制御します。デフォルトは 1m で、ttl とは独立しているため、成功時の TTL が長くても
一時的なバックエンド障害からの回復が遅れることはありません。
注:
onepassword-sdk-goのバグによりCGO_ENABLED=0でのビルドが失敗するため、 iron-proxy は修正が上流に取り込まれるまで、go.modのreplaceディレクティブを介して フォーク を固定しています。
judge 変換は LLM を呼び出して、その URL ルールに一致するリクエストに対する許可/拒否の判断を
生成します。transforms: の各エントリは、独自の自然言語ポリシー、LLM バックエンド、
タイムアウト、セマフォ、およびサーキットブレーカーを持つ独立した judge インスタンスです。
オペレーターは、異なるルールにスコープされた異なるプロンプトを持つ judge を 0 個、1 個、
または複数デプロイできます。```yaml
不変条件:
- 判定は拒否のみを行います。静的許可リストが拒否するリクエストを承認することはありません。
静的拒否が常に優先されます。
- 一致しないリクエストは無視されます。LLM呼び出しも監査注釈も行われません。
- LLMエラー、タイムアウト、サーキットブレーカー解放、または不正なモデル出力の場合、
設定された`fallback`が適用されます。`deny`はリクエストをブロックします
(本番環境での推奨デフォルト)。`skip`はパイプラインの残りに委ねます。iron-proxyは
デフォルト拒否のため、一致しないリクエストも引き続きブロックされます。
シークレット変換とのパイプライン順序:
- **推奨:** 判定をシークレット変換の**前**に配置します。LLMプロバイダーには
プロキシトークンのみが表示され、ワークロードがアクセスできる実際の認証情報が
表示されることはありません。
- あるいは、判定をシークレット変換の後に配置すると、実際に外部へ送信されるワイヤー形式
そのものを評価できますが、実際の認証情報をLLMプロバイダーに送信するという
コストがかかります。このトレードオフを脅威モデルが許容できる場合にのみ選択してください。
対応プロバイダー:
- **`anthropic`** (Messages API)。`api_key_env`、`model`、オプションの
`base_url`と`max_tokens`を使用します。
- **`openai`** (Chat Completions API)。上記と同じフィールドです。
`type: openai`を設定し、`api_key_env`をOpenAIキーを保持する環境変数
に向け、`gpt-5.4-nano`のようなモデルを選択してください。
監査出力: 一致したすべてのリクエストは、変換トレースの下に構造化フィールドを追加します。
これには`judge.instance`、`judge.decision`、`judge.reason`、
`judge.duration_ms`、`judge.input_tokens`、`judge.output_tokens`、
`judge.fallback_applied`(フォールバックが発動した場合)、および
`judge.circuit_breaker_tripped`(ブレーカーが開いている場合)が含まれます。
クレジット: この設計に影響を与えたBrexのCrabTrapプロジェクト(MITライセンス)に
感謝します。
## MCPポリシー
iron-proxyは[MCPのStreamable HTTPトランスポート](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports)に対応しています。リクエストが設定済みのMCPサーバーに一致すると、プロキシはJSON-RPCボディを解析し、デフォルト拒否のツール許可リストを適用し、`tools/list`レスポンスをフィルタリングして、拒否されたツールがエージェントに届かないようにします。SSEレスポンスはイベントごとにフィルタリングされるため、長期間存続するMCPストリームもライブのまま維持されます。
これはトランスフォームではなく、第一級のプロキシ機能です。MCPレスポンスは、サーバーが任意に開始するメッセージを運ぶオープンエンドのSSEストリームになり得るため、リクエスト/レスポンスのトランスフォーム契約には適合しません。```yaml
mcp:
# JSON-RPC error envelope returned to the agent on policy denial.
# Defaults: code -32001, message "blocked by iron-proxy policy".
error:
code: -32001
message: "blocked by iron-proxy policy"
servers:
- name: github # appears in audit as mcp.server
rules: # standard host/method/path rules
- host: "mcp.github.com"
paths: ["/mcp", "/mcp/*"]
tools:
- name: "search_repositories" # always allowed
- name: "create_issue"
when: # all clauses must hold; otherwise deny
- path: "owner" # dotted path against arguments
equals: "ironsh"
- path: "repo"
in: ["iron-proxy", "tunis-v2"]
# Anything not listed is denied (default-deny).
動作:
tools/call の強制。 サーバーの tools リストにないツールへの呼び出し、またはその arguments が 1 つでも when 節を満たさない呼び出しは、アップストリームに到達せずに拒否されます。プロキシは、設定されたコードとメッセージ、およびリクエストの元の id を含む JSON-RPC エラーレスポンスを返すため、MCP クライアントは HTTP エラーではなく通常のプロトコルエラーを受け取ります。tools/list のフィルタリング。 tools/list へのレスポンスでは、許可リストにないツールはすべて、エージェントに到達する前に削除されます。これは application/json と text/event-stream の両方のレスポンスで機能します。SSE フィルタリングはイベント単位で動作するため、ストリーム上のハートビートやその他のメッセージはそのまま通過します。when 節は、ドット区切りの path(例: arguments.repo、labels.0)と、equals(任意の JSON スカラー)、(スカラーのリスト)、または (文字列値に対する正規表現)のいずれかを持ちます。節は AND で結合されます。 を省略すると、そのツールは無条件に許可されます。パイプラインの順序: MCP インターセプターは変換パイプラインの後に実行されるため、allowlist は依然として到達可能なホストを制限し、インターセプターがボディを評価する時点では secrets がすでにプロキシトークンを置き換えています。
mcp_gateway は、MCP ポリシーがリクエストを受理した後、クライアント向けの MCP ホストを具体的なアップストリームサーバーにルーティングします。これにより、エージェントは安定した内部ホストを呼び出し、iron-proxy が実際のアップストリームに転送して、サンドボックスに入ることのない認証情報を注入できます。
ゲートウェイルートは、MCP サーバーに一致したリクエストにのみ適用されます。MCP ポリシーは引き続き、ツールの許可リストを最初に適用します。ポリシーが tools/call を拒否した場合、ゲートウェイルートは適用されず、アップストリームには到達しません。```yaml
mcp:
servers:
- name: github
rules:
- host: "github.mcp.local"
paths: ["/mcp", "/mcp/*"]
tools:
- name: "search_repositories"
mcp_gateway: routes: - name: github rules: - host: "github.mcp.local" paths: ["/mcp", "/mcp/*"] upstream: "https://mcp.github.com/v1" credentials: - source: type: env var: GITHUB_MCP_TOKEN inject: header: Authorization formatter: "Bearer {{ .Value }}"
資格情報は、`secrets` 変換と同じシークレットソースを使用します。これらはデフォルトで必須です。資格情報に `require: false` を設定すると、利用できない場合にスキップできます。監査ログには、ルート、アップストリームURL、資格情報の挿入場所が記録されますが、挿入された資格情報の値は決して記録されません。
v1での制限事項:
- Streamable HTTP トランスポートのみがサポートされます。レガシーの HTTP+SSE トランスポート(個別の `/messages` エンドポイントと `/sse` エンドポイント)はサポートされません。
- 拒否されたエントリを含む JSON-RPC バッチは、バッチ全体として拒否されます。部分的なバッチ転送はサポートされません。
- リソースとプロンプトは適用されません。エージェントは、ポリシーフィルタリングなしで `resources/list`、`resources/read` などを引き続き呼び出すことができます。
### ボディ制限
リクエスト/レスポンスボディを検査または転送する変換(シークレットのボディマッチング、gRPC 変換)は、バッファリングされたボディに対して動作します。最大バッファサイズを制御する2つのグローバル設定があります:
- **`max_request_body_bytes`** (デフォルト: `1048576` / 1 MiB): 変換用にバッファリングされるリクエストボディの量を制限します。この制限を超えるデータは、変換の観点からは切り捨てられますが、それでもアップストリームに転送されます。
- **`max_response_body_bytes`** (デフォルト: `0` / 無制限): バッファリングされるレスポンスボディの量を制限します。`0` に設定するとレスポンス全体がバッファリングされます。これはほとんどのワークロード(例: npm パッケージ、モデルウェイト)の適切なデフォルトです。
ボディは、変換が読み取る際に増分的にバッファリングされ、パイプラインステージ間で自動的に巻き戻されます。変換がボディを読み取らない場合、バッファリングは発生せず、ボディは変更されずにストリーミングされます。
### トンネルリスナー (HTTP/CONNECT/SOCKS5)
トンネルリスナーは、専用ポートで絶対形式の HTTP プロキシリクエスト、HTTP CONNECT、SOCKS5 接続を受け入れます。これは、DNS ベースのルーティングに依存するのではなく、`HTTP_PROXY`/`HTTPS_PROXY`/`ALL_PROXY` 環境変数または SOCKS5 設定を介したプロキシ設定をネイティブにサポートするツールに役立ちます。
これを有効にするには、`proxy` の下に `tunnel_listen` を設定します:```yaml
proxy:
tunnel_listen: ":8080"
省略すると、トンネルリスナーは無効になります。
すべてのプロトコルは、通常のHTTP/HTTPSリクエストと同じ変換パイプラインを通過します。absolute-form HTTPリクエストは通常のHTTPプロキシパスで処理されます。CONNECTおよびSOCKS5の場合、プロキシは合成CONNECTリクエストを許可リストとシークレット変換に対して評価するため、トンネル接続も同じデフォルト拒否ポリシーの対象となります。
CONNECTまたはSOCKS5ハンドシェイクの後、プロキシは最初のバイトを覗き見て内部プロトコルを検出します:
HTTP CONNECTの例:```bash
curl -x http://172.20.0.2:8080
--cacert /certs/ca.crt
https://httpbin.org/get
**プレーンHTTPプロキシの例:**```bash
curl -x http://172.20.0.2:8080 \
http://httpbin.org/get
SOCKS5 の例:```bash
curl --socks5-hostname 172.20.0.2:8080
--cacert /certs/ca.crt
https://httpbin.org/get
また、標準環境変数を設定すると、すべてのツールが自動的にトンネル
経由でルーティングされます:```bash
export HTTP_PROXY=http://172.20.0.2:8080
export HTTPS_PROXY=http://172.20.0.2:8080
export ALL_PROXY=socks5h://172.20.0.2:8080
SOCKS5 実装は no-auth のみをサポートし、IPv4、IPv6、およびドメイン名アドレスタイプを受け入れます。
iron-proxy は、提供された CA によって署名されたリーフ証明書をその場で生成します。クライアントコンテナはこの CA を信頼する必要があります(システムのトラストストアに追加するか、--cacert で渡します)。証明書は、SNI ホスト名をキーとする LRU キャッシュにキャッシュされます。
強制力が高まる3つのアプローチがあります。
コンテナの DNS を iron-proxy に向けます。すべてのルックアップがプロキシ IP に解決されるため、HTTP/HTTPS トラフィックは自然にプロキシを通過します。これは、Docker Compose の例で使用されている方法です。```yaml services: client: dns: - 172.20.0.2 # iron-proxy IP
セットアップは簡単ですが、迂回も簡単です。ワークロードはIPをハードコードするか、独自のDNSリゾルバを使用してプロキシを完全に迂回できます。
### DNS + nftables エグレスファイアウォール (強制)
DNSルーティングの上にnftablesファイアウォールを重ねます。DNSは依然としてトラフィックをプロキシへ誘導しますが、nftablesは、IPをハードコードしていても、ワークロードが他の何とも通信_できない_ことを保証します。
[`examples/nftables`](https://github.com/paradigmxyz/iron-proxy/blob/HEAD/examples/nftables/) ディレクトリには、動作するセットアップがあります。クライアントコンテナは、アプリケーショントラフィックを実行する前に、起動時にファイアウォールルールをロードします。
**nftables.conf** はプロキシへのトラフィックを許可し、その他すべてをドロップします:```
table ip iron {
chain output {
type filter hook output priority 0; policy drop;
# allow loopback
oif lo accept
# allow traffic to the proxy itself (DNS + HTTP/HTTPS)
ip daddr 172.20.0.2 tcp dport { 80, 443 } accept
ip daddr 172.20.0.2 udp dport 53 accept
# allow established/related (return traffic)
ct state established,related accept
# log and drop everything else
log prefix "iron-proxy-drop: " drop
}
}
docker-compose.yml: クライアントイメージは nftables
があらかじめインストールされた状態でビルドされます。エントリポイントはルールを読み込んでから、デモを実行します。CAP_NET_ADMIN はルールを読み込むために必要です:```yaml
services:
proxy:
# ... same as DNS example ...
networks:
demo:
ipv4_address: 172.20.0.2
client: build: context: . dockerfile: Dockerfile.client # alpine + curl + nftables dns: - 172.20.0.2 cap_add: - NET_ADMIN volumes: - ./nftables.conf:/etc/nftables.conf:ro - certs:/certs:ro networks: demo: ipv4_address: 172.20.0.4
本番環境では、エントリーポイントのラッパーでルールをロードし、その後
`CAP_NET_ADMIN` を持たない非 root ユーザーとして実際のプロセスを `exec` します。
### TPROXY (透過プロキシ)
ワークロードのDNSをまったく制御できない環境では、nftables
TPROXY がカーネルレベルでトラフィックをリダイレクトでき、ワークロード側の協力を
一切必要としません。これは PREROUTING チェーンでパケットを傍受し、それらを
直接 iron-proxy に渡します:```
table ip iron {
chain prerouting {
type filter hook prerouting priority mangle; policy accept;
# redirect HTTP/HTTPS to iron-proxy via TPROXY
tcp dport 80 tproxy to 172.20.0.2:80 meta mark set 1 accept
tcp dport 443 tproxy to 172.20.0.2:443 meta mark set 1 accept
}
chain output {
type route hook output priority mangle; policy accept;
# mark locally-originated packets for policy routing
tcp dport { 80, 443 } meta mark set 1
}
}
マークされたパケットをローカルソケットにルーティングするには、ip rule と ip route のセットアップが必要です。さらに iron-proxy は IP_TRANSPARENT でバインドしなければなりません。セットアップはより複雑ですが、トラフィックがプロキシをバイパスできないという最も強力な保証を提供します。TPROXY は DNS の下位層で動作するため、ハードコードされた IP、カスタムリゾルバ、そしてワークロードが試みる可能性のあるその他のあらゆるものを捕捉します。
examples/docker-compose ディレクトリには、動作するセットアップが含まれています。主要な構成要素は次のとおりです:
docker-compose.yml: proxy と client は共有ブリッジネットワーク上に配置されます。実際のシークレットは、プロキシコンテナのみに環境変数として設定されます:```yaml services: proxy: build: context: ../.. dockerfile: examples/docker-compose/Dockerfile environment: - OPENAI_API_KEY=sk-real-openai-key-do-not-share - INTERNAL_TOKEN=real-internal-secret-value volumes: - certs:/certs networks: demo: ipv4_address: 172.20.0.2
client: image: alpine:latest dns: - 172.20.0.2 # Point DNS at the proxy volumes: - certs:/certs:ro networks: demo: ipv4_address: 172.20.0.4
**proxy.yaml** は `httpbin.org` と `icanhazip.com` を許可リストに登録し、2つの
シークレットを交換します:```yaml
transforms:
- name: allowlist
config:
domains:
- "httpbin.org"
- "icanhazip.com"
cidrs:
- "172.20.0.0/24"
- name: secrets
config:
secrets:
- source:
type: env
var: OPENAI_API_KEY
replace:
proxy_value: "proxy-openai-abc123"
match_headers: ["Authorization"]
match_query: true # scan the query string
rules:
- host: "httpbin.org"
- source:
type: env
var: INTERNAL_TOKEN
proxy_value: "proxy-internal-tok"
match_headers: [] # scan all headers
rules:
- host: "httpbin.org"
クライアントスクリプトは、各動作を示すために5つのリクエストを送信します:```bash
curl https://example.com/
curl -H "Authorization: Bearer proxy-openai-abc123" https://httpbin.org/headers
curl -H "X-Internal: proxy-internal-tok" https://httpbin.org/headers
curl "https://httpbin.org/get?token=proxy-openai-abc123&q=hello"
## 監査ログ形式
プロキシされたすべてのリクエストは、構造化されたJSONログエントリを生成します:```json
{
"host": "httpbin.org",
"method": "GET",
"path": "/headers",
"action": "allow",
"status_code": 200,
"duration_ms": 142,
"request_transforms": [
{
"name": "allowlist",
"action": "continue"
},
{
"name": "secrets",
"action": "continue",
"annotations": {
"swapped": [{ "secret": "OPENAI_API_KEY", "locations": ["header:Authorization"] }]
}
}
],
"response_transforms": []
}
拒否されたリクエストには rejected_by フィールドが含まれ、WARN レベルでログに記録されます。
監査イベントは OpenTelemetry 構造化ログレコードとしてエクスポートでき、Axiom、ClickHouse、Logfire などのバックエンドでオフライン分析に使用できます。有効にするには OTEL_EXPORTER_OTLP_ENDPOINT を設定します:```bash
docker run -d --name iron-proxy
-e OTEL_EXPORTER_OTLP_ENDPOINT=https://logfire-us.pydantic.dev
-e OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
-e OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer "
-e OTEL_SERVICE_NAME=iron-proxy
-e OTEL_RESOURCE_ATTRIBUTES="deployment.environment=staging" \
ironsh/iron-proxy:latest -config /etc/iron-proxy/proxy.yaml
すべての設定は、標準のOTEL環境変数を使用します:
| 変数 | 説明 | デフォルト |
| ------------------------------ | ---------------------------------------------------------- | ------------------ |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | OTLPコレクタのURL。未設定の場合はOTELエクスポートが無効になります。 | (無効) |
| `OTEL_EXPORTER_OTLP_PROTOCOL` | `http/protobuf` または `grpc`。 | `http/protobuf` |
| `OTEL_EXPORTER_OTLP_HEADERS` | 認証ヘッダー用のカンマ区切り `key=value` ペア。 | (なし) |
| `OTEL_SERVICE_NAME` | すべてのログレコードに付加されるサービス名。 | `iron-proxy` |
| `OTEL_RESOURCE_ATTRIBUTES` | カンマ区切りの `key=value` リソース属性。 | (なし) |
有効にすると、既存のJSON stderrログに加えて、すべての監査イベントがOTELログレコードとして出力されます。ログレコードはJSON監査エントリと同じスキーマを保持します: `host`、`method`、`path`、`action`、`status_code`、`duration_ms`、および注釈付きの完全な `request_transforms`/`response_transforms` 配列です。
## 管理API
iron-proxyは、運用タスク用の認証付きHTTP APIをオプションで公開できます。現在は単一のエンドポイント `POST /v1/reload` を提供しており、ディスクからYAML設定を再読み込みし、新しく構築された変換パイプラインを原子的に交換します。新しい設定が無効な場合、実行中のパイプラインは維持されます。
管理サーバーはデフォルトでは無効です。有効にするには、設定に `management` ブロックを追加します:```yaml
management:
# Bind on loopback unless you front this with a private network or auth proxy:
# /v1/reload can rebuild the entire transform pipeline.
listen: "127.0.0.1:9092"
# Env var that holds the bearer token. Defaults to IRON_MANAGEMENT_API_KEY.
api_key_env: "IRON_MANAGEMENT_API_KEY"
スタンドアロンモードのみ — コントロールプレーン管理モードとは互換性がありません。
実行中のプロキシをリロードします:```bash
curl -X POST http://127.0.0.1:9092/v1/reload
-H "Authorization: Bearer $IRON_MANAGEMENT_API_KEY"
## iron.sh
Need Vault/KMS シークレットバックエンド、Kubernetes オペレーター、または集中化されたポリシー管理が必要ですか? [iron.sh](https://iron.sh) は iron-proxy をベースに、これを大規模に運用するチーム向けのエンタープライズ機能を提供します。
## リリース署名の検証
リリースアーティファクトには、署名付きチェックサムマニフェストが含まれています:
- `checksums.txt`
- `checksums.txt.asc`(ASCIIアーマー形式の分離署名)
同梱の公開鍵 [`public-key.asc`](https://github.com/paradigmxyz/iron-proxy/blob/HEAD/public-key.asc) を使用して検証してください:```bash
# 1) Download release artifacts for a tag
TAG=vX.Y.Z
gh release download "$TAG" --pattern "checksums.txt" --pattern "checksums.txt.asc"
# 2) Import the project signing key
gpg --import public-key.asc
# 3) Verify the signature over checksums.txt
gpg --verify checksums.txt.asc checksums.txt
検証が成功すると、GPG は Matthew Slipper <[email protected]> からの良好な署名を報告します。
必要に応じて、インポートした鍵のフィンガープリントを検査し、検証前に信頼する情報源と一致することを確認できます。
署名付きチェックサムリストに対して特定のバイナリを検証するには(例: iron-proxy-linux-amd64):```bash
shasum -a 256 iron-proxy-linux-amd64 | grep -F "$(grep -F 'iron-proxy-linux-amd64' checksums.txt | awk '{print $1}')"
falsehosts: 交換を特定のドメインまたは CIDR に制限します。aws_ssm: AWS Systems Manager Parameter Store から name を読み取ります。オプションの
region、with_decryption、ttl、failure_ttl がサポートされています。
with_decryption はデフォルトで true になり、これは SecureString パラメータに期待される設定です。1password: 1Password サービスアカウントトークンを使用して secret_ref
(op://vault/item/[section/]field 参照)を解決します。トークンは
OP_SERVICE_ACCOUNT_TOKEN から読み取られます。オプションの ttl と failure_ttl がサポートされています。1password_connect: セルフホスト型 1Password Connect サーバーに対して同じ
op://vault/item/[section/]field の secret_ref を解決します。サーバー URL は
OP_CONNECT_HOST から、API トークンは OP_CONNECT_TOKEN から読み取られます。
オプションの ttl と failure_ttl がサポートされています。inmatcheswhenmcp セクションに記録されます。記録内容は、サーバー名、方向(request または response)、メソッド、ツール、判定(allow、deny、または filtered)、拒否時の理由、およびフィルターイベントで削除されたツールの数です。