GKE クラスタで Anthos Service Mesh Egress ゲートウェイを使用する: チュートリアル


このチュートリアルでは、Anthos Service Mesh の Egress ゲートウェイやその他の Google Cloud コントロールを使用して、Google Kubernetes Engine クラスタにデプロイされたワークロードからの送信トラフィック(下り、外向き)を保護する方法について説明します。このチュートリアルは、ベスト プラクティス ガイドの補足として位置付けられています。

このチュートリアルの対象読者は、1 つ以上のソフトウェア配信チームが使用する Google Kubernetes Engine クラスタを管理するネットワーク エンジニア、プラットフォーム エンジニア、セキュリティ エンジニアです。ここで説明する制御は、規制(GDPRPCI など)の遵守を実証しなければならない組織に特に有効です。

目標

  • Anthos Service Mesh を実行するためのインフラストラクチャを設定します。
  • 専用ノードプールで実行される Egress ゲートウェイを備えた Anthos Service Mesh をインストールします。
  • Egress ゲートウェイを使用して、外部トラフィックのマルチテナント ルーティング ルールを構成します。
    • 名前空間「team-x」のアプリケーションは example.com に接続できます。
    • 名前空間「team-y」のアプリケーションは httpbin.org に接続できます。
  • Sidecar リソースを使用して、各名前空間のサイドカー プロキシ下り(外向き)構成の範囲を制限します。
  • 認証ポリシーを構成して、下り(外向き)ルールを適用します。
  • プレーン HTTP リクエストを TLS にアップグレードするように Egress ゲートウェイを構成します(TLS発信)。
  • TLS トラフィックを通過させるように Egress ゲートウェイを構成します。
  • Kubernetes ネットワーク ポリシーを追加の下り(外向き)制御として設定します。
  • 限定公開の Google アクセスと Identity and Access Management(IAM)権限を使用して、Google API への直接アクセスを構成します。

費用

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

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

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

始める前に

  1. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  2. Make sure that billing is enabled for your Google Cloud project.

  3. In the Google Cloud console, activate Cloud Shell.

    Activate Cloud Shell

  4. このチュートリアルで使用する作業ディレクトリを作成します。

    mkdir -p ~/WORKING_DIRECTORY
    cd ~/WORKING_DIRECTORY
    
  5. シェル スクリプトを作成して、チュートリアル用に環境を初期化します。変数は、プロジェクトや設定に応じて置き換えて編集します。シェル セッションが期限切れになった場合は、source コマンドを使用してこのスクリプトを実行して環境を再初期化します。

    cat << 'EOF' > ./init-egress-tutorial.sh
    #! /usr/bin/env bash
    PROJECT_ID=YOUR_PROJECT_ID
    REGION=REGION
    ZONE=ZONE
    
    gcloud config set project ${PROJECT_ID}
    gcloud config set compute/region ${REGION}
    gcloud config set compute/zone ${ZONE}
    
    EOF
    
  6. スクリプトを実行可能にして、source コマンドで実行し、環境を初期化します。

    chmod +x ./init-egress-tutorial.sh
    source ./init-egress-tutorial.sh
    
  7. 必要な Identity and Access Management(IAM)のロールを設定します。プロジェクト オーナーの場合は、インストールを完了するために必要なすべての権限が付与されます。プロジェクト オーナーでない場合は、管理者に次の IAM ロールを付与するよう依頼します。次のコマンドで、 PROJECT_EMAIL_ADDRESS を Google Cloud へのログインに使用するアカウントに変更します。

    gcloud projects add-iam-policy-binding ${PROJECT_ID} \
        --member user:PROJECT_EMAIL_ADDRESS \
        --role=roles/editor \
        --role=roles/compute.admin \
        --role=roles/container.admin \
        --role=roles/resourcemanager.projectIamAdmin \
        --role=roles/iam.serviceAccountAdmin \
        --role=roles/iam.serviceAccountKeyAdmin \
        --role=roles/gkehub.admin \
        --role=roles/serviceusage.serviceUsageAdmin
    
  8. チュートリアルに必要な API を有効にします。

    gcloud services enable \
        dns.googleapis.com \
        container.googleapis.com \
        compute.googleapis.com \
        monitoring.googleapis.com \
        logging.googleapis.com \
        cloudtrace.googleapis.com \
        meshca.googleapis.com \
        meshtelemetry.googleapis.com \
        meshconfig.googleapis.com \
        iamcredentials.googleapis.com \
        gkeconnect.googleapis.com \
        gkehub.googleapis.com \
        cloudresourcemanager.googleapis.com \
        stackdriver.googleapis.com
    

    API の有効化に数分かかることがあります。API が有効になると、次のような出力が表示されます。

    Operation "operations/acf.601db672-88e6-4f98-8ceb-aa3b5725533c" finished
    successfully.
    

インフラストラクチャの設定

VPC ネットワークとサブネットを作成する

  1. 新しい VPC ネットワークを作成します。

    gcloud compute networks create vpc-network \
        --subnet-mode custom
    
  2. Pod とサービスに事前に割り振られたセカンダリ IP アドレス範囲を使用して、クラスタを実行するためのサブネットを作成します。限定公開の Google アクセスを有効にすると、内部 IP アドレスしか割り当てられていないアプリケーションが Google API やサービスにアクセスできるようになります。

    gcloud compute networks subnets create subnet-gke \
        --network vpc-network \
        --range 10.0.0.0/24 \
        --secondary-range pods=10.1.0.0/16,services=10.2.0.0/20 \
        --enable-private-ip-google-access
    

Cloud NAT を構成する

Cloud NAT を使用すると、外部 IP アドレスのないワークロードがインターネット上の宛先に接続し、それらの宛先からの受信レスポンスを受け取れるようになります。

  1. Cloud Router を作成します。

    gcloud compute routers create nat-router \
        --network vpc-network
    
  2. ルーターに NAT 構成を追加します。

    gcloud compute routers nats create nat-config \
        --router nat-router \
        --nat-all-subnet-ip-ranges \
        --auto-allocate-nat-external-ips
    

GKE ノードプールごとにサービス アカウントを作成する

2 つの GKE ノードプールで使用する 2 つのサービス アカウントを作成します。VPC ファイアウォール ルールを特定のノードに適用できるように、ノードプールには別々のサービス アカウントが割り当てられます。

  1. デフォルトのノードプール内のノードで使用するサービス アカウントを作成します。

    gcloud iam service-accounts create sa-application-nodes \
        --description="SA for application nodes" \
        --display-name="sa-application-nodes"
    
  2. ゲートウェイ ノードプール内のノードで使用するサービス アカウントを作成します。

    gcloud iam service-accounts create sa-gateway-nodes \
        --description="SA for gateway nodes" \
        --display-name="sa-gateway-nodes"
    

サービス アカウントに権限を付与する

最小限の IAM ロールをアプリケーションとゲートウェイのサービス アカウントに追加します。これらのロールは、Container Registry から非公開コンテナ イメージをロギング、モニタリング、pull するために必要です。

    project_roles=(
        roles/logging.logWriter
        roles/monitoring.metricWriter
        roles/monitoring.viewer
        roles/storage.objectViewer
    )
    for role in "${project_roles[@]}"
    do
        gcloud projects add-iam-policy-binding ${PROJECT_ID} \
            --member="serviceAccount:sa-application-nodes@${PROJECT_ID}.iam.gserviceaccount.com" \
            --role="$role"
        gcloud projects add-iam-policy-binding ${PROJECT_ID} \
            --member="serviceAccount:sa-gateway-nodes@${PROJECT_ID}.iam.gserviceaccount.com" \
            --role="$role"
    done

