IAP を Cloud Service Mesh と統合する

このガイドでは、Identity-Aware Proxy(IAP)を Cloud Service Mesh と統合する方法について説明します。IAP を Cloud Service Meshh と統合すると、Google の BeyondCorp の原則に基づいて、サービスに安全にアクセスできるようになります。IAP は、ユーザー ID とリクエストのコンテキストを確認し、ユーザーにアプリケーションまたはリソースへのアクセスを許可するかどうかを決定します。IAP を Cloud Service Mesh と統合すると、次のメリットがあります。

  • Cloud Service Mesh で実行されるワークロードに対するコンテキストアウェア アクセスの制御。発信元リクエストの属性(ユーザー ID、IP アドレス、デバイスの種類など)に基づいて、きめ細かいアクセス ポリシーを設定できます。リクエスト URL のホスト名とパスに基づいて、アクセス ポリシーと制限を組み合わせることができます。

  • Cloud Service Mesh 認証におけるコンテキストアウェア クレームのサポート。

  • Google Cloud ロードバランサを介したアプリケーションに対する、スケーラブルで安全かつ可用性の高いアクセス。高パフォーマンスの負荷分散では、組み込みの保護機能で分散型サービス拒否(DDoS)攻撃を防ぎ、グローバルなエニーキャスト IP アドレスを使用できます。

前提条件

依存ツールのインストールとクラスタの検証の手順に沿って、次の操作を行います。

さらに、このガイドでは、次の準備ができていることを前提としています。

Anthos Service Mesh によるクラスタの設定

このセクションでは、新しくインストールされた Cloud Service Mesh とアップグレードの IAP 統合を設定する方法について説明します。

新規インストール

  1. iap.googleapis.com を有効にします。次のコマンドで、PROJECT_ID を Cloud Service Mesh をインストールするプロジェクトに置き換えます。

    gcloud services enable \
      --project=PROJECT_ID \
      iap.googleapis.com
    
  2. 更新するクラスタに --addons=HttpLoadBalancing オプションを設定する必要があります。HttpLoadBalancing アドオンを使用すると、クラスタの HTTP(L7)負荷分散コントローラが有効になります。次のコマンドを実行し、Cloud Service Mesh に必要なオプションを使用してクラスタを更新します。デフォルトのゾーンまたはリージョンを設定済みでない限り、コマンドでリージョン(--region=REGION)またはゾーン(--zone=ZONE)を指定する必要があります。

    gcloud container clusters update CLUSTER_NAME \
      --project=PROJECT_ID \
      --update-addons=HttpLoadBalancing=ENABLED
    
  3. デフォルトでは、iap-operator.yaml ファイルのポート 31223 がステータス ポート、ポート 31224 が HTTP ポートとして設定されています。31223 ポートがすでにクラスタで使用されている場合は、次のコマンドを実行して別のステータス ポートを設定します。

    kpt cfg set asm gcloud.container.cluster.ingress.statusPort STATUS_PORT
    

    31224 がすでにクラスタで使用されている場合は、次のコマンドを実行して別の HTTP ポートを設定します。

    kpt cfg set asm gcloud.container.cluster.ingress.httpPort HTTP_PORT
    
  4. デフォルトの機能と Mesh CA をインストールするの手順に沿って、Google 提供のスクリプトを使用して Cloud Service Mesh をインストールします。スクリプトの実行時に、次のオプションを指定します。

    --option iap-operator
    

    例:

    ./asmcli install \
      --project_id "PROJECT_ID" \
      --cluster_name "CLUSTER_NAME" \
      --cluster_location "CLUSTER_LOCATION" \
      --fleet_id FLEET_PROJECT_ID \
      --output_dir DIR_PATH \
      --enable_all \
      --option iap-operator
    

    Cloud Service Mesh のインストール時に、iap-operator.yaml ファイルが istio-ingressgateway サービスの type フィールドを NodePort に設定します。これにより、サービス メッシュ上の特定のポートを開くようにゲートウェイが構成されます。また、ドメイン名に送信されたトラフィックがこのポートに転送されるようにロードバランサを設定できます。

  5. マネージド Cloud Service Mesh をインストールする場合は、次の手順も完了してください。

    1. istio-system 名前空間にリビジョン ラベルを追加します。

    2. IAP 向け Istio Ingress ゲートウェイ サービス仕様をダウンロードし、名前を iap_operator.yaml にします。

    3. Ingress を NodePort Service としてインストールします。詳細については、IstioOperator から移行するをご覧ください。

      asmcli experimental mcp-migrate-check -f iap_operator.yaml
      
      istioctl install -f /asm-generated-configs/gateways-istiooperator/"GATEWAY_NAME".yaml
      

