Certificate Authority Service を使用して Cloud Service Mesh Ingress ゲートウェイの TLS 証明書管理を自動化する

このチュートリアルでは、プラットフォーム オペレーターが cert-manager ツールの Certificate Authority Service(CA Service)発行者を使用して、Cloud Service Mesh Ingress ゲートウェイの TLS 証明書の管理を自動化する方法について説明します。この証明書により、Ingress ゲートウェイは、Virtual Private Cloud(VPC)内でサービス メッシュの外部にあるクライアントから送信された HTTPS やその他の TLS、mTLS トラフィックを終端できます。このチュートリアルでは、Kubernetes と TLS 証明書について基本的な知識があることを前提としています。

はじめに

Cloud Service Meshh は、サービス メッシュ内のすべてのワークロードに TLS 証明書をプロビジョニングします。これらの証明書により、サービス メッシュ内のワークロード間で暗号化と相互認証 TLS(mTLS)通信が可能になります。サポートされている CA の 1 つが証明書を発行して署名します。

ただし、Cloud Service Mesh は、サービス メッシュに送信されるトラフィックに対して Ingress ゲートウェイに証明書を自動的にプロビジョニングしません。一般的な解決策は、オープンソースの cert-manager ツールを使用して、Ingress ゲートウェイでの証明書管理を自動化することです。

cert-manager ツールは、認証局(CA)を表す発行者の証明書をリクエストします。CA Service とは、独自のプライベート CA を作成できる Google Cloud サービスです。cert-manager ツールは、オープンソースの CA Service の外部発行者を使用して、CA Service の証明書をリクエストできます。

プライベート CA は、内部ネットワーク内でトラフィックを認証して暗号化する TLS 証明書を発行できます。多くの場合、Cloud Service Mesh の Ingress ゲートウェイは、VPC 内でサービス メッシュ外のクライアントからの受信トラフィックを許可するように設定されています。内部ネットワーク トラフィックに対しては、CA Service のプライベート CA を使用して Ingress ゲートウェイの証明書を発行できます。

このチュートリアルでは、Ingress ゲートウェイの TLS 証明書のプロビジョニングと更新を自動化するために、cert-manager ツールと CA Service 発行者を設定する方法について説明します。cert-manager ツールは、TLS タイプの Kubernetes Secret リソースとして証明書をプロビジョニングします。cert-manager ツールが証明書を更新すると、Secret リソースが新しい証明書で更新されます。Ingress ゲートウェイは Envoy プロキシを実行し、Envoy のシークレット検出サービス(SDS)をサポートします。SDS を使用すると、管理者がプロセスを再起動または再読み込みすることなく、Ingress ゲートウェイが新しい証明書を使用できるようになります。

メッシュの一部であるサイドカー プロキシは、CA Service または Cloud Service Mesh 認証局から TLS 証明書を取得できます。このチュートリアルでは、サイドカー プロキシと Ingress ゲートウェイの両方の証明書に CA Service を使用します。これにより、すべての TLS 証明書に 1 つのルート CA を使用できます。

次の図は、このチュートリアルでプロビジョニングするリソースを示しています。Ingress ゲートウェイに内部パススルー ネットワーク ロードバランサをプロビジョニングします。内部パススルー ネットワーク ロードバランサはプロキシではないため、TCP 接続の終端処理や TLS handshake は行いません。代わりに、istio-ingressgateway Deployment の Pod に接続がルーティングされます。

hello-example-com-credential Secret には証明書と秘密鍵が含まれています。hello ゲートウェイは、この証明書と秘密鍵を使用してホスト名 hello.example.com を持つリクエストに対して TLS handshake を実行するように istio-ingressgateway Deployment の Pod を構成します。

CA サービスを使用した mTLS 管理

cert-manager Namespace 内の google-cas-issuer Deployment の Pod が、CA Service で作成した CA の証明書をリクエストします。ca-service-isser Pod が GKE 用 Workload Identity 連携を使用して Google サービス アカウントの権限を借用できるようにする Identity and Access Management ポリシー バインディングを作成します。この Google サービス アカウントに CA Service の CA に証明書をリクエストする権限を付与するには、CA プールで IAM ポリシー バインディングを作成します。

目標

費用

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

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

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

始める前に

  1. Google Cloud コンソールでプロジェクト セレクタのページに移動し、プロジェクトを選択または作成します。

  2. Google Cloud プロジェクトの課金が有効になっていることを確認します。

  3. Google Cloud Console で、Cloud Shell に移動します。

    Google Cloud コンソールの下部で Cloud Shell セッションが開き、コマンドライン プロンプトが表示されます。このチュートリアルでは、コマンドの実行はすべて Cloud Shell で行います。

  4. このチュートリアルで使用する Google Cloud コンソール プロジェクトを設定します。

    gcloud config set core/project PROJECT_ID
    

    PROJECT_ID は、Cloud プロジェクト ID に置き換えます。

    [Cloud Shell の承認] ダイアログで、[承認] をクリックします。[承認] をクリックすると、Cloud Shell で実行する gcloud コマンドで、ユーザー認証情報を使用して Google API に対する認証が可能となります。

  5. Resource Manager、GKE、GKE Hub、Cloud Service Mesh 認証局、CA Service の API を有効にします。

    gcloud services enable \
        cloudresourcemanager.googleapis.com \
        container.googleapis.com \
        gkehub.googleapis.com \
        meshca.googleapis.com \
        privateca.googleapis.com
    

