Claude apps gatewayをproxy経由だけの環境で動かす設定
DNSを引けないpodやIP宛のCONNECTを拒否するプロキシ環境では、環境変数1つでゲートウェイのアドレス検査をproxyに任せます。条件と注意点、upstreamsのheaders:も扱います。
Claude apps gatewayのpodが外へ出られるのは社内のforward proxy経由だけ、という環境では、CLAUDE_GATEWAY_PROXY_IS_EGRESS_BOUNDARY=1を設定します。ゲートウェイは宛先のDNS解決をやめ、ホスト名をそのままproxyに渡すようになります。v2.1.277以降が必要です。
この変数はプロキシ越しの通信を通すためのものですが、効き目の裏返しとして、ゲートウェイ自身が持っていたアドレス検査をproxyへ移します。有効にする条件と、proxy側で用意すべき拒否ルールを先に押さえておくと、導入で迷いません。あわせて同じv2.1.277で入った、upstreamごとのheaders:も扱います。
どんな環境でこの変数が要るのか
既定の動きでは、ゲートウェイは宛先ホスト名を自分で解決し、検査を通ったIPアドレスに対して、proxyへCONNECTを依頼します。次のどちらかに当たる環境では、この流れが成り立ちません。
- podが公開DNS名を解決できない。外へ出る経路がforward proxyだけで、名前解決もproxy任せになっている
- proxyがIPアドレス宛の
CONNECTを拒否する。ホスト名宛しか許可しない運用になっている
前者ではゲートウェイが宛先を引けずに失敗し、後者ではIPに直したあとのCONNECTがproxyで落ちます。変数を有効にすると、ゲートウェイは解決を試みず、ホスト名のままproxyに渡します。名前解決はproxyの側が担います。
設定はgateway.yamlでなく環境変数で行う
設定する場所はゲートウェイプロセスの環境です。HTTPS_PROXYと並べて置きます。
export HTTPS_PROXY=http://proxy.corp.example.com:3128
export NO_PROXY=
export no_proxy=
export CLAUDE_GATEWAY_PROXY_IS_EGRESS_BOUNDARY=1gateway.yamlのキーにしていない理由は、設定ファイルの中身からアドレス検査を緩められないようにするためです。検査の緩和は、ゲートウェイを起動する側が環境で宣言する形になっています。
有効になると、ゲートウェイは起動時にnetwork:で始まるログ行を1本出します。有効になっているかどうかは、この行で確かめられます。
通信の種類ごとに何が変わるか
HTTPS_PROXYを設定したゲートウェイの外向き通信は、種類によって扱いが異なります。
| 外向きの通信 | 既定 | proxy-only egress有効時 |
|---|---|---|
provider: anthropicのupstream、Workload Identity Federationのトークン交換、telemetry.forward_toの送信 | 既定ローカルで解決・検査し、検査済みIPへproxy経由でCONNECT | proxy-only egress有効時ホスト名をproxyに渡す |
| IdPのdiscovery・JWKS・token・userinfo | 既定直接接続。oidc.use_proxy: trueならIPへCONNECT | proxy-only egress有効時ホスト名をproxyに渡す。oidc.use_proxy: falseなら直接接続のまま |
| Amazon Bedrock・Claude Platform on AWS・Google CloudのAgent Platform・Microsoft Foundryのupstream、Googleグループの照会 | 既定ホスト名をproxyに渡す | proxy-only egress有効時変わらない |
最後の行は、もともとホスト名をproxyに渡していた通信です。この変数で変わるのは、ゲートウェイがローカルで解決していた通信のほうです。
テレメトリの送信先がNO_PROXYに入っていると、既定では直接接続になります。後述の条件に関わるので、次の節で説明します。
有効にならない3つの条件
proxy-only egressは、次の3つがすべて満たされたときだけ有効になります。
HTTPS_PROXYかHTTP_PROXYが設定されているNO_PROXYとno_proxyがどちらも空であるCLAUDE_GATEWAY_ALLOW_LOOPBACKが有効になっていない
どれかが欠けると、ゲートウェイは起動時に、止めている変数の名前を含む警告を出し、既定の動作を続けます。変数を入れたのに挙動が変わらないときは、まずこの警告を探します。
見落としやすいのは2番目です。プラットフォームがNO_PROXYをpodへ自動で注入する場合は、ゲートウェイのコンテナで両方を空にします。たとえばKubernetesのコンテナ定義なら、次のような形になります(書き方の例です)。
env:
- name: HTTPS_PROXY
value: http://proxy.corp.example.com:3128
- name: NO_PROXY
value: ""
- name: no_proxy
value: ""
- name: CLAUDE_GATEWAY_PROXY_IS_EGRESS_BOUNDARY
value: "1"3番目のCLAUDE_GATEWAY_ALLOW_LOOPBACKとは併用できません。podのループバック上にあるコレクターやIdPのアドレスをproxyに渡すと、それはpod自身ではなくproxyホスト自身のループバックを指してしまうためです。有効な間、ゲートウェイはlocalhostのような名前を最初から拒否します。サイドカーのOTLPコレクターをlocalhostで呼んでいる構成は、proxyから届く別のアドレスに付け替えます。
有効にする前にproxy側で確かめること
この変数は、検査をproxyが肩代わりしてくれる前提で成り立っています。proxyの許可リストがゲートウェイの検査と同じかそれ以上に厳しいときだけ、有効にします。
どこへでも接続するproxyでは、この通信についてゲートウェイのSSRFガードが事実上なくなります。SSRFガードの設計はClaude apps gatewayの脅威モデルで扱っています。ガードが何を止めていたかを知っておくと、proxyの許可リストに何を書くかが決めやすくなります。
有効にしたあとは、次の宛先をproxy側で許可します。
- 内部のテレメトリコレクター
- IPアドレスで指定したホスト
IPで指定したホストも、proxyに渡る対象です。許可リストが名前しか想定していないと、ここで止まります。
IdPだけ直接つなぐ場合はoidc.use_proxy
有効な間、IdPへの通信もproxy経由になります。社内のIdPには直接つなぎたい、という構成では、oidc.use_proxy: falseを指定します。こちらは、IdPのリクエストをproxyに通すかどうかを決めるキーです。use_proxy自体はv2.1.227以降で使えます。
oidc:
issuer: https://idp.internal.example.com
use_proxy: falseuse_proxy: trueのときの動きは、proxy-only egressの有無で変わります。有効でなければ、podがIdPの各エンドポイントのホスト名を自分で解決し、解決したIPへCONNECTを依頼します。discoveryドキュメントに現れるホストのIPすべてに、proxyがCONNECTを許す必要があります。issuerのホストだけでは足りません。有効なら、ホスト名のままproxyに渡します。proxyURLはhttp://で指定します。
upstreamsにheaders:で固定ヘッダーを足す
v2.1.277では、もう1つ関連する機能が入りました。ゲートウェイの前段にproxyを挟み、そのproxyがヘッダーで経路や課金先を振り分ける場合に使うheaders:です。upstreamごとに、固定のヘッダーを付けられます。
次の例は、upstream-proxy.internal.example.comのproxyを介してprovider: vertexのupstreamへ届く設定です。proxyが読むx-sourceと、環境変数PROXY_TOKENから取ったトークンをx-proxy-tokenとして送ります。
upstreams:
- provider: vertex
region: us-east5
project_id: example-prod
base_url: https://upstream-proxy.internal.example.com
auth: {}
headers:
x-source: claude-apps-gateway
x-proxy-token: ${PROXY_TOKEN}ヘッダーは、base_urlが指すサーバーへ送られます。base_urlを省略すれば、プロバイダー自身のエンドポイントへ送られます。proxyが取り除かない限り、プロバイダーにも届く点は押さえておきます。
書き方の決まりは次のとおりです。
- 値は印字可能なASCII文字で、前後に空白を置かない
- 数値や
true・falseは引用符で囲み、文字列として読ませる - 秘密の値は
${VAR}か${file:/path}で外から読み込む。${VAR}が空に解決されるとゲートウェイは起動しない headers:はどのプロバイダーでも書けるが、各upstreamは自分の分だけを送る
予約された名前を使うと、ゲートウェイは起動を拒否し、エラーにヘッダー名が出ます。予約済みなのは、authorization・x-api-key・host・content-type・user-agentと、anthropic-・x-goog-・x-amz-・x-amzn-で始まる名前です。
ヘッダーが付くリクエストと付かないリクエストは、次のとおり分かれます。
| ゲートウェイがupstreamへ送るリクエスト | headers:が付くか |
|---|---|
/v1/messages(ストリーミングを含む)と/v1/messages/count_tokens | headers:が付くか付く |
| 別のupstreamからフェイルオーバーしてきたリクエスト | headers:が付くか付く。ただし送り先のupstream自身のheaders:だけ |
クライアントが取りやめたリクエストに対するAmazon BedrockのCountTokens呼び出し | headers:が付くか付かない |
| Workload Identity Federationのトークン交換 | headers:が付くか付かない |
AWS SigV4で署名するAmazon Bedrock、またはClaude Platform on AWSのupstreamでは、これらのヘッダーが署名の対象に含まれます。間に挟むproxyは、ヘッダーを書き換えずに通さなければなりません。
バージョンの扱いにも注意が要ります。headers:はゲートウェイ側でv2.1.277以降が必要で、古いゲートウェイはキーを見つけると起動を拒否します。全レプリカを上げてからキーを足し、ロールバックの前にはキーを消します。
切り分けのときに見る場所
proxy越しの構成でゲートウェイが立ち上がらないとき、見る順序は決まります。
- 起動ログの
network:行があるか。なければproxy-only egressは無効で、警告が理由を示している NO_PROXY・no_proxyが空か。注入される環境では見落としやすい- IdPが起動時のOIDC discoveryで落ちていないか。IdPがproxy経由でしか届かないなら
oidc.use_proxyの指定が要る - proxyのログに、ゲートウェイのホスト名宛の要求が来ているか。来ていれば許可リストの側の問題になる
IdPの到達性が原因のdiscoveryエラーには、oidc.use_proxy: trueで対応できます。podがIdPのホスト名を解決できない、またはproxyがIP宛のCONNECTを拒否する場合は、この記事の変数が次の手です。この切り分けは、運用手順の側にも載っています。
AWSやGCPへの配置の全体像は、AWSへのデプロイとGCPへのデプロイの記事にまとめています。gateway.yamlの各キーは設定リファレンスで確認できます。なおサンドボックス側のプロキシ設定は別物で、httpProxyPortとsocksProxyPortの記事が対象です。リリースの全体はClaude Code v2.1.277のリリースノートにあります。
まとめ
CLAUDE_GATEWAY_PROXY_IS_EGRESS_BOUNDARY=1は、forward proxyが唯一の出口になっている環境で、ホスト名の解決とアドレス検査をproxyに移すための環境変数です。設定ファイルから緩められないよう、gateway.yamlではなく環境に置かれています。
導入の勘所は2つあります。NO_PROXYを空にして3つの条件を満たすこと、そしてproxyの許可リストを、メタデータ宛先やループバックを解決先アドレスでも拒否できる状態にしておくことです。後者を欠いたまま有効にすると、検査がどこにも残りません。ヘッダーで振り分けるproxyを前段に置く構成では、headers:を併用し、SigV4のupstreamではヘッダーを改変させないようにします。