Amazon Web Services ブログ

VPC Lattice と Pod Identity IAM Session Tags を活用した、セキュアな EKS クラスター間通信の実現

この記事は Secure Cross-Cluster Communication in EKS with VPC Lattice and Pod Identity IAM Session Tags (記事公開日: 2024 年 9 月 18 日) を翻訳したものです。

ソリューション概要

アプリケーションを開発し、内部向けに API エンドポイントを公開したい場合は、AWS LambdaAmazon Elastic Container Service(ECS)Amazon Elastic Kubernetes Service(Amazon EKS) などのさまざまなコンピューティングオプションを使用して、マイクロサービスを構築できます。そして、アプリケーションを複数の AWS アカウントと複数の Amazon Virtual Private Cloud(VPC) にまたがってデプロイできますが、それらをセキュアに接続する方法が必要です。Amazon VPC Lattice はアカウント間の East-West トラフィック通信を可能にし、サービスディスカバリ・トラフィック管理・アクセス制御といった機能を提供します。Amazon EKS で取り組む場合、Amazon VPC Lattice には Kubernetes Gateway API を実装した AWS Gateway API Controller を利用します。

このアーキテクチャ全体のトラフィックは、通信の暗号化により保護できます。また、Amazon VPC Lattice の各 Service できめ細やかな AWS Identity and Access Management(IAM) による認可を有効にできます。暗号化のためには、プライベートドメインを管理する AWS Private Certificate Authority(CA) と、各サービスの証明書を作成する AWS Certificate Manager(ACM) を利用できます。認可のためには、Amazon VPC Lattice の IAM 認証ポリシー機能と、クラスター管理者が Kubernetes アプリケーションを設定して IAM アクセス許可を取得する方法をシンプルに実現する EKS Pod Identity を利用できます。これらのアクセス許可は、Amazon EKS コンソール、API、CLI を通じて、より少ないステップで直接設定できるようになりました。EKS Pod Identity を使用すると、IAM ロールを複数のクラスター間で再利用でき、ポリシー管理がシンプルになります。

以下の API を使用して、IAM ロールをPod の ServiceAccount に関連付けることができます。

aws eks create-pod-identity-association \
  --cluster-name $CLUSTER_NAME \
  --namespace $NAMESPACE \
  --service-account $SERVICE_ACCOUNT \
  --role-arn arn:aws:iam::$AWS_ACCOUNT:role/$POD_ROLE_NAME
Bash

Amazon EKS が使用できるようにするため、IAM ロール $POD_ROLE_NAME には、以下の信頼ポリシーが必要です。

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {
      "Service": "pods.eks.amazonaws.com"
    },
    "Action": ["sts:AssumeRole","sts:TagSession"]
  }]
}
Bash

EKS Pod Identity が IAM ロールを引き受けると、IAM セッションにセッションタグを設定します。 これらのタグには、eks-cluster-namekubernetes-namespacekubernetes-pod-name などの情報が含まれており、属性ベースのアクセス制御 (ABAC) に使用できます。 ABAC を使用すると、特定の Kubernetes Pod や、特定の EKS クラスター内の特定の Namespace 内からのみ、AWS リソースへのアクセスを許可できます。 この機能は、IAM 認証を有効にした場合に Amazon VPC Lattice サービスにアクセスするときにも使用でき、Amazon EKS 内または異なる EKS クラスター間のマイクロサービスに対して直接的かつ豊富なアクセス制御の仕組みを提供します。

Amazon VPC Lattice サービスにアクセスするために、IAM 認証ポリシーを有効にした場合、アプリケーションのリクエストは AWS Sigv4 アルゴリズム(またはクロスリージョンの場合は Sigv4A)で署名する必要があります。これは、他の AWS サービスで使用されているのと同じ機能です。 アプリケーションコード内の AWS SDK を使用してリクエスト署名を実装できます(サンプルを参照)。これは推奨される方法ですが、アプリケーションコードを変更する必要があります。そこで、私たちは別のソリューションを提案します。関連する Amazon EKS Blueprints パターンは、アプリケーションコードを変更せず、サイドカープロキシを使用する異なるアプローチを示しています。 このプロキシは、Amazon VPC Lattice サービスをターゲットとするリクエストの Sigv4 署名を自動的に処理します。 Kubernetes クラスター内から AWS サービスとのシームレスな統合を可能にする、EKS Pod Identity と AWS Sigv4 署名機能をサポートする、幅広く採用されているプロキシとして、 Envoy を使用します。

