Anthos Service Mesh を使用した Anthos clusters 間の通信の保護と暗号化

Last reviewed 2021-04-30 UTC

このチュートリアルでは、Anthos Service Mesh の下り(外向き)ゲートウェイと上り(内向き)ゲートウェイを使用して、mTLS(mutual Transport Layer Security)でクラスタ間のトラフィックを保護する方法について説明します。このチュートリアルは、ネットワーク、セキュリティ、プラットフォームを担当する Kubernetes クラスタ管理者を対象としています。ここで説明する制御は、セキュリティ要件が厳しい組織や規制の前提条件を満たす場合に特に役立ちます。このチュートリアルには、関連するコンセプト ガイドが付属しています。

このチュートリアルは、Kubernetes と Anthos Service Mesh に精通していることを前提としています。

目標

  • Terraform を使用してインフラストラクチャを設定します。
    • 2 つのプライベート サブネットを持つカスタム VPC ネットワークを作成します。
    • Anthos Service Mesh を有効にして 2 つの Kubernetes クラスタを作成します。
    • クラスタを GKE Hub に登録します。
  • GKE クラスタに MySQL クライアントをデプロイします。
  • kOps クラスタに MySQL サーバーをデプロイします。
  • mTLS を使用してサーバーを公開するように下り(外向き)ゲートウェイと上り(内向き)ゲートウェイを構成します。
  • さまざまなクラスタまたは VPC で実行されている MySQL クライアントを使用して、MySQL サーバーへのアクセスをテストします。

費用

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

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

このドキュメントに記載されているタスクの完了後、作成したリソースを削除すると、それ以上の請求は発生しません。詳細については、クリーンアップをご覧ください。

始める前に

このチュートリアルでは、Google Cloud プロジェクトが必要です。新しいプロジェクトを作成することも、すでに作成済みのプロジェクトを選択することもできます。

  1. Google Cloud コンソールでプロジェクトの選択ページに移動します。

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

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

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

  4. Google Cloud コンソールで、Cloud Shell に移動します。

    Cloud Shell に移動

    Google Cloud コンソールの下部で Cloud Shell セッションが開き、コマンドライン プロンプトが表示されます。Cloud Shell は、Google Cloud CLI など、Google Cloud CLI がすでにインストールされているシェル環境です。セッションが初期化されるまで数秒かかることがあります。

  5. Cloud Shell では、作成または選択したプロジェクトで作業していることを確認します。
    export PROJECT_ID=PROJECT_ID
    gcloud config set project ${PROJECT_ID}
    

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

  6. Google Cloud に使用するメールアドレスの環境変数を作成します。
    export GOOGLE_CLOUD_EMAIL_ADDRESS=GOOGLE_CLOUD_EMAIL_ADDRESS
    

    GOOGLE_CLOUD_EMAIL_ADDRESS は、Google Cloud で使用するメールアドレスに置き換えてください。

  7. コンピューティング リソースのリージョンとゾーンを設定します。
    export REGION=us-central1
    export ZONE=us-central1-b
    gcloud config set compute/region ${REGION}
    gcloud config set compute/zone ${ZONE}
    

    このチュートリアルでは、リージョンに us-central1 を使用し、ゾーンに us-central1-b を使用します。選択したリージョンにデプロイできます。

  8. 必要な Identity and Access Management(IAM)のロールを設定します。プロジェクト オーナーの場合は、インストールを完了するために必要なすべての権限が付与されます。それ以外の場合は、Cloud Shell で次のコマンドを実行して、管理者に Identity and Access Management(IAM)のロールを付与するよう依頼してください。
    ROLES=(
    'roles/container.admin' \
    'roles/gkehub.admin' \
    'roles/iam.serviceAccountAdmin' \
    'roles/iam.serviceAccountKeyAdmin' \
    'roles/resourcemanager.projectIamAdmin' \
    'roles/compute.securityAdmin' \
    'roles/compute.instanceAdmin' \
    'roles/storage.admin' \
    'roles/serviceusage.serviceUsageAdmin'
    )
    for role in "${ROLES[@]}"
    do
     gcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member "user:${GOOGLE_CLOUD_EMAIL_ADDRESS}" \
      --role="$role"
    done
    
  9. チュートリアルに必要な API を有効にします。
    gcloud services enable \
        anthos.googleapis.com \
        anthosgke.googleapis.com \
        anthosaudit.googleapis.com \
        compute.googleapis.com \
        container.googleapis.com \
        cloudresourcemanager.googleapis.com \
        serviceusage.googleapis.com \
        stackdriver.googleapis.com \
        monitoring.googleapis.com \
        logging.googleapis.com \
        cloudtrace.googleapis.com \
        meshca.googleapis.com \
        meshconfig.googleapis.com \
        iamcredentials.googleapis.com \
        gkeconnect.googleapis.com \
        gkehub.googleapis.com
    

