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


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

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

目標

  • Cloud Service Mesh を実行するためのインフラストラクチャを設定します。
  • Cloud Service Mesh をインストールします。
  • 専用のノードプールで実行される Egress ゲートウェイ プロキシをインストールします。
  • Egress ゲートウェイを使用して、外部トラフィックのマルチテナント ルーティング ルールを構成します。
    • Namespace team-x のアプリケーションは example.com に接続できます
    • Namespace 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. compute.googleapis.com の有効化:

    gcloud services enable compute.googleapis.com --project=YOUR_PROJECT_ID
    
  7. スクリプトを実行可能にして、source コマンドで実行し、環境を初期化します。compute.googleapis.com を有効にするよう求められたら、[Y] を選択します。

    chmod +x ./init-egress-tutorial.sh
    source ./init-egress-tutorial.sh
    

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

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 \
        --target-service-accounts sa-application-nodes@${PROJECT_ID}.iam.gserviceaccount.com,sa-gateway-nodes@${PROJECT_ID}.iam.gserviceaccount.com \
        --destination-ranges 10.5.0.0/28 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow nodes to reach the Kubernetes API server."
    
  4. 省略可: マネージド Cloud Service Mesh を使用する場合、このファイアウォール ルールは必要ありません。

    Cloud Service Mesh は、ワークロードにサイドカー プロキシを挿入する際に Webhook を使用します。GKE API サーバーが、ノードで実行されているサービス メッシュ コントロール プレーンによって公開された Webhook を呼び出すようにします。

    gcloud compute firewall-rules create allow-ingress-api-server-to-webhook \
        --action ALLOW \
        --direction INGRESS \
        --rules tcp:15017 \
        --target-service-accounts sa-application-nodes@${PROJECT_ID}.iam.gserviceaccount.com,sa-gateway-nodes@${PROJECT_ID}.iam.gserviceaccount.com \
        --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 は、対応する上り(内向き)ルールを自動的に作成します。iptables ルーティング チェーンは常に Service の IP アドレスを Pod の IP アドレスに変換するため、Service の接続にルールは必要ありません。

    gcloud compute firewall-rules create allow-egress-nodes-and-pods \
        --action ALLOW \
        --direction EGRESS \
        --rules all \
        --target-service-accounts sa-application-nodes@${PROJECT_ID}.iam.gserviceaccount.com,sa-gateway-nodes@${PROJECT_ID}.iam.gserviceaccount.com \
        --destination-ranges 10.0.0.0/24,10.1.0.0/16 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow egress to other Nodes and Pods"
    
  6. Google API や Container Registry などのサービスを提供するために、限定公開の Google アクセスで使用される予約済みの IP アドレスセットへのアクセスを許可します。

    gcloud compute firewall-rules create allow-egress-gcp-apis \
        --action ALLOW \
        --direction EGRESS \
        --rules tcp \
        --target-service-accounts sa-application-nodes@${PROJECT_ID}.iam.gserviceaccount.com,sa-gateway-nodes@${PROJECT_ID}.iam.gserviceaccount.com \
        --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)"
    
  7. クラスタで実行されている Pod へのアクセスを Google Cloud ヘルス チェッカー サービスに許可します。詳細については、ヘルスチェックをご覧ください。

    gcloud compute firewall-rules create allow-ingress-gcp-health-checker \
        --action ALLOW \
        --direction INGRESS \
        --rules tcp:80,tcp:443 \
        --target-service-accounts sa-application-nodes@${PROJECT_ID}.iam.gserviceaccount.com,sa-gateway-nodes@${PROJECT_ID}.iam.gserviceaccount.com \
        --source-ranges 35.191.0.0/16,130.211.0.0/22,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 ネットワークの外に出ることはありません。

Cloud DNS API を有効にします。

gcloud services enable dns.googleapis.com

限定公開の 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. Google Kubernetes Engine API を有効にします。

    gcloud services enable container.googleapis.com
    
  3. 限定公開 GKE クラスタを作成します。

    gcloud container clusters create cluster1 \
        --enable-ip-alias \
        --enable-private-nodes \
        --release-channel "regular" \
        --enable-master-authorized-networks \
        --master-authorized-networks ${SHELL_IP//\"}/32 \
        --master-ipv4-cidr 10.5.0.0/28 \
        --enable-dataplane-v2 \
        --service-account "sa-application-nodes@${PROJECT_ID}.iam.gserviceaccount.com" \
        --machine-type "e2-standard-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 が割り振られます。

    クラスタ内コントロール プレーンを使用する Cloud Service Mesh では、クラスタノードで 4 つ以上の vCPU を備えたマシンタイプを使用する必要があります。

    ノードが Cloud Service Mesh でサポートされている Kubernetes バージョンを実行できるように、クラスタを「通常の」リリース チャネルに登録することをおすすめします。

    クラスタ内コントロール プレーンを使用して Cloud Service Mesh を実行する場合の前提条件については、クラスタ内の前提条件をご覧ください。

    マネージド Cloud Service Mesh を実行するための要件と制限事項の詳細については、マネージド Cloud Service Mesh でサポートされている機能をご覧ください。

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

  4. 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 のみがゲートウェイ ノードプール内のノードで実行されるようにするのに役立ちます。

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

    gcloud container clusters get-credentials cluster1
    
  6. ゲートウェイ ノードに適切な 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]
    

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

