Istio と Identity Platform を使用して Cloud Run for Anthos サービスのエンドユーザーの認証を行う

このチュートリアルでは、Istio 認証ポリシーIdentity Platform を使用して Cloud Run for Anthos on Google Cloud にデプロイされたアプリケーションのエンドユーザーを認証する方法について説明します。認証に Istio を使用するため、アプリケーション コードに認証ロジックを組み込む必要はありません。この分離により、アプリケーション コードと認証ポリシーを別々のチームで担当できます。また、複数のアプリケーションやサービスに認証ポリシーを適用できます。

はじめに

Cloud Run for Anthos は、GKE にアプリケーションと機能をデプロイしてサービスを提供するための、デベロッパー第一の環境を用意するプラットフォームです。これにより、開発チームに、サーバーレスの開発環境、必要に応じた自動スケーリング、Blue/Green デプロイ用のルーティングとトラフィック管理などが提供されます。Cloud Run for Anthos は、オープンソース プロジェクトの Istio と Knative を基盤とし、Cloud Logging などの Google Cloud プロダクトと統合されています。

Istio では、認証ポリシーに従って JSON ウェブトークン(JWT)を検証し、受信リクエストの認証を行います。認証ポリシーは、名前空間内のすべてのサービスまたは特定の名前付きサービスに適用できます。ポリシーには、特定の HTTP リクエストパスを追加また除外できます。たとえば、公開サイトのアセットやヘルスチェック エンドポイントへの未認証アクセスを許可できます。このチュートリアルでは、Istio Ingress Gateway が認証ポリシーを適用します。

次の図に、このチュートリアルで説明する認証フローを示します。

Istio が受信リクエストを認証

このチュートリアルでは、エンドユーザーのログインに Identity Platform を使用しますが、OpenID Connect をサポートするほかのプロバイダを採用することもできます。たとえば、Google ログインFirebase Authentication、また、Auth0GluuOktaPing Identity などのサードパーティ サービスを使用できます。OpenID Connect の実装を独自にデプロイすることもできます。

目標

  • Cloud Run アドオンを使用して GKE クラスタを作成する。
  • Identity Platform を設定する。
  • 公開のフロントエンドとバックエンドの API で構成されるサンプル アプリケーションをデプロイする。
  • バックエンド API の認証ポリシーを追加する。
  • 認証を検証する。

費用

このチュートリアルでは、課金対象である次の Google Cloud コンポーネントを使用します。

料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを生成できます。新しい Google Cloud ユーザーは無料トライアルをご利用いただけます。

このチュートリアルを終了した後、作成したリソースを削除すると、それ以上の請求は発生しません。詳しくは、クリーンアップをご覧ください。

始める前に

  1. Google アカウントにログインします。アカウントがない場合は、新しいアカウントを登録します。
  2. Cloud Console で、[VM インスタンス] ページに移動します。

    プロジェクト セレクタのページに移動

  3. Cloud プロジェクトを選択または作成します。

  4. Google Cloud プロジェクトに対して課金が有効になっていることを確認します。プロジェクトに対して課金が有効になっていることを確認する方法を学習する

  5. Cloud Build、Cloud Run、Container Analysis、Google Kubernetes Engine API、Cloud APIs を有効にします。

    API を有効にする

環境を初期化する

このセクションでは、チュートリアルの後半で使用する環境変数と gcloud のデフォルトを設定します。

  1. Cloud Console の [プロジェクトの選択] のプルダウンから、使用するプロジェクトを選択します。

  2. Cloud Shell を開きます。

    Cloud Shell に移動

    このチュートリアルでは、コマンドの実行はすべて Cloud Shell で行います。

  3. このチュートリアルで使用する Compute Engine ゾーンと GKE クラスタ名の環境変数を定義します。また、gcloud のデフォルトも定義します。

    ZONE=us-central1-c
    CLUSTER=cloud-run-gke-auth-tutorial
    
    gcloud config set compute/zone $ZONE
    gcloud config set run/cluster $CLUSTER
    gcloud config set run/cluster_location $ZONE
    

    ゾーンとクラスタ名は、必要に応じて変更できます。

GKE クラスタを作成して Cloud Run を有効にする

  • Cloud Run アドオンを使用して GKE クラスタを作成します。

    gcloud beta container clusters create $CLUSTER \
        --addons HorizontalPodAutoscaling,HttpLoadBalancing,CloudRun \
        --enable-ip-alias \
        --enable-stackdriver-kubernetes \
        --machine-type n1-standard-2
    

