Cloud Service Mesh の例: mTLS


Cloud Service Mesh 1.5 以降では、自動相互 TLS(自動 mTLS)がデフォルトで有効になっています。自動 mTLS の場合、クライアント サイドカー プロキシがサーバーにサイドカーがあるかどうかを自動的に検出します。クライアント サイドカーは、サイドカーを含むワークロードには mTLS を送信し、サイドカーを含まないワークロードには平文を送信します。ただし、サービスは平文と mTLS トラフィックの両方を受け入れます。Pod にサイドカー プロキシを挿入する場合、mTLS トラフィックのみを受け入れるようにサービスを構成することをおすすめします。

Cloud Service Mesh では、単一の YAML ファイルを適用することで、アプリケーション コードの外部で mTLS を適用できます。Cloud Service Mesh には柔軟性があり、サービス メッシュ全体、Namespace、個々のワークロードに認証ポリシーを適用できます。

相互 mTLS

費用

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

料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを生成できます。

新規の Google Cloud ユーザーは無料トライアルをご利用いただける場合があります。

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

始める前に

Ingress ゲートウェイをデプロイする

  1. kubectl の現在のコンテキストをクラスタに設定します。

    gcloud container clusters get-credentials CLUSTER_NAME  \
    --project=PROJECT_ID \
    --zone=CLUSTER_LOCATION 
    
  2. Ingress ゲートウェイの名前空間を作成します。

    kubectl create namespace asm-ingress
    
  3. インジェクションの名前空間を有効にします。手順は、コントロール プレーンの実装によって異なります。

    マネージド(TD)

    デフォルトのインジェクション ラベルを Namespace に適用します。

    kubectl label namespace asm-ingress \
        istio.io/rev- istio-injection=enabled --overwrite
    

    マネージド(Istiod)

    推奨: 次のコマンドを実行して、デフォルトのインジェクション ラベルを Namespace に適用します。

      kubectl label namespace asm-ingress \
          istio.io/rev- istio-injection=enabled --overwrite
    

    マネージド Istiod コントロール プレーンを使用している既存のユーザーの場合: デフォルトのインジェクションを使用することをおすすめしますが、リビジョンベースのインジェクションもサポートされています。次の手順を行います。

    1. 次のコマンドを実行して、利用可能なリリース チャンネルを探します。

      kubectl -n istio-system get controlplanerevision
      

      出力は次のようになります。

      NAME                AGE
      asm-managed-rapid   6d7h
      

      出力の NAME 列の値は、Cloud Service Mesh バージョンで使用可能なリリース チャンネルに対応するリビジョン ラベルです。

    2. リビジョン ラベルを Namespace に適用します。

      kubectl label namespace asm-ingress \
          istio-injection- istio.io/rev=REVISION_LABEL --overwrite
      
  4. anthos-service-mesh-samples リポジトリにサンプル ゲートウェイをデプロイします。

    kubectl apply -n asm-ingress \
    -f docs/shared/asm-ingress-gateway
    

    予想される出力:

    serviceaccount/asm-ingressgateway configured
    service/asm-ingressgateway configured
    deployment.apps/asm-ingressgateway configured
    gateway.networking.istio.io/asm-ingressgateway configured
    

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

  1. まだ行っていない場合は、kubectl の現在のコンテキストをクラスタに設定します。

      gcloud container clusters get-credentials CLUSTER_NAME  \
        --project=PROJECT_ID \
        --zone=CLUSTER_LOCATION 
    
  2. サンプル アプリケーションの名前空間を作成します。

      kubectl create namespace onlineboutique
    
  3. Envoy プロキシを自動的に挿入するには、onlineboutique 名前空間にラベルを付けます。自動サイドカー インジェクションを有効にする手順を行います。

  4. サンプルアプリ、フロントエンド用の VirtualService、ワークロードのサービス アカウントをデプロイします。このチュートリアルでは、マイクロサービス デモアプリである Online Boutique をデプロイします。

      kubectl apply \
      -n onlineboutique \
      -f docs/shared/online-boutique/virtual-service.yaml
      kubectl apply \
      -n onlineboutique \
      -f docs/shared/online-boutique/service-accounts
    

