Claude apps gateway設定の全項目 — gateway.yamlリファレンス
gateway.yamlの必須5セクションと任意セクションを1つずつ整理し、secret展開・upstream選定・managed policyのマージ規則まで実務で迷う箇所を解説します。
gateway.yamlは1ファイルで何を決めるか
Claude apps gatewayはgateway.yamlという1つのYAMLファイルだけで動作が決まります。どこでリッスンするか、開発者がどうサインインするか、推論がどこへ流れるか、どのポリシーとテレメトリを適用するかのすべてがこのファイルに書かれます。基本の使い方では最小構成の5セクションを組み立てましたが、実際の運用ではadminやmanagedのような任意セクションまで含めた全項目を把握しておく必要があります。本記事はその全項目のリファレンスです。
ゲートウェイは起動時に一度だけこのファイルを読み込み、claude gateway --config /path/to/gateway.yamlで起動します。すべての項目はスキーマに対して起動時に検証され、記述ミスは起動失敗時にフィールド単位のエラーで出ます。未知のキーを書くと起動が失敗するので、タイポも黙って無視されません。証明書認証のIdPはprivate_key_jwtの設定と更新順序も参照してください。
壊れた設定で起動すると何が出るか
v2.1.285のclaude gateway --helpが出すオプションは--config <path>だけです。PostgresもIdPも用意せず、わざと壊した設定ファイルを渡して起動エラーを確かめました。
$ printf 'listen:\n port: 8080\n typo_key: 1\n' > bad1.yaml
$ claude gateway --config bad1.yaml
claude gateway: [
{
"code": "unrecognized_keys",
"keys": [ "typo_key" ],
"path": [ "listen" ],
"message": "Unrecognized key(s) in object: 'typo_key'"
},
...出力はJSON形式の配列で、pathが壊れた場所を指します。上の設定には必須のoidcなどが無いので、同じ配列に必須セクション欠落のエラーも続いて並びました。1回の起動で複数の不備がまとめて出る形です。
host: 0.0.0.0だけでpublic_urlを省いた設定では、次のメッセージが出ました。
listen.public_url is required when listen.host is not a loopback address —
set it to the externally-visible origin (e.g. https://claude-gateway.corp.example.com).
Without it the IdP redirect_uri and token issuer would be derived from the
client-controlled Host header.未定義の環境変数を${VAR}で参照した場合は、スキーマ検証の配列ではなく1行で止まります(undefined env var in config: NOPE_UNDEF)。シークレットの参照ミスは、JSONの配列とは別の形で出ると覚えておくと、ログを読むときに迷いません。
必須セクションと任意セクションの構成
必須は5つ、それ以外はすべて任意で、省略したセクションは既定値で動きます。
| セクション | 種別 | 役割 |
|---|---|---|
listen | 種別必須 | 役割バインドアドレス・公開URL・TLS終端 |
oidc | 種別必須 | 役割IdPの発行者・クライアント・クレームマッピング |
session | 種別必須 | 役割ゲートウェイが発行するベアラートークンの署名鍵と寿命 |
store | 種別必須 | 役割Postgres接続情報 |
upstreams | 種別必須 | 役割推論の転送先(順序付きリスト) |
admin | 種別任意 | 役割支出上限のAdmin APIを有効化 |
enforcement | 種別任意 | 役割Postgres障害時の支出上限の挙動 |
pricing | 種別任意 | 役割契約レートでの課金メーター上書き |
models | 種別任意 | 役割モデルIDのカタログとupstreamごとの変換 |
managed | 種別任意 | 役割IdPグループ別のmanaged settingsポリシー |
telemetry | 種別任意 | 役割OTLPメトリクス・ログ・トレースの転送先 |
シークレットはclient_secret・jwt_secret・postgres_urlのようにファイルへ直書きせず、${VAR}(環境変数)または${file:/path}(ファイル内容、末尾トリム済み)で展開します。
oidc:
client_secret: ${OIDC_CLIENT_SECRET} # 環境変数から展開
session:
jwt_secret: ${file:/var/run/secrets/jwt} # マウントされたファイルから展開${file:/path}はフィールドの値全体を置き換える形でしか使えず、${VAR}のように長い文字列の中に埋め込むことはできません。データベースのパスワードをpostgres_urlに埋め込まずstore.passwordに分離するのはこの制約が理由です。
listen — バインドとTLS終端
| フィールド | 必須 | 内容 |
|---|---|---|
host | 必須任意 | 内容バインドアドレス。既定0.0.0.0 |
port | 必須任意 | 内容バインドポート。既定8080 |
public_url | 必須hostがループバックでない限り必須 | 内容外部から見えるHTTPSオリジン。IdPのredirect_uriとディスカバリーメタデータの構築に使う |
tls.cert / tls.key | 必須任意 | 内容ゲートウェイ自身がTLS終端する場合のPEMパス |
trusted_proxies | 必須任意 | 内容前段ロードバランサーのCIDR。設定するとX-Forwarded-Forをここからの接続のみ信頼する |
public_urlはALB・Ingress・Cloud Runのような前段プロキシでTLS終端する場合でも省略できません。ゲートウェイはX-Forwarded-*ヘッダーから自分のオリジンを推測しないためです。クライアントが偽装できるヘッダーを信頼のルーツにしないという設計で、trusted_proxiesはクライアントIPの解決だけに使われ、オリジンの決定には関与しません。telemetryを有効にする場合もこの値からOTLPエンドポイントを組み立てるため必須になります。
oidc — IdP接続とサインインの許可条件
oidcブロックはIdPの発行者・OAuthクライアント・誰がサインインできるかを決めます。主要フィールドは次の通りです。
| フィールド | 内容 |
|---|---|
issuer | 内容OIDCディスカバリーのベース。/.well-known/openid-configurationを配信している必要がある |
client_id / client_secret | 内容OAuthクライアント登録から取得 |
token_endpoint_auth_method | 内容トークンエンドポイントへの認証方式。client_secret_basic・client_secret_post・private_key_jwtから選ぶ。未指定なら、IdPが公開している対応方式からclient_secret系の2つのどちらかを選ぶ |
client_assertion | 内容private_key_jwtのとき必須。private_key_pemとcertificate_pemを持つブロックで、v2.1.284以降。client_secretとは併用できない。手順はprivate_key_jwtで証明書認証にするにある |
allowed_email_domains | 内容このドメイン以外のid_tokenを拒否。email_verifiedが明示的にfalseのトークンは設定に関わらず常に拒否される |
allowed_groups | 内容指定したIdPグループのメンバーのみサインインを許可。ネストしたグループは展開されないため、サブグループを許可するにはそのサブグループ自体を列挙する |
groups_claim | 内容グループ所属を運ぶクレーム名。既定groups。Microsoft Entraはアプリロールをrolesで返す |
google_groups | 内容Google Workspaceのid_tokenにはグループクレームが無いため、Admin SDK Directory APIで代替取得する設定 |
email_claim | 内容メールアドレスを運ぶクレーム名。既定email。ADFSやEntra B2Cはupnやpreferred_usernameを使うことがある |
scopes | 内容OIDCスコープの完全上書き。既定[openid, profile, email, offline_access]。offline_accessを外すとリフレッシュトークンが発行されず、開発者はsession.ttl_hoursごとにブラウザサインインをやり直す |
use_pkce | 内容PKCE(S256)チャレンジの送信。既定true |
userinfo_fallback | 内容id_tokenにemail・groupsが欠けている場合に/userinfoから補う。既定false |
Okta・Microsoft Entra ID・Google Workspaceはそれぞれクレームの出方に癖があります。userinfo_fallback: trueが要るのは、Keycloakの軽量アクセストークン・Oktaの組織認可サーバー・ADFSの最小トークンのように、id_tokenにemailやgroupsが載らないIdPです。Oktaでも、https://example.okta.com/oauth2/defaultのようなカスタム認可サーバーならid_tokenにemailを載せられるので、fallbackは要りません。id_tokenが正で、userinfoは欠けた項目を埋めるだけです。Entraはグループ名でなくObject ID(GUID)を返します。managed.policies.match.groupsにはGUIDを書くか、アプリロール(rolesクレーム)を使います。Googleのid_tokenにはグループクレームが無いので、グループで絞るならgoogle_groupsが前提になります。ES256署名のIdPやKeycloakのブローカー構成は、署名方式とazpの設定で扱っています。
google_groupsを使うと、各ユーザーのグループのメールアドレスがグループクレームになります。そのためallowed_groupsとmanaged.policies.match.groupsには、グループ名でなくグループのメールアドレスを書きます。サービスアカウントのドメイン全体委任と、管理者ユーザーへのなりすまし(admin_email)も必要です。
google_groupsを使わないなら、サインインの絞り込みはoidc.allowed_email_domains、ポリシーの割り当てはmanaged.policies.match.email_domainで行います。Googleは標準のoffline_accessスコープも無視します。リフレッシュトークンが要るときは、scopesとextra_auth_params: { access_type: offline, prompt: consent }を組み合わせます。
groups_claimはフラットなキーのほか、/resource_access/gateway/rolesのようなRFC 6901のJSONポインターも受け付けます。ネストしたクレームからロールを読みたいIdPで使います。IdPへのリクエストをフォワードプロキシ経由にするoidc.use_proxyはv2.1.227以降です。プロキシ経由でしか外へ出られない環境では、環境変数CLAUDE_GATEWAY_PROXY_IS_EGRESS_BOUNDARY=1(v2.1.277以降)が別に要ります。こちらはYAMLのキーではありません。設定ファイルの側から住所チェックを緩められないようにするためです。有効にする条件とproxy側の拒否ルールはproxy経由だけの環境での設定にあります。
sessionとstore — トークン寿命とPostgres接続
session.jwt_secretはゲートウェイが発行するHS256ベアラートークンの署名鍵で、32バイト以上のエントロピーが必要です(openssl rand -base64 32で生成)。単一文字列だけでなく配列も受け付け、インデックス0が署名に使われ、配列内の全エントリが検証に使われます。新しい鍵を先頭に追加し、ttl_hours経過後に古い鍵を外すことでローテーションできます。ttl_hoursの既定は1で、IdPがリフレッシュトークンを発行できる場合はCLIが期限前に自動更新します。リフレッシュトークンが使えないIdPでは、毎時ブラウザに戻されるのを避けるため8や12に伸ばすのが実務的です。
store.postgres_urlはデバイスサインインフローの状態(ブラウザコールバックが書き込み、ポーリング中のCLIが読み取る)とレート制限カウンターを保持します。ゲートウェイは起動時と更新時に自分のスキーママイグレーションを実行するため、指定するロールにはテーブルの作成・変更権限が必要です。store.max_connectionsはレプリカ1台あたりのコネクションプールサイズで、既定は5と控えめです。支出上限を有効にすると推論リクエストごとにPostgresへの操作が増えるため、専用データベースで負荷がかかる場合はこの値を引き上げますが、その際は「レプリカ数 × この値」をデータベース側のmax_connections未満に収める必要があります。ゲートウェイを複数レプリカで動かしている場合、各レプリカがこの上限までコネクションを張るためです。
storeにはほかに、接続タイムアウトのconnect_timeout_seconds(1〜60、既定5、v2.1.274以降)と、Postgresが応答しなくなってから/readyzが正常を返し続ける秒数のreadiness_grace_seconds(0〜3600、既定0、v2.1.282以降)があります。どちらも古いゲートウェイは起動を拒否するので、レプリカを全部上げてから設定に足します。値の決め方はPostgres障害中も/readyzを保つ2つの設定にまとめています。
upstreams — 推論の転送先とフェイルオーバー
upstreamsは順序付きリストで、ゲートウェイは要求されたモデルを解決できる最初のupstreamへ転送します。5xx・429・401・403・404・タイムアウト・エンドポイント欠落(501)では次のupstreamへフェイルオーバーし、それ以外の4xxはリクエスト側の問題とみなしフェイルオーバーしません。404でのフェイルオーバーはv2.1.198以降が必要で、それより古いリリースは後続のupstreamがモデルを提供できても最初の404をそのままクライアントへ返していました。
対応する4つのクラウドupstream(Amazon Bedrock・Claude Platform on AWS・Google Cloud's Agent Platform・Microsoft Foundry)は起動時に1度だけクライアントが構築され、各SDKが内部でクレデンシャルを更新するため、クラウド認証情報のローテーションに再起動は不要です。一方Anthropic APIの静的APIキーやbearerは起動時に読み込まれる点が異なります。
Anthropic API upstreamの最小構成はapi_keyかoauth_tokenのどちらかです。api_keyはx-api-keyヘッダーを送り、Claude Consoleでのローテーションが必要です。oauth_tokenはAuthorization: Bearerを送る形式で、組織が長期キーではなく短命トークンを発行する場合に使います。この2つの代わりにWorkload Identity Federationも使え、ワークロードのOIDC JWTをファイルとしてマウントすると、ゲートウェイが短命bearerに自動交換します。
複数のupstreamでプロバイダーが重複する場合は、nameフィールドで区別する必要があります。開発者に返るエラーは、次の順で決まります。
全upstreamが失敗したときの応答の決まり方
- 1
フェイルオーバー対象外のステータス
そのupstreamの応答をそのまま返し、後続のupstreamは試しません。
- 2
全部が対象内のステータスで失敗
最後に見た
429を返します。 - 3
`429`が無い場合
最後の
401か403、なければ最後の404、さらに501の順で返します。 - 4
どれも無い場合
ゲートウェイ自身の
502(all upstreams failed (N attempted))を返します。Nは、モデルを提供しないため飛ばしたupstreamも含むupstreamsの全エントリ数です。
クラウドupstreamの400と413は、AnthropicのエラーエンベロープならそのままのメッセージがCLIに届きます。プロバイダー独自の形式ならcapability_rejected:トークンに置き換わります。たとえばBedrockのInput is too long for requested model.はcapability_rejected: prompt_too_longになり、Claude Codeはこのトークンを受けて自動でコンパクトします(v2.1.233以降)。アカウントIDやロールARNを含むエラー全文は、開発者ではなくゲートウェイの運用ログにだけ残ります。
forward_user_identity: trueを付けたupstreamは、開発者のメールを載せたリクエストへの429をフェイルオーバーせずそのまま返します(v2.1.267以降。それより前は429もフェイルオーバーしました)。自社運用のプロキシで開発者ごとの予算を守るための設定で、base_urlが既定のAnthropic APIのままだとゲートウェイは起動を拒否します(v2.1.233以降)。
admin・enforcement・pricing — 支出上限まわりの設定キー
adminブロックを設定すると/v1/organizations/spend_limitsのAdmin APIと/v1/messagesでのリアルタイム支出上限強制が有効になります。上限の設定方法そのものはAdmin API側の話題になるため、ここではgateway.yaml側の鍵に絞ります。
| フィールド | 内容 |
|---|---|
write_keys | 内容{id, key}の配列。このx-api-keyを持つクライアントは上限の一覧・設定・削除ができる。keyは32文字以上で、idはread_keysと合わせて重複不可。idは監査ログにadmin-key:<id>で残る |
read_keys | 内容読み取り専用のx-api-key配列。GET系すべて(上限の一覧、/effective、/audit)を読める |
admin_groups | 内容通常のゲートウェイJWTでフルアクセスできるIdPグループ名。人間の管理者向けで、機械にはAPIキーを使う。空のエントリがあると起動が止まる |
blocked_message | 内容上限超過時の429にそのまま追記されるメッセージ |
audit_retention_days | 内容既定365。監査ログの保持期間 |
spend_retention_months | 内容既定13。支出カウンターの保持期間 |
identity_retention_days | 内容既定90。開発者のメール・表示名・グループ(PII)の保持期間 |
group_limit_mode | 内容min(既定)かmax。複数グループに所属する開発者にどちらの上限を適用するか |
enforcement.fail_closed_on_errorは既定falseで、Postgresが落ちた場合は支出上限チェックがフェイルオープンし推論は止まりません。trueにするとフェイルクローズになり、上限超過者だけでなくストアに到達できない全開発者がブロックされます。この鍵はadminブロックが設定されている場合のみ有効で、設定なしにtrueにするとゲートウェイは起動を拒否します。
pricingは契約レートを反映するための上書きです。金額はUSDのままで、請求書ではなく見積もりの扱いです。前提はv2.1.227以降であることと、adminブロックがあることです。v2.1.268以降なら、ポリシーを1つ以上持つmanagedブロックでも足ります。どちらも無い状態でpricingだけ書くと、読む側がいないので起動を拒否します。
multiplierは既定1で、算出額全体に掛かります。0より大きく10以下で、1を超える値(割り増し。社内チャージバック用など)はv2.1.271以降です。overridesは{upstream, model, input, output, cache_read, cache_write}をトークン100万あたりのUSDで指定し、4つのレートはすべて必須で、0より大きく10000以下です。未知のupstream名、または同じupstreamで同じモデルを指す2行(同一モデルの表記違いも含む)は、起動失敗になります。
models — モデルIDカタログ
modelsブロックは/v1/modelsで配信され、upstreamごとにモデルIDを変換する管理者キュレーションのカタログです。非米国リージョンのAmazon Bedrock、Bedrockのprovisioned-throughput ARN、Microsoft Foundryのデプロイ名を使う場合は必須になります。auto_include_builtin_models: falseを設定すると、ここに列挙したモデルだけが公開されます。各エントリのupstream_model配下のキーは設定済みupstreamのname(既定はプロバイダー名)と一致する必要があり、一致しないキーは起動を失敗させます。
managed — IdPグループ別のポリシー
managed.policiesはIdPグループやメールドメインをキーにしたロールベースのアクセスポリシーで、順に評価され最初に一致したものが選ばれ、match: {}のキャッチオールへマージされます。マージ規則はキーの種類で異なります。
ベース(match: {})への重ね方は3種類
置き換え
availableModelsとpermissions.allow。個別ポリシーの値がベースを丸ごと置き換えます。和集合
permissions.deny・.ask、disabledMcpjsonServers、deniedMcpServers、blockedMarketplaces、hooksの各イベント配列。ベースと個別の両方が効きます。シャローマージ
env、modelOverrides、skillOverrides。個別ポリシーが書いたキーだけ上書きし、残りはベースを引き継ぎます。
availableModelsは/v1/messagesでもサーバー側で強制され、許可外のモデルは400になります。クライアントが何を送っても同じです。どのポリシーにも一致しないユーザーは、カタログの全モデルと管理設定なしというゲートウェイの既定で動きます。保証したい既定があるなら、最後にmatch: {}を置きます。マッチャーは次の4パターンです。
match: {}— 全認証済みユーザーに一致match: { groups: [a, b] }— 大文字小文字を区別する完全一致match: { email_domain: example.com }— 最後の@より後ろの部分、大文字小文字を無視。1ポリシーに書けるドメインは1つmatch: { groups: [a], email_domain: example.com }— 両方の条件を満たす場合のみ一致
managed:
policies:
- match: { groups: [eng-contractors] }
cli:
availableModels: [claude-sonnet-4-6]
- match: {} # キャッチオール(最後に置く)
cli:
availableModels: [claude-opus-4-8, claude-sonnet-4-6, claude-haiku-4-5]空のgroupsリストや空のエントリ、空のemail_domain、@・空白・カンマを含むemail_domainは、v2.1.232以降だと起動時にフィールド名付きで停止します。ゲートウェイは前後の空白と先頭の@を1つ取ってから検査するので、example.comと裸のドメインで書けば通ります。v2.1.232より前は起動できました。空のemail_domainはドメイン検査が飛ばされ、groupsも無いポリシーなら全員に一致しました。空のgroupsリストや@入りのemail_domainは誰にも一致しませんでした。
cliキーの中身はClaude Codeのmanaged-settings.jsonそのもので、CLIはこれをmanagedティアで適用しサーバー管理設定の代わりに使います。よく使われるキーは次の通りです。
| キー | 役割 |
|---|---|
availableModels | 役割モデル許可リスト |
permissions.deny / disableBypassPermissionsMode | 役割権限とbypassの禁止 |
env | 役割DISABLE_UPDATES等の環境変数注入 |
hooks | 役割組織全体のフック。コマンドは開発者のマシン上で実行されるため、ポリシー対象の全OSにパスが存在する必要がある |
hooks・承認が必要なenv変数・シェル実行系の設定は、開発者にセキュリティ承認ダイアログを出します。拒否するとそのセッションは終了し、ポリシーは適用されません。claude -pのような非対話の実行は、ダイアログを出せないため、その実行だけ設定を適用し、承認済みとしては記録しません。次の対話セッションで改めてダイアログが出ます。広いポリシーへフックを足すと、一致する開発者全員にダイアログが出ます。
Claude Desktopも同じゲートウェイに接続する場合はdesktopブロックをcliと並べて設定します。空のdesktop: {}だけでオプトインになり(ゲートウェイ側がv2.1.203以降であることが前提)、モデル一覧や無効化ツールリストはcliブロックから自動導出されます。desktopブロックを持たないポリシーにマッチした開発者は/user/bootstrapが404を返し、Desktopの接続設定が完了しません。ブロックの書き方と配布手順はClaude apps gatewayのdesktopブロックでClaude Desktopに設定を配るにまとめています。
telemetry — OTLPの信号別opt-in
telemetry.forward_toは転送先のリストで、各宛先はmetrics・logs・tracesを個別にopt-inでき、既定はmetricsのみです。メトリクスはトークン数・リクエスト数・レイテンシーの集計値ですが、ログとトレースは開発者のマシンで実行されたbashコマンドやツール入力、ファイルパスまで含みうるため、アクセス制御と保持ポリシーが要件を満たす宛先だけで有効にします。CLIは認証済みユーザーのuser.id・user.email・user.groupsをエクスポートに自動でスタンプするため、開発者側の追加設定なしに利用者単位の帰属が取れます。
forward_toのURLはhttps://が必須で、ゲートウェイ自身のループバック上のコレクターだけが例外です(CLAUDE_GATEWAY_ALLOW_LOOPBACK=1が必要)。telemetry.forward_toとlisten.public_urlを両方設定すると、ゲートウェイはCLAUDE_CODE_ENABLE_TELEMETRY=1を含む6つの環境変数を接続済みクライアントへ/managed/settings経由で送ります。中身はOTEL_METRICS_EXPORTER・OTEL_LOGS_EXPORTER・OTEL_TRACES_EXPORTER(その信号を有効にした宛先があればotlp、なければnone)、OTEL_EXPORTER_OTLP_ENDPOINT(=public_url)、OTEL_EXPORTER_OTLP_PROTOCOL=http/protobufです。v2.1.265より前は、どの宛先も有効にしていない信号にも3つともotlpを送っていました。
開発者がローカルで設定したOTELの変数や宛先は、この配信値に負けます。ゲートウェイを迂回して自分のコレクターへ送ることはできません。トレースはCLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1も要りますが、ゲートウェイは送らないので、managed policyのenvブロックに書きます。トレースを送らせたくないグループのポリシーには0を書きます。
HTTPチューニングの4ブロック
access_control・limits・timeouts・rate_limitsはHTTPサーフェスを微調整する任意ブロックで、既定値のままでほとんどのデプロイに対応できます。
| ブロック | キー | 既定 | 内容 |
|---|---|---|---|
access_control | キーallow_cidrs / deny_cidrs | 既定空 | 内容クライアントIPによる許可・拒否。deny_cidrsが先に評価される。allow_cidrsを1件でも設定するとゲートウェイはdefault-denyになり、列挙外の全クライアントが即座に拒否される点に注意 |
limits | キーmax_request_bytes | 既定32MiB | 内容リクエストボディの上限 |
timeouts | キーupstream_ttfb_ms | 既定120000 | 内容upstreamの応答ヘッダーを待つ最大時間。応答本文のストリームには時間制限がない。Anthropic直upstreamパスにのみ適用され、それ以外のプロバイダーは応答の開始まで最長1時間待つ |
limits | キーmax_request_header_bytes / max_url_length | 既定未設定 | 内容設定すると、超過したリクエストに431(ヘッダー)/414(URL)を返す |
rate_limits | キーdevice_authorization.max / .window_seconds | 既定30 / 600 | 内容デバイス認可エンドポイントへのIP単位レート制限。共有の出口IPやNATの内側に大きな組織がいる場合に引き上げる |
rate_limits | キーdevice_verify.max / .window_seconds | 既定10 / 600 | 内容/deviceでのユーザーコード入力へのIP単位制限。他人のコードの推測を防ぐ。推論(/v1/messages)には適用されない |
/healthzと/readyzはallow_cidrsの対象外で、アクセスを絞ったリスナーでもヘルスチェックは動き続けます。
allow_cidrsが空のままだと、誰からのアクセスも通ります。その状態ではブート時に警告が出て、10.0.0.0/8・172.16.0.0/12・192.168.0.0/16などの私的レンジだけを許可するよう促されます。ゲートウェイは開発者のマシンでコマンドを実行させる管理設定を配れるので、到達できる範囲の絞り込みが実質的な防御線です。ロードバランサーがtrusted_proxiesに載っていないと、ゲートウェイには中継機の私的アドレスしか見えず、この警告も私的レンジ限定の許可リストも意図どおりに働きません。
managedのマージ規則はなぜキーの種類で挙動を変えるのか
許可リストは置き換え、拒否リストは和集合、レコード型はシャローマージという3種類の異なる規則は、一見複雑に見えますが、組み合わせると制限を緩めにくい方向に働きます。もし全キーが単純な置き換えだった場合、個別グループ向けのポリシーを書いた担当者が組織全体のpermissions.denyを意図せず上書きし、監査フックが特定のグループだけ効かなくなるという事故が起きます。拒否リストとフック配列だけを和集合にすることで、個別ポリシーは「制限を追加できるが、組織のベースラインを緩められない」という一方向の権限になります。
一方でavailableModelsのような許可リストを和集合にしてしまうと逆の問題が起きます。契約者グループにclaude-sonnet-4-6だけを許可したつもりが、ベースのmatch: {}が持つ全モデル許可リストと合算され、意図した制限が効かなくなるからです。許可リストは制限を狭める用途、拒否リストとフックは制限を広げない用途と考えると、置き換えと和集合の使い分けが一貫していることが分かります。
matcher(groups・email_domain)の空値やフォーマット違反をv2.1.232からboot時エラーに変えた変更も、同じ方向に働きます。それ以前は空のemail_domain(groupsなし)が「全員にマッチする」という意図しない挙動を引き起こしていましたが、これは設定ミスをサイレントな過剰許可として処理していたことになります。boot失敗に変えたことで、設定ミスは「動くが間違っている」状態から「動かない」状態に変わり、運用担当者が気づかないまま過剰な権限を配布するリスクを構造的に減らしています。
まとめ
gateway.yamlは5つの必須セクションだけで最小構成が動きますが、実運用ではadminでの支出上限有効化、managedでのグループ別ポリシー、telemetryでの信号別opt-inまで組み合わせることになります。新しいキーの多くはバージョン依存で、古いゲートウェイは未知のキーとして起動を拒否します。レプリカを全部上げてからキーを足す順序を守ってください。ロールバックするときは、古い版が知らないキーを先に設定から外してから戻します。本番デプロイの具体的な手順はデプロイと運用にまとめています。