Cloud Service Mesh のインストール後、このガイドに戻り、次のセクションに進んで IAP との統合を設定します。

アップグレード

このセクションでは、次のアップグレードのユースケースについて説明します。

  • IAP 統合はすでにセットアップ済みで、Cloud Service Mesh をアップグレードする。この例では、プロジェクトで iap.googleapis.com が、クラスタで HttpLoadBalancing アドオンがすでに有効になっています。手順 3 に進んで asm パッケージをダウンロードし、Cloud Service Mesh をアップグレードします。

  • Cloud Service Mesh をアップグレードし、初めて IAP との統合を設定する。この場合、以下の手順をすべて完了し、Cloud Service Mesh をアップグレードします。アップグレードが完了したらこのガイドに戻り、統合を完了します。

  1. iap.googleapis.com を有効にします。次のコマンドで、PROJECT_ID を Cloud Service Mesh をインストールするプロジェクトに置き換えます。

    gcloud services enable \
      --project=PROJECT_ID \
      iap.googleapis.com
    
  2. 更新するクラスタに --addons=HttpLoadBalancing オプションを設定する必要があります。HttpLoadBalancing アドオンを使用すると、クラスタの HTTP(L7)負荷分散コントローラが有効になります。次のコマンドを実行し、Cloud Service Mesh に必要なオプションを使用してクラスタを更新します。デフォルトのゾーンまたはリージョンを設定済みでない限り、コマンドでリージョン(--region=REGION)またはゾーン(--zone=ZONE)を指定する必要があります。

    gcloud container clusters update CLUSTER_NAME \
      --project=PROJECT_ID
      --update-addons=HttpLoadBalancing=ENABLED
    
  3. 機能している既存の HTTP Cloud ロードバランサを更新する場合は、次のコマンドを実行して既存の http とステータス ポートを保持してください。

    kpt cfg set asm gcloud.container.cluster.ingress.httpPort $(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
    
    kpt cfg set asm gcloud.container.cluster.ingress.statusPort $(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="status-port")].nodePort}')
    
  4. Cloud Service Mesh のアップグレードの手順に沿って Google 提供のスクリプトを使用し、Cloud Service Mesh をアップグレードします。

    Cloud Service Mesh のアップグレード時に、iap-operator.yaml ファイルが istio-ingressgateway サービスの type フィールドを NodePort に設定します。これにより、サービス メッシュ上の特定のポートを開くようにゲートウェイが構成されます。また、ドメイン名に送信されたトラフィックがこのポートに転送されるようにロードバランサを設定できます。

    デフォルトでは、iap-operator.yaml ファイルのポート 31223 がステータス ポート、ポート 31224 が HTTP ポートとして設定されています。

    スクリプトの実行時に、次のオプションを指定します。

    --option iap-operator
    

    例:

    ./asmcli install \
      --project_id "PROJECT_ID" \
      --cluster_name "CLUSTER_NAME" \
      --cluster_location "CLUSTER_LOCATION" \
      --fleet_id FLEET_PROJECT_ID \
      --output_dir DIR_PATH \
      --enable_all \
      --option iap-operator
    
  5. ワークロードの自動サイドカー プロキシ インジェクションをトリガーして、アップグレードを完了します。詳しくは、ワークロードのデプロイと再デプロイをご覧ください。

    アップグレードの完了後、このガイドに戻り、次のセクションに進んで IAP との統合を設定します。

静的 IP アドレスの予約と DNS の構成