CA Service の構成

このセクションでは、ルート CA と 2 つの下位 CA を CA サービスに作成します。1 つの下位 CA が Ingress ゲートウェイに証明書を発行し、もう 1 つの下位 CA がメッシュ内のサイドカー プロキシに証明書を発行します。

わかりやすくするため、このチュートリアルでは GKE クラスタ、ルート CA、下位 CA に同じプロジェクトを使用します。独自の環境では、GKE クラスタと CA に異なるプロジェクトを使用できます。

  1. Cloud Shell で、ルート CA に使用する CA プールを作成します。

    gcloud privateca pools create ROOT_CA_POOL \
        --location CA_LOCATION \
        --tier enterprise
    
    • ROOT_CA_POOL は、CA プールの名前です。例: root-ca-pool-tutorial
    • CA_LOCATION は、CA プールのロケーションです。(例: us-central1)です。

    利用可能な CA Service のロケーションを一覧表示するには、gcloud privateca locations list コマンドを使用します。

  2. ルート CA を作成して有効にします。

    gcloud privateca roots create ROOT_CA \
        --auto-enable \
        --key-algorithm ec-p384-sha384 \
        --location CA_LOCATION \
        --pool ROOT_CA_POOL \
        --subject "CN=Example Root CA, O=Example Organization" \
        --use-preset-profile root_unconstrained
    
    • ROOT_CA は、ルート CA に使用する名前です。たとえば、root-ca-tutorial です。
  3. Ingress ゲートウェイに証明書を発行する下位 CA に使用する CA プールを作成します。

    gcloud privateca pools create SUBORDINATE_CA_POOL_GATEWAYS \
        --location CA_LOCATION \
        --tier devops
    
    • SUBORDINATE_CA_POOL_GATEWAYS は、CA プールの名前です。たとえば、subordinate-ca-mtls-pool-gateways-tutorial です。
  4. Ingress ゲートウェイに証明書を発行する下位 CA を作成して有効にします。

    gcloud privateca subordinates create SUBORDINATE_CA_GATEWAYS \
        --auto-enable \
        --issuer-location CA_LOCATION \
        --issuer-pool ROOT_CA_POOL \
        --key-algorithm ec-p256-sha256 \
        --location CA_LOCATION \
        --pool SUBORDINATE_CA_POOL_GATEWAYS \
        --subject "CN=Example Gateway mTLS CA, O=Example Organization" \
        --use-preset-profile subordinate_mtls_pathlen_0
    
    • SUBORDINATE_CA_GATEWAYS は、下位 CA に使用する名前です。例: subordinate-ca-mtls-gateways-tutorial
    • --use-preset-profile フラグは、下位 mTLS 証明書プロファイルを使用するように下位 CA を構成します。このプロファイルにより、下位 CA は mTLS に対してクライアント TLS とサーバー TLS 証明書の両方を発行できます。

    Ingress ゲートウェイで mTLS の代わりに単純な TLS を使用する場合は、下位 CA でサーバー TLS 証明書を発行するだけで済みます。この場合は、代わりに下位サーバーの TLSsubordinate_server_tls_pathlen_0)証明書プロファイルを使用できます。

  5. 証明書の発行ポリシーを作成します。

    cat << EOF > policy.yaml
    baselineValues:
      keyUsage:
        baseKeyUsage:
          digitalSignature: true
          keyEncipherment: true
        extendedKeyUsage:
          serverAuth: true
          clientAuth: true
      caOptions:
        isCa: false
    identityConstraints:
      allowSubjectPassthrough: false
      allowSubjectAltNamesPassthrough: true
      celExpression:
        expression: subject_alt_names.all(san, san.type == URI && san.value.startsWith("spiffe://PROJECT_ID.svc.id.goog/ns/") )
    EOF
    

    この発行ポリシーにより、CA はメッシュ内のワークロードの証明書のみを発行するように制限されます。

  6. メッシュ内のサイドカー プロキシに証明書を発行する下位 CA に使用する CA プールを作成します。発行ポリシーを CA プールに適用します。

    gcloud privateca pools create SUBORDINATE_CA_POOL_SIDECARS \
     --issuance-policy policy.yaml \
     --location CA_LOCATION \
     --tier devops
    
    • SUBORDINATE_CA_POOL_SIDECARS は、CA プールの名前です。たとえば、subordinate-ca-mtls-pool-sidecars-tutorial です。
  7. メッシュ内のサイドカー プロキシに証明書を発行する下位 CA を作成して有効にします。

    gcloud privateca subordinates create SUBORDINATE_CA_SIDECARS \
        --auto-enable \
        --issuer-location CA_LOCATION \
        --issuer-pool ROOT_CA_POOL \
        --key-algorithm ec-p256-sha256 \
        --location CA_LOCATION \
        --pool SUBORDINATE_CA_POOL_SIDECARS \
        --subject "CN=Example Sidecar mTLS CA, O=Example Organization" \
        --use-preset-profile subordinate_mtls_pathlen_0
    
    • SUBORDINATE_CA_GATEWAYS は、下位 CA に使用する名前です。例: subordinate-ca-mtls-sidecars-tutorial