Cloud Service Mesh のいずれかのインストール ガイドに従ってください。

Cloud Service Mesh をインストールしたら、Ingress または Egress ゲートウェイをインストールせずに、このチュートリアルに戻ります。

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

  1. Egress ゲートウェイの Kubernetes Namespace を作成します。

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

    マネージド(TD)

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

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

    マネージド(Istiod)

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

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

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

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

      kubectl -n istio-system get controlplanerevision
      

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

      NAME                AGE
      asm-managed-rapid   6d7h
      

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

    2. リビジョン ラベルを名前空間に適用します。

      kubectl label namespace istio-egress \
          istio-injection- istio.io/rev=REVISION_LABEL --overwrite
      

    クラスタ内

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

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

    デフォルトのインジェクションを使用することをおすすめしますが、リビジョンベースのインジェクションがサポートされています。 次の手順を使用します。

    1. 次のコマンドを使用して、istiod のリビジョン ラベルを探します。

      kubectl get deploy -n istio-system -l app=istiod -o \
         jsonpath={.items[*].metadata.labels.'istio\.io\/rev'}'{"\n"}'
      
    2. リビジョン ラベルを名前空間に適用します。次のコマンドで、REVISION_LABEL は前の手順でメモした istiod リビジョン ラベルの値です。

      kubectl label namespace istio-egress \
          istio-injection- istio.io/rev=REVISION_LABEL --overwrite
      
  3. Egress ゲートウェイのオペレータ マニフェストを作成します。

    cat << EOF > egressgateway-operator.yaml
    apiVersion: install.istio.io/v1alpha1
    kind: IstioOperator
    metadata:
      name: egressgateway-operator
      annotations:
        config.kubernetes.io/local-config: "true"
    spec:
      profile: empty
      revision: REVISION
      components:
        egressGateways:
        - name: istio-egressgateway
          namespace: istio-egress
          enabled: true
      values:
        gateways:
          istio-egressgateway:
            injectionTemplate: gateway
            tolerations:
              - key: "dedicated"
                operator: "Equal"
                value: "gateway"
            nodeSelector:
              cloud.google.com/gke-nodepool: "gateway"
    EOF
    
  4. istioctl ツールをダウンロードします。Cloud Service Mesh バージョン 1.15 以前を使用している場合でも、バージョン 1.16.2-asm.2 以降を使用する必要があります。正しい istioctl バージョンのダウンロードをご覧ください。

  5. ダウンロードしたアーカイブを展開したら、istioctl ツールのパスを格納する環境変数を設定し、初期化スクリプトに追加します。

    ISTIOCTL=$(find "$(pwd -P)" -name istioctl)
    echo "ISTIOCTL=\"${ISTIOCTL}\"" >> ./init-egress-tutorial.sh
    
  6. オペレータ マニフェストと istioctl を使用して、Egress ゲートウェイのインストール マニフェストを作成します。

    ${ISTIOCTL} manifest generate \
        --filename egressgateway-operator.yaml \
        --output egressgateway \
        --cluster-specific
    
  7. Egress ゲートウェイをインストールします。

    kubectl apply --recursive --filename egressgateway/
    
  8. Egress ゲートウェイが gateway ノードプールのノードで実行されていることを確認します。

    kubectl get pods -n istio-egress -o wide
    
  9. Egress ゲートウェイ Pod には、gateway ノードプール内のノードに対する affinity と、taint が付与されたゲートウェイ ノードで実行できる toleration があります。Egress ゲートウェイ Pod のノード アフィニティと toleration を確認します。

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

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

    name                                   node-affinity                                                                                   tolerations
    istio-egressgateway-754d9684d5-jjkdz   [map[matchExpressions:[map[key:cloud.google.com/gke-nodepool operator:In values:[gateway]]]]]   map[key:dedicated operator:Equal value:gateway]
    

Envoy アクセス ロギングを有効にする

Envoy アクセスログを有効にする手順は、Cloud Service Mesh のタイプ(マネージドまたはクラスタ内)によって異なります。

管理対象

マネージド Cloud Service Mesh でアクセスログを有効にするの手順に沿って操作します。

クラスタ内