Identity-Aware Proxy を Cloud Service Mesh と統合するには、Google Cloud HTTP(S) ロードバランサを設定する必要があります。この設定では、静的 IP アドレスを参照するドメイン名が必要です。静的外部 IP アドレスを予約すると、明示的に解放するまで、このアドレスを無期限でプロジェクトに割り当てることができます。

  1. 静的外部 IP アドレスを予約します。

    gcloud compute addresses create example-static-ip --global
    
  2. 静的 IP アドレスを取得します。

    gcloud compute addresses describe example-static-ip --global
    
  3. ドメイン名登録事業者で、静的 IP アドレスを含む完全修飾ドメイン名(FQDN)を構成します。通常、DNS 設定に A レコードを追加します。FQDN の A レコードを追加する構成手順と用語は、使用するドメイン名登録事業者によって異なります。

    DNS 設定が反映されるまでに、24~48 時間かかります。このガイドに従って引き続き設定を行うことはできますが、DNS 設定が反映されるまで設定のテストはできません。

サンプル アプリケーションのデプロイ

IAP を有効にする前に、GKE クラスタでアプリケーションを実行して、すべてのリクエストに ID があることを確認する必要があります。このガイドでは、Bookinfo サンプルを使用して、HTTP(S) ロードバランサを設定して IAP を有効にします。

Bookinfo をデプロイします。ロードバランサをデプロイするまで、Bookinfo アプリケーションは GKE クラスタの外部(ブラウザなど)からアクセスできません。

外部リクエスト

Bookinfo のゲートウェイ リソース(samples/bookinfo/networking/bookinfo-gateway.yaml で定義)は、事前構成の istio-ingressgateway を使用します。Cloud Service Mesh をデプロイするときに、istio-ingressgatewayNodePort を指定しました。これにより、サービス メッシュで特定のポートが開きます。クラスタ内のノードには外部 IP アドレスが設定されていますが、クラスタの外部からのリクエストは Google Cloud ファイアウォール ルールによってブロックされます。IAP を使用すると、ロードバランサを使用してアプリケーションを公共のインターネットに公開できます。IAP をバイパスするファイアウォール ルールを使用してノードアドレスを公開しないでください。

Bookinfo にリクエストを転送するには、Google Cloud プロジェクトに HTTP(S) ロードバランサを設定します。ロードバランサはプロジェクト内にあります。これはファイアウォールの内側にあるため、クラスタ内のノードにアクセスできます。静的 IP アドレスとドメイン名を使用してロードバランサを構成すると、ドメイン名にリクエストを送信できるようになります。ロードバランサは、これらのリクエストをクラスタ内のノードに転送します。

IAP の有効化

IAP を有効にする手順は以下のとおりです。

  1. クライアント ID(上記の手順では CLIENT_ID)と CLIENT_SECRET を使用して Cloud Service Mesh を有効にします。OAuth クライアントのマテリアルを使用して Kubernetes Secret を作成します。

    kubectl create secret generic -n istio-system my-secret --from-literal=client_id=CLIENT_ID \
    --from-literal=client_secret=CLIENT_SECRET

ロードバランサのデプロイ