Google Kubernetes Engine クラスタを作成する

  1. Cloud Shell で、GKE クラスタを作成します。

    gcloud container clusters create CLUSTER_NAME \
        --enable-ip-alias \
        --num-nodes 4 \
        --release-channel regular \
        --scopes cloud-platform \
        --workload-pool PROJECT_ID.svc.id.goog \
        --zone ZONE
    

    CLUSTER_NAME は、クラスタの名前に置き換えます。たとえば、asm-ingress-cert-manager-ca-service です。

    ZONE は、クラスタに使用するゾーンに置き換えます。たとえば、us-central1-f です。

    コマンドについて、次の点に注意してください。

    • --release-channel フラグを指定すると、クラスタの GKE リリース チャンネルが選択されます。
    • Cloud Service Mesh と cert-manager ツールの CA Service 発行元の両方で、クラスタノードに cloud-platform スコープを設定する必要があります。
    • --workload-pool 引数を使用すると、Workload Identity Federation for GKE が有効になり、CA サービス発行元の Kubernetes サービス アカウントが Google サービス アカウントになりすますことができます。この権限借用により、CA Service 発行元 Pod が Google サービス アカウントの鍵ファイルをダウンロードすることなく、CA Service API にアクセスできます。
  2. ユーザー アカウントにクラスタ管理者の権限を付与します。

    kubectl create clusterrolebinding cluster-admin-binding \
        --clusterrole cluster-admin \
        --user $(gcloud config get-value core/account)
    

    Cloud Service Mesh のロールベース アクセス制御(RBAC)ルールを作成し、cert-manager ツールをインストールするには、Kubernetes cluster-admin ClusterRole によって提供される権限が必要です。

Anthos Service Mesh コントロール プレーンをインストールする

このチュートリアルでは、Google Cloud 上の GKE クラスタにマネージド Cloud Service Mesh をインストールし、すべてのリソースを 1 つのプロジェクトにまとめます。独自の環境では、このドキュメントで説明するソリューションはマネージド Cloud Service Mesh またはクラスタ内コントロール プレーンを使用して適用できます。

Cloud Service Mesh には、さまざまなシナリオに対応する各種のインストール オプションが用意されています。このチュートリアルを完了したら、インストール ガイドを確認して、実際の環境に最適なオプションを選択することをおすすめします。

  1. Cloud Shell で、asmcli インストール ツールをダウンロードします。

    curl --location --output asmcli https://storage.googleapis.com/csm-artifacts/asm/asmcli_1.16
    
    chmod +x asmcli
    

    asmcli を使用して Cloud Service Mesh コントロール プレーンをインストールします。

  2. Cloud Service Mesh コントロール プレーンをインストールします。

    ./asmcli install \
        --ca gcp_cas \
        --ca_pool projects/PROJECT_ID/locations/CA_LOCATION/caPools/SUBORDINATE_CA_POOL_SIDECARS \
        --cluster_location ZONE \
        --cluster_name CLUSTER_NAME \
        --enable_all \
        --enable_registration \
        --fleet_id PROJECT_ID \
        --managed \
        --output_dir asm-files \
        --project_id PROJECT_ID \
        --verbose
    

    インストールには数分かかります。インストールが完了すると、次の出力が表示されます。

    asmcli: Successfully installed ASM.
    

Ingress ゲートウェイをインストールする

  1. Cloud Shell で、Ingress ゲートウェイの Kubernetes 名前空間を作成します。

    kubectl create namespace GATEWAY_NAMESPACE
    
    • GATEWAY_NAMESPACE は、Ingress ゲートウェイに使用する名前空間の名前です。例: istio-ingress
  2. Ingress ゲートウェイの内部パススルー ネットワーク ロードバランサに使用する静的内部 IP アドレスを予約します。

    LOAD_BALANCER_IP=$(gcloud compute addresses create \
        asm-ingress-gateway-ilb \
        --region REGION \
        --subnet default \
        --format 'value(address)')
    
    • REGION は、GKE クラスタノードが使用するゾーンを含むリージョンに置き換えます。たとえば、クラスタで us-central1-f ゾーンを使用する場合は、REGIONus-central1 に置き換えます。

    このコマンドは、指定したリージョンのデフォルト サブネットの IP アドレスを予約します。

  3. Ingress ゲートウェイのオペレータ マニフェストを作成します。

    cat << EOF > ingressgateway-operator.yaml
    apiVersion: install.istio.io/v1alpha1
    kind: IstioOperator
    metadata:
      name: ingressgateway-operator
      annotations:
        config.kubernetes.io/local-config: "true"
    spec:
      profile: empty
      revision: asm-managed
      components:
        ingressGateways:
        - name: istio-ingressgateway
          namespace: GATEWAY_NAMESPACE
          enabled: true
          k8s:
            overlays:
            - apiVersion: apps/v1
              kind: Deployment
              name: istio-ingressgateway
              patches:
              - path: spec.template.metadata.annotations
                value:
                  inject.istio.io/templates: gateway
              - path: spec.template.metadata.labels.sidecar\.istio\.io/inject
                value: "true"
              - path: spec.template.spec.containers[name:istio-proxy]
                value:
                  name: istio-proxy
                  image: auto
            service:
              loadBalancerIP: $LOAD_BALANCER_IP
            serviceAnnotations:
              networking.gke.io/load-balancer-type: Internal
              networking.gke.io/internal-load-balancer-allow-global-access: "true"
    EOF
    

    オペレータ マニフェストについては、次の点に注意してください。

  4. オペレータ マニフェストと、コントロール プレーンのインストール時に asmcli スクリプトがダウンロードした istioctl ツールを使用して、Ingress ゲートウェイ インストール マニフェストを作成します。

    ./asm-files/istioctl manifest generate \
        --filename ingressgateway-operator.yaml \
        --output ingressgateway
    
  5. Ingress ゲートウェイをインストールします。

    kubectl apply --recursive --filename ingressgateway/
    

