フロントエンドアプリケーションが異なるドメインまたはポート上のバックエンドサービスに HTTP リクエストを送信すると、ブラウザはデフォルトでその応答をブロックします。オリジン間リソース共有 (CORS) を使用すると、どのオリジン、メソッド、ヘッダーからのアクセスをサービスが受け入れるかを制御できます。Service Mesh (ASM) では、corsPolicy ブロックを VirtualService に追加することで、Envoy サイドカーがネゴシエーションを自動的に処理します。アプリケーションコードの変更は不要です。
corsPolicy リファレンス
corsPolicy ブロックを VirtualService の http セクションに追加します。ASM はこのポリシーを Envoy サイドカーのレベルで適用し、シンプルリクエストとプレフライトリクエストの両方を自動的に処理します。
フィールド
| フィールド | 説明 |
|---|---|
allowOrigins | サービスへのアクセスを許可するオリジン。各エントリは、次のいずれかのマッチタイプを持つ StringMatch です:exact(完全一致)、prefix(プレフィックスベースの一致)、または regex(RE2 構文の正規表現)。認証情報なしのリクエストの場合、すべてのオリジンを許可するにはワイルドカード (*) を設定します。 |
allowMethods | クロスオリジンリクエストで許可される HTTP メソッド。Access-Control-Allow-Methods レスポンスヘッダーに対応します。 |
allowHeaders | クロスオリジンリクエストで許可されるリクエストヘッダー。Access-Control-Allow-Headers レスポンスヘッダーに対応します。 |
exposeHeaders | ブラウザがアクセス可能なレスポンスヘッダー。Access-Control-Expose-Headers レスポンスヘッダーに対応します。 |
maxAge | ブラウザがプレフライトレスポンスをキャッシュできる期間。Access-Control-Max-Age レスポンスヘッダーに対応します。例:"24h"。 |
allowCredentials | クロスオリジンリクエストに認証情報(クッキー、Authorization ヘッダー、TLS クライアント証明書)を含められるかどうかを指定します。 |
allowCredentials が true に設定されている場合、allowOrigins、allowHeaders、または allowMethods にワイルドカード (*) を使用しないでください。ブラウザは、認証情報とワイルドカード値を組み合わせた応答を拒否します。代わりに、明示的なオリジンを指定してください。
クロスオリジンアクセスのための CORS 有効化
以下の手順では、フロントエンドおよびバックエンドアプリケーションを個別のイングレスゲートウェイ上にデプロイし、フロントエンドがオリジンをまたいでバックエンドにアクセスできるように CORS ポリシーを追加します。ワークフローは次のとおりです。
バックエンドおよびフロントエンドアプリケーションをデプロイします。
異なるオリジンをシミュレートするために、2 つのイングレスゲートウェイを作成します。
両方のアプリケーションのルーティングルールを設定します。
CORS がない状態でクロスオリジンリクエストが失敗することを確認します。
corsPolicyを追加し、リクエストが成功することを検証します。
前提条件
作業を開始する前に、次の要件を満たしていることを確認してください。
default名前空間およびfoo名前空間が作成されており、両方に対して自動サイドカープロキシ注入が有効になっていること。詳細については、「グローバル名前空間の管理」をご参照ください。
ステップ 1:アプリケーションのデプロイ
バックエンドアプリケーションのデプロイ
details.yamlという名前のファイルを作成します。default名前空間にアプリケーションをデプロイします。kubectl apply -f details.yaml -n default
フロントエンドアプリケーションのデプロイ
istio-cors-demo.yamlという名前のファイルを作成します。foo名前空間にアプリケーションをデプロイします。kubectl apply -f istio-cors-demo.yaml -n foo
ステップ 2:イングレスゲートウェイのデプロイ
フロントエンドとバックエンドを異なる IP アドレスでアクセス可能にして、異なるオリジンをシミュレートするため、2 つのイングレスゲートウェイを作成します。
ASM コンソール にログインします。左側のナビゲーションウィンドウで、Service Mesh > メッシュ管理 を選択します。
メッシュ管理 ページで、ASM インスタンスの名前をクリックします。左側のナビゲーションウィンドウで、ASM ゲートウェイ > イングレスゲートウェイ を選択します。
イングレスゲートウェイ ページで、作成 をクリックします。
作成 ページで、名前 に
ingressgatewayを設定します。クラスター ドロップダウンリストから対象のクラスターを選択します。CLB インスタンスタイプ に インターネットアクセス を設定します。CLB インスタンスの作成 でクラシックロードバランサー (CLB) インスタンスタイプを選択します。残りのパラメーターはデフォルトのままにして、作成 をクリックします。手順 3 と 4 を繰り返して、
ingressgateway2という名前の 2 番目のイングレスゲートウェイを作成します。
ステップ 3:ルーティングルールの作成
バックエンドアプリケーションへのトラフィックルーティング
detailsサービスをingressgatewayゲートウェイに関連付ける Istio ゲートウェイを作成します。ASM インスタンスの詳細ページで、左側のナビゲーションウィンドウで ASM ゲートウェイ > ゲートウェイ を選択します。ゲートウェイ ページで、YAML から作成 をクリックします。
名前空間 ドロップダウンリストで default を選択し、次の YAML を貼り付けて 作成 をクリックします。
apiVersion: networking.istio.io/v1beta1 kind: Gateway metadata: name: bookinfo-gateway namespace: default spec: selector: istio: ingressgateway servers: - hosts: - '*' port: name: http number: 80 protocol: HTTP
バックエンド用の VirtualService を作成します。
ASM インスタンスの詳細ページで、左側のナビゲーションウィンドウから [トラフィック管理センター] > [VirtualService] を選択し、[YAML から作成] をクリックします。
名前空間 ドロップダウンリストで default を選択し、次の YAML を貼り付けて 作成 をクリックします。
apiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: bookinfo namespace: default spec: gateways: - bookinfo-gateway hosts: - '*' http: - match: - uri: prefix: /details route: - destination: host: details port: number: 9080
バックエンドにアクセスできることを確認します。
ingressgatewayゲートウェイの IP アドレスを取得します。詳細については、「イングレスゲートウェイの作成」をご参照ください。ブラウザで
http://<ingressgateway-ip>/details/2を開きます。
JSON 応答が表示されれば、バックエンドに到達可能であることが確認できます。
フロントエンドアプリケーションへのトラフィックルーティング
istio-cors-demoサービスをingressgateway2ゲートウェイに関連付ける Istio ゲートウェイを作成します。ASM インスタンスの詳細ページで、左側のナビゲーションウィンドウから [ASM ゲートウェイ] > [ゲートウェイ] を選択します。[YAML から作成] をクリックします。
名前空間 ドロップダウンリストで foo を選択し、次の YAML を貼り付けて 作成 をクリックします。
apiVersion: networking.istio.io/v1beta1 kind: Gateway metadata: name: istio-cors-demo-gateway namespace: foo spec: selector: istio: ingressgateway2 servers: - hosts: - '*' port: name: http number: 80 protocol: HTTP
フロントエンド用の VirtualService を作成します。
ASM インスタンス詳細ページの左側のナビゲーションウィンドウで、[トラフィック管理センター] > [VirtualService] を選択し、[YAML から作成] をクリックします。
名前空間 ドロップダウンリストで foo を選択し、次の YAML を貼り付けて 作成 をクリックします。
apiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: istio-cors-demo namespace: foo spec: gateways: - istio-cors-demo-gateway hosts: - '*' http: - route: - destination: host: istio-cors-demo port: number: 8000
CORS なしでクロスオリジンリクエストが失敗することの確認
ingressgateway2ゲートウェイの IP アドレスを取得します。詳細については、「イングレスゲートウェイの作成」をご参照ください。Google Chrome で
http://<ingressgateway2-ip>を開きます。ページの URL 入力欄に
http://<ingressgateway-ip>/details/2を入力し、送信 をクリックします。
Chrome DevTools を開きます (
F12または その他のツール > デベロッパーツール)。コンソールに CORS エラーが表示されます。ingressgateway2上のフロントエンドがingressgateway上のバックエンドにクロスオリジンリクエストを送信していますが、まだ CORS ポリシーが存在しないためです。
ステップ 4:CORS ポリシーの追加
ASM コンソール にログインします。左側のナビゲーションウィンドウで、Service Mesh > メッシュ管理 を選択します。
ASM インスタンスの名前をクリックします。トラフィック管理センター > VirtualService を左側のナビゲーションウィンドウで選択します。
「仮想サービス」ページで、
bookinfo仮想サービスを見つけ、[操作] 列の [YAML] をクリックします。編集 ダイアログボックスで、
httpフィールドに次のcorsPolicyブロックを追加し、OK をクリックします。<ingressgateway2-ip>は、ingressgateway2ゲートウェイの実際の IP アドレスに置き換えてください。corsPolicy: allowCredentials: false allowMethods: - POST - GET allowOrigins: - prefix: 'http://<ingressgateway2-ip>' maxAge: 24h
ステップ 5:CORS ポリシーの検証
Google Chrome で
http://<ingressgateway2-ip>を開きます。URL 入力欄に
http://<ingressgateway-ip>/details/2を入力し、送信 をクリックします。これでフロントエンドがバックエンドからデータを正常に取得できるようになります。CORS ポリシーが有効になっています。
CORS の仕組み
CORS は HTTP ヘッダーを使用して、サーバーがどのオリジン、メソッド、ヘッダーを許可するかを宣言できるようにします。リクエストの内容に応じて、ブラウザはシンプルリクエストまたはプレフライトリクエストのいずれかのフローに従います。
シンプルリクエスト
ブラウザは Origin ヘッダーを追加して、リクエストを直接送信します。サーバーはオリジンを評価し、応答に Access-Control-Allow-Origin および Access-Control-Allow-Methods ヘッダーを返します。
GET /api/data HTTP/1.1
Host: api.example.com
Origin: https://app.example.com
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POSTリクエストがシンプルリクエストとして扱われるには、次の条件がすべて満たされている必要があります。
HTTP メソッドが
GET、HEAD、またはPOSTであること。Content-Typeヘッダー(設定されている場合)がtext/plain、application/x-www-form-urlencoded、またはmultipart/form-dataであること。手動で設定されたヘッダーが Fetch 標準で定義された CORS セーフリストヘッダーのみであること:
Accept、Accept-Language、Content-Language、およびContent-Type(上記の値に限定)。
プレフライトリクエスト
上記の条件のいずれかが満たされない場合、ブラウザは実際のリクエストの前にプレフライトリクエスト(HTTP OPTIONS リクエスト)を送信します。プレフライトにより、サーバーが意図したメソッド、ヘッダー、オリジンを受け入れるかどうかが検証されます。
OPTIONS /api/data HTTP/1.1
Host: api.example.com
Origin: https://app.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Max-Age: 86400サーバーがリクエストを許可する場合、ブラウザは実際のクロスオリジンリクエストを続行します。許可されない場合は、ブラウザがそれをブロックします。
セキュリティに関する考慮事項
認証情報を使用する際にワイルドカードオリジンを避ける。
allowCredentialsがtrueの場合、ブラウザはAccess-Control-Allow-Originが*に設定された応答を拒否します。代わりに、明示的なオリジンを指定してください。許可するメソッドを制限する。 サービスに必要な HTTP メソッドのみを許可します。
DELETEやPATCHは、必要でない限り追加しないでください。適切な
maxAgeを設定する。 プレフライトレスポンスをキャッシュすることで、繰り返されるクロスオリジンリクエストの遅延を軽減できますが、キャッシュ期間が長すぎるとポリシー変更の反映が遅れます。ほとんどのユースケースでは、1h~24hの値が適しています。regexよりもexactまたはprefixマッチを優先する。 広範な正規表現パターンは、意図せず信頼できないオリジンを許可してしまう可能性があります。本番環境にデプロイする前に、正規表現パターンをテストしてください。