Ingress リソースを使用すると、自動構成の SSL 証明書で HTTP(S) ロードバランサを作成できます。マネージド SSL 証明書はドメインに対してプロビジョニング、更新、管理が行われます。

  1. ManagedCertificate リソースを作成します。このリソースは、SSL 証明書のドメインを指定します。spec.domains リストに複数のドメインを入れることはできません。ワイルドカード ドメインはサポートされていません。次の YAML で、DOMAIN_NAME を外部静的 IP アドレス用に構成したドメイン名に置き換えます。

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.gke.io/v1
    kind: ManagedCertificate
    metadata:
      name: example-certificate
      namespace: istio-system
    spec:
      domains:
        - DOMAIN_NAME
    EOF
  2. BackendConfig リソースを作成します。 このリソースは、Ingress Gateway でヘルスチェックを実行する方法と Identity-Aware Proxy の構成方法を GCLB に指示します。まず、Ingress Gateway からヘルスチェックに関するいくつかの値を収集します。

    • ヘルスチェックの Ingress ポート: istio-ingress のヘルスチェック ポート。

      export HC_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="status-port")].nodePort}')

    • ヘルスチェック Ingress パス: istio-ingress のヘルスチェック パス。

      export HC_INGRESS_PATH=$(kubectl get pods -n istio-system -l app=istio-ingressgateway -o jsonpath='{.items[0].spec.containers[?(@.name=="istio-proxy")].readinessProbe.httpGet.path}')

    cat <<EOF | kubectl apply -n istio-system -f -
    apiVersion: cloud.google.com/v1
    kind: BackendConfig
    metadata:
      name: http-hc-config
    spec:
      healthCheck:
        checkIntervalSec: 2
        timeoutSec: 1
        healthyThreshold: 1
        unhealthyThreshold: 10
        port: ${HC_INGRESS_PORT}
        type: HTTP
        requestPath: ${HC_INGRESS_PATH}
      iap:
        enabled: true
        oauthclientCredentials:
          secretName: my-secret
    EOF
  3. Ingress サービスに BackendConfig のアノテーションを付けます。

        kubectl annotate -n istio-system service/istio-ingressgateway --overwrite \
          cloud.google.com/backend-config='{"default": "http-hc-config"}' \
          cloud.google.com/neg='{"ingress":false}'
    
  4. ロードバランサを作成するには、Ingress リソースを定義します。

    • 前の手順で作成した証明書の名前 example-certificatenetworking.gke.io/managed-certificates アノテーションに設定します。

    • 予約した静的 IP アドレスの名前 example-static-ipkubernetes.io/ingress.global-static-ip-name アノテーションに設定します。

    • serviceNameistio-ingressgateway に設定します。これは、Bookinfo サンプルのゲートウェイ リソースで使用されます。

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: example-ingress
      namespace: istio-system
      annotations:
        kubernetes.io/ingress.global-static-ip-name: example-static-ip
        networking.gke.io/managed-certificates: example-certificate
    spec:
      defaultBackend:
        service:
          name: istio-ingressgateway
          port:
            number: 80
    EOF
  5. Google Cloud コンソールで、[Kubernetes Engine] > [Services と Ingress] ページに移動します。

    [Services と Ingress] ページに移動

    [ステータス] 列に「Creating Ingress」と表示されます。GKE に Ingress が完全にプロビジョニングされてから次に進みます。数分ごとにページを更新し、Ingress の最新ステータスを確認します。Ingress がプロビジョニングされると、「OK」ステータスが表示されるか、「All backend services are in UNHEALTHY state.」というエラーが表示されます。デフォルトのヘルスチェックでは、GKE がプロビジョニングするリソースの 1 つが対象になります。エラー メッセージが表示された場合、Ingress がプロビジョニングされ、デフォルトのヘルスチェックが実行されています。「OK」ステータスまたはエラーが表示されている場合は、次のセクションでロードバランサのヘルスチェックを構成します。

IAP アクセスリストを構成する

IAP のアクセス ポリシーにユーザーを追加するには:

gcloud beta iap web add-iam-policy-binding \
    --member=user:EMAIL_ADDRESS \
    --role=roles/iap.httpsResourceAccessor

EMAIL_ADDRESS は、ユーザーの完全なメールアドレスです(例: alice@example.com)。

  1. ロードバランサをテストします。ブラウザで次の URL にアクセスします。

    http://DOMAIN_NAME/productpage

    ここで、DOMAIN_NAME は、外部静的 IP アドレスで構成したドメイン名です。

    Bookinfo アプリケーションの productpage が表示されます。ページを複数回更新すると、さまざまなバージョンのビューがラウンドロビン スタイル(赤い星、黒い星、星なし)で表示されます。

    Bookinfo への https アクセスもテストする必要があります。

サービス メッシュで RCToken サポートを有効にする

デフォルトでは、IAP は OAuth クライアントをスコープとする JSON Web Token(JWT)を生成します。Cloud Service Mesh では、RequestContextToken(RCToken)を生成するように IAP を構成できます。これは、JWT ですが、オーディエンスは構成可能です。RCToken を使用すると、JWT のオーディエンスに任意の文字列を構成できます。これを Cloud Service Mesh ポリシーで使用し、詳細な認証を実施できます。