cert-manager ツールをインストールする

  1. Cloud Shell で、cert-manager ツールのインストール マニフェストをダウンロードして適用します。

    CERT_MANAGER_VERSION=v1.5.4
    
    curl --location --output cert-manager.yaml "https://github.com/jetstack/cert-manager/releases/download/${CERT_MANAGER_VERSION}/cert-manager.yaml"
    
    kubectl apply --filename cert-manager.yaml
    

    cert-manager ツールのインストールには 1 分ほどかかります。

CA Service 発行元コントローラをインストールする

CA Service 発行元コントローラを使用すると、cert-manager ツールは CA Service を使用して証明書をリクエストできます。コントローラは、cert-manager ツールの外部発行元の拡張メカニズムを使用します。

  1. Cloud Shell で、Google サービス アカウントを作成します。

    gcloud iam service-accounts create CAS_ISSUER_GSA \
        --display-name "CA Service issuer for cert-manager"
    
    • CAS_ISSUER_GSA は、Google サービス アカウントの名前です。たとえば、cert-manager-ca-service-issuer です。

    Certificate Authority サービス発行元コントローラは、この Google サービス アカウントを使用して、Certificate Authority サービス API に対する認証を行います。

  2. Certificate Authority サービス発行元コントローラの Google サービス アカウントが下位 CA を含む CA プールの証明書をリクエストできるように、Identity and Access Management ポリシー バインディングを作成します。

    gcloud privateca pools add-iam-policy-binding SUBORDINATE_CA_POOL_GATEWAYS \
        --location CA_LOCATION \
        --member "serviceAccount:CAS_ISSUER_GSA@PROJECT_ID.iam.gserviceaccount.com" \
        --role roles/privateca.certificateRequester
    
  3. Certificate Authority サービス発行元コントローラのインストール マニフェストをダウンロードします。

    CAS_ISSUER_VERSION=v0.5.3
    
    curl --location --output ca-service-issuer.yaml "https://github.com/jetstack/google-cas-issuer/releases/download/${CAS_ISSUER_VERSION}/google-cas-issuer-${CAS_ISSUER_VERSION}.yaml"
    
  4. IAM ポリシー バインディングを作成して、cert-manager 名前空間の ksa-google-cas-issuer Kubernetes サービス アカウントが Workload Identity Federation for GKE を使用して Google サービス アカウント(GSA)の権限を借用することを許可します。

    gcloud iam service-accounts add-iam-policy-binding \
     CAS_ISSUER_GSA@PROJECT_ID.iam.gserviceaccount.com \
        --member "serviceAccount:PROJECT_ID.svc.id.goog[cert-manager/ksa-google-cas-issuer]" \
        --role roles/iam.workloadIdentityUser
    

    CA サービス発行元コントローラ Pod は、ksa-google-cas-issuer Kubernetes サービス アカウントを使用します。

  5. GKE クラスタに CA サービス発行元コントローラをインストールします。

    kubectl apply --filename ca-service-issuer.yaml
    
  6. Workload Identity Federation for GKE のアノテーション iam.gke.io/gcp-service-account を、CA サービス発行元コントローラ Pod が使用する Kubernetes サービス アカウントに追加します。

    kubectl annotate serviceaccount ksa-google-cas-issuer --namespace cert-manager \
       "iam.gke.io/gcp-service-account=CAS_ISSUER_GSA@PROJECT_ID.iam.gserviceaccount.com"
    

    このアノテーションは、Kubernetes サービス アカウントが Google サービス アカウントに成り代わって Google API にアクセスできることを GKE に通知します。

証明書の発行者を作成する

  1. Cloud Shell で、GoogleCASIssuer マニフェストを作成して適用します。

    cat << EOF > gateway-cas-issuer.yaml
    apiVersion: cas-issuer.jetstack.io/v1beta1
    kind: GoogleCASIssuer
    metadata:
      name: gateway-cas-issuer
      namespace: GATEWAY_NAMESPACE
    spec:
      caPoolId: SUBORDINATE_CA_POOL_GATEWAYS
      location: CA_LOCATION
      project: PROJECT_ID
    EOF
    
    kubectl apply --filename gateway-cas-issuer.yaml
    

    発行元により、cert-manager ツールで Ingress ゲートウェイ名前空間の下位 CA プールの証明書をプロビジョニングできます。

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