クラスタ内 Cloud Service Mesh でアクセスログを有効にするの手順に沿って操作します。

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

  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. Cloud Service Mesh がプロキシ サイドカーを自動的に挿入するように、ワークロードの Namespace にコントロール プレーンのリビジョン ラベルを設定します。

    kubectl label ns team-x istio.io/rev- istio-injection=enabled --overwrite
    kubectl label ns team-y istio.io/rev- istio-injection=enabled --overwrite
    
  5. テストデプロイに使用する 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
    
  6. テスト アプリケーションを team-x 名前空間にデプロイします。

    kubectl -n team-x create -f ./test.yaml
    
  7. テスト アプリケーションがデフォルト プール内のノードにデプロイされ、プロキシ サイドカー コンテナが挿入されていることを確認します。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 は、デフォルトのノードプール内のノードで実行されます。

  8. テストコンテナから外部サイトに 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 リソースを適用することをおすすめします。

Cloud 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
    

    このリストには、Egress ゲートウェイを含む Envoy クラスタが約 11 個あります。

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

    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 に設定すると、プロキシ構成が制限されます。サービス エントリを定義することで、メッシュのサービス レジストリに明示的に追加された外部ホストのみが含まれるようになります。

    egress.hosts を設定すると、サイドカー プロキシは exportTo 属性を使用して使用可能な Egress Namespace からのルートのみを選択します。「team-x/*」の部分には、team-x Namespace でローカルに構成されたルートが含まれます。

  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 ゲートウェイ経由でトラフィックをルーティングするように Cloud Service Mesh を構成する

  1. ポート 80 上の HTTP トラフィックに対して Gateway を構成します。Gateway は、Egress Namespace にデプロイした Egress ゲートウェイ プロキシを選択します。Gateway 構成が Egress Namespace に適用され、すべてのホストのトラフィックを処理します。

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: Gateway
    metadata:
      name: egress-gateway
      namespace: istio-egress
    spec:
      selector:
        istio: egressgateway
      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:
          tls:
            mode: ISTIO_MUTUAL
    EOF
    
  3. Egress Namespace に ServiceEntry を作成し、team-x Namespace のメッシュのサービス レジストリに example.com を明示的に登録します。

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: ServiceEntry
    metadata:
      name: example-com-ext
      namespace: istio-egress
      labels:
        # Show this service and its telemetry in the Cloud Service Mesh page of the Google Cloud console
        service.istio.io/canonical-name: example.com
    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 --revision REVISION
    

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

    ✔ 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=egressgateway \
        -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
      labels:
        # Show this service and its telemetry in the Cloud Service Mesh page of the Google Cloud console
        service.istio.io/canonical-name: httpbin.org
    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 --revision REVISION
    

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

    ✔ 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:
      action: ALLOW
      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:
      action: ALLOW
      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 開始

TLS または相互 TLS へのプレーン HTTP リクエストの upgrade(開始)を行うように、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:
            portLevelSettings:
            - port:
                number: 443
              tls:
                mode: SIMPLE
                sni: example.com
    EOF
    
  2. example.com の仮想サービスを更新し、ゲートウェイのポート 80 に対するリクエストが宛先ホストへの送信時にポート 443 の TLS に upgraded されるようにします。

    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=egressgateway \
        -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: egressgateway
      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:
          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:
      action: ALLOW
      rules:
        - when:
          - key: destination.port
            values: ["8443"]
    EOF
    
  6. istioctl analyze を実行して、構成エラーを確認します。

    ${ISTIOCTL} analyze -n istio-egress --revision REVISION
    

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

    ✔ 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=egressgateway \
        -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:
              "kubernetes.io/metadata.name": istio-system
          podSelector:
            matchLabels:
              istio: istiod
        - namespaceSelector:
            matchLabels:
              "kubernetes.io/metadata.name": istio-egress
          podSelector:
            matchLabels:
              istio: egressgateway
    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:
              "kubernetes.io/metadata.name": kube-system
        ports:
        - port: 53
          protocol: UDP
        - port: 53
          protocol: TCP
    EOF
    
  3. ワークロードとプロキシが、Cloud Service Mesh 認証局を含む 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 # Prior to 1.21.0-gke.1000
        - ipBlock:
            cidr: 169.254.169.252/32 # 1.21.0-gke.1000 and later
        ports:
        - protocol: TCP
          port: 987
        - 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:
              "kubernetes.io/metadata.name": 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 サービス アカウント(ここでは Namespace)で使用できる 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
      labels:
        # Show this service and its telemetry in the Cloud Service Mesh page of the Google Cloud console
        service.istio.io/canonical-name: metadata.google.internal
    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
      labels:
        # Show this service and its telemetry in the Cloud Service Mesh page of the Google Cloud console
        service.istio.io/canonical-name: googleapis.com
    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
    gcloud storage buckets create gs://${PROJECT_ID}-bucket
    gcloud storage cp /tmp/hello gs://${PROJECT_ID}-bucket/
    
  8. サービス アカウントにバケット内のファイルを一覧表示する権限を付与します。

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

    kubectl -n team-x exec -it \
    $(kubectl -n team-x get pod -l app=test -o jsonpath={.items..metadata.name}) \
    -c test \
    -- gcloud storage 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.

次のステップ