さらに、Pod 間通信の暗号化のために、Amazon VPC Lattice サービスを HTTPS リスナーで設定できます。 アプリケーションコンテナが HTTPS リクエストの TLS 暗号化をサポートできない場合は、Sigv4 署名とともにこれを Envoy サイドカープロキシにオフロードできます。このセットアップでは、HTTPS として設定された Amazon VPC Lattice サービスへの HTTP リクエストをアプリケーションが生成し、リクエストは Envoy サイドカープロキシにルーティングされ、Envoy はクライアントとして Amazon VPC Lattice に署名された HTTPS リクエストを発行します。 アプリケーションが Amazon VPC Lattice サービスへの HTTP リクエストを行うと、リクエストは自動的にローカルの iptables ルールで Envoy サイドカーにルーティングされます。 そこから、Envoy はこのリクエストの Sigv4 署名を作成し、HTTPS で Amazon VPC Lattice にリクエストを転送します。また ACM によって発行されたプライベートドメイン証明書を検証するために、AWS Private CA を利用します。

プロキシの利用を簡単にするために、このパターンは Deployment の Annotations に依存しています。これにより、Kyverno クラスターポリシーがトリガーされ、アプリケーション Pod 内に Envoy サイドカープロキシが自動的に注入されます。

ウォークスルー

このソリューションは、EKS Blueprints for Terraform に依存しています。EKS Blueprints は、Amazon EKS やその他の AWS サービスの具体的な使用法を示すパターンのコレクションです。ここでは、network/cross-cluster-pod-communication を使用します。

このパターンは、以下の図にも示すように、3 つの Terraform スタックに分割されています。

1. 次の 2 つのスタックは、クラスターディレクトリの同じ Terraform コードを使用してデプロイされます。 Terraform の workspace 機能を使用して、クラスタースタックを 2 回インスタンス化し、2 つの EKS クラスター (cluster1 と cluster2) を作成します。

  • Amazon Route 53 のプライベートホストゾーンである example.com を、ダミーのプライベート VPC (プライベートホストゾーンを持つためだけにこの段階で作成) にアタッチします。
  • プライベートドメインを管理する AWS Private CA から、後ほど Amazon VPC Lattice サービスにアタッチするワイルドカードの ACM 証明書を作成します。
  • EKS Pod Identity によってアプリケーションで使用する IAM ロールも作成します。このロールは Amazon VPC Lattice サービスを呼び出す権限と、アプリケーションがプライベートドメインを信頼できるように AWS Private CA のルート証明書をダウンロードする権限を持っています。

2. 次の 2 つのスタックは、クラスターディレクトリの同じ Terraform コードを使用してデプロイされます。 Terraform の workspace 機能を使用して、クラスタースタックを 2 回インスタンス化し、2 つの EKS クラスター (cluster1 と cluster2) を作成します。

まず、スタックは専用 VPC (正確に同じ設定なのでオーバーラップする CIDR を持つ) を作成します。

そして、スタックは専用のマネージドノードグループを用いて EKS クラスターを作成します。

次に、いくつかの Amazon EKS と Kubernetes のアドオンをインストールします。

  • HTTPRoute および IAMAuthPolicy の定義から Amazon VPC Lattice オブジェクトの作成を管理するGateway API Controller
  • カスタムドメイン名を含む HTTPRoute オブジェクトに基づいて、Route53 プライベートホストゾーンにレコードを作成する責務を持つ ExternalDNS
  • そして最後に、アプリケーション Pod 内に Envoy プロキシを注入する責務を持つ Kyverno をデプロイします

次に、2 つの Helm チャートをインストールします。1 つ目のplatform という名前のチャートは、Amazon VPC Latticeサービスネットワークを作成する GatewayClass およびGateway オブジェクトを作成し、アプリケーションに Envoy プロキシを注入するために使用される Kyverno クラスターポリシーを作成します。2 つ目の Helm チャートである demo は、以下の図に示すように、最初のクラスターでは demo-cluster1、2 つ目のクラスタでは demo-cluster2 と名付けられたデモ用のアプリケーションをデプロイします。

