Amazon Web Services ブログ

Amazon EKS クラスターリソースへのクロスアカウントアクセスを有効にする

この記事は、Satya Sai Naga Venkata Kumar Vajrapu と Jason Smith が寄稿しました。

Amazon Elastic Kubernetes Service (Amazon EKS) は、Kubernetes コントロールプレーンを立ち上げたり維持したりすることなく、AWS で Kubernetes を簡単に実行できるマネージドサービスです。管理対象ノードグループAWS Fargate での Amazon EKS の最近のリリースにより、ポッド向けにインフラストラクチャをプロビジョニングおよび管理する必要がなくなりました。Kubernetes は、コンテナ化されたアプリケーションのデプロイ、スケーリング、および管理を自動化するためのオープンソースシステムです

お客様は複数の AWS アカウントを使用して AWS 環境を管理していることがよくあります。そのようなお客様は、実稼働リソースが開発またはステージングリソースと対話したり共存したりすることを望んでいません。これにより、リソースの分離が向上するという利点が得られますが、アクセス管理のオーバーヘッドが増加します。

AWS Security Token Service (STS) および IAM ロールを使用して、一時的な AWS セキュリティ認証情報を活用することにより、複数のアカウントへのユーザーアクセスを管理できます。けれども、あるアカウントでホストされている Amazon EKS クラスター内のコンテナ化されたワークロードやポッドなどのリソースが、別のアカウントでホストされている Amazon EKS クラスターリソースとやり取りしたい場合はどうでしょうか? こちらの以前のブログでは、サービスアカウントの IAM ロール (IRSA) を使用して、ポッドレベルできめ細かいロールを使用する方法について説明しました。このブログでは、このソリューションを拡張し、あるアカウントでホストされている Amazon EKS クラスター内のポッドが、異なるアカウント内の AWS リソースと Amazon EKS クラスターリソースを操作および管理する方法を示します。

シナリオ

複数のアカウント (devstg、および prod) を持ち、継続的インテグレーション (CI) アカウントからリソースを管理したいお客様がいると仮定しましょう。この CI アカウントの Amazon EKS クラスターは、AWS リソース、そしてこれらのターゲットアカウントにアクセスする必要があります。CI アカウントのポッドへのアクセスを許可し、クロスアカウントリソースをターゲットとする簡単な方法は次のとおりです。

  • ターゲットアカウントでロールを作成する
  • ターゲットアカウントロールの CI アカウント Amazon EKS クラスターノードインスタンスプロファイルにロール引受アクセス許可を付与します。
  • そして最後に、ターゲットアカウントのロールでこのクラスターノードインスタンスプロファイルを信頼します。

これにより、CI アカウントの Amazon EKS クラスターはターゲットアカウントの AWS リソースと通信できますが、このノードで実行されているすべてのポッドにこのロールへのアクセスが許可されます。AWS では、常に最小権限を付与するか、タスクの実行に必要なアクセス許可のみを付与するという標準的なセキュリティアドバイスに従うことを強くお勧めします。最小限の権限セットから開始し、必要に応じて追加の権限を付与するのです。

ソリューション

AWS Identity and Access Management (IAM) は、OpenID Connect (OIDC) を使用するフェデレーションユーザーをサポートします。Amazon EKS は、ProjectedServiceAccountToken JSON ウェブトークンの署名キーを含むクラスターごとにパブリック OIDC ディスカバリエンドポイントをホストするため、IAM などの外部システムが Kubernetes によって発行された OIDC トークンを検証して受け入れることができます。以下の手順では、アクセスをポッドに割り当てられたサービスアカウントに制限しながら、CI アカウントの Amazon EKS クラスターへのアクセスを AWS リソースとターゲットアカウントの Amazon EKS クラスターに許可するプロセスの概要を説明します。

前提条件

この記事で説明するステップを実行するには、AWS アカウントが必要です。

-profile オプションを指定して名前を割り当てることで、名前で参照できる複数の設定を使用できるように、AWS コマンドラインインターフェイス (AWS CLI) 設定を行います。以下に概説する手順では、ci-envtarget-env という 2 つの名前付きプロファイルを使用します。プロファイルの設定の詳細については、こちらをご確認ください。

1.    CI アカウントクラスターの OIDC 発行者 URL を取得する

Amazon EKS クラスターバージョンが 1.14 があるか、2019 年 9 月 3 日以降に 1.13 に更新した場合、OpenID Connect の発行者 URL があります。この URL は Amazon EKS コンソールから直接取得するか、次の AWS CLI コマンドを使用して取得できます。

aws eks describe-cluster --name <CI_EKS_CLUSTER_NAME> --query "cluster.identity.oidc.issuer" --output text --profile ci-env

2.    CI アカウントでクラスターの OIDC プロバイダーを作成する