パブリック IP アドレスを確認する

Cloud Run for Anthos on Google Cloud は、Istio Ingress Gateway のパブリック IP アドレスで外部サービスを公開します。

  1. istio-ingress Kubernetes Service の外部 IP アドレスの作成ステータスを確認します。

    kubectl get services istio-ingress -n gke-system --watch
    

    EXTERNAL-IP 値が <pending> から IP アドレスに変化するまで待機します。NotFound エラーが発生した場合は、少し待ってからコマンドを再度実行します。待機状態を解除するには、Control+C キーを押します。

  2. Istio Ingress Gateway のパブリック IP アドレスを格納する環境変数を作成します。

    export EXTERNAL_IP=$(kubectl get services istio-ingress \
        --namespace gke-system \
        --output jsonpath='{.status.loadBalancer.ingress[0].ip}')
    
  3. Istio Ingress Gateway のパブリック IP アドレスを表示します。このアドレスは後で必要になります。

    echo $EXTERNAL_IP
    

注: このチュートリアルでは、IP アドレスと暗号化されていない HTTP を使用してサービスにアクセスします。本番環境で設定する場合は、次の両方を行うことをおすすめします。

  • ドメイン名に DNS A レコードを作成し、その値を Istio Ingress Gateway のパブリック IP アドレスに設定します。設定した場合は、このチュートリアルの残りの部分で $EXTERNAL_IP を DNS A レコードのドメイン名に置き換えてください。Cloud DNS を使用している場合は、クイックスタート ガイドの説明に従ってください。他のプロバイダを使用している場合は、そのドキュメントをご覧ください。
  • Cloud Run for Anthos on Google Cloud サービスの HTTPS を有効にします

Identity Platform を設定する

  1. Cloud Shell を開いたままにして Google Cloud Marketplace にアクセスし、新しいウェブブラウザ ウィンドウで Identity Platform を有効にします。

    Google Cloud Marketplace の Identity Platform に移動

  2. [プロジェクトの選択] プルダウンから、Identity Platform を設定する Google Cloud プロジェクトを選択します。GKE クラスタと Identity Platform を別々の Google Cloud プロジェクトに設定できます。便宜上、このチュートリアルでは同じプロジェクトを使用します。

  3. [Identity Platform を有効化] をクリックします。

    次に、Cloud Console で [Identity Platform] > [プロバイダ] ページへ移動します。

  4. [プロバイダ] ページで [プロバイダを追加] をクリックします。

  5. [プロバイダの選択] プルダウンで下にスクロールし、[メール / パスワード] を選択します。独自のアプリケーションの場合は、有効にするプロバイダを選択できます。

  6. [有効] が選択されていることを確認します。

  7. [パスワードを使用しないログインを許可する] をオフにします。

  8. [保存] をクリックします。

  9. [ID プラットフォーム] > [設定] ページの順に移動します。

  10. [セキュリティ] タブをクリックします。

  11. [ドメインを追加] をクリックします。[承認済みドメインの追加] ダイアログが開きます。

  12. [ドメイン] ボックスに、前のセクションで確認した Istio Ingress Gateway のパブリック IP アドレス($EXTERNAL_IP)を入力します。

  13. [追加] をクリックします。ダイアログが閉じます。入力した IP アドレスが [承認済みドメイン] テーブルに表示されます。

  14. [ID プラットフォーム] > [設定] ページの順に移動し、[保存] をクリックします。

テストユーザーを作成する

  1. Cloud Console で、[Identity Platform] > [ユーザー] ページに移動します。
  2. [ユーザーを追加] をクリックして、このチュートリアルのテストユーザーを追加します。[ユーザーを追加] ダイアログが開きます。
  3. [メール] ボックスに、エンドユーザーのメールアドレスを入力します。テストのために、架空のメールアドレスでもかまいません。このチュートリアルでは、user@example.com を使用します。
  4. [パスワード] ボックスに、テストユーザーのパスワードを入力します。このパスワードは忘れないでください。後で必要になります。
  5. [追加] をクリックして、ユーザーの追加を完了します。ダイアログが閉じて、[ID プラットフォーム] > [ユーザー] ページに戻ります。

サンプル アプリケーションを作成する