Gateway API Controller が platform Helm チャートから作成された Gateway オブジェクトを検知すると、VPC と VPC Lattice サービスネットワークの間に VPC との関連付けを作成します。VPC との関連付けには、サービスネットワークへのインバウンドリクエストを許可する VPC 内の対象を定義するセキュリティグループを含めることができます。さまざまな条件を用いて、ネットワークを利用できる対象を制限することができます。たとえば、VPC 識別子によって制限することもできます。

単純化のため、このパターンは同じ AWS アカウントに閉じていますが、Amazon VPC Lattice は AWS Resource Access Manager(AWS RAM) を用いることで、クロスアカウントで使用できます。

Amazon VPC Lattice ターゲットグループのヘルスチェックは、Amazon VPC Lattice サービスからリクエストされます。Amazon VPC Lattice には、Terraform の定義によって EKS クラスターのセキュリティグループで許可する必要がある、特定のマネージドプレフィックスリストがあります。Amazon EKS アプリケーションがサービスネットワークに接続する必要がある場合は、適切なポートでノードグループのセキュリティグループからのインバウンドトラフィックを許可するために、VPC への関連付けのセキュリティグループも更新する必要があります。こちらについても Terraform で設定されます。

デモ用の Helm チャートは、デモサービスの定義を含むHTTPRoute オブジェクトを作成し、カスタムドメイン名を使用します。

apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
  name: demo-cluster2
  namespace: apps
spec:
  hostnames:
  - demo-cluster2.example.com
  parentRefs:
  - group: gateway.networking.k8s.io
    kind: Gateway
    name: lattice-gateway
    namespace: lattice-gateway
    sectionName: http-listener
  - group: gateway.networking.k8s.io
    kind: Gateway
    name: lattice-gateway
    namespace: lattice-gateway
    sectionName: https-listener-with-custom-domain
  rules:
  - backendRefs:
    - group: ""
      kind: Service
      name: demo-cluster2-v1
      port: 80
      weight: 1
    matches:
    - path:
        type: PathPrefix
        value: /
Bash

Gateway API Controller は、関連する Amazon VPC Lattice リソース (サービス、リスナー、ターゲットグループ) を作成します。 このデプロイでは、各リスナー上の各サービスに対して TLS 終端を有効にします。これにより、トラフィックは各サービスに定義されたルートを尊重して、関連するターゲットグループに転送されます。 ターゲットグループは IP タイプとして設定されており、直接ターゲットとなる Kubernetes Service に関連づけられた Pod に転送します。

Amazon VPC Lattice サービスは、異なるクラスターから、ターゲットに対するリクエストを分散するように設定できます。

カスタムドメイン名を定義したため、Gateway API Controller は Kubernetes の DNSEndpoint オブジェクトも作成します。

kind: DNSEndpoint
metadata:
  name: demo-cluster1-dns
  namespace: apps
  ownerReferences:
  - apiVersion: gateway.networking.k8s.io/v1beta1
    blockOwnerDeletion: true
    controller: true
    kind: HTTPRoute
    name: app4
spec:
  endpoints:
  - dnsName: demo-cluster1.example.com
    recordTTL: 300
    recordType: CNAME
    targets:
    - demo-cluster1-apps-082dc3111b7018633.7d67968.vpc-lattice-svcs.eu-west-1.on.aws
status:
  observedGeneration: 1
Bash

ExternalDNS は CRD による拡張を用いて、このオブジェクトを監視します。 以下を設定することで、ExternalDNS が DNSEndpoint カスタムリソース定義から読み取れるように構成します。

--source=crd --crd-source-apiversion=externaldns.k8s.io/v1alpha1 --crd-source-kind=DNSEndpoint

API Gateway HTTPRoute をカスタムドメインを指定して作成すると、Amazon VPC Lattice はそのエントリの DNS エンドポイントを作成します。