サービスを表示する

  1. onlineboutique Namespace の Pod を表示します。

    kubectl get pods -n onlineboutique
    

    予想される出力:

    NAME                                     READY   STATUS    RESTARTS   AGE
    adservice-85598d856b-m84m6               2/2     Running   0          2m7s
    cartservice-c77f6b866-m67vd              2/2     Running   0          2m8s
    checkoutservice-654c47f4b6-hqtqr         2/2     Running   0          2m10s
    currencyservice-59bc889674-jhk8z         2/2     Running   0          2m8s
    emailservice-5b9fff7cb8-8nqwz            2/2     Running   0          2m10s
    frontend-77b88cc7cb-mr4rp                2/2     Running   0          2m9s
    loadgenerator-6958f5bc8b-55q7w           2/2     Running   0          2m8s
    paymentservice-68dd9755bb-2jmb7          2/2     Running   0          2m9s
    productcatalogservice-84f95c95ff-c5kl6   2/2     Running   0          114s
    recommendationservice-64dc9dfbc8-xfs2t   2/2     Running   0          2m9s
    redis-cart-5b569cd47-cc2qd               2/2     Running   0          2m7s
    shippingservice-5488d5b6cb-lfhtt         2/2     Running   0          2m7s
    

    アプリケーションのすべての Pod が稼働状態になり、READY 列に 2/2 が入力されます。これは、Pod に Envoy サイドカー プロキシが正常に挿入されたことを示します。数分経っても 2/2 が表示されない場合は、トラブルシューティング ガイドをご覧ください。

  2. 外部 IP を取得して、変数に設定します。

    kubectl get services -n asm-ingress
    export FRONTEND_IP=$(kubectl --namespace asm-ingress \
    get service --output jsonpath='{.items[0].status.loadBalancer.ingress[0].ip}' \
    )
    

    次のような出力が表示されます。

    NAME                   TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                                      AGE
    asm-ingressgateway   LoadBalancer   10.19.247.233   35.239.7.64   80:31380/TCP,443:31390/TCP,31400:31400/TCP   27m
    
    
  3. ウェブブラウザで EXTERNAL-IP のアドレスにアクセスします。ブラウザに Online Boutique のショップが表示されます。

    オンライン ショップのフロントエンド

TestCurl Pod を作成する

TestCurl Pod を作成して、テスト用の平文トラフィックを送信します。

  apiVersion: v1
  kind: Pod
  metadata:
    name: testcurl
    namespace: default
    annotations:
      sidecar.istio.io/inject: "false"
  spec:
    containers:
    - name: curl
      image: curlimages/curl
      command: ["sleep", "600"]