このセクションでは、cert-manager ツールが CA サービス発行元を使用して CA サービスから証明書を取得できることを確認します。確認するために、リクエスト ルーティング構成と Ingress ゲートウェイの証明書を使用してサンプル アプリケーションをデプロイします。

  1. Cloud Shell で、サンプル アプリケーション リソースの名前空間を作成します。

    cat << EOF > sample-app-namespace.yaml
    apiVersion: v1
    kind: Namespace
    metadata:
      name: APP_NAMESPACE
      annotations:
        mesh.cloud.google.com/proxy: '{"managed":"true"}'
      labels:
        istio.io/rev: asm-managed
    EOF
    
    kubectl apply --filename sample-app-namespace.yaml
    
    • APP_NAMESPACE は、サンプル アプリケーションの名前空間の名前です。例: sample-app

    アノテーション mesh.cloud.google.com/proxy を使用すると、名前空間に対してマネージド データプレーンが有効になります。

    ラベル istio.io/rev: asm-managed は、サンプル アプリケーションの名前空間にあるマネージド データプレーンの Regular リリース チャンネルを選択します。Rapid または Stable のリリース チャンネルを使用している場合は、このラベルの値を変更します。

  2. サンプル アプリケーションの Deployment リソースを作成します。

    cat << EOF > deployment.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: hello
      namespace: APP_NAMESPACE
      labels:
        app: hello
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: hello
      template:
        metadata:
          labels:
            app: hello
        spec:
          containers:
          - image: gcr.io/google-samples/hello-app:1.0
            name: hello-app
            ports:
            - containerPort: 8080
    EOF
    
    kubectl apply --filename deployment.yaml
    
  3. サンプル アプリケーションの Service リソースを作成します。

    cat << EOF > service.yaml
    apiVersion: v1
    kind: Service
    metadata:
      name: SERVICE_NAME
      namespace: APP_NAMESPACE
    spec:
      ports:
      - name: http-hello
        port: 8080
      selector:
        app: hello
      type: ClusterIP
    EOF
    
    kubectl apply --filename service.yaml
    
    • SERVICE_NAME は、サービスの名前です。(例: hello)です。
  4. 証明書の発行元を使用して、ドメイン名 hello.example.com の Certificate リソースを作成します。

    cat << EOF > certificate.yaml
    apiVersion: cert-manager.io/v1
    kind: Certificate
    metadata:
      name: hello-example-com-certificate
      namespace: GATEWAY_NAMESPACE
    spec:
      secretName: hello-example-com-credential
      commonName: hello.example.com
      dnsNames:
      - hello.example.com
      duration: 24h
      renewBefore: 8h
      issuerRef:
        group: cas-issuer.jetstack.io
        kind: GoogleCASIssuer
        name: gateway-cas-issuer
    EOF
    
    kubectl apply --filename certificate.yaml
    

    Certificate の名前空間が Ingress ゲートウェイの名前空間と一致する必要があります。サービス メッシュ全体に影響する可能性があるため、通常、この名前空間のリソースを変更できるのはプラットフォーム管理者のみです。cert-manager ツールにより、同じ名前空間に TLS 証明書の Secret リソースが作成されます。つまり、アプリケーション管理者は Ingress ゲートウェイ名前空間にアクセスする必要はありません。

    Certificate の dnsNames リストで別のホスト名を追加できます。これらのホスト名は、サブジェクトの代替名(SAN)として証明書に含まれています。

  5. サンプル アプリケーションの Gateway リソースを作成します。

    cat << EOF > gateway.yaml
    apiVersion: networking.istio.io/v1beta1
    kind: Gateway
    metadata:
      name: GATEWAY_NAME
      namespace: GATEWAY_NAMESPACE
    spec:
      selector:
        istio: ingressgateway
      servers:
      - hosts:
        - APP_NAMESPACE/hello.example.com
        port:
          name: https-hello
          number: 443
          protocol: HTTPS
        tls:
          credentialName: hello-example-com-credential
          mode: MUTUAL
    EOF
    
    kubectl apply --filename gateway.yaml
    
    • GATEWAY_NAME はゲートウェイ名です。例: hello
    • ゲートウェイの credentialName フィールドは、証明書の secretName フィールドと一致します。cert-manager ツールにより、CA Service の TLS 証明書を使用して Kubernetes Secret が作成されます。この証明書により、Ingress ゲートウェイは hello.example.com 宛ての TLS トラフィックを終端できます。

    Gateway マニフェストには、MUTUAL TLS(mTLS)を指定します。通常の TLS にゲートウェイを構成する場合は、代わりに Gateway の TLS モードを SIMPLE に設定します。

  6. サンプル アプリケーションの VirtualService リソースを作成します。

    cat << EOF > virtual-service.yaml
    apiVersion: networking.istio.io/v1beta1
    kind: VirtualService
    metadata:
      name: hello
      namespace: APP_NAMESPACE
    spec:
      hosts:
      - hello.example.com
      gateways:
      - GATEWAY_NAMESPACE/GATEWAY_NAME
      http:
      - route:
        - destination:
            host: SERVICE_NAME
            port:
              number: 8080
    EOF
    
    kubectl apply --filename virtual-service.yaml
    

    Gateway と VirtualService の名前空間は異なります。この共通パターンでは、Ingress ゲートウェイ名前空間内のリソースを変更する権限を持つプラットフォーム管理者にのみ、Gateway のホストベースのルーティングの変更が許可されます。

    サンプル アプリケーションの名前空間で VirtualService を編集する権限を持つアプリケーション管理者は、プラットフォーム管理者と調整を行わずに、URL パスなどの他のリクエスト フィールドによるルーティングを変更できます。