この設定により、ExternalDNS はプライベートホストゾーンに専用の Route53 CNAME レコードを作成できるため、カスタムドメイン名からのトラフィックを Amazon VPC Lattice サービスのエンドポイントにルーティングできます。 これにより、内部サービスは内部ドメイン名を使用して検出およびアクセスできます。

Kubernetes Gateway オブジェクトには、Amazon VPC Lattice がリクエストの TLS セッションを終了するために使用する ACM 証明書の Amazon リソースネーム (ARN) も含まれています。

デモ用の Helm チャートは、HTTPRoute に関連付けられたIAMAuthPolicy オブジェクトもデプロイします。これは、各リクエストに Sigv4 アルゴリズムでの署名が必要であることと、demo-cluster2 のアプリケーションの場合、cluster1 EKS クラスターの apps Namespace からのリクエストのみが許可される ABAC ルールを定義しています。そして、demo-cluster1 のアプリケーションの場合は、cluster2 EKSクラスターの apps Namespace からのリクエストのみを受け入れます。

Amazon VPC Lattice サービスへのすべてのリクエストをログに記録するために、アクセスログのサブスクリプションを設定することもできます。ログは Amazon CloudWatch のロググループに記録されます。

デプロイ

関連する EKS Blueprints のパターンにあるデプロイ手順に従うことができます。

簡単なセットアップとして、以下のコマンドで実行できます。

# Clone EKS Blueprint repository
git clone https://github.com/aws-ia/terraform-aws-eks-blueprints.git
cd patterns/vpc-lattice/cross-cluster-pod-communication

# Deploy the environment
cd environment
terraform init
terraform apply --auto-approve

# Deploy cluster 1
cd ../cluster
./deploy.sh cluster1
eval `terraform output -raw configure_kubectl`

# Deploy cluster 2
cd ../cluster
./deploy.sh cluster2
eval `terraform output -raw configure_kubectl`
Bash

3 つのスタックが正常にデプロイされると、クラスター間通信が意図したとおりに機能していることを検証するために、次のコマンドを実行できます。

これを実現するために、Pod 内に exec して HTTP でサービスをターゲットにする cURL コマンドを実行します。すでに説明したように、リクエストは HTTP で Envoy サイドカーにルーティングされ、AWS Private CA 証明書を使用して HTTPS で署名され転送されます。

1. cluster1 の app1 から、cluster2 の app2 を呼び出す→ 成功

$ kubectl --context eks-cluster1 exec -ti -n apps deployments/demo-cluster1-v1 \
  -c demo-cluster1-v1 -- curl demo-cluster2.example.com

Requesting to Pod(demo-cluster2-v1-c99c7bb69-2gm5f): Hello from demo-cluster2-v1
Bash

2. cluster2 の app2 から、cluster1 の app1 を呼び出す → 成功

$ kubectl --context eks-cluster2 exec -ti -n apps deployments/demo-cluster2-v1 \
  -c demo-cluster2-v1 -- curl demo-cluster1.example.com

Requesting to Pod(demo-cluster1-v1-6d7558f5b4-zk5cg): Hello from demo-cluster1-v1
Bash

前のコマンドのように認証フローを使用しない場合、Amazon VPC Lattice が未認証のリクエストを拒否することがわかります。

3. cluster1 app1 から、cluster1 app1 を呼び出し → 禁止

$ kubectl --context eks-cluster1 exec -ti -n apps deployments/demo-cluster1-v1 \
  -c demo-cluster1-v1 -- curl demo-cluster1.example.com 

AccessDeniedException: User: arn:aws:sts::12345678910:assumed-role/vpc-lattice-sigv4-client/eks-eks-cluste-demo-clust-1b575f8d-fb77-486a-8a13-af5a2a0f78ae is not authorized to perform: vpc-lattice-svcs:Invoke on resource: arn:aws:vpc-lattice:eu-west-1:12345678910:service/svc-002349360ddc5a463/ because no service-based policy allows the vpc-lattice-svcs:Invoke action
Bash

4. cluster2 app2 から、cluster2 app2 を呼び出し → 禁止


$ kubectl --context eks-cluster2 exec -ti -n apps deployments/demo-cluster2-v1 \
-c demo-cluster2-v1 -- curl demo-cluster2.example.com