Online Boutique にアクセスする

  1. kubectl の現在のコンテキストを、Online Boutique をデプロイしたクラスタに設定します。

    gcloud container clusters get-credentials CLUSTER_NAME  \
        --project=PROJECT_ID \
        --zone=CLUSTER_LOCATION 
    
  2. frontend 名前空間のサービスを一覧表示します。

    kubectl get services -n frontend
    

    frontend-externalLoadBalancer であり、外部 IP アドレスがあります。サンプル アプリケーションには、Cloud Service Mesh を使用せずに GKE にデプロイできるようにロードバランサのサービスが含まれています。

  3. frontend-external サービスの外部 IP アドレスを使用して、ブラウザでアプリケーションにアクセスします。

    http://FRONTEND_EXTERNAL_IP/
    
  4. Cloud Service Mesh では、Ingress ゲートウェイをデプロイできます。Ingress ゲートウェイの外部 IP アドレスを使用して Online Boutique にアクセスすることもできます。アプリケーションの外部 IP を取得します。プレースホルダを次の情報に置き換えます。

    • GATEWAY_SERVICE_NAME: Ingress ゲートウェイ サービスの名前。サンプル ゲートウェイをそのままデプロイしたか、デフォルトの Ingress ゲートウェイをデプロイした場合、名前は istio-ingressgateway になります。
    • GATEWAY_NAMESPACE: Ingress ゲートウェイをデプロイした名前空間。デフォルトの Ingress ゲートウェイをデプロイした場合、名前空間は istio-system です。
    kubectl get service GATEWAY_NAME -n GATEWAY_NAMESPACE
    
  5. ブラウザで別のタブを開き、ingress ゲートウェイの外部 IP アドレスを使用してアプリケーションにアクセスします。

    http://INGRESS_GATEWAY_EXTERNAL_IP/
    
  6. 次のコマンドを実行して、別の Pod からプレーン HTTP で frontend サービスの curl を実行します。サービスが異なる Namespace にあるため、frontend サービスの DNS 名を curl で転送する必要があります。

    kubectl debug --image istio/base --target istio-proxy -it \
      $(kubectl get pod -l app=productcatalogservice -n product-catalog -o jsonpath={.items..metadata.name}) \
      -n product-catalog -- \
      curl http://frontend.frontend.svc.cluster.local:80/ -o /dev/null -s -w '%{http_code}\n'
    

    デフォルトでは TLS と平文の両方のトラフィックが受け入れられるため、ステータス 200 でリクエストが成功します。

名前空間ごとに相互 TLS を有効にする

mTLS を適用するには、kubectlPeerAuthentication ポリシーを適用します。

  1. 次の認証ポリシーを mtls-namespace.yaml として保存します。

    cat <<EOF > mtls-namespace.yaml
    apiVersion: "security.istio.io/v1beta1"
    kind: "PeerAuthentication"
    metadata:
      name: "namespace-policy"
    spec:
      mtls:
        mode: STRICT
    EOF
    

    YAML の mode: STRICT 行は、mTLS のみを受け入れるようにサービスを構成します。デフォルトでは、modePERMISSIVE であり、平文と mTLS の両方を受け入れるようにサービスを構成します。

  2. mTLS のみを受け入れるように、認証ポリシーを適用してすべての Online Boutique サービスを構成します。

    for ns in ad cart checkout currency email frontend loadgenerator \
         payment product-catalog recommendation shipping; do
    kubectl apply -n $ns -f mtls-namespace.yaml
    done
    

    予想される出力:

    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created

  3. frontend-external サービスの外部 IP アドレスを使用して、Online Boutique にアクセスするブラウザのタブに移動します。

    http://FRONTEND_EXTERNAL_IP/
    
  4. ページを更新すると、ブラウザに次のエラーが表示されます。

    サイトにアクセスできません

    ページを更新すると、平文が frontend サービスに送信されます。STRICT 認証ポリシーにより、サイドカー プロキシはサービスに対するリクエストをブロックします。

  5. ブラウザのタブから、istio-ingressgateway の外部 IP アドレスを使用して Online Boutique にアクセスし、ページを更新して正常に表示します。Ingress ゲートウェイを使用して Online Boutique にアクセスする場合、リクエストは次のパスを取得します。

    相互 mTLS

    mTLS 認証フロー:

    1. ブラウザが平文の HTTP リクエストをサーバーに送信します。
    2. Ingress ゲートウェイ プロキシ コンテナがリクエストをインターセプトします。
    3. Ingress ゲートウェイ プロキシは、サーバー側プロキシ(この例ではフロントエンド サービス)を使用して TLS handshake を実行します。この handshake には、証明書交換が含まれています。これらの証明書は、Cloud Service Mesh によってプロキシ コンテナにあらかじめ読み込まれています。
    4. Ingress ゲートウェイ プロキシは、サーバーの証明書に対して安全な名前チェックを実行し、認可された ID がサーバーで実行されていることを確認します。
    5. Ingress ゲートウェイとサーバー プロキシは相互 TLS 接続を確立し、サーバー プロキシがリクエストをサーバー アプリケーション コンテナ(フロントエンド サービス)に転送します。
  6. 次のコマンドを実行して、別の Pod からプレーン HTTP で frontend サービスの curl を実行します。

    kubectl exec testcurl -n default -- curl \
      http://frontend.frontend.svc.cluster.local:80/ -o /dev/null -s -w '%{http_code}\n'
    

    STRICT peerAuthentication ポリシーが適用されているサイドカーレス ワークロードから平文トラフィックを送信しているため、このリクエストは失敗します。