2 つのサービスを提供するサンプル アプリケーションをデプロイします。1 つのサービスはパブリック フロントエンドのユーザー インターフェース、もう 1 つはバックエンド API です。

  1. [ID プラットフォーム] > [ユーザー] ページで、ウィンドウの右側にある [アプリケーション設定の詳細] リンクをクリックします。[アプリケーションの構成] ダイアログが開きます。

  2. apiKey の値をハイライト表示してクリップボードにコピーします(Chrome OS / Linux / Windows では Ctrl+C、macOS では Cmd+C を使用)。

  3. [閉じる] をクリックして、[アプリケーションの構成] ダイアログを閉じます。

  4. Cloud Shell で、apiKey を格納する環境変数を作成します。ここで、api-key は、[アプリケーションの構成] ダイアログの apiKey です。

    export AUTH_APIKEY=api-key
    
  5. authDomain の環境変数を作成します。

    export AUTH_DOMAIN=$GOOGLE_CLOUD_PROJECT.firebaseapp.com
    
  6. GitHub から Cloud Run サンプル リポジトリのクローンを作成します。

    git clone https://github.com/GoogleCloudPlatform/cloud-run-samples.git
    
  7. このチュートリアルのファイルを含むディレクトリに切り替えます。

    cd cloud-run-samples/identity-platform/gke
    
  8. フロントエンドの JavaScript ファイルの Identity Platform の変数を置き換えます。

    envsubst < frontend/index.template.js > frontend/index.js
    

サンプル アプリケーションをデプロイする

  1. Cloud Build を使用して、サンプル アプリケーションのコンテナ イメージを 2 つ作成します。1 つはフロントエンド用、もう 1 つはバックエンド用です。

    gcloud builds submit -t gcr.io/$GOOGLE_CLOUD_PROJECT/cloud-run-gke-auth-frontend frontend
    
    gcloud builds submit -t gcr.io/$GOOGLE_CLOUD_PROJECT/cloud-run-gke-auth-backend backend
    

    Cloud Build がイメージを Container Registry に保存します。

  2. GKE クラスタで、publicapi という 2 つの名前空間を作成します。

    kubectl create namespace public
    
    kubectl create namespace api
    
  3. フロントエンド コンテナのイメージを public 名前空間のサービスとして Cloud Run for Anthos on Google Cloud にデプロイします。

    gcloud run deploy frontend \
        --namespace public \
        --image gcr.io/$GOOGLE_CLOUD_PROJECT/cloud-run-gke-auth-frontend \
        --platform gke
    
  4. バックエンド コンテナのイメージを api 名前空間のサービスとして Cloud Run for Anthos on Google Cloud にデプロイします。

    gcloud run deploy backend \
        --namespace api \
        --image gcr.io/$GOOGLE_CLOUD_PROJECT/cloud-run-gke-auth-backend \
        --platform gke
    
  5. URI パスでリクエストを転送する Istio 仮想サービスを作成します。

    kubectl apply -f istio/virtualservice.yaml
    

    この仮想サービスは、URI パスが /api/ で始まるリクエストをバックエンド API に転送し、それ以外のリクエストをフロントエンドのユーザー インターフェースに転送します。

    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: cloud-run-gke-auth
    spec:
      gateways:
      - gke-system-gateway.knative-serving.svc.cluster.local
      hosts:
      - "*"
      http:
      - match:
        - uri:
            prefix: "/api/"
        rewrite:
          authority: backend.api.svc.cluster.local
        route:
        - destination:
            host: cluster-local-gateway.gke-system.svc.cluster.local
      - match:
        - uri:
            prefix: "/"
        rewrite:
          authority: frontend.public.svc.cluster.local
        route:
        - destination:
            host: cluster-local-gateway.gke-system.svc.cluster.local
  6. バックエンド API への未認証アクセスが成功しているかどうか確認します。

    curl -si $EXTERNAL_IP/api/secure.json | head -n1
    

    出力が「HTTP/1.1 200 OK」でない場合は、1 分ほど待ってからもう一度試してください。