CI アカウントの IAM コンソールに移動し、ID プロバイダーを選択してから、[プロバイダーの作成] を選択します。プロバイダーの種類として [OpenID Connect] を選択し、プロバイダー URL のクラスターの OIDC 発行者 URL を貼り付けます。以下に示すように、オーディエンスとして sts.amazonaws.com を入力します。

情報を入力したら、次のステップを選択します。ページで提供されたすべての情報が正しいかどうかを確認し、最後に次のページで [作成] を選択して ID プロバイダーを作成します。次のステップのために、OIDC プロバイダーの URL を保存します。

3.    CI アカウントの設定 – IAM ロールとポリシーのアクセス許可

CI アカウント ci-account-iam-role に IAM ロールを作成し、クラスターの OIDC プロバイダーとの信頼関係を設定して、サービスアカウント、ネームスペースを指定してアクセスを制限します。この場合、名前空間とサービスアカウントにそれぞれ ci-namespaceci-serviceaccount を指定しています。OIDC_PROVIDER を前の手順で保存したプロバイダー URL に置き換えます。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::CI_ACCOUNT_ID:oidc-provider/OIDC_PROVIDER"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "<OIDC_PROVIDER>:sub": "system:serviceaccount:ci-namespace:ci-serviceaccount"
        }
      }
    }
  ]
}

ロールが作成されたら、serviceaccount に関連付ける IAM ポリシーをアタッチします。この場合、serviceaccount はターゲットアカウントに対してロールを引き受けることができる必要があるため、次のロール引受アクセス許可を付与します。

ターゲットアカウント IAM ロールを作成していない場合は、ステップ 4 に進み、ターゲット AWS アカウントの設定を完了してから、このポリシーの関連付けを完了してください。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "sts:AssumeRole",
            "Resource": "arn:aws:iam::TARGET_ACCOUNT_ID:role/target-account-iam-role"
        }
    ]
}

Kubernetes では、eks.amazonaws.com/role-arn アノテーションをサービスアカウントに追加して、クラスターでサービスアカウントに関連付ける IAM ロールを定義します。つまり、以下に示すように、CI アカウントのクラスターに関連付けられているサービスアカウントに、ロール ARN のアノテーションを付けます。

apiVersion: v1
kind: ServiceAccount
metadata:
  name: ci-serviceaccount

  namespace: ci-namespace
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::CI_ACCOUNT_ID:role/ci-account-iam-role

同等の kubectl コマンドは次のとおりです。

kubectl annotate serviceaccount -n ci-namespace ci-serviceaccount eks.amazonaws.com/role-arn=arn:aws:iam::CI_ACCOUNT_ID:role/ci-account-iam-role

4.    ターゲットアカウントの設定 – IAM ロールとポリシーのアクセス許可

ターゲットアカウントで、前の手順で作成した CI アカウントの IAM ロールに対する AssumeRole アクセス許可を行う信頼関係を持つ target-account-iam-role という名前の IAM ロールを作成します。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::CI_ACCOUNT_ID:role/ci-account-iam-role"
      },
      "Action": "sts:AssumeRole",
      "Condition": {}
    }
  ]
}

CI アカウントクラスター内のサービスアカウントのポッドが管理する必要があるアクセス許可を持つ IAM ポリシーを作成します。以下に示すポリシーは、ターゲットアカウントの一部の AWS リソースに対する基本的なリストアクセス許可を付与します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "s3:ListAllMyBuckets",
                "eks:DescribeCluster",
                "eks:ListClusters"
            ],
            "Resource": "*"
        }
    ]
}

ターゲットポリシーをステップ 3 で説明した CI アカウントに関連付けていない場合は、次のステップに進む前に関連付けを行ってください。

5.    AWS リソースへのクロスアカウントアクセスを確認する

IAM アクセスのロールとポリシーが CI アカウントとターゲットアカウントの両方で設定されたので、CI クラスターで Kubernetes ポッドのサンプルを作成し、ターゲットアカウントの AWS リソースへのクロスアカウントアクセスをテストします。

お気に入りのエディタを開き、以下の内容を awsconfig-configmap.yaml という名前のファイルに保存します。この configmap は、デプロイポッドで使用される AWS CLI プロファイルを設定するためのものです。

[profile ci-env]
role_arn = arn:aws:iam::CI_ACCOUNT_ID:role/ci-account-iam-role
web_identity_token_file = /var/run/secrets/eks.amazonaws.com/serviceaccount/token.

[profile target-env]
role_arn = arn:aws:iam::TARGET_ACCOUNT_ID:role/target-account-iam-role
source_profile = ci-env
role_session_name = xactarget

次の Kubectl コマンドを使用して Kubernetes configMap リソースを作成します。

kubectl create configmap awsconfig-configmap --from-file=config=awsconfig-configmap.yaml -n ci-namespace

別の configMap リソースを作成して、インストールスクリプトを保存しましょう。script-configmap.yaml という名前のファイルに名前を付け、以下の内容をファイルに保存します。