環境の準備

  1. Cloud Shell で、次のリポジトリのクローンを作成します。

    git clone https://github.com/GoogleCloudPlatform/anthos-service-mesh-samples
    cd anthos-service-mesh-samples/docs/mtls-egress-ingress
    
  2. お使いの環境の Terraform を更新します。デフォルトでは、Google Cloud コンソールには Terraform 0.12 が付属しています。このチュートリアルでは、Terraform 0.13.5 以降がインストールされていることを前提としています。次のコマンドを実行して、一時的に別のバージョンの Terraform を使用できます。

    mkdir ~/bin
    curl https://releases.hashicorp.com/terraform/0.13.5/terraform_0.13.5_linux_amd64.zip -o ~/bin/terraform.zip
    unzip ~/bin/terraform.zip -d ~/bin/
    
  3. terraform サブフォルダに移動して、Terraform を初期化します。

    cd terraform/
    ~/bin/terraform init
    
  4. 前に作成した環境変数に基づいて terraform.tfvars ファイルを作成します。

    cat << EOF > terraform.tfvars
    project_id = "${PROJECT_ID}"
    region = "${REGION}"
    zones = ["${ZONE}"]
    EOF
    

    次のステップでは、初期インフラストラクチャを作成します。これを行うには、この構成の Terraform 実行プランを作成して適用します。このプランのスクリプトとモジュールにより、次のものが作成されます。

    • 2 つのプライベート サブネットを持つカスタム VPC ネットワーク
    • Anthos Service Mesh が有効になっている 2 つの Kubernetes クラスタ
    • 1 つの GKE クラスタ
    • カスタム VPC ネットワークで実行される 1 つの kOps クラスタ

    この実行プランでは GKE Hub にもクラスタを登録します。

  5. 実行プランを実行します。

    ~/bin/terraform plan -out mtls-terraform-plan
    ~/bin/terraform apply "mtls-terraform-plan"
    

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

    Apply complete! Resources: 27 added, 0 changed, 0 destroyed.
    Outputs:
    server_token = <sensitive>
    

    <sensitive> 部分は、コンソールには表示されませんが、たとえば、~/bin/terraform output server_token のようにクエリできます。

  6. terraform ディレクトリからサーバー クラスタの kubeconfig ファイルを取得します。次に、取得したファイルを client-cluster 構成ファイルとマージします。

    cd ..
    export KUBECONFIG=client-server-kubeconfig
    cp ./terraform/server-kubeconfig $KUBECONFIG
    gcloud container clusters get-credentials client-cluster --zone ${ZONE} --project ${PROJECT_ID}
    

    これで、client-server-kubeconfig ファイルに両方のクラスタの構成が保持されるようになりました。この構成を確認するには、次のコマンドを実行します。

    kubectl config view -ojson | jq -r '.clusters[].name'
    

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

    gke_PROJECT_ID_us-central1-c_client-cluster
    server-cluster.k8s.local
    
  7. 後で使用できるように、2 つのクラスタのコンテキストを取得します。

    export CLIENT_CLUSTER=$(kubectl config view -ojson | jq -r '.clusters[].name' | grep client)
    export SERVER_CLUSTER=$(kubectl config view -ojson | jq -r '.clusters[].name' | grep server)
    echo -e "${CLIENT_CLUSTER}\n${SERVER_CLUSTER}"
    

    この場合も、出力は次のようになります。

    gke_PROJECT_ID_us-central1-c_client-cluster
    server-cluster.k8s.local
    

    これで、これらのクラスタ名を今後の kubectl コマンドのコンテキストとして使用できるようになりました。

  8. クライアント クラスタを参照します。

    kubectl --context ${CLIENT_CLUSTER} get pods -n istio-system
    
  9. サーバー クラスタを参照します。

    kubectl --context ${SERVER_CLUSTER} get pods -n istio-system
    

クライアント側を構成する

コンセプト ガイドで説明されているように、クライアント側では、Anthos Service Mesh で下り(外向き)ゲートウェイを構成する必要があります。