ファイアウォール ルールを作成する

次のステップでは、デフォルトですべての下り(外向き)トラフィックを拒否するように、VPC ネットワークにファイアウォール ルールを適用します。クラスタが機能し、ゲートウェイ ノードが VPC の外部の宛先に到達できるようにするには、専用の接続が必要です。特定のファイアウォール ルールの最小限のセットは、デフォルトのすべて拒否ルールをオーバーライドして、必要な接続を許可します。

  1. VPC ネットワークからの下り(外向き)トラフィックをすべて拒否するデフォルトの(優先度が低い)ファイアウォール ルールを作成します。

    gcloud compute firewall-rules create global-deny-egress-all \
        --action DENY \
        --direction EGRESS \
        --rules all \
        --destination-ranges 0.0.0.0/0 \
        --network vpc-network \
        --priority 65535 \
        --description "Default rule to deny all egress from the network."
    
  2. ゲートウェイ サービス アカウントを持つノードのみがインターネットに到達できるようにルールを作成します。

    gcloud compute firewall-rules create gateway-allow-egress-web \
        --action ALLOW \
        --direction EGRESS \
        --rules tcp:80,tcp:443 \
        --target-service-accounts sa-gateway-nodes@${PROJECT_ID}.iam.gserviceaccount.com \
        --network vpc-network \
        --priority 1000 \
        --description "Allow the nodes running the egress gateways to connect to the web"
    
  3. ノードが Kubernetes コントロール プレーンに到達できるようにします。

    gcloud compute firewall-rules create allow-egress-to-api-server \
        --action ALLOW \
        --direction EGRESS \
        --rules tcp:443,tcp:10250 \
        --destination-ranges 10.5.0.0/28 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow nodes to reach the Kubernetes API server."
    
  4. Anthos Service Mesh は、ワークロードにサイドカー プロキシを挿入する際に Webhook を使用します。GKE API サーバーが、ノードで実行されているサービス メッシュ コントロール プレーンによって公開された Webhook を呼び出すようにします。

    gcloud compute firewall-rules create allow-ingress-api-server-to-webhook \
        --action ALLOW \
        --direction INGRESS \
        --rules tcp:15017 \
        --source-ranges 10.5.0.0/28 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow the API server to call the webhooks exposed by istiod discovery"
    
  5. クラスタ上で実行されている Pod とサービス間の下り(外向き)接続を許可します。GKE は、対応する上り(内向き)ルールを自動的に作成します。

    gcloud compute firewall-rules create allow-egress-pods-and-services \
        --action ALLOW \
        --direction EGRESS \
        --rules all \
        --destination-ranges 10.1.0.0/16,10.2.0.0/20 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow pods and services on nodes to reach each other"
    
  6. Calico というサービスは、GKE に NetworkPolicy API 機能を提供します。サブネット内での Calico の接続を許可します。

    gcloud compute firewall-rules create allow-egress-calico \
        --action ALLOW \
        --direction EGRESS \
        --rules tcp:5473 \
        --destination-ranges 10.0.0.0/24 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow Calico Typha within the subnet"
    
  7. GKE がノード指標を読み取るには、kubelet 読み取り専用ポートが必要です。サブネット内でのこのポートへのアクセスを許可します。

    gcloud compute firewall-rules create allow-egress-kubelet-readonly \
        --action ALLOW \
        --direction EGRESS \
        --rules tcp:10255 \
        --destination-ranges 10.0.0.0/24 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow access to the kubelet read-only port within the subnet"
    
  8. Google API や Container Registry などのサービスを提供するために、限定公開の Google アクセスで使用される予約済みの IP アドレスセットへのアクセスを許可します。

    gcloud compute firewall-rules create allow-egress-gcp-apis \
        --action ALLOW \
        --direction EGRESS \
        --rules tcp \
        --destination-ranges 199.36.153.8/30 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow access to the VIPs used by Google Cloud APIs (Private Google Access)"
    
  9. クラスタで実行されている Pod へのアクセスを Google Cloud ヘルス チェッカー サービスに許可します。

    gcloud compute firewall-rules create allow-ingress-gcp-health-checker \
        --action ALLOW \
        --direction INGRESS \
        --rules tcp:80,tcp:443 \
        --source-ranges 130.211.0.0/22,35.191.0.0/16,35.191.0.0/16,209.85.152.0/22,209.85.204.0/22 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow workloads to respond to Google Cloud health checks"
    

Google Cloud APIs へのプライベート アクセスの構成

限定公開の Google アクセスを使用すると、内部 IP アドレスしか持たない VM や Pod から Google API やサービスにアクセスできるようになります。Google API とサービスは外部 IP から提供されますが、限定公開の Google アクセスを使用する場合、ノードからのトラフィックが Google ネットワークの外に出ることはありません。

限定公開の Google アクセスとホスト名 private.googleapis.com を使用してノードとワークロードが Google API およびサービスに接続できるように、限定公開の DNS ゾーン、CNAME レコード、A レコードを作成します。

gcloud dns managed-zones create private-google-apis \
    --description "Private DNS zone for Google APIs" \
    --dns-name googleapis.com \
    --visibility private \
    --networks vpc-network

gcloud dns record-sets transaction start --zone private-google-apis

gcloud dns record-sets transaction add private.googleapis.com. \
    --name *.googleapis.com \
    --ttl 300 \
    --type CNAME \
    --zone private-google-apis

gcloud dns record-sets transaction add "199.36.153.8" \
"199.36.153.9" "199.36.153.10" "199.36.153.11" \
    --name private.googleapis.com \
    --ttl 300 \
    --type A \
    --zone private-google-apis

gcloud dns record-sets transaction execute --zone private-google-apis

Container Registry へのプライベート アクセスの構成

限定公開の Google アクセスと gcr.io ホスト名を使用して、ノードが Container Registry に接続できるように、限定公開 DNS ゾーン、CNAME レコード、A レコードを作成します。

gcloud dns managed-zones create private-gcr-io \
    --description "private zone for Container Registry" \
    --dns-name gcr.io \
    --visibility private \
    --networks vpc-network

gcloud dns record-sets transaction start --zone private-gcr-io

gcloud dns record-sets transaction add gcr.io. \
    --name *.gcr.io \
    --ttl 300 \
    --type CNAME \
    --zone private-gcr-io

gcloud dns record-sets transaction add "199.36.153.8" "199.36.153.9" "199.36.153.10" "199.36.153.11" \
    --name gcr.io \
    --ttl 300 \
    --type A \
    --zone private-gcr-io

gcloud dns record-sets transaction execute --zone private-gcr-io