Istio 認証ポリシーを追加する

  1. Istio 認証ポリシーを作成します。

    envsubst < istio/authenticationpolicy.template.yaml | kubectl apply -f -
    
    apiVersion: authentication.istio.io/v1alpha1
    kind: Policy
    metadata:
      name: api-origin-auth
      namespace: gke-system
    spec:
      targets:
      - name: istio-ingress
        ports:
        - number: 80
        - number: 443
      origins:
      - jwt:
          issuer: "https://securetoken.google.com/$GOOGLE_CLOUD_PROJECT"
          audiences:
          - "$GOOGLE_CLOUD_PROJECT"
          jwksUri: "https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com"
          trigger_rules:
          - excluded_paths:
            - exact: /api/healthz
            included_paths:
            - prefix: /api/
      principalBinding: USE_ORIGIN

    このポリシーは、URI パスが /api/ で始まるリクエストを認証します(ただし、/api/healthz は除きます)。前のセクションでデプロイした Istio 仮想サービスのルーティング ルールのため、このポリシーはバックエンド API に対するリクエストを認証します。

  2. ポリシーが有効になるまでに少し時間がかかることがあります。次のコマンドを実行して、コンソールに「HTTP/1.1 401 Unauthorized」が表示されるまで待ちます。

    while sleep 2; do
      curl -si $EXTERNAL_IP/api/secure.json | head -n1
    done
    

    最初は、出力に「HTTP/1.1 200 OK」と「HTTP/1.1 401 Unauthorized」が交互に表示されることがあります。この状況は、IstioEnvoy Proxy の結果整合性が原因で発生します。

    コンソールに「HTTP/1.1 401 Unauthorized」だけが表示されたら、Ctrl+C キーを押して待機状態を解除します。

ソリューションのテスト

  1. ブラウザ ウィンドウでアドレス http://$EXTERNAL_IP/api/secure.json を開きます。ここで、$EXTERNAL_IP は、パブリック IP アドレスを確認するで確認した Istio Ingress Gateway のパブリック IP アドレスです。

    これは、バックエンド API に直接送信されるリクエストです。リクエストに Istio 認証ポリシーを満たす認証情報が含まれていないため、ブラウザ ウィンドウに「Origin authentication failed」というメッセージが表示されます。

  2. ブラウザ ウィンドウでアドレス $EXTERNAL_IP を開きます。ログイン フォームが表示されます。

  3. テストユーザーを作成するで作成したテストユーザーとしてログインします。

    このページには、テストユーザーのメールアドレスと「The secret message is: Hello World」というテキストが表示されます。メッセージが表示されるまでに少し時間がかかることがあります。

    ブラウザは、ログイン時に Identity Platform から取得したトークンを使用して、バックエンド API(/api/secure.json)への HTTP リクエストでメッセージを取得します。frontend/index.js ファイルで実装を確認します。詳細については、FirebaseUI ライブラリのドキュメントをご覧ください。

トラブルシューティング

このチュートリアルで問題が発生した場合は、次のドキュメントを確認することをおすすめします。

クリーンアップ

このチュートリアルで使用したリソースについて、Google Cloud アカウントに課金されないようにする手順は次のとおりです。

  1. Cloud Console で [リソースの管理] ページに移動します。

    [リソースの管理] ページに移動

  2. プロジェクト リストで、削除するプロジェクトを選択し、[削除] をクリックします。
  3. ダイアログでプロジェクト ID を入力し、[シャットダウン] をクリックしてプロジェクトを削除します。

リソースの削除

このチュートリアルで使用した Google Cloud プロジェクトを残しておく場合は、個々のリソースを削除します。

  1. GKE クラスタを削除します。

    gcloud container clusters delete $CLUSTER --async --quiet
    
  2. Container Registry でサンプル アプリケーションのコンテナ イメージを削除します。

    gcloud container images delete gcr.io/$GOOGLE_CLOUD_PROJECT/cloud-run-gke-auth-frontend \
        --force-delete-tags --quiet
    
    gcloud container images delete gcr.io/$GOOGLE_CLOUD_PROJECT/cloud-run-gke-auth-backend \
        --force-delete-tags --quiet
    
  3. Identity Platform でメール / パスワードの ID プロバイダを削除します。

    1. Cloud Console で [Identity Platform] > [プロバイダ] ページに移動します。

      [プロバイダ] ページに移動

    2. プロバイダのテーブルで [メール / パスワード] の ID プロバイダを見つけて、 をクリックします。

    3. 表示されたダイアログで、[削除] をクリックして確定します。

  4. テストユーザーを削除します。

    1. [ID プラットフォーム] > [ユーザー] ページに移動します。

      [ユーザー] ページに移動

    2. ユーザーのテーブルで user@example.com を見つけて、 をクリックします。

    3. 表示されたダイアログで、[削除] をクリックして確定します。

  5. 承認済みドメインを削除します。

    1. [ID プラットフォーム] > [設定] ページに移動します。

      [設定] ページに移動

    2. 承認済みドメインのテーブルで、Identity Platform を設定するで追加した IP アドレス($EXTERNAL_IP)を見つけて、 をクリックします。

    3. [保存] をクリックします。

次のステップ