#!/bin/bash
apt-get update -y && apt-get install -y python curl wget unzip jq nano -y
curl "https://s3.amazonaws.com/aws-cli/awscli-bundle.zip" -o "awscli-bundle.zip"
unzip awscli-bundle.zip
./awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws
cp -r aws_config/ ~/.aws/
curl -o kubectl https://amazon-eks.s3-us-west-2.amazonaws.com/1.14.6/2019-08-22/bin/linux/amd64/kubectl
chmod +x ./kubectl
mv ./kubectl /usr/local/bin/kubectl
kubectl version

次の kubectl コマンドを使用して、2 番目の Kubernetes configMap リソースを作成します。

kubectl create configmap script-configmap --from-file=script.sh=script-configmap.yaml -n ci-namespace

次は、デプロイを作成し、クロスアカウントアクセスをテストする番です。test-deployment.yaml という名前の別のファイルを作成します。

OIDC 信頼関係の作成中に手順 3 で異なる値を指定した場合、名前空間と serviceaccount の値を置き換えます。

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: test-deployment
  name: test-deployment
  namespace: ci-namespace
spec:
  replicas: 1
  selector:
    matchLabels:
      app: test-pod
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: test-pod
    spec:
      containers:
      - image: ubuntu
        name: ubuntu
        command: ["sleep","10000"]
        volumeMounts:
        - name: test-volume
          mountPath: /aws_config
        - name: script-volume
          mountPath: /scripts
      volumes:
      - name: test-volume
        configMap:
          name: awsconfig-configmap
      - name: script-volume
        configMap:
          name: script-configmap
      serviceAccountName: ci-serviceaccount

次のコマンドを使用して、ポッドマニフェストファイルを適用し、テストコンテナポッドを作成します。

kubectl apply -f test-deployment.yaml

ポッドの bash ターミナルにログインします。

POD_NAME=$(kubectl get pod -l app=test-pod -n ci-namespace -o jsonpath='{.items[0].metadata.name}')
kubectl exec $POD_NAME -it -n ci-namespace -- bash

ポッドにマウントされたスクリプトを実行して、必要なバイナリとライブラリをインストールします。

sh /scripts/script.sh

以下の呼び出しを発行して、ポッドが ci-env と target-env の両方のロールを引き受けることができるかどうかを確認します。

aws sts get-caller-identity --profile ci-env

aws sts get-caller-identity --profile target-env

これで、ポッドには、前の手順で定義した AWS リソースに対する基本的なリストアクセス許可があります。以下のコマンドでは、ターゲットアカウントの Amazon EKS クラスターのリストを出力するはずです。

aws eks list-clusters --region <AWS_REGION> --profile target-env

DescribeCluster コマンドを実行して、クラスターの内容を記述することもできます。

aws eks describe-cluster --region <AWS_REGION> --profile target-env --name <TARGET_EKS_CLUSTER_NAME>

6.    ターゲットアカウントの Amazon EKS クラスターの設定 – aws-auth configmap の変更

CI アカウントクラスターポッドがターゲットクラスターのリソースにアクセスして管理するには、system:masters グループにロールを追加して、ターゲットアカウントのクラスターの aws-auth configmap を編集する必要があります。以下は、変更後の configmap の外観です。

  mapRoles: |
. . .
    - groups:
      - system:masters
      rolearn: arn:aws:iam::TARGET_ACCOUNT_ID:role/target-account-iam-role
      username: test-user

7.    ターゲットアカウントの Amazon EKS クラスターへのアクセスをテストする

手順 5 で作成したポッドで、kubeconfig を更新して、ターゲットアカウントの EKS クラスターへのアクセスをテストします。

aws --region <AWS_REGION> eks update-kubeconfig --name <TARGET_EKS_CLUSTER_NAME> --profile target-env

これで、ポッドはターゲットクラスターの kube リソースにアクセスできるはずです。kubectl get のサンプル呼び出しをいくつか発行して、ターゲットアカウントの EKS リソースにアクセスして確認します。

kubectl get namespaces     

kubectl get pods -n kube-system

まとめ

このブログでは、1 つのターゲットアカウントを使用してクロスアカウントアクセスを示しましたが、実際にはターゲットアカウントに制限はありません。理想的な環境では、CI アカウントで、開発、ステージ、テスト、本番などの複数のターゲットアプリケーションアカウントを管理することができます。ソリューションは、アクセスする CI クラスターポッドのこれらの各ターゲットアカウントで CI アカウントの IAM ロールを信頼することにより拡張できます。

このブログでは、AssumeRole チェーン操作を使用して Amazon EKS クラスターリソース間のクロスアカウントアクセスを有効にする手順を説明しました。けれども、別のアカウントのクラスターから ID プロバイダーを作成して、クロスアカウント IAM アクセス許可を設定および実装することもできます。