RCToken を構成するには:

  1. RCToken オーディエンスの環境変数を作成します。任意の文字列を指定できます。

    export RCTOKEN_AUD="your-rctoken-aud"
    
  2. 省略可: 次のステップでは BACKEND_SERVICE_ID が必要です。BACKEND_SERVICE_ID を確認する必要がある場合は、次のコマンドを実行します。

    kubectl -n istio-system get Ingress example-ingress -o json | jq \
     '.metadata.annotations."ingress.kubernetes.io/backends"'
    

    予想される出力は "{\"BACKEND_SERVICE_ID\":\"HEALTHY\"}" のようになります。例: "ingress.kubernetes.io/backends": "{\"k8s-be-31224--51f3b55cd1457fb6\":\"HEALTHY\"}"。この例の BACKEND_SERVICE_IDk8s-be-31224--51f3b55cd1457fb6 です。

  3. 既存の IAP 設定を取得します。

    gcloud iap settings get --format json \
    --project=${PROJECT_ID} --resource-type=compute --service=BACKEND_SERVICE_ID > iapSettings.json
    
  4. RCToken オーディエンスで IapSettings を更新します。

    cat iapSettings.json | jq --arg RCTOKEN_AUD_STR $RCTOKEN_AUD \
    '. + {applicationSettings: {csmSettings: {rctokenAud: $RCTOKEN_AUD_STR}}}' \
    > updatedIapSettings.json
    
    gcloud iap settings set updatedIapSettings.json --format json \
    --project=${PROJECT_ID} --resource-type=compute --service=BACKEND_SERVICE_ID
    
  5. Istio Ingress ゲートウェイで RCToken 認証を有効にします。

    cat <<EOF | kubectl apply -f -
    apiVersion: "security.istio.io/v1beta1"
    kind: "RequestAuthentication"
    metadata:
      name: "ingressgateway-jwt-policy"
      namespace: "istio-system"
    spec:
      selector:
        matchLabels:
          app: istio-ingressgateway
      jwtRules:
      - issuer: "https://cloud.google.com/iap"
        jwksUri: "https://www.gstatic.com/iap/verify/public_key-jwk"
        audiences:
        - $RCTOKEN_AUD
        fromHeaders:
        - name: ingress-authorization
          prefix: "Istio "
        outputPayloadToHeader: "verified-jwt"
        forwardOriginalToken: true
    EOF
    
  6. 省略可: 有効な JWT のないリクエストが拒否されていることを確認します。

      cat <<EOF | kubectl apply -f -
      apiVersion: security.istio.io/v1beta1
      kind: AuthorizationPolicy
      metadata:
        name: iap-gateway-require-jwt
        namespace: istio-system
      spec:
        selector:
          matchLabels:
            app: istio-iap-ingressgateway
        action: DENY
        rules:
          - from:
              - source:
                  notRequestPrincipals: ["*"]
      EOF
      

  7. Bookinfo productpage へのリクエストが成功していることを確認します。

    http://DOMAIN_NAME/productpage

ポリシーをテストするには:

  1. IapSettings リクエスト オブジェクトを作成します。ただし、rctokenAud には別の文字列を設定します。

    cat iapSettings.json | jq --arg RCTOKEN_AUD_STR wrong-rctoken-aud \
    '. + {applicationSettings: {csmSettings: {rctokenAud: $RCTOKEN_AUD_STR}}}' \
    > wrongIapSettings.json
    
  2. IapSettings API を呼び出して、RCtoken オーディエンスを設定します。

    gcloud beta iap settings set wrongIapSettings.json --project=PROJECT_ID --resource-type=compute --service=BACKEND_SERVICE
  3. Bookinfo productpage にリクエストを送信すると失敗します。

    http://DOMAIN_NAME/productpage

クリーンアップ

このチュートリアルを完了したら、アカウントで不要な請求が発生しないように、以下のリソースを削除します。

  1. マネージド証明書を削除します。

    kubectl delete managedcertificates example-certificate
  2. Ingress を削除します。これにより、負荷分散リソースの割り当てが解除されます。

    kubectl -n istio-system delete ingress example-ingress

  3. 静的 IP アドレスを削除します。

    gcloud compute addresses delete example-static-ip --global

    これを行う場合は、ドメイン登録事業者から IP アドレスを削除してください。

  4. クラスタを削除します。これにより、クラスタを構成するリソース(コンピューティング インスタンス、ディスク、ネットワーク リソースなど)が削除されます。

    gcloud container clusters delete CLUSTER_NAME