AccessDeniedException: User: arn:aws:sts::12345678910:assumed-role/vpc-lattice-sigv4-client/eks-eks-cluste-demo-clust-a5c2432b-b84a-492f-8cbc-16f1fa5053eb is not authorized to perform: vpc-lattice-svcs:Invoke on resource: arn:aws:vpc-lattice:eu-west-1:12345678910:service/svc-00b57f32ed0a7b7c3/ because no service-based policy allows the vpc-lattice-svcs:Invoke action
Bash

demo-cluster1 アプリケーションで IAMAuthPolicy がどのように定義されているかを確認できます。

kubectl --context eks-cluster1 get IAMAuthPolicy -n apps demo-cluster1-iam-auth-policy  -o json | jq ".spec.policy | fromjson"

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::12345678910:root"
      },
      "Action": "vpc-lattice-svcs:Invoke",
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "aws:PrincipalTag/eks-cluster-name": "eks-cluster2",
          "aws:PrincipalTag/kubernetes-namespace": "apps"
        }
      }
    }
  ]
}
Bash

eks-cluster2 クラスターから apps Namespace に発信されるリクエストのみが許可されていることを確認できます。

クリーンアップ

将来的な課金を回避するために、EKS Blueprints パターンのクリーンアップセクションに従ってリソースを削除するか、次のスニペットを実行してください。

  1. cluster2 Terraform スタックを削除することから始めます。

Kubernetes のコントローラーが、別のコントローラーや Kubernetes Node を削除する前に、外部リソースをクリーンアップできるよう、順番に処理する必要があることに注意してください。

./destroy.sh cluster2

  1. 次に、cluster1 の Terraform スタックを削除できます。

./destroy.sh cluster1

  1. 最後に、environment Terraform スタックを削除します。

cd ../environment
terraform destroy -auto-approve

まとめ

このソリューションでは、Amazon VPC Lattice を使用して 独自のマイクロサービスアプリケーションに適用可能な EKS クラスター間のアプリケーション通信を保護する方法を、自動化されたサンプルを用いて、デモンストレーションしました。

このアプローチの主なメリットは以下のとおりです。

  • セキュアな通信:Amazon VPC Lattice を使用することで、EKS クラスター間の通信の転送中の暗号化、きめ細やかな IAM 認可ポリシーによる保護を実現できます。これにより、クラスターやアカウント間の転送であっても、アプリケーションデータのセキュリティと整合性を維持できます。
  • サービスディスカバリの簡素化:Gateway API Controller と ExternalDNS の統合により、カスタムドメイン名を使用してサービスを公開できるため、他のサービスがそれらを見つけて通信するのがより簡単になります。
  • スケーラビリティと柔軟性:Amazon VPC Lattice を使用することでアプリケーションを複数の VPC とアカウントに分散できるため、コンポーネント間のセキュアな接続性を維持しながら必要に応じてインフラストラクチャをスケールできます。
  • 自動デプロイ:Terraform 用 EKS Blueprints を使用することで、EKS クラスター、Amazon VPC Lattice リソース、その他のサービスのデプロイと設定を自動化できるため、手動による誤りのリスクを減らし、一貫したデプロイを実現できます。
  • 再利用性:このソリューションは、アプリケーションコードを変更することなく、EKS Pod Identity と Envoy プロキシを使用してセキュアな通信を可能にする方法を示しています。このアプローチは他のアプリケーションにも適用できるため、同じパターンとベストプラクティスを組織全体で再利用できます。
  • 可観測性とモニタリング:Amazon VPC Lattice サービスのアクセスログを設定することで、クラスター間を流れるトラフィックの貴重な洞察を得られるため、問題をより効果的に監視およびトラブルシューティングできます。

全体として、このソリューションは Amazon VPC Lattice やその他の AWS サービスの力を利用して、Amazon EKS ベースのアプリケーションのクラスター間通信を包括的かつセキュアに実現するアプローチを提供します。この例で示されているパターンとベストプラクティスに従うことで、スケーラブルでセキュアな高可用性のマイクロサービスアーキテクチャを構築でき、これによりモダンなクラウドネイティブアプリケーションの要求を満たすことができます。

翻訳はソリューションアーキテクトの後藤が担当しました。原文はこちらです。