認証ポリシーを検索して削除する

  1. サービス メッシュ内のすべての PeerAuthentication ポリシーの一覧を表示するには、次のコマンドを実行します。

    kubectl get peerauthentication --all-namespaces
    

    出力は次のようになります。

    NAMESPACE         NAME               MODE     AGE
    ad                namespace-policy   STRICT   17m
    cart              namespace-policy   STRICT   17m
    checkout          namespace-policy   STRICT   17m
    currency          namespace-policy   STRICT   17m
    email             namespace-policy   STRICT   17m
    frontend          namespace-policy   STRICT   17m
    loadgenerator     namespace-policy   STRICT   17m
    payment           namespace-policy   STRICT   17m
    product-catalog   namespace-policy   STRICT   17m
    recommendation    namespace-policy   STRICT   17m
    shipping          namespace-policy   STRICT   17m
    
  2. すべての Online Boutique の名前空間から認証ポリシーを削除します。

    for ns in ad cart checkout currency email frontend loadgenerator payment \
      product-catalog recommendation shipping; do
        kubectl delete peerauthentication -n $ns namespace-policy
    done;
    

    予想される出力:

    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    
  3. frontend-external サービスの外部 IP アドレスを使用して Online Boutique にアクセスし、ページを更新します。ページが想定どおりに表示されます。

  4. 次のコマンドを実行して、別の Pod からプレーン HTTP で frontend サービスの curl を実行します。

    kubectl debug --image istio/base --target istio-proxy -it \
      $(kubectl get pod -l app=productcatalogservice -n product-catalog -o jsonpath={.items..metadata.name}) \
      -n product-catalog -- \
      curl http://frontend.frontend.svc.cluster.local:80/ -o /dev/null -s -w '%{http_code}\n'
    

    デフォルトでは TLS と平文の両方のトラフィックが受け入れられるため、ステータス 200 でリクエストが成功します。

Google Cloud コンソールでページを更新して [ワークロード] リストを表示すると、mTLS ステータスが Permissive であることが示されます。

ワークロードごとに相互 TLS を有効にする

特定のワークロードに PeerAuthentication ポリシーを設定するには、selector セクションを構成し、目的のワークロードに一致するラベルを指定する必要があります。ただし、Cloud Service Mesh は、サービスへのアウトバウンド mTLS トラフィックのワークロード レベルのポリシーを集計できません。この動作を管理するには、宛先ルールを構成する必要があります。

  1. 特定のワークロードに認証ポリシーを適用します。次のポリシーでは、ラベルとセレクタを使用して特定の frontend デプロイをターゲットにしていることに注意してください。

    cat <<EOF | kubectl apply -n frontend -f -
    apiVersion: "security.istio.io/v1beta1"
    kind: "PeerAuthentication"
    metadata:
      name: "frontend"
      namespace: "frontend"
    spec:
      selector:
        matchLabels:
          app: frontend
      mtls:
        mode: STRICT
    EOF
    

    予想される出力:

    peerauthentication.security.istio.io/frontend created
  2. 一致する宛先ルールを構成します。

    cat <<EOF | kubectl apply -n frontend -f -
    apiVersion: "networking.istio.io/v1alpha3"
    kind: "DestinationRule"
    metadata:
      name: "frontend"
    spec:
      host: "frontend.demo.svc.cluster.local"
      trafficPolicy:
        tls:
          mode: ISTIO_MUTUAL
    EOF
    

    予想される出力:

    destinationrule.networking.istio.io/frontend created
  3. frontend-external サービスの外部 IP アドレスを使用して Online Boutique にアクセスし、ページを更新します。frontend serviceSTRICT mTLS に設定され、サイドカー プロキシがリクエストをブロックしているため、このページは表示されません。

  4. 次のコマンドを実行して、別の Pod からプレーン HTTP で frontend サービスの curl を実行します。

    kubectl exec testcurl -n default -- curl \
      http://frontend.frontend.svc.cluster.local:80/ -o /dev/null -s -w '%{http_code}\n'
    

    STRICT peerAuthentication ポリシーが適用されているサイドカーレス ワークロードから平文トラフィックを送信しているため、このリクエストは失敗します。

  5. 認証ポリシーを削除します。

    kubectl delete peerauthentication -n frontend frontend
    

    予想される出力:

    peerauthentication.security.istio.io "frontend" deleted
    
  6. 宛先ルールを削除します。

    kubectl delete destinationrule -n frontend frontend
    

    予想される出力:

    destinationrule.networking.istio.io "frontend" deleted
    