他の構成オプションを確認する場合は、CertificateGatewayVirtualService リソースの API ドキュメントをご覧ください。

Ingress ゲートウェイを経由してサービス メッシュに到達するトラフィックに、認証ポリシーと認可ポリシーを適用できます。方法については、Istio PeerAuthentication API と AuthorizationPolicy API のドキュメントをご覧ください。

問題の解決を確認する

このセクションでは、サービス メッシュの外部からサンプル アプリケーションに mTLS を使用して HTTPS リクエストを送信できることを確認します。これを確認するには、Compute Engine VM インスタンスを作成して、CA Service からクライアント TLS 証明書をリクエストし、その証明書を使用してサンプル アプリケーションへのリクエストを認証します。

VM インスタンスへの SSH アクセスが必要です。デフォルト ネットワークには、SSH アクセスを許可するファイアウォール ルールが含まれています。SSH でアクセスできない場合は、ファイアウォール ルールのドキュメントに沿って、ポート 22 で受信 TCP 接続を許可するファイアウォール ルールを作成してください。

  1. Cloud Shell で、Google サービス アカウントを作成します。

    gcloud iam service-accounts create CLIENT_VM_GSA \
        --display-name "CA Service tutorial VM instance service account"
    
    • CLIENT_VM_GSA は、Google サービス アカウントの名前です。たとえば、cas-tutorial-client です。

    この Google サービス アカウントを Compute Engine VM インスタンスに割り当てます。

  2. Google サービス アカウントにゲートウェイの下位 CA プールの CA サービス証明書リクエスト元ロールを付与します。

    gcloud privateca pools add-iam-policy-binding SUBORDINATE_CA_POOL_GATEWAYS \
        --location CA_LOCATION \
        --member "serviceAccount:CLIENT_VM_GSA@PROJECT_ID.iam.gserviceaccount.com" \
        --role roles/privateca.certificateRequester
    

    このロールは、CA プールの証明書をリクエストする権限を付与します。

  3. GKE クラスタと同じ VPC 内に Compute Engine VM インスタンスを作成します。

    gcloud compute instances create cas-tutorial-client \
        --scopes cloud-platform \
        --service-account CLIENT_VM_GSA@PROJECT_ID.iam.gserviceaccount.com \
        --zone ZONE
    

    VM インスタンスが CA Service API にアクセスするには、cloud-platform スコープが必要です。

  4. Ingress ゲートウェイの内部パススルー ネットワーク ロードバランサの IP アドレスをファイルに保存します。

    kubectl get services istio-ingressgateway \
       --namespace GATEWAY_NAMESPACE \
       --output jsonpath='{.status.loadBalancer.ingress[0].ip}' > ilb-ip.txt
    
  5. ルート CA の公開鍵証明書をファイルに保存します。

    gcloud privateca roots describe ROOT_CA \
        --location CA_LOCATION \
        --pool ROOT_CA_POOL \
        --format 'value(pemCaCertificates)' > root-ca-cert.pem
    
  6. ルート CA 証明書と Ingress ゲートウェイの内部パススルー ネットワーク ロードバランサの IP アドレスを含むファイルを VM インスタンスにコピーします。

    gcloud compute scp root-ca-cert.pem ilb-ip.txt cas-tutorial-client:~ \
       --zone ZONE
    
  7. SSH を使用して VM インスタンスに接続します。

    gcloud compute ssh cas-tutorial-client --zone ZONE
    

    このセクションの残りのコマンドは、SSH セッションから実行します。

  8. ca-certificates パッケージ、coreutils パッケージ、curlopenssljq コマンドライン ツールをインストールします。

    sudo apt-get update --yes
    
    sudo apt-get install --yes ca-certificates coreutils curl jq openssl
    
  9. クライアント TLS 証明書の鍵ペアを作成します。

    openssl genrsa -out private-key.pem 2048
    
    openssl rsa -in private-key.pem -pubout -out public-key.pem
    
  10. メタデータ サーバーにクエリを実行し、VM インスタンスに接続している Google サービス アカウント ID のメールアドレスを取得します。

    GSA_EMAIL=$(curl --silent --header "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email)
    
  11. Certificate Authority サービス API からクライアント TLS 証明書をリクエストするときに、リクエスト本文として使用する JSON ファイルを作成します。

    cat << EOF > request.json
    {
      "config": {
        "publicKey": {
          "format": "PEM",
          "key": "$(base64 --wrap 0 public-key.pem)"
        },
        "subjectConfig": {
          "subject": {
            "commonName": "$(hostname --short)",
            "organization": "Example Organization"
          },
          "subjectAltName": {
            "dnsNames": [
              "$(hostname --fqdn)"
            ],
            "emailAddresses": [
              "$GSA_EMAIL"
            ]
          }
        },
        "x509Config": {
          "caOptions": {
            "isCa": false
          },
          "keyUsage": {
            "baseKeyUsage": {
              "digitalSignature": true,
              "keyEncipherment": true
            },
            "extendedKeyUsage": {
              "clientAuth": true
            }
          }
        }
      },
      "lifetime": "86400s"
    }
    EOF
    

    構成セクションのフィールドの詳細については、CA サービス API ドキュメントの CertificateConfig タイプをご覧ください。

  12. メタデータ サーバーOAuth 2.0 アクセス トークンをリクエストします。

    TOKEN=$(curl --silent --header "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token | jq --raw-output ".access_token")
    

    このアクセス トークンにより、VM インスタンスに接続している Google サービス アカウントに付与された権限が提供されます。

  13. CA サービス API のクライアント TLS 証明書をリクエストし、レスポンスの本文をファイルに保存します。

    curl --silent --request POST \
        --header "Authorization: Bearer $TOKEN" \
        --header "Content-Type: application/json" \
        --data @request.json \
        --output response.json \
        "https://privateca.googleapis.com/v1/projects/PROJECT_ID/locations/CA_LOCATION/caPools/SUBORDINATE_CA_POOL_GATEWAYS/certificates"
    

    このコマンドでは、アクセス トークンを使用して API リクエストを認証しています。

  14. クライアント証明書と証明書チェーンをファイルに保存します。

    jq --raw-output --join-output ".pemCertificate , .pemCertificateChain[]" response.json > client-cert-chain.pem
    
  15. curl を使用して、VM インスタンスからサンプル アプリケーションに HTTPS リクエストを送信します。

    curl --cert client-cert-chain.pem --key private-key.pem \
        --cacert root-ca-cert.pem \
        --resolve hello.example.com:443:$(cat ilb-ip.txt) \
        --silent https://hello.example.com | head -n1
    

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

    Hello, world!
    

    このレスポンスは、curl が mTLS を使用して HTTPS リクエストを送信したことを示しています。サンプル アプリケーションは、ターミナルの出力に表示されるメッセージで応答しています。

    curl コマンドは、次のことを行います。

    • --cert フラグと --key フラグを指定すると、curl はクライアントの TLS 証明書と秘密鍵を使用してリクエストを認証します。クライアント証明書ファイルには、クライアント証明書からルート CA までの完全な証明書チェーンが含まれています。

    • --cacert フラグを指定すると、このチュートリアルで作成したルート CA またはその下位 CA のいずれかがサーバー証明書を発行したことを curl に確認するように指示します。

      このフラグを省略すると、curl は、オペレーティング システムのデフォルト CA バンドル(Debian の ca-certificates パッケージなど)を使用してサーバー証明書の検証を試みます。デフォルトの CA バンドルに、このチュートリアルで作成したルート CA が含まれていないため、検証は失敗します。

    • --resolve フラグは、ポート 443 で hello.example.com をホストするリクエストの宛先として、内部パススルー ネットワーク ロードバランサの IP アドレスを使用するように curl に指示します。

      このフラグを省略すると、curl は DNS を使用して hello.example.com ホスト名を解決しようとします。このホスト名の DNS エントリがないため、DNS の解決は失敗します。

      独自の環境では、内部パススルー ネットワーク ロードバランサの IP アドレス($LOAD_BALANCER_IP)を指す DNS A レコードを作成することをおすすめします。レコードの管理のドキュメントに従って、Cloud DNS を使用してこのレコードを作成します。

    • --silent フラグを指定すると、ターミナル出力でレスポンスのダウンロード進捗状況レポートが抑制されます。

    • このコマンドは、curl 出力を head -n1 にパイプします。その結果、ターミナルの出力にはレスポンスの本文の最初の行だけが含まれます。

  16. SSH セッションを終了します。

    exit
    

このセクションでは、クライアント TLS 証明書を CA サービス API から直接リクエストしました。クライアントが別の Kubernetes クラスタ内の別のサービス メッシュの下り(外向き)ゲートウェイである場合は、同じルート CA を持つ cert-manager ツールと Certificate Authority サービス発行元を使用して、Egress ゲートウェイにクライアント証明書を提供できます。

場合によっては、Hashicorp Vault、Terraform、gcloud などのツールを使用して、サービス メッシュ外のワークロードのクライアント TLS 証明書をリクエストすることもできます。サンプル ソリューションの詳細については CA Service のドキュメントを、CA Service の詳細については gcloud のドキュメントをご覧ください。

(オプション)CA 証明書をトラストストアに追加する

このセクション(オプション)では、Linux の Debian ディストリビューションの信頼できる CA 証明書のストアに CA 証明書を追加する方法を説明します。以下の手順は、Ubuntu などの Debian から生成されたディストリビューションにも適用されます。

このストアに CA 証明書を追加すると、curl、Python、Go、Ruby を使用して HTTPS リクエストを送信する場合に、信頼できる CA 証明書のロケーションを指定する必要がなくなります。

  1. SSH を使用して VM インスタンスに接続します。

    gcloud compute ssh cas-tutorial-client --zone ZONE
    

    このセクションの残りのコマンドは、SSH セッションから実行します。

  2. ルート CA 証明書をディレクトリ /usr/local/share/ca-certificates にコピーし、ファイルの拡張子が .crt であることを確認します。

    sudo cp root-ca-cert.pem /usr/local/share/ca-certificates/cas-rootca.crt
    
  3. すべてのユーザーがルート CA 証明書ファイルを読み取れるように、ファイル権限を設定します。

    sudo chmod 644 /usr/local/share/ca-certificates/cas-rootca.crt
    
  4. update-ca-certificates スクリプトを実行します。

    sudo update-ca-certificates
    

    このスクリプトでは、ディレクトリ /etc/ssl/certs 内の信頼できる証明書のセットとファイル /etc/ssl/certs/ca-certificates.crt に証明書を追加します。

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

    Updating certificates in /etc/ssl/certs...
    1 added, 0 removed; done.
    Running hooks in /etc/ca-certificates/update.d...
    done.
    
  5. curl を使用して、VM インスタンスからサンプル アプリケーションに HTTPS リクエストを送信します。

    curl --cert client-cert-chain.pem --key private-key.pem \
       --resolve hello.example.com:443:$(cat ilb-ip.txt) \
       --silent https://hello.example.com | head -n1
    

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

    Hello, world!
    

    このレスポンスは、curl が mTLS を使用して HTTPS リクエストを正常に送信し、デフォルトの CA 証明書ストアを使用して Ingress ゲートウェイのサーバー TLS 証明書を検証したことを示しています。

  6. SSH セッションを終了します。

    exit
    

トラブルシューティング

CA サービス発行元コントローラが TLS 証明書のシークレットを作成しない場合は、CA サービス発行元コントローラのログを表示します。

kubectl logs deployment/google-cas-issuer --namespace cert-manager

Cloud Service Mesh のインストールで問題が発生した場合は、asmcli ツールを使用して、Cloud プロジェクトと GKE クラスタを検証します

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

クリーンアップ

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

プロジェクトを削除する

  1. Cloud Shell で、プロジェクトを削除します。

    gcloud projects delete PROJECT_ID
    

リソースの削除

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

  1. Cloud Shell で、GKE Hub から GKE クラスタの登録を解除します。

    gcloud container hub memberships unregister CLUSTER_NAME \
        --gke-cluster ZONE/CLUSTER_NAME
    
  2. GKE クラスタを削除します。

    gcloud container clusters delete CLUSTER_NAME \
        --zone ZONE --async --quiet
    
  3. 下位 CA プールの IAM ポリシー バインディングを削除します。

    gcloud privateca pools remove-iam-policy-binding SUBORDINATE_CA_POOL_GATEWAYS \
        --location CA_LOCATION \
        --member "serviceAccount:CAS_ISSUER_GSA@PROJECT_ID.iam.gserviceaccount.com" \
        --role roles/privateca.certificateRequester
    
    gcloud privateca pools remove-iam-policy-binding SUBORDINATE_CA_POOL_GATEWAYS \
        --location CA_LOCATION \
        --member "serviceAccount:CLIENT_VM_GSA@PROJECT_ID.iam.gserviceaccount.com" \
        --role roles/privateca.certificateRequester
    
  4. 下位 CA とルート CA の無効化と削除のスケジュール設定を行います。

    gcloud privateca subordinates disable SUBORDINATE_CA_GATEWAYS \
        --location CA_LOCATION \
        --pool SUBORDINATE_CA_POOL_GATEWAYS \
        --quiet
    
    gcloud privateca subordinates delete SUBORDINATE_CA_GATEWAYS \
        --location CA_LOCATION \
        --pool SUBORDINATE_CA_POOL_GATEWAYS \
        --ignore-active-certificates \
        --quiet
    
    gcloud privateca subordinates disable SUBORDINATE_CA_SIDECARS \
        --location CA_LOCATION \
        --pool SUBORDINATE_CA_POOL_SIDECARS \
        --quiet
    
    gcloud privateca subordinates delete SUBORDINATE_CA_SIDECARS \
        --location CA_LOCATION \
        --pool SUBORDINATE_CA_POOL_SIDECARS \
        --ignore-active-certificates \
        --quiet
    
    gcloud privateca roots disable ROOT_CA \
        --location CA_LOCATION \
        --pool ROOT_CA_POOL \
        --quiet
    
    gcloud privateca roots delete ROOT_CA \
        --location CA_LOCATION \
        --pool ROOT_CA_POOL \
        --ignore-active-certificates \
        --quiet
    
  5. CA サービス発行元コントローラの Google サービス アカウントの IAM ポリシー バインディングを削除します。

    gcloud iam service-accounts remove-iam-policy-binding \
        CAS_ISSUER_GSA@PROJECT_ID.iam.gserviceaccount.com \
        --member "serviceAccount:PROJECT_ID.svc.id.goog[cert-manager/ksa-google-cas-issuer]" \
        --role roles/iam.workloadIdentityUser
    
  6. Google サービス アカウントを削除します。

    gcloud iam service-accounts delete --quiet \
        CAS_ISSUER_GSA@PROJECT_ID.iam.gserviceaccount.com
    
    gcloud iam service-accounts delete --quiet \
        CLIENT_VM_GSA@PROJECT_ID.iam.gserviceaccount.com
    
  7. 予約済みのロードバランサの IP アドレスを削除します。

    gcloud compute addresses delete asm-ingress-gateway-ilb \
        --region REGION --quiet
    
  8. Compute Engine VM インスタンスを削除する:

    gcloud compute instances delete cas-tutorial-client \
        --zone ZONE --quiet
    

次のステップ