このセクションでは、送信元に基づいて外部トラフィックを識別し、カスタム証明書を使用して通信を暗号化するために、Anthos Service Mesh 要素を構成します。さらに、そのトラフィックのみを宛先(コンテナ内の MySQL DB)にルーティングする必要があります。通常、これを行うには Kubernetes の Service を使用します。この場合、そのトラフィックをメッシュ通信内でキャッチする必要があります。トラフィックをキャッチするには、Istio 要素を使用して Service の特別な定義を作成します。次の要素を定義します。

  • 下り(外向き)ゲートウェイ
  • サービス エントリ
  • 仮想サービス
  • TLS 証明書(Secret として)
  • 宛先ルール
  1. Cloud Shell で、サーバー側のコンテキスト(先ほど作成した $SERVER_CLUSTER)を使用して istio-ingressgateway サービスのロードバランサの IP アドレスに対するクエリを実行し、サーバー側の上り(内向き)ゲートウェイの IP アドレスを取得します。

    INGRESS_HOST=$(kubectl -n istio-system --context ${SERVER_CLUSTER} get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    

    INGRESS_HOST はホストの IP アドレス部分でしかないため、完全修飾ドメイン名(FQDN)を作成する必要があります。この手順が必要なのは、証明書が適切に機能するためにはドメイン名が必要であるためです。

    このチュートリアルでは、ワイルドカード DNS サービス nip.io を使用して、上り(内向き)IP アドレスの FQDN を作成します。このサービスによって、ドメインを所有せずに FQDN を作成できます。

  2. 環境変数に FQDN サービス URL を格納します。

    export SERVICE_URL="${INGRESS_HOST}.nip.io"
    

    これで、SERVICE_URL を FQDN として定義したため、クライアント クラスタの Istio の部分の定義を開始できます。

下り(外向き)ゲートウェイを作成する

最初に、外部サービスに対して送信されたトラフィックをリッスンする下り(外向き)ゲートウェイを作成します。

外部サービスに対して送信されたトラフィックをリッスンする下り(外向き)ゲートウェイ。

  1. Cloud Shell で、次の YAML ファイルを作成し、client-egress-gateway.yaml という名前を付けます。

    cat <<EOF > client-egress-gateway.yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: Gateway
    metadata:
     name: istio-egressgateway-mysql
    spec:
     selector:
       istio: egressgateway
     servers:
     - port:
         number: 15443
         name: tls
         protocol: TLS
       hosts:
       - $SERVICE_URL
       tls:
         mode: ISTIO_MUTUAL
    EOF
    
  2. 前述の YAML ファイルをクライアント クラスタに適用します。

    kubectl --context ${CLIENT_CLUSTER} apply -f client-egress-gateway.yaml
    

    ポートに注意してください。ここでは、下り(外向き)サーバー スイッチに default ポート(15443)を使用しました。別のポートを使用する場合は、下り(外向き)ゲートウェイの service オブジェクトを編集してカスタムポートを追加する必要があります。

    hosts スイッチは、トラフィックの行き先を表示するエンドポイントを定義します。

サービス エントリを定義する

次のステップでは、外部サービスの情報をサービス メッシュに提供します。Istio には、メッシュのサービス エンドポイントが保存される独自のレジストリがあります。Istio が Kubernetes 上にインストールされている場合、クラスタで定義されたサービスは Istio レジストリに自動的に追加されます。次の図に示すように、サービス エントリの定義を使用して、Istio レジストリに新しいエンドポイントを追加します。

サービス エントリ定義を使用して、Istio レジストリにエンドポイントを追加。

  1. Cloud Shell で、次の YAML ファイルを作成し、client-service-entry.yaml という名前を付けます。

    cat <<EOF > client-service-entry.yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: ServiceEntry
    metadata:
     name: mysql-external
    spec:
     hosts:
       - $SERVICE_URL
     location: MESH_EXTERNAL
     ports:
       - number: 3306
         name: tcp
         protocol: TCP
       - number: 13306
         name: tls
         protocol: TLS
     resolution: DNS
     endpoints:
       - address: $SERVICE_URL
         ports:
           tls: 13306
    EOF
    
  2. 前述の YAML ファイルをクライアント クラスタに適用します。

    kubectl --context ${CLIENT_CLUSTER} apply -f client-service-entry.yaml
    

    この YAML ファイルのクライアント サービス定義は、予想されるトラフィックの種類(MySQL L4 - Network Layer 4、ポート 3306 を使用)をサービスに指示します。また、通信が「メッシュ外部」に対して行われるように定義します。エンドポイント セクションで、先ほど設定した FQDN アドレス $SERVICE_URL(サーバー クラスタ(kOps)の上り(内向き)ゲートウェイにマッピングされます)に、フローが配置されることを定義します。

仮想サービスを定義する

仮想サービスは、ホストがアドレス指定されたときに適用される一連のトラフィック ルーティング ルールです。各ルーティング ルールは、特定のプロトコルのトラフィックの一致条件を定義します。トラフィックが一致した場合は、レジストリで定義された名前付き宛先サービス(またはそのサブセットまたはバージョン)に送信されます。詳細については、Istio のドキュメントをご覧ください。

外部サービスに到達するトラフィックのルーティングを適用する方法について Istio に指示する仮想サービスを定義する。

仮想サービスの定義は、外部サービスに到達するトラフィックのルーティングを適用する方法を Istio に指示します。次の定義を使用して、クライアントから下り(外向き)ゲートウェイのポート 15443 にトラフィックをルーティングするようメッシュに指示します。下り(外向き)ゲートウェイから、ポート 13306 のホスト $SERVICE_URL にトラフィックをルーティングします。このポートでは、サーバー側の上り(内向き)ゲートウェイがリッスンしています。

  1. 次の YAML ファイルを作成し、client-virtual-service.yaml という名前を付けます。

    cat <<EOF > client-virtual-service.yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
     name: direct-mysql-through-egress-gateway
    spec:
     hosts:
       - $SERVICE_URL
     gateways:
       - istio-egressgateway-mysql
       - mesh
     tcp:
       - match:
           - gateways:
               - mesh
             port: 3306
         route:
           - destination:
               host: istio-egressgateway.istio-system.svc.cluster.local
               subset: mysql
               port:
                 number: 15443
             weight: 100
       - match:
           - gateways:
               - istio-egressgateway-mysql
             port: 15443
         route:
           - destination:
               host: $SERVICE_URL
               port:
                 number: 13306
             weight: 100
    EOF
    
  2. YAML 定義をクライアント クラスタに適用します。

    kubectl --context ${CLIENT_CLUSTER} apply -f client-virtual-service.yaml
    

    YAML ファイルで gateways スイッチを編集することで、構成を適用するゲートウェイを指定できます。

    この定義で重要なのは、メッシュ内のすべてのサイドカーを意味する予約語 mesh を使用することです。Istio のドキュメントによると、このフィールドを省略すると、デフォルト ゲートウェイ(メッシュ)が使用され、メッシュ内のすべてのサイドカーにルールが適用されます。ゲートウェイ名のリストを指定すると、ルールは指定したゲートウェイにのみ適用されます。ルールをゲートウェイとサイドカーに適用するには、ゲートウェイ名の 1 つとして mesh を指定します。

次のセクションでは、クライアント prod のプロキシ match.gateways.mesh から発信されたトラフィックの処理方法を定義します。また、match.gateways.istio-egressgateway-mysql スイッチを使用して、下り(外向き)から外部サービスへトラフィックをルーティングする方法も定義します。

クライアントから下り(外向き)ゲートウェイへの宛先ルールを定義する

これで、トラフィックを外部サービスにルーティングする方法を定義できました。次に、適用するトラフィック ポリシーを定義する必要があります。先ほど定義した仮想サービスは、2 つのルーティング ケースを同時に処理します。1 つはサイドカー プロキシから下り(外向き)ゲートウェイへのトラフィックを処理し、もう 1 つは下り(外向き)ゲートウェイから外部サービスへのトラフィックを処理します。

このようなケースと宛先ルールを照合するには、2 つの個別のルールが必要です。次の図は、プロキシから下り(外向き)ゲートウェイへのトラフィックを処理する最初のルールを示しています。この定義では、Anthos Service Mesh に mTLS 通信用のデフォルトの証明書を使用するように指示します。

サイドカー プロキシから下り(外向き)ゲートウェイへのトラフィックの処理方法を定義する宛先ルール。

  1. Cloud Shell で、次の YAML ファイルを作成し、client-destination-rule-to-egress-gateway.yaml という名前を付けます。

    cat <<EOF > client-destination-rule-to-egress-gateway.yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: DestinationRule
    metadata:
      name: egressgateway-for-mysql
    spec:
      host: istio-egressgateway.istio-system.svc.cluster.local
      subsets:
        - name: mysql
          trafficPolicy:
            loadBalancer:
              simple: ROUND_ROBIN
            portLevelSettings:
              - port:
                  number: 15443
                tls:
                  mode: ISTIO_MUTUAL
                  sni: $SERVICE_URL
    EOF
    
  2. 前述の YAML 定義をクライアント クラスタに適用します。

    kubectl --context ${CLIENT_CLUSTER} apply -f client-destination-rule-to-egress-gateway.yaml
    

    前述の YAML ファイルでは、hosts スイッチを使用して、クライアント プロキシから下り(外向き)ゲートウェイにトラフィックをルーティングする方法を定義しました。また、下り(外向き)ゲートウェイで自動的に開いているポートの 1 つであるポート 15443ISTIO_MUTUAL を使用するようにメッシュの構成も行いました。

下り(外向き)ゲートウェイから外部サービスへの宛先ルールを作成する

次の図は、2 番目の宛先ルールを示しています。これは、下り(外向き)ゲートウェイから外部サービスへのトラフィックの処理方法をメッシュに指示します。

下り(外向き)ゲートウェイから外部サービスへのトラフィックの処理方法を定義する 2 番目の宛先ルール。

追加された証明書を外部サービスとの相互 TLS 通信に使用するように、メッシュに指示する必要があります。

  1. Cloud Shell で、anthos-service-mesh-samples/docs/mtls-egress-ingress ディレクトリから証明書を作成します。

     ./create-keys.sh
    

    スクリプトでパスワードを求められたときには、必ずパスワードを入力してください。

  2. 生成されたファイルを現在のディレクトリにコピーします。

    cp ./certs/2_intermediate/certs/ca-chain.cert.pem ca-chain.cert.pem
    cp ./certs/4_client/private/$SERVICE_URL.key.pem client-$SERVICE_URL.key.pem
    cp ./certs/4_client/certs/$SERVICE_URL.cert.pem client-$SERVICE_URL.cert.pem
    
  3. 証明書を格納する Kubernetes Secret を作成し、後でこの証明書をゲートウェイで参照できるようにします。

     kubectl --context ${CLIENT_CLUSTER} create secret -n istio-system \
      generic client-credential \
      --from-file=tls.key=client-$SERVICE_URL.key.pem \
      --from-file=tls.crt=client-$SERVICE_URL.cert.pem \
      --from-file=ca.crt=ca-chain.cert.pem
    

    前述のコマンドにより、次の証明書ファイルが Secret に追加されます。

    client-$SERVICE_URL.key.pem
    client-$SERVICE_URL.cert.pem
    ca-chain.cert.pem
    

    ここでは、現在のベスト プラクティスに従って、ファイル マウントではなく Secret Discovery Service(SDS)を使用して、証明書が配布されます。これにより、新しい証明書を追加するときに Pod が再起動されることを回避します。Istio 1.8/1.9 以降では、この方法により、ゲートウェイの Secret の読み取りアクセス権(RBAC)が不要になりました。

  4. 証明書を DestinationRule に追加し、client-destination-rule-to-external-service.yaml と名付けます。

    cat <<EOF > client-destination-rule-to-external-service.yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: DestinationRule
    metadata:
     name: originate-mtls-for-mysql
    spec:
     host: $SERVICE_URL
     trafficPolicy:
       loadBalancer:
         simple: ROUND_ROBIN
       portLevelSettings:
       - port:
           number: 13306
         tls:
           mode: MUTUAL
           credentialName: client-credential
           sni: $SERVICE_URL
    EOF
    
  5. 前述の YAML 定義をクライアント クラスタに適用します。

    kubectl --context ${CLIENT_CLUSTER} apply -f client-destination-rule-to-external-service.yaml
    

    このルールは、事前に Secret を作成した場合にのみ機能します。Secret により、下り(外向き)ゲートウェイから外部エンドポイントへの mTLS 暗号化に証明書が使用されます。

これらの手順を完了すると、クライアント側の設定が終了し次の図のようになります。

クライアント側の最終設定。

サーバー側を構成する

コンセプト ガイドで説明したように、サーバー側の Anthos Service Mesh で上り(内向き)ゲートウェイを構成する必要があります。必要なファイルを作成する前に、ソリューションのサーバー部分を実現するために必要なコンポーネントを確認することをおすすめします。

基本的には、送信元と証明書に基づいて受信トラフィックを識別します。また、そのトラフィックのみを宛先であるコンテナの MySQL DB にルーティングすることも可能です。これを行うには、通常、Kubernetes の Service を使用しますが、この例ではメッシュ コミュニケーションの受信トラフィックを識別するため、以下を含む Service の特別な定義が必要です。

  • TLS 証明書(Secret として)
  • 上り(内向き)ゲートウェイ
  • 仮想サービス

上り(内向き)ゲートウェイの Secret を作成する

下り(外向き)ゲートウェイの場合と同様に、上り(内向き)ゲートウェイの通信を保護するために同じ証明書が必要です。これを行うには、証明書を Secret に格納し、この Secret を上り(内向き)ゲートウェイ オブジェクトで定義します。

  1. Cloud Shell で、次のコマンドを実行する場所に、生成されたファイルをコピーします。

    cp ./certs/3_application/private/$SERVICE_URL.key.pem server-$SERVICE_URL.key.pem
    cp ./certs/3_application/certs/$SERVICE_URL.cert.pem server-$SERVICE_URL.cert.pem
    
  2. サーバー Secret を作成します。

    kubectl --context ${SERVER_CLUSTER} create secret -n istio-system \
        generic mysql-credential \
        --from-file=tls.key=server-$SERVICE_URL.key.pem \
        --from-file=tls.crt=server-$SERVICE_URL.cert.pem \
        --from-file=ca.crt=ca-chain.cert.pem
    

    次の証明書ファイルを Secret に追加しました。

    server-$SERVICE_URL.key.pem
    server-$SERVICE_URL.cert.pem
    ca-chain.cert.pem
    

上り(内向き)ゲートウェイを定義する

クライアント側クラスタからトラフィックを受信するには、証明書を使用して TLS 通信を復号して検証できる上り(内向き)ゲートウェイを指定する必要があります。

上り(内向き)ゲートウェイを定義して、トラフィックを検査し、セキュリティ基準と転送基準に適合しているかどうかを確認します。

この図は、上り(内向き)ゲートウェイがクラスタ内に存在する場所を示しています。トラフィックが通過すると、セキュリティ基準と転送基準を満たしているかどうか検査されます。

  1. Cloud Shell で、次の YAML ファイルを使用し、server-ingress-gatway.yaml という名前を付けます。

    cat <<EOF > server-ingress-gatway.yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: Gateway
    metadata:
     name: gateway-mysql
    spec:
     selector:
       istio: ingressgateway # Istio default gateway implementation
     servers:
     - port:
         number: 13306
         name: tls-mysql
         protocol: TLS
       tls:
         mode: MUTUAL
         credentialName: mysql-credential
       hosts:
       - "$SERVICE_URL"
    EOF
    
  2. 前述の YAML 定義をクライアント クラスタに適用します。

    kubectl --context ${SERVER_CLUSTER} apply -f server-ingress-gatway.yaml
    

    tls: は特に重要なセクションですので、注意してください。このセクションでは、mTLS が必要であることを定義します。これが意図したとおりに機能するように、証明書を含む作成した Secret を提供する必要があります。

  3. 上り(内向き)サービスをパッチ適用してポート 13306 を有効にします。このポートを有効にするには、次の JSON ファイルを作成し、gateway-patch.json という名前を付けます。

    cat <<EOF > gateway-patch.json
    [{
      "op": "add",
      "path": "/spec/ports/0",
      "value": {
        "name": "tls-mysql",
        "protocol": "TCP",
        "targetPort": 13306,
        "port": 13306
      }
    }]
    EOF
    
  4. ゲートウェイ サービスにパッチを適用します。

    kubectl --context ${SERVER_CLUSTER} -n istio-system patch --type=json svc istio-ingressgateway -p "$(cat gateway-patch.json)"
    

上り(内向き)ゲートウェイでポートを開いたら、新しい上り(内向き)ゲートウェイからトラフィックを受け取り、データベース Pod に転送する必要があります。これを行うには、メッシュの内部オブジェクト(仮想サービス)を使用します。

仮想サービスを定義する

前述のように、仮想サービスはメッシュ内のトラフィックを形成するトラフィック一致パターンの定義です。次の図に示すように、上り(内向き)ゲートウェイから MySQL DB サービスへのトラフィックを正しく識別して転送する必要があります。

上り(内向き)ゲートウェイから MySQL DB サービスへのトラフィックの識別と転送を行います。

  1. Cloud Shell で、次の YAML ファイルを作成し、server-virtual-service.yaml という名前を付けます。

    cat <<EOF > server-virtual-service.yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
     name: mysql-virtual-service
    spec:
     hosts:
       - "$SERVICE_URL"
     gateways:
       - gateway-mysql
     tcp:
       - route:
         - destination:
             port:
               number: 3306
             host: mysql.default.svc.cluster.local
    EOF
    

    この仮想サービスがトラフィックが来る上り(内向き)ゲートウェイを参照することが重要です。ゲートウェイは gateway-mysql という名前です。

    この仮想サービスは L4 で適用されるため、MySQL トラフィックを説明する tcp フラグを指定する必要があります。このフラグは、基本的に、このトラフィックに L4 が使用されていることをメッシュに通知します。

    上り(内向き)サービスではトラフィックを転送するためにポート 13306 が使用されていることにお気づきかと思います。仮想サービスは、その出力を選択して変換し 3306 に戻します。

    最後に、サーバーの Kubernetes クラスタ内の MySQL サーバーにトラフィックを転送します。この例では、サーバーは標準の MySQL ポート 3306 でリッスンしています。

  2. YAML 定義をサーバー クラスタに適用します。

    kubectl --context ${SERVER_CLUSTER} apply -f server-virtual-service.yaml
    

この 2 つの定義は、mTLS でカプセル化された MySQL クライアント リクエストを復号し、メッシュ内の MySQL データベースに転送します。

メッシュの内部転送も暗号化を使用して行われますが、この場合の暗号化はメッシュの内部証明書に基づいて行われることを理解することが重要です。mTLS 終端はゲートウェイで行われます。

これで、MySQL サーバーとの通信の完全に暗号化された相互通信方法ができました。この暗号化形式は MySQL クライアントとサーバーに対して透過的であるため、アプリケーションの変更は不要です。これにより、証明書の変更やローテーションなどのタスクが簡単になります。また、この通信方法は、多くのさまざまなシナリオで使用できます。

設定をテストする

クライアント側とサーバー側の配置が完了したため、設定をテストできます。

クライアント側でサーバー側からトラフィックの流れをテスト。

ここで、クライアント側からサーバー側へのトラフィックの一部を生成します。トラフィックのフローを追って、トラフィックのルーティングと暗号化、復号が想定どおりに行われることを確認します。

  1. Cloud Shell で、MySQL サーバーをサーバー クラスタにデプロイします。

    kubectl --context ${SERVER_CLUSTER} apply -f server/mysql-server/mysql.yaml
    
  2. クライアント クラスタで MySQL クライアントを開始します。

    kubectl run --context ${CLIENT_CLUSTER} --env=SERVICE_URL=$SERVICE_URL -it --image=mysql:5.6 mysql-client-1 --restart=Never -- /bin/bash
    

    コンテナが開始されると、次のようなシェルが表示されます。

    root@mysql-client-1:/#
    
  3. MySQL サーバーに接続します。

    mysql -pyougottoknowme -h $SERVICE_URL
    
  4. 次のコマンドを使用して、DB とテーブルを追加します。

    CREATE DATABASE test_encrypted_connection;
    USE test_encrypted_connection;
    CREATE TABLE message (id INT, content VARCHAR(20));
    INSERT INTO message (id,content) VALUES(1,"Crypto Hi");
    
  5. 接続してテーブルを追加したら、MySQL 接続と Pod を終了します。

    exit
    exit
    

    exit を 2 回入力する必要があります。1 回目は DB 接続を終了し、2 回目は Pod を終了するためです。終了時に Pod が応答しなくなった場合は、Ctrl+C キーを押して bash シェルをキャンセルして終了します。

この手順を行うことで、有意義なロギング出力を生成し、詳細に分析できます。

次のセクションでは、クライアント側トラフィックがプロキシと下り(外向き)ゲートウェイを通過していることを確認します。また、トラフィックが上り(内向き)ゲートウェイを通ってサーバー側に着信することを確認できるかどうかもテストします。

クライアント側プロキシと下り(外向き)ゲートウェイをテストする

  1. Cloud Shell のクライアント側プロキシで、Istio プロキシのログを表示できることを確認します。

    kubectl --context ${CLIENT_CLUSTER} logs -l run=mysql-client-1 -c istio-proxy -f
    

    デバッグ出力は次のようになります。

    [2021-02-10T21:19:08.292Z] "- - -" 0 - "-" "-" 176 115 10 - "-" "-" "-" "-" "192.168.1.4:15443" outbound|15443|mysql|istio-egressgateway.istio-system.svc.cluster.local 192.168.1.12:58614 34.66.165.46:3306 192.168.1.12:39642 - -
    

    ログ出力を終了するには、Ctrl+C キーを押します。

    このログエントリでは、クライアントが IP アドレス 34.66.165.46 のポート 3306 で実行されているサーバーをリクエストしていることが確認できます。リクエストは、IP アドレス 192.168.1.4 のポート 15443 をリッスンする istio-egressgateway に転送(outbound)されます。この転送は仮想サービス(client-virtual-service.yaml)で定義したものです。

  2. 下り(外向き)ゲートウェイ プロキシのログを読み取ります。

    kubectl --context ${CLIENT_CLUSTER} logs -n istio-system -l app=istio-egressgateway -f
    

    デバッグ出力は次のようになります。

    [2021-02-10T21:19:08.292Z] "- - -" 0 - "-" "-" 176 115 19 - "-" "-" "-" "-" "34.66.165.46:13306" outbound|13306||34.66.165.46.nip.io 192.168.1.4:53542 192.168.1.4:15443 192.168.1.12:58614 34.66.165.46.nip.io -
    

    ログ出力を終了するには、Ctrl+C キーを押します。

    このログエントリでは、IP アドレス 192.168.1.4 のポート 15443 をリッスンする istio-egressgateway にルーティングされたクライアント リクエストが、さらに IP アドレス 34.66.165.46 のポート 13306. をリッスンするサービス メッシュ外部に存在する外部サービスにルーティングされることを確認できます。この転送は、仮想サービス(client-virtual-service.yaml)の 2 つ目のパートで定義したものです。

サーバー側の上り(内向き)ゲートウェイをテストする

  1. Cloud Shell のサーバー側で、上り(内向き)ゲートウェイ プロキシのログを表示します。

    kubectl --context ${SERVER_CLUSTER} logs -n istio-system -l app=istio-ingressgateway -f
    

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

    [2021-02-10T21:22:27.381Z] "- - -" 0 - "-" "-" 0 78 5 - "-" "-" "-" "-" "100.96.4.8:3306" outbound|3306||mysql.default.svc.cluster.local 100.96.1.3:55730 100.96.1.3:13306 100.96.1.1:42244 34.66.165.46.nip.io -
    

    ログ出力を終了するには、Ctrl+C キーを押します。

    このログエントリでは、IP アドレス 34.66.165.46 のポート 13306 をリッスンしている istio-ingressgateway にルーティングされた外部クライアント リクエストが、ポート 3306. のサービス名 mysql.default.svc.cluster.local によって識別されたメッシュ内の MySQL サービスにさらにルーティングされていることを確認できます。この転送は、上り(内向き)ゲートウェイ(server-ingress-gateway.yaml)で定義したものです。

  2. MySQL サーバーの場合、Istio プロキシログを表示します。

    kubectl --context ${SERVER_CLUSTER} logs -l app=mysql -c istio-proxy -f
    

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

    [2021-02-10T21:22:27.382Z] "- - -" 0 - "-" "-" 1555 1471 4 - "-" "-" "-" "-" "127.0.0.1:3306" inbound|3306|mysql|mysql.default.svc.cluster.local 127.0.0.1:45894 100.96.4.8:3306 100.96.1.3:55730 outbound_.3306_._.mysql.default.svc.cluster.local -
    

    ログ出力を終了するには、Ctrl+C キーを押します。

    このログエントリで、IP アドレス 100.96.4.8 のポート 3306 をリッスンしている MySQL データベース サーバーへの受信呼び出しを確認できます。呼び出しは、IP アドレス 100.96.1.3 の上り(内向き)Pod から行われます。

    詳細については、Envoy のアクセスログを取得するをご覧ください。

  3. データベースをテストして、生成された入力を確認します。

    MYSQL=$(kubectl --context ${SERVER_CLUSTER} get pods -n default | tail -n 1 | awk '{print $1}')
    kubectl --context ${SERVER_CLUSTER} exec $MYSQL -ti -- /bin/bash
    
  4. 作成されたデータベースを確認します。

    mysql -pyougottoknowme
    USE test_encrypted_connection;
    SELECT * from message;
    

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

    +------+-----------+
    | id   | content   |
    +------+-----------+
    |    1 | Crypto Hi |
    +------+-----------+
    1 row in set (0.00 sec)
    
  5. MySQL データベースを終了します。

    exit
    exit
    

    exit を 2 回入力する必要があります。1 回目は DB 接続を終了し、2 回目は Pod を終了するためです。

証明書を省略してアクセスをテストする

挿入された証明書を使用してアクセスが機能することをテストおよび確認したら、反対の場合についてもテストします。すなわち、下り(外向き)ゲートウェイと証明書の挿入を省略した場合の動作をテストします。このテストはネガティブ テストとも呼ばれます。

このテストは、サイドプロキシ インジェクションを有効にせずに名前空間で別の Pod を起動することによって実行できます。

  1. Cloud Shell で、新しい名前空間を作成します。

    kubectl --context ${CLIENT_CLUSTER} create ns unencrypted
    
  2. Pod を作成し、コンテナ内で対話型シェルを起動します。

    kubectl --context ${CLIENT_CLUSTER} run -it --image=mysql:5.6 \
    mysql-client-2 --env=SERVICE_URL=$SERVICE_URL \
    -n unencrypted --restart=Never -- /bin/bash
    
  3. 対話型シェルが起動したら、データベースへの接続を試行します。

    mysql -pyougottoknowme -h $SERVICE_URL
    

    30 秒後、次のような出力が表示されます。

    Warning: Using a password on the command line interface can be insecure.
    ERROR 2003 (HY000): Can't connect to MySQL server on '104.154.164.12.nip.io' (110)
    

    この Pod は下り(外向き)ゲートウェイを省略し、インターネット経由で上り(内向き)ゲートウェイ($SERVICE_URL)に直接アクセスしようとするので、この警告は想定内です。

  4. サービスの IP アドレスの解決を試行します。

    resolveip $SERVICE_URL
    

    出力は次のようになります。実際にご利用の IP アドレスは異なります。

    IP address of 104.154.164.12.nip.io is 104.154.164.12
    

    これで、FQDN が解決可能であり、証明書の挿入を省略したことが原因で実際に接続の失敗が発生することが証明されました。

  5. MySQL の接続と MySQL サーバー Pod を終了します。

    exit
    exit
    

詳細な調査

このチュートリアルの対象外の題目の 1 つは、通常、下り(外向き)構成は、istio-system 名前空間でホストされるため、社内の別のロールまたは組織が所有するということです。ネットワーク管理者だけが、このチュートリアルで説明するリソースを直接作成、変更できるように、Kubernetes RBAC 権限を構成します。

サービス メッシュを使用して安全な通信を確保する方法を学んだので、次に、データを安全に交換する必要があるアプリケーションと、証明書レイヤまでの暗号化を制御するアプリケーションを使用してサービス メッシュを試してみます。開始するには、Anthos Service Mesh をインストールします。

2 つの GKE クラスタを使用して、このチュートリアルの手法で統合することを試してみます。この手法は、2 つの外部 Kubernetes クラスタ間にある Anthos プラットフォームでも機能します。

サービス メッシュは、クラスタ内部と外部サービスのセキュリティを改善するための優れた方法です。試行対象の最後の 1 つのユースケースは、2 つ目の Kubernetes クラスタではなく、サードパーティ プロバイダ(決済機関など)である mTLS エンドポイントを設定することです。

クリーンアップ

このチュートリアルで使用したリソースに対する Google Cloud アカウントへの課金を回避するには、プロジェクトを削除します。

プロジェクトを削除する

  1. Google Cloud コンソールで、[リソースの管理] ページに移動します。

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

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

次のステップ