メッシュ全体の mTLS を適用する

メッシュ内のすべてのサービスが平文トラフィックを受け入れるのを防ぐには、mTLS モードを STRICT に設定してメッシュ全体の PeerAuthentication ポリシーを設定します。メッシュ全体の PeerAuthentication ポリシーにはセレクタがないため、このポリシーはルート名前空間 istio-system に適用する必要があります。ポリシーをデプロイすると、コントロール プレーンが TLS 証明書を自動的にプロビジョニングし、ワークロードの相互認証を可能にします。

  1. メッシュ全体の mTLS を適用します。

    kubectl apply -f - <<EOF
    apiVersion: "security.istio.io/v1beta1"
    kind: "PeerAuthentication"
    metadata:
      name: "mesh-wide"
      namespace: "istio-system"
    spec:
      mtls:
        mode: STRICT
    EOF
    

    予想される出力:

    peerauthentication.security.istio.io/mesh-wide created

  2. frontend-external サービスの外部 IP アドレスを使用して Online Boutique にアクセスし、ページを更新します。ページは表示されません。

  3. 次のコマンドを実行して、別の Pod からプレーン HTTP で frontend サービスの curl を実行します。

    kubectl exec testcurl -n default -- curl \
      http://frontend.frontend.svc.cluster.local:80/ -o /dev/null -s -w '%{http_code}\n'
    

    STRICT peerAuthentication ポリシーが適用されているサイドカーレス ワークロードから平文トラフィックを送信しているため、このリクエストは失敗します。

  4. mesh-wide ポリシーを削除します。

    kubectl delete peerauthentication -n istio-system mesh-wide
    

    予想される出力:

    peerauthentication.security.istio.io "mesh-wide" deleted
    

クリーンアップ

このチュートリアルで使用したリソースについて、Google Cloud アカウントに課金されないようにするには、リソースを含むプロジェクトを削除するか、プロジェクトを維持して個々のリソースを削除します。

  • 追加料金の発生を回避するには、クラスタを削除します。

    gcloud container clusters delete  CLUSTER_NAME  \
        --project=PROJECT_ID \
        --zone=CLUSTER_LOCATION 
    
  • クラスタを維持して Online Boutique のサンプルを削除するには:

    1. アプリケーションの名前空間を削除します。
      kubectl delete -f online-boutique/kubernetes-manifests/namespaces
    

    予想される出力:

    namespace "ad" deleted
    namespace "cart" deleted
    namespace "checkout" deleted
    namespace "currency" deleted
    namespace "email" deleted
    namespace "frontend" deleted
    namespace "loadgenerator" deleted
    namespace "payment" deleted
    namespace "product-catalog" deleted
    namespace "recommendation" deleted
    namespace "shipping" deleted
    
    1. サービス エントリを削除します。
      kubectl delete -f online-boutique/istio-manifests/allow-egress-googleapis.yaml
    

    予想される出力:

    serviceentry.networking.istio.io "allow-egress-googleapis" deleted
    serviceentry.networking.istio.io "allow-egress-google-metadata" deleted
    

次のステップ