限定公開 GKE クラスタの作成

  1. Cloud Shell の外部 IP アドレスを見つけて、クラスタの API サーバーへのアクセスが許可されているネットワークのリストに追加できるようにします。

    SHELL_IP=$(dig TXT -4 +short @ns1.google.com o-o.myaddr.l.google.com)
    

    Cloud Shell VM の外部 IP アドレスは、一定期間使用されていない場合に変更できます。その場合は、クラスタの承認済みネットワークのリストを更新する必要があります。次のコマンドを初期化スクリプトに追加します。

    cat << 'EOF' >> ./init-egress-tutorial.sh
    SHELL_IP=$(dig TXT -4 +short @ns1.google.com o-o.myaddr.l.google.com)
    gcloud container clusters update cluster1 \
        --enable-master-authorized-networks \
        --master-authorized-networks ${SHELL_IP//\"}/32
    EOF
    
  2. 限定公開 GKE クラスタを作成します。

    gcloud container clusters create cluster1 \
        --enable-ip-alias \
        --enable-private-nodes \
        --release-channel "regular" \
        --no-enable-basic-auth \
        --no-issue-client-certificate \
        --enable-master-authorized-networks \
        --master-authorized-networks ${SHELL_IP//\"}/32 \
        --master-ipv4-cidr 10.5.0.0/28 \
        --enable-network-policy \
        --service-account "sa-application-nodes@${PROJECT_ID}.iam.gserviceaccount.com" \
        --machine-type "e2-standard-4" \
        --num-nodes "4" \
        --network "vpc-network" \
        --subnetwork "subnet-gke" \
        --cluster-secondary-range-name "pods" \
        --services-secondary-range-name "services" \
        --workload-pool "${PROJECT_ID}.svc.id.goog" \
        --zone ${ZONE}
    

    クラスタの作成には数分かかります。クラスタには、内部 IP アドレスを持つプライベート ノードが含まれます。Pod とサービスには、VPC サブネットの作成時に定義した名前付きセカンダリ範囲から IP が割り振られます。

    Anthos Service Mesh では、少なくとも 4 つの vCPU を備えたマシンタイプをクラスタノードで使用する必要があります。ノードが Anthos Service Mesh でサポートされている Kubernetes バージョンを実行できるように、クラスタを「通常の」リリース チャネルに登録することをおすすめします。詳細については、Anthos Service Mesh のインストール ガイドをご覧ください。

    Workload Identity はクラスタで有効になります。Anthos Service Mesh には Workload Identity が必要です。また、Anthos Service Mesh は GKE ワークロードから Google API にアクセスする場合のおすすめの方法です。

  3. gateway というノードプールを作成します。Egress ゲートウェイは、このノードプールにデプロイされます。ゲートウェイ ノード プール内のすべてのノードに dedicated=gateway:NoSchedule taint が追加されます。

    gcloud container node-pools create "gateway" \
        --cluster "cluster1" \
        --machine-type "e2-standard-4" \
        --node-taints dedicated=gateway:NoSchedule \
        --service-account "sa-gateway-nodes@${PROJECT_ID}.iam.gserviceaccount.com" \
        --num-nodes "1"
    

    Kubernetes の taint と容認は、Egress ゲートウェイ Pod のみがゲートウェイ ノードプール内のノードで実行されるようにするのに役立ちます。

  4. kubectl でクラスタに接続できるように認証情報をダウンロードします。

    gcloud container clusters get-credentials cluster1
    
  5. ゲートウェイ ノードに適切な taint があることを確認します。

    kubectl get nodes -l cloud.google.com/gke-nodepool=gateway -o yaml \
    -o=custom-columns='name:metadata.name,taints:spec.taints[?(@.key=="dedicated")]'
    

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

    name                                 taints
    gke-cluster1-gateway-9d65b410-cffs   map[effect:NoSchedule key:dedicated value:gateway]
    

Anthos Service Mesh のインストールと設定

このチュートリアルでは、Anthos Service Mesh のオプション機能を使用します。スクリプトを使用した Anthos Service Mesh のインストールについては、ドキュメントのインストール ガイドをご覧ください。

  1. デプロイするサービス メッシュ コントロール プレーンと Egress ゲートウェイの名前空間を作成します。

    kubectl create ns istio-system
    kubectl create ns istio-egress
    
  2. istio-egress、istio-system、kube-system の名前空間にラベルを付けます。

    kubectl label ns istio-egress istio=egress istio-injection=disabled
    kubectl label ns istio-system istio=system
    kubectl label ns kube-system kube-system=true
    

    これらのラベルは、Kubernetes NetworkPolicy を適用するために後で使用されます。istio-injection=disabled ラベルは、istioctl 分析の実行時に誤った警告が表示されることを防止します。

  3. Istio OperatorAPI を使用して、マニフェスト ファイルを作成し、Anthos Service Mesh のインストールをカスタマイズします。

    cat << 'EOF' > ./asm-custom-install.yaml
    apiVersion: install.istio.io/v1alpha1
    kind: IstioOperator
    metadata:
      name: "egress-gateway"
    spec:
      meshConfig:
        accessLogFile: "/dev/stdout"
      components:
        egressGateways:
          - name: "istio-egressgateway"
            enabled: true
            namespace: "istio-egress"
            label:
              istio: "egress"
            k8s:
              tolerations:
              - key: "dedicated"
                operator: "Equal"
                value: "gateway"
              nodeSelector:
                cloud.google.com/gke-nodepool: "gateway"
    EOF
    

    このファイルはインストール スクリプトの引数として提供され、次の構成を指定します。

    • 容認と nodeSelector を使用して istio-egress 名前空間で実行される Egress ゲートウェイの Deployment。gateway ノード上でのみ実行されます。
    • すべてのサイドカー プロキシの stdout へのアクセスロギング。
  4. インストール スクリプトをダウンロードします。

    curl -O https://storage.googleapis.com/csm-artifacts/asm/install_asm
    
  5. スクリプトを実行可能にします。

    chmod +x install_asm
    
  6. 次のコマンドを実行して、Anthos Service Mesh をインストールします。

    ./install_asm \
        --mode install \
        --project_id ${PROJECT_ID} \
        --cluster_name cluster1 \
        --cluster_location ${ZONE} \
        --custom_overlay ./asm-custom-install.yaml \
        --output_dir ./ \
        --enable_all
    
  7. スクリプトが完了したら、環境変数を設定して istioctl ツールへのパスを保持し、初期化スクリプトに追加します。

    ISTIOCTL=$(find "$(pwd -P)" -name istioctl)
    echo "ISTIOCTL=\"${ISTIOCTL}\"" >> ./init-egress-tutorial.sh
    

Anthos Service Mesh のインストールを確認する

  1. Anthos Service Mesh のコントロール プレーン コンポーネントが istio-system 名前空間で実行されていることを確認します。

    kubectl get pod -n istio-system
    

    istio-ingressgateway Pod と istiod-asm Pod が実行中であることが確認できます。

  2. Egress ゲートウェイ Pod が istio-egress 名前空間と gateway ノードプールのノードで実行されていることを確認します。

    kubectl get pods -n istio-egress -o wide
    
  3. Egress ゲートウェイ Pod には、gateway ノードプール内のノードを選択するための nodeSelector と、taint されたゲートウェイ ノードで実行するための容認機能が備わっています。Egress ゲートウェイ Pod の nodeSelector と容認機能を調べます。

    kubectl -n istio-egress get pod -l app=istio-egressgateway \
        -o=custom-columns='name:metadata.name,nodeSelector:spec.nodeSelector,\
        tolerations:spec.tolerations[?(@.key=="dedicated")]'
    

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

    name                                   nodeSelector                                 tolerations
    istio-egressgateway-74687946f5-dg9mp   map[cloud.google.com/gke-nodepool:gateway]   map[key:dedicated operator:Equal value:gateway]
    

メッシュとテスト アプリケーションの準備

  1. STRICT 相互 TLS が有効になっていることを確認します。istio-system 名前空間のメッシュにデフォルトの PeerAuthentication ポリシーを適用します。

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

    この構成をオーバーライドするには、特定の名前空間に PeerAuthentication リソースを作成します。

  2. テスト ワークロードのデプロイに使用する名前空間を作成します。このチュートリアルの後半のステップでは、名前空間ごとに異なる下り(外向き)転送ルールを構成する方法について説明します。

    kubectl create namespace team-x
    kubectl create namespace team-y
    
  3. 名前空間にラベルを付けて、Kubernetes ネットワーク ポリシーで選択できるようにします。

    kubectl label namespace team-x team=x
    kubectl label namespace team-y team=y
    
  4. Anthos Service Mesh でプロキシ サイドカーを自動的に挿入するには、ワークロードの名前空間にリビジョン ラベルを設定する必要があります。リビジョン ラベルは、クラスタにデプロイされている Anthos Service Mesh コントロール プレーンのリビジョンと一致する必要があります。istiod Pod のリビジョン ラベルを検索して、環境変数に保存します。

    REVISION_LABEL=$(kubectl get pod -n istio-system -l app=istiod \
      -o jsonpath='{.items[0].metadata.labels.istio\.io/rev}')
    
  5. team-x および team-y 名前空間にリビジョン ラベルを設定します。

    kubectl label ns team-x istio.io/rev=${REVISION_LABEL}
    kubectl label ns team-y istio.io/rev=${REVISION_LABEL}
    
  6. テストデプロイに使用する YAML ファイルを作成します。

    cat << 'EOF' > ./test.yaml
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: test
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: test
      labels:
        app: test
    spec:
      ports:
      - port: 80
        name: http
      selector:
        app: test
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: test
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: test
      template:
        metadata:
          labels:
            app: test
        spec:
          serviceAccountName: test
          containers:
          - name: test
            image: gcr.io/google.com/cloudsdktool/cloud-sdk:slim
            command: ["/bin/sleep", "infinity"]
            imagePullPolicy: IfNotPresent
    EOF
    
  7. テスト アプリケーションを team-x 名前空間にデプロイします。

    kubectl -n team-x create -f ./test.yaml
    
  8. テスト アプリケーションがデフォルト プール内のノードにデプロイされ、プロキシ サイドカー コンテナが挿入されていることを確認します。Pod のステータスが Running になるまで、次のコマンドを繰り返します。

    kubectl -n team-x get po -l app=test -o wide
    

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

    NAME                   READY   STATUS    RESTARTS   AGE   IP          NODE                                      NOMINATED NODE   READINESS GATES
    test-d5bdf6f4f-9nxfv   2/2     Running   0          19h   10.1.1.25   gke-cluster1-default-pool-f6c7a51f-wbzj
    

    2 つのコンテナは両方 Running です。1 つのコンテナはテスト アプリケーション、もう 1 つはプロキシ サイドカーです。

    Pod は、デフォルトのノードプール内のノードで実行されます。

  9. テストコンテナから外部サイトに HTTP リクエストを作成できないことを確認します。

    kubectl -n team-x exec -it \
        $(kubectl -n team-x get pod -l app=test -o jsonpath={.items..metadata.name}) \
        -c test -- curl -v http://example.com
    

    global-deny-egress-all ファイアウォール ルールによってアップストリーム接続が拒否されるため、サイドカー プロキシからのエラー メッセージが生成されます。

サイドカー リソースを使用してサイドカー プロキシ構成の範囲を制限する

サイドカー リソースを使用して、サイドカー プロキシ用に構成された下り(外向き)リスナーのスコープを制限できます。構成の肥大化とメモリ使用量を減らすために、すべての名前空間にデフォルトの Sidecar リソースを適用することをおすすめします。

Anthos Service Mesh がサイドカーで実行するプロキシは Envoy です。Envoy の用語では、cluster は負荷分散の宛先として使用される、論理的に類似したアップストリーム エンドポイントのグループです。

  1. istioctl proxy-config コマンドを実行して、テスト Pod の Envoy サイドカー プロキシで構成されている送信クラスタを調べます。

    ${ISTIOCTL} pc c $(kubectl -n team-x get pod -l app=test \
        -o jsonpath={.items..metadata.name}).team-x --direction outbound
    

    このリストには Envoy クラスタが約 20 個あり、Egress ゲートウェイ用のものもいくつかあります。

  2. プロキシ構成を、istio-egress 名前空間と team-x 名前空間のサービス エントリによって明示的に定義された下り(外向き)ルートに制限します。Sidecar リソースを team-x 名前空間に適用します。

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: Sidecar
    metadata:
      name: default
      namespace: team-x
    spec:
      outboundTrafficPolicy:
        mode: REGISTRY_ONLY
      egress:
      - hosts:
        - 'istio-egress/*'
        - 'team-x/*'
    EOF
    

    送信トラフィック ポリシーモードを REGISTRY_ONLY に設定すると、プロキシ構成が制限されます。サービス エントリを定義することで、メッシュのサービス レジストリに明示的に追加された外部ホストのみが含まれるようになります。

    istio-egress/*」部分は、サイドカー プロキシが exportTo 属性を使用して使用可能になる istio-egress 名前空間からルートを選択することを指定します。「team-x/*」部分には、team-x 名前空間でローカルに構成されたルートが含まれます。

  3. Envoy サイドカー プロキシで構成された送信クラスタを確認し、Sidecar リソースを適用する前に構成されたクラスタのリストと比較します。

    ${ISTIOCTL} pc c $(kubectl -n team-x get pod -l app=test \
        -o jsonpath={.items..metadata.name}).team-x --direction outbound
    

    出力には、Egress ゲートウェイのクラスタとテスト Pod 自体のクラスタのみが含まれます。

Egress ゲートウェイ経由でトラフィックをルーティングするように Anthos Service Mesh を構成する

  1. ポート 80 上の HTTP トラフィックに対して Gateway を構成します。Gateway により、インストーラが istio-egress 名前空間にデプロイした istio-egressgateway プロキシが選択されます。Gateway 構成が istio-egress 名前空間に適用され、すべてのホストのトラフィックを処理します。

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: Gateway
    metadata:
      name: egress-gateway
      namespace: istio-egress
    spec:
      selector:
        istio: egress
      servers:
      - port:
          number: 80
          name: https
          protocol: HTTPS
        hosts:
          - '*'
        tls:
          mode: ISTIO_MUTUAL
    EOF
    
  2. 認証および暗号化用の相互 TLS を使用する Egress ゲートウェイの DestinationRule を作成します。すべての外部ホストに対して単一の共有宛先ルールを使用します。

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: DestinationRule
    metadata:
      name: target-egress-gateway
      namespace: istio-egress
    spec:
      host: istio-egressgateway.istio-egress.svc.cluster.local
      subsets:
      - name: target-egress-gateway-mTLS
        trafficPolicy:
          loadBalancer:
            simple: ROUND_ROBIN
          tls:
            mode: ISTIO_MUTUAL
    EOF
    
  3. istio-egress 名前空間に ServiceEntry を作成し、team-x 名前空間のメッシュのサービス レジストリに example.com を明示的に登録します。

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: ServiceEntry
    metadata:
      name: example-com-ext
      namespace: istio-egress
    spec:
      hosts:
      - example.com
      ports:
      - number: 80
        name: http
        protocol: HTTP
      - number: 443
        name: tls
        protocol: TLS
      resolution: DNS
      location: MESH_EXTERNAL
      exportTo:
      - 'team-x'
      - 'istio-egress'
    EOF
    
  4. Egress ゲートウェイ経由で example.com にトラフィックを転送する VirtualService を作成します。一致条件は 2 つあります。1 つ目の条件はトラフィックを Egress ゲートウェイに転送し、2 番目の条件はトラフィックを Egress ゲートウェイから宛先ホストに転送します。exportTo プロパティは、仮想サービスを使用できる名前空間を制御します。

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: VirtualService
    metadata:
      name: example-com-through-egress-gateway
      namespace: istio-egress
    spec:
      hosts:
      - example.com
      gateways:
      - istio-egress/egress-gateway
      - mesh
      http:
      - match:
        - gateways:
          - mesh
          port: 80
        route:
        - destination:
            host: istio-egressgateway.istio-egress.svc.cluster.local
            subset: target-egress-gateway-mTLS
            port:
              number: 80
          weight: 100
      - match:
        - gateways:
          - istio-egress/egress-gateway
          port: 80
        route:
        - destination:
            host: example.com
            port:
              number: 80
          weight: 100
      exportTo:
      - 'istio-egress'
      - 'team-x'
    EOF
    
  5. istioctl analyze を実行して、構成エラーを確認します。

    ${ISTIOCTL} analyze -n istio-egress
    

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

    ✔ No validation issues found when analyzing namespace: istio-egress.
    
  6. Egress ゲートウェイ経由で外部サイトに複数のリクエストを送信します。

    for i in {1..4}
    do
        kubectl -n team-x exec -it $(kubectl -n team-x get pod -l app=test \
            -o jsonpath={.items..metadata.name}) -c test -- \
        curl -s -o /dev/null -w "%{http_code}\n" http://example.com
    done
    

    4 つのレスポンスすべてに 200 ステータス コードが表示されます。

  7. プロキシ アクセスログを調べて、リクエストが Egress ゲートウェイ経由で送信されていることを確認します。まず、テスト アプリケーションでデプロイされたプロキシ サイドカーのアクセスログを確認します。

    kubectl -n team-x logs -f $(kubectl -n team-x get pod -l app=test \
        -o jsonpath={.items..metadata.name}) istio-proxy
    

    送信するリクエストごとに、次のようなログエントリが表示されます。

    [2020-09-14T17:37:08.045Z] "HEAD / HTTP/1.1" 200 - "-" "-" 0 0 5 4 "-" "curl/7.67.0" "d57ea5ad-90e9-46d9-8b55-8e6e404a8f9b" "example.com" "10.1.4.12:8080" outbound|80||istio-egressgateway.istio-egress.svc.cluster.local 10.1.0.17:42140 93.184.216.34:80 10.1.0.17:60326 - -
    
  8. また、Egress ゲートウェイのアクセスログも確認します。

    kubectl -n istio-egress logs -f $(kubectl -n istio-egress get pod -l istio=egress \
        -o jsonpath="{.items[0].metadata.name}") istio-proxy
    

    送信するリクエストごとに、次のような Egress ゲートウェイのアクセスログ エントリが表示されます。

    [2020-09-14T17:37:08.045Z] "HEAD / HTTP/2" 200 - "-" "-" 0 0 4 3 "10.1.0.17" "curl/7.67.0" "095711e6-64ef-4de0-983e-59158e3c55e7" "example.com" "93.184.216.34:80" outbound|80||example.com 10.1.4.12:37636 10.1.4.12:8080 10.1.0.17:44404 outbound_.80_.target-egress-gateway-mTLS_.istio-egressgateway.istio-egress.svc.cluster.local -
    

2 番目の名前空間用に別のルーティングを構成する

2 番目の外部ホストのルーティングを構成して、チームごとに異なる外部接続を構成する方法を確認します。

  1. team-y 名前空間の Sidecar リソースを作成します。

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: Sidecar
    metadata:
      name: default
      namespace: team-y
    spec:
      outboundTrafficPolicy:
        mode: REGISTRY_ONLY
      egress:
      - hosts:
        - 'istio-egress/*'
        - 'team-y/*'
    EOF
    
  2. テスト アプリケーションを team-y 名前空間にデプロイします。

    kubectl -n team-y create -f ./test.yaml
    
  3. 2 番目の外部ホストを登録して、team-x 名前空間と team-y 名前空間にエクスポートします。

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: ServiceEntry
    metadata:
      name: httpbin-org-ext
      namespace: istio-egress
    spec:
      hosts:
      - httpbin.org
      ports:
      - number: 80
        name: http
        protocol: HTTP
      - number: 443
        name: tls
        protocol: TLS
      resolution: DNS
      location: MESH_EXTERNAL
      exportTo:
      - 'istio-egress'
      - 'team-x'
      - 'team-y'
    EOF
    
  4. Egress ゲートウェイ経由で httpbin.org にトラフィックを転送する仮想サービスを作成します。

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: VirtualService
    metadata:
      name: httpbin-org-through-egress-gateway
      namespace: istio-egress
    spec:
      hosts:
      - httpbin.org
      gateways:
      - istio-egress/egress-gateway
      - mesh
      http:
      - match:
        - gateways:
          - mesh
          port: 80
        route:
        - destination:
            host: istio-egressgateway.istio-egress.svc.cluster.local
            subset: target-egress-gateway-mTLS
            port:
              number: 80
          weight: 100
      - match:
        - gateways:
          - istio-egress/egress-gateway
          port: 80
        route:
        - destination:
            host: httpbin.org
            port:
              number: 80
          weight: 100
      exportTo:
      - 'istio-egress'
      - 'team-x'
      - 'team-y'
    EOF
    
  5. istioctl analyze を実行して、構成エラーを確認します。

    ${ISTIOCTL} analyze -n istio-egress
    

    次の出力を確認できます。

    ✔ No validation issues found when analyzing namespace: istio-egress.
    
  6. team-y テストアプリから httpbin.org にリクエストを送信します。

    kubectl -n team-y exec -it $(kubectl -n team-y get pod -l app=test -o \
        jsonpath={.items..metadata.name}) -c test -- curl -I http://httpbin.org
    

    200 OK レスポンスが表示されます。

  7. また、team-x テストアプリからも httpbin.org にリクエストを送信します。

    kubectl -n team-x exec -it $(kubectl -n team-x get pod -l app=test \
        -o jsonpath={.items..metadata.name}) -c test -- curl -I http://httpbin.org
    

    200 OK レスポンスが表示されます。

  8. team-y 名前空間から example.com へのリクエストを試みます。

    kubectl -n team-y exec -it $(kubectl -n team-y get pod -l app=test \
        -o jsonpath={.items..metadata.name}) -c test -- curl -I http://example.com
    

    example.com ホスト用に送信ルートが構成されていないため、このリクエストは失敗します。

認証ポリシーを使用してトラフィックを詳細に制御する

このチュートリアルでは、Egress ゲートウェイの認証ポリシーが istio-egress 名前空間に作成されます。ネットワーク管理者のみが istio-egress 名前空間にアクセスできるように Kubernetes RBAC を構成できます。

  1. AuthorizationPolicy を作成して、team-x 名前空間のアプリケーションが example.com に接続できるが、ポート 80 を使用してリクエストを送信するときに他の外部ホストに接続できないようにします。Egress ゲートウェイ Pod の対応する targetPort は 8080 です。

    cat <<EOF | kubectl apply -f -
    apiVersion: security.istio.io/v1beta1
    kind: AuthorizationPolicy
    metadata:
      name: egress-team-x-to-example-com
      namespace: istio-egress
    spec:
      rules:
        - from:
          - source:
              namespaces:
              - 'team-x'
          to:
          - operation:
              hosts:
                - 'example.com'
          when:
          - key: destination.port
            values: ["8080"]
    EOF
    
  2. team-x 名前空間のテスト アプリケーションから example.com にリクエストできることを確認します。

    kubectl -n team-x exec -it $(kubectl -n team-x get pod -l app=test \
        -o jsonpath={.items..metadata.name}) -c test -- curl -I http://example.com
    

    200 OK レスポンスが表示されます。

  3. team-x 名前空間のテスト アプリケーションから httpbin.org へのリクエストを試みます。

    kubectl -n team-x exec -it $(kubectl -n team-x get pod -l app=test \
        -o jsonpath={.items..metadata.name}) -c test -- curl -s -w " %{http_code}\n" \
        http://httpbin.org
    

    このリクエストは失敗し、RBAC: access denied メッセージと 403 Forbidden ステータス コードが返されます。認証ポリシーが有効になるまでに少しの遅延があるため、数秒かかることがあります。

  4. 認証ポリシーは、どのトラフィックを許可または拒否するかを詳細に制御します。次の認証ポリシーを適用して、team-y 名前空間内のテストアプリが、ポート 80 を使用してリクエストを送信するときに、1 つの特定の URL パスを使用して httpbin.org にリクエストを送信できるようにします。Egress ゲートウェイ Pod の対応する targetPort は 8080 です。

    cat <<EOF | kubectl apply -f -
    apiVersion: security.istio.io/v1beta1
    kind: AuthorizationPolicy
    metadata:
      name: egress-team-y-to-httpbin-teapot
      namespace: istio-egress
    spec:
      rules:
        - from:
          - source:
              namespaces:
              - 'team-y'
          to:
          - operation:
              hosts:
              - httpbin.org
              paths: ['/status/418']
          when:
          - key: destination.port
            values: ["8080"]
    EOF
    
  5. team-y 名前空間のテストアプリから httpbin.org への接続を試みます。

    kubectl -n team-y exec -it $(kubectl -n team-y get pod -l app=test \
        -o jsonpath={.items..metadata.name}) -c test -- curl -s -w " %{http_code}\n" \
        http://httpbin.org
    

    このリクエストは失敗し、RBAC: access denied メッセージと 403 Forbidden ステータス コードが返されます。

  6. 次に、同じアプリから httpbin.org/status/418 にリクエストを送信します。

    kubectl -n team-y exec -it $(kubectl -n team-y get pod -l app=test \
        -o jsonpath={.items..metadata.name}) -c test -- curl http://httpbin.org/status/418
    

    パスが認証ポリシーのパターンと一致するため、このリクエストは成功します。出力は次のようになります。

       -=[ teapot ]=-
          _...._
        .'  _ _ `.
       | ."` ^ `". _,
       \_;`"---"`|//
         |       ;/
         \_     _/
           `"""`
    

Egress ゲートウェイでの TLS 開始

プレーン HTTP リクエストを TLS にアップグレード(TLS 開始)するように、Egress ゲートウェイを構成できます。Istio 相互 TLS および TLS 発信において、アプリケーションでプレーン HTTP リクエストを行えるようにすることの利点はいくつかあります。詳しくは、ベスト プラクティス ガイドをご覧ください。

Egress ゲートウェイでの TLS 開始

  1. DestinationRule. The DestinationRule を作成すると、example.com への TLS 接続をゲートウェイで開始するように指定できます。

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: DestinationRule
    metadata:
      name: example-com-originate-tls
      namespace: istio-egress
    spec:
      host: example.com
      subsets:
        - name: example-com-originate-TLS
          trafficPolicy:
            loadBalancer:
              simple: ROUND_ROBIN
            portLevelSettings:
            - port:
                number: 443
              tls:
                mode: SIMPLE
                sni: example.com
    EOF
    
  2. example.com の仮想サービスを更新して、ゲートウェイのポート 80 へのリクエストが、宛先ホストに送信されるときにポート 443 の TLS にアップグレードされるようにします。

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: example-com-through-egress-gateway
      namespace: istio-egress
    spec:
      hosts:
      - example.com
      gateways:
      - mesh
      - istio-egress/egress-gateway
      http:
      - match:
        - gateways:
          - mesh
          port: 80
        route:
        - destination:
            host: istio-egressgateway.istio-egress.svc.cluster.local
            subset: target-egress-gateway-mTLS
            port:
              number: 80
      - match:
        - gateways:
          - istio-egress/egress-gateway
          port: 80
        route:
        - destination:
            host: example.com
            port:
              number: 443
            subset: example-com-originate-TLS
          weight: 100
    EOF
    
  3. team-x 名前空間のテストアプリから example.com へのリクエストをいくつか行います。

    for i in {1..4}
    do
        kubectl -n team-x exec -it $(kubectl -n team-x get pod -l app=test \
            -o jsonpath={.items..metadata.name}) -c test -- curl -I http://example.com
    done
    

    前と同様に、リクエストが成功すると 200 OK レスポンスが返されます。

  4. Egress ゲートウェイのログを調べて、ゲートウェイが送信元の TLS 接続経由でリクエストを宛先ホストに転送したことを確認します。

    kubectl -n istio-egress logs -f $(kubectl -n istio-egress get pod -l istio=egress \
        -o jsonpath="    {.items[0].metadata.name}") istio-proxy
    

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

    [2020-09-24T17:58:02.548Z] "HEAD / HTTP/2" 200 - "-" "-" 0 0 6 5 "10.1.1.15" "curl/7.67.0" "83a77acb-d994-424d-83da-dd8eac902dc8" "example.com" "93.184.216.34:443" outbound|443|example-com-originate-TLS|example.com 10.1.4.31:49866 10.1.4.31:8080 10.1.1.15:37334 outbound_.80_.target-egress-gateway-mTLS_.istio-egressgateway.istio-egress.svc.cluster.local -
    

    プロキシ サイドカーは、ポート 80 とポート 443 で開始された TLS を使用してゲートウェイにリクエストを送信し、宛先ホストにリクエストを送信しました。

HTTPS / TLS 接続のパススルー

既存のアプリケーションで、外部サービスと通信するときにすでに TLS 接続を使用している場合があります。TLS 接続を復号せずに通過させるように Egress ゲートウェイを構成できます。

TLS パススルー

  1. Egress ゲートウェイがポート 443 への接続に TLS パススルーを使用するように構成を変更します。

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: Gateway
    metadata:
      name: egress-gateway
      namespace: istio-egress
    spec:
      selector:
        istio: egress
      servers:
      - port:
          number: 80
          name: https
          protocol: HTTPS
        hosts:
          - '*'
        tls:
          mode: ISTIO_MUTUAL
      - port:
          number: 443
          name: tls
          protocol: TLS
        hosts:
        - '*'
        tls:
          mode: PASSTHROUGH
    EOF
    
  2. Egress ゲートウェイを指す DestinationRule を更新し、ゲートウェイのポート 443 に対して 2 つ目のサブセットを追加します。この新しいサブセットは相互 TLS を使用しません。Istio 相互 TLS は、TLS 接続のパススルーではサポートされていません。ポート 80 上の接続では、引き続き mTLS が使用されます。

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1alpha3
    kind: DestinationRule
    metadata:
      name: target-egress-gateway
      namespace: istio-egress
    spec:
      host: istio-egressgateway.istio-egress.svc.cluster.local
      subsets:
      - name: target-egress-gateway-mTLS
        trafficPolicy:
          loadBalancer:
            simple: ROUND_ROBIN
          portLevelSettings:
          - port:
              number: 80
            tls:
              mode: ISTIO_MUTUAL
      - name: target-egress-gateway-TLS-passthrough
    EOF
    
  3. ポート 443 での TLS トラフィックがゲートウェイを通過するように example.com の仮想サービスを更新します。

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: example-com-through-egress-gateway
      namespace: istio-egress
    spec:
      hosts:
      - example.com
      gateways:
      - mesh
      - istio-egress/egress-gateway
      http:
      - match:
        - gateways:
          - mesh
          port: 80
        route:
        - destination:
            host: istio-egressgateway.istio-egress.svc.cluster.local
            subset: target-egress-gateway-mTLS
            port:
              number: 80
      - match:
        - gateways:
          - istio-egress/egress-gateway
          port: 80
        route:
        - destination:
            host: example.com
            port:
              number: 443
            subset: example-com-originate-TLS
          weight: 100
      tls:
      - match:
        - gateways:
          - mesh
          port: 443
          sniHosts:
          - example.com
        route:
        - destination:
            host: istio-egressgateway.istio-egress.svc.cluster.local
            subset: target-egress-gateway-TLS-passthrough
            port:
              number: 443
      - match:
        - gateways:
          - istio-egress/egress-gateway
          port: 443
          sniHosts:
          - example.com
        route:
        - destination:
            host: example.com
            port:
              number: 443
          weight: 100
      exportTo:
      - 'istio-egress'
      - 'team-x'
    EOF
    
  4. ポート 443 での TLS トラフィックがゲートウェイを通過するように httpbin.org の仮想サービスを更新します。

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: VirtualService
    metadata:
      name: httpbin-org-through-egress-gateway
      namespace: istio-egress
    spec:
      hosts:
      - httpbin.org
      gateways:
      - istio-egress/egress-gateway
      - mesh
      http:
      - match:
        - gateways:
          - mesh
          port: 80
        route:
        - destination:
            host: istio-egressgateway.istio-egress.svc.cluster.local
            subset: target-egress-gateway-mTLS
            port:
              number: 80
          weight: 100
      - match:
        - gateways:
          - istio-egress/egress-gateway
          port: 80
        route:
        - destination:
            host: httpbin.org
            port:
              number: 80
          weight: 100
      tls:
      - match:
        - gateways:
          - mesh
          port: 443
          sniHosts:
          - httpbin.org
        route:
        - destination:
            host: istio-egressgateway.istio-egress.svc.cluster.local
            subset: target-egress-gateway-TLS-passthrough
            port:
              number: 443
      - match:
        - gateways:
          - istio-egress/egress-gateway
          port: 443
          sniHosts:
          - httpbin.org
        route:
        - destination:
            host: httpbin.org
            port:
              number: 443
          weight: 100
      exportTo:
      - 'istio-egress'
      - 'team-x'
      - 'team-y'
    EOF
    
  5. Egress ゲートウェイ サービスのポート 443 に送信されるトラフィックを許可する認証ポリシーを追加します。ゲートウェイ Pod の対応する targetPort は 8443 です。

    cat <<EOF | kubectl apply -f -
    apiVersion: security.istio.io/v1beta1
    kind: AuthorizationPolicy
    metadata:
      name: egress-all-443
      namespace: istio-egress
    spec:
      rules:
        - when:
          - key: destination.port
            values: ["8443"]
    EOF
    
  6. istioctl analyze を実行して、構成エラーを確認します。

    ${ISTIOCTL} analyze -n istio-egress
    

    次の出力を確認できます。

    ✔ No validation issues found when analyzing namespace: istio-egress.
    
  7. team-x 名前空間のテスト アプリケーションから example.com へのプレーン HTTP リクエストを作成します。

    kubectl -n team-x exec -it $(kubectl -n team-x get pod -l app=test \
        -o jsonpath={.items..metadata.name}) -c test -- curl -I http://example.com
    

    リクエストが成功すると、200 OK レスポンスが返されます。

  8. 次に、team-x 名前空間のテスト アプリケーションから TLS(HTTPS)リクエストをいくつか作成します。

    for i in {1..4}
    do
        kubectl -n team-x exec -it $(kubectl -n team-x get pod -l app=test \
            -o jsonpath={.items..metadata.name}) -c test -- curl -s -o /dev/null \
            -w "%{http_code}\n" \
            https://example.com
    done
    

    200 件のレスポンスが表示されます。

  9. Egress ゲートウェイのログを再度確認します。

    kubectl -n istio-egress logs -f $(kubectl -n istio-egress get pod -l istio=egress \
        -o jsonpath="{.items[0].metadata.name}") istio-proxy
    

    次のようなログエントリが表示されます。

    [2020-09-24T18:04:38.608Z] "- - -" 0 - "-" "-" 1363 5539 10 - "-" "-" "-" "-" "93.184.216.34:443" outbound|443||example.com 10.1.4.31:51098 10.1.4.31:8443 10.1.1.15:57030 example.com -
    

    HTTPS リクエストは TCP トラフィックとして処理され、ゲートウェイ経由で宛先ホストに渡されるため、HTTP 情報はログに含まれません。

Kubernetes NetworkPolicy を追加の制御として使用する

アプリケーションがサイドカー プロキシをバイパスできるシナリオは数多くあります。Kubernetes NetworkPolicy を使用して、ワークロードに許可する接続をさらに指定できます。単一のネットワーク ポリシーを適用した後、特に許可されていないすべての接続は拒否されます。

このチュートリアルでは、ネットワーク ポリシーの下り接続と下り(外向き)セレクタのみが考慮されています。自身のクラスタで、ネットワーク ポリシーで上り(内向き)を制御する場合は、下り(外向き)ポリシーに合わせて上り(内向き)ポリシーを作成する必要があります。たとえば、team-x 名前空間内のワークロードから team-y 名前空間への下り(外向き)を許可する場合は、team-x 名前空間から team-y 名前空間への上り(内向き)も許可する必要があります。

  1. team-x 名前空間にデプロイされたワークロードとプロキシが istiod と Egress ゲートウェイに接続できるようにします。

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-egress-to-control-plane
      namespace: team-x
    spec:
      podSelector: {}
      policyTypes:
        - Egress
      egress:
      - to:
        - namespaceSelector:
            matchLabels:
              istio: system
          podSelector:
            matchLabels:
              istio: istiod
        - namespaceSelector:
            matchLabels:
              istio: egress
          podSelector:
            matchLabels:
              istio: egress
    EOF
    
  2. ワークロードとプロキシが DNS をクエリできるようにします。

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-egress-to-dns
      namespace: team-x
    spec:
      podSelector: {}
      policyTypes:
        - Egress
      egress:
      - to:
        - namespaceSelector:
            matchLabels:
              kube-system: "true"
        ports:
        - port: 53
          protocol: UDP
        - port: 53
          protocol: TCP
    EOF
    
  3. ワークロードとプロキシが、Mesh CA を含む Google API とサービスを提供する IP に接続できるようにします。

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-egress-to-google-apis
      namespace: team-x
    spec:
      podSelector: {}
      policyTypes:
        - Egress
      egress:
      - to:
        - ipBlock:
            cidr: 199.36.153.4/30
        - ipBlock:
            cidr: 199.36.153.8/30
    EOF
    
  4. ワークロードとプロキシが GKE メタデータ サーバーに接続できるようにします。

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-egress-to-metadata-server
      namespace: team-x
    spec:
      podSelector: {}
      policyTypes:
        - Egress
      egress:
      - to: # For GKE data plane v2
        - ipBlock:
            cidr: 169.254.169.254/32
      - to: # For GKE data plane v1
        - ipBlock:
            cidr: 127.0.0.1/32
        ports:
        - protocol: TCP
          port: 988
    EOF
    
  5. 省略可: team-x 名前空間のワークロードとプロキシが相互に接続できるようにします。

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-egress-to-same-namespace
      namespace: team-x
    spec:
      podSelector: {}
      ingress:
        - from:
          - podSelector: {}
      egress:
        - to:
          - podSelector: {}
    EOF
    
  6. 省略可: team-x 名前空間のワークロードとプロキシが、別のチームによりデプロイされたワークロードに接続できるようにします。

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-egress-to-team-y
      namespace: team-x
    spec:
      podSelector: {}
      policyTypes:
        - Egress
      egress:
      - to:
        - namespaceSelector:
            matchLabels:
              team: 'y'
    EOF
    
  7. サイドカー プロキシ間の接続は維持されます。新しいネットワーク ポリシーを適用しても、既存の接続は閉じられません。team-x 名前空間のワークロードを再起動して、既存の接続が閉じられていることを確認します。

    kubectl -n team-x rollout restart deployment
    
  8. team-x 名前空間のテスト アプリケーションから example.com に HTTP リクエストを実行できることを確認します。

    kubectl -n team-x exec -it $(kubectl -n team-x get pod -l app=test \
        -o jsonpath={.items..metadata.name}) -c test -- curl -I http://example.com
    

    リクエストが成功すると、200 OK レスポンスが返されます。

限定公開の Google アクセスと IAM 権限を使用して Google API に直接アクセスする

Google の API とサービスは、外部 IP アドレスを使用して公開されます。VPC ネイティブのエイリアス IP アドレスを持つ Pod が限定公開の Google アクセスを使用して Google API に接続する場合、トラフィックが Google のネットワークの外に出ることはありません。

このチュートリアルのインフラストラクチャを設定する際、GKE Pod で使用されるサブネットの限定公開の Google アクセスを有効にしています。限定公開の Google アクセスで使用される IP アドレスへのアクセスを許可するために、ルート、VPC ファイアウォール ルール、プライベート DNS ゾーンを作成しています。この構成のため、Pod は Egress ゲートウェイ経由でトラフィックを送信しなくても、Google API に直接アクセスできます。Workload Identity と IAM を使用すると、特定の Kubernetes サービス アカウント(ここでは名前空間)で使用できる API を制御できます。Egress ゲートウェイが Google API への接続を処理していないため、Istio の承認は有効になりません。

Pod が Google API を呼び出せるようにするには、IAM を使用して権限を付与する必要があります。このチュートリアルで使用するクラスタは Workload Identity を使用するように構成されているため、Kubernetes サービス アカウントを Google サービス アカウントとして機能させることができます。

  1. アプリケーションで使用する Google サービス アカウントを作成します。

    gcloud iam service-accounts create sa-test-app-team-x
    
  2. Kubernetes サービス アカウントを Google サービス アカウントとして使用できるようにします。

    gcloud iam service-accounts add-iam-policy-binding \
      --role roles/iam.workloadIdentityUser \
      --member "serviceAccount:${PROJECT_ID}.svc.id.goog[team-x/test]" \
      sa-test-app-team-x@${PROJECT_ID}.iam.gserviceaccount.com
    
  3. team-x 名前空間のテストアプリの Kubernetes サービス アカウントに、Google サービス アカウントのメールアドレスでアノテーションを付けます。

    cat <<EOF | kubectl apply -f -
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      annotations:
        iam.gke.io/gcp-service-account: sa-test-app-team-x@${PROJECT_ID}.iam.gserviceaccount.com
      name: test
      namespace: team-x
    EOF
    
  4. テスト アプリケーション Pod は、Google API を呼び出すための一時的な認証情報を取得するために、Google メタデータ サーバー(DaemonSet として稼働)にアクセスできる必要があります。GKE メタデータ サーバーのサービス エントリを作成します。

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: ServiceEntry
    metadata:
      name: metadata-google-internal
      namespace: istio-egress
    spec:
      hosts:
      - metadata.google.internal
      ports:
      - number: 80
        name: http
        protocol: HTTP
      - number: 443
        name: tls
        protocol: TLS
      resolution: DNS
      location: MESH_EXTERNAL
      exportTo:
      - 'istio-egress'
      - 'team-x'
    EOF
    
  5. また、private.googleapis.com と storage.googleapis.com のサービス エントリも作成します。

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: ServiceEntry
    metadata:
      name: private-googleapis-com
      namespace: istio-egress
    spec:
      hosts:
      - private.googleapis.com
      - storage.googleapis.com
      ports:
      - number: 80
        name: http
        protocol: HTTP
      - number: 443
        name: tls
        protocol: TLS
      resolution: DNS
      location: MESH_EXTERNAL
      exportTo:
      - 'istio-egress'
      - 'team-x'
    EOF
    
  6. Kubernetes サービス アカウントが Google サービス アカウントとして機能するように構成されていることを確認します。

    kubectl -n team-x exec -it $(kubectl -n team-x get pod -l app=test \
        -o jsonpath={.items..metadata.name}) -c test -- gcloud auth list
    

    Google サービス アカウントがアクティブな唯一の ID として表示されます。

  7. Cloud Storage バケットにテストファイルを作成します。

    echo "Hello, World!" > /tmp/hello
    gsutil mb gs://${PROJECT_ID}-bucket
    gsutil cp /tmp/hello gs://${PROJECT_ID}-bucket/
    
  8. サービス アカウントにバケット内のファイルを一覧表示する権限を付与します。

    gsutil iam ch \
    serviceAccount:sa-test-app-team-x@${PROJECT_ID}.iam.gserviceaccount.com:objectViewer \
        gs://${PROJECT_ID}-bucket/
    
  9. テスト アプリケーションがテストバケットにアクセスできることを確認します。

    kubectl -n team-x exec -it \
    $(kubectl -n team-x get pod -l app=test -o jsonpath={.items..metadata.name}) \
    -c test \
    -- gsutil cat gs://${PROJECT_ID}-bucket/hello
    

    次の出力を確認できます。

    Hello, World!
    

クリーンアップ

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

このチュートリアルで使用したリソースについて、Google Cloud アカウントに課金されないようにするには、以下のセクションの手順を完了します。

プロジェクトの削除

課金を停止する最も簡単な方法は、チュートリアル用に作成したプロジェクトを削除することです。

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

次のステップ