EKSを理解する(第2回)IRSAを用いたPod単位のIAMロール割り当て

こんにちはクラウド推進チームの宮國です。

久々とはなりますが、今回はEKSを理解する(第2回)という事で、IRSA(IAM Roles for Service Accounts)の仕組みと使い方について書いてみたいと思います。

IRSA(IAM Roles for Service Accounts)とは

IRSAはKubernetesにおけるサービスアカウントリソースにIAMロールを紐づける機能を指します。
サービスアカウントはKubernetes内の認証、認可に関連するリソースで、Pod内のコンテナから各リソースへのアクセスを制御するために用いられます。

なぜ必要か

このIRSAという機能が登場するまでは、Pod単位でIAMロールを割り当てる標準的な手順はなく、EC2インスタンスにIAMロールを割り当てることでAWSリソースに対する権限を付与していました。
しかし、ノードレベルでIAMロールを割り当ててしまうと、そのノードで動作する全てのpodに同様の権限を付与してしまうことになるので、各Podが本来アクセスすべきでないリソースにアクセスできてしまうリスクが伴います。
そこでkube2iamやkiamといったOSSを利用してPodに対してIAMロールを適用させていましたが、AWS公式のソリューションである分、今後はIRSAが主流になると考えられます。

EKSでIRSAを利用するには、
・OpenID Connect Provider Endpointを作成して有効なIdpとしてIAMに登録する
・サービスアカウントに対して、アノテーションとしてIAMロールのARNを付与する

といった2つの前提条件が必要ですが、eksctlコマンドを使うことで簡単に実現できます。それでは実際にやってみます。

検証用クラスタ作成

まずは事前準備としてeksctlを用いてクラスタを作成します。

今回クラスタ構築はCloud9から行います。eksworkshopを参考にkubectlやeksctlコマンドをインストールします。
https://www.eksworkshop.com/020_prerequisites/workspace/

※このワークショップは、各種インストールコマンドがkubernetesのバージョンアップに追従しているので重宝しています。

クラスタを作成する際はeksctl create clusterコマンドを利用します。オプションでもノードの情報は渡せますが、今回は基本的にyaml化して適用します。

eksctl create cluster -f eks-cluster.yaml

eks-cluster.yaml

apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: irsa-test
  region: us-west-2
  version: "1.17"
managedNodeGroups:
  - name: nodegroup
    instanceType: t3.medium
    minSize: 3
    maxSize: 3
    desiredCapacity: 3
    privateNetworking: true
    iam:
      attachPolicyARNs: 
        - arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy
        - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
        - arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
    preBootstrapCommands:
      - |
        #!/bin/bash
        sudo yum install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm
        sudo systemctl enable amazon-ssm-agent
        sudo systemctl start amazon-ssm-agent

preBootstrapCommandsで、ワーカーノードに対してSSMのSession managerで入れるようなユーザデータを設定しています。
ノード毎に1台動かすようなエージェントはKubernetesの思想に則るならば本来的にはDaemonsetで導入すべきですが、コンテナイメージ化が難しいエージェントのインストールはこのように対応します。
また、Session managerで入れるような権限をEC2に付与するためにattachPolicyARNsでAmazonSSMManagedInstanceCoreを指定しています。

クラスタの作成には数十分ほどかかります。
eksctlの裏で動いているのはCloudFormationなので、エラーが発生して削除も再作成もうまくいかない場合はAWSのマネジメントコンソールのCloudFormationから該当のスタックを削除します。

 

Test Jobの作成

クラスタが作成されたらテスト用のJobを作成します。

今回は以下のようなJobを用意しました。
testjob.yaml

apiVersion: batch/v1
kind: Job
metadata:
  name: s3-listonly
spec:
  template:
    spec:
      containers:
      - name: s3-listonly
        image: amazon/aws-cli
        command: ["aws", "s3" , "ls"]
      restartPolicy: Never
  backoffLimit: 0

公式AWS CLI v2のDockerイメージを利用して、S3のバケット一覧を取得する簡単なジョブです。

試しに今の状態でapplyしてみると、

kubectl apply -f testjob.yaml

ワーカーノードにもpodにもS3に対する権限を付与していないため、

kubectl get pod
NAME READY STATUS RESTARTS AGE
s3-listonly-nff2g 0/1 Error 0 21m

AccessDeniedというエラーが表示されます。

kubectl logs s3-listonly-nff2g
An error occurred (AccessDenied) when calling the ListBuckets operation: Access Denied

IRSAの導入

それではいよいよIRSAの導入です。

以下のコマンドを打つことで、OpenID Connect Provider Endpointを作成して有効なIdpとしてIAMに登録することができます。

eksctl utils associate-iam-oidc-provider \
--name irsa-test \
--approve
[ℹ] eksctl version 0.26.0
[ℹ] using region us-west-2
[ℹ] will create IAM Open ID Connect provider for cluster "irsa-test" in "us-west-2"
[✔] created IAM Open ID Connect provider for cluster "irsa-test" in "us-west-2"

IAMのIDプロバイダーを確認すると作成されていることを確認できます。

次に、サービスアカウントとIAMロールを紐づけるために、サービスアカウントのアノテーションとしてIAMロールのARNを付与する必要がありますが、
eksctlコマンドでは以下のように、IAMロール(とアタッチするポリシー)とサービスアカウントのペアを簡単に作成できます。
もしクラスター内にすでに作成されているサービスアカウントがある場合(IAMロールなし)、`–override-existing-serviceaccounts`を使用することで上書き可能です。

eksctl create iamserviceaccount \
--cluster irsa-test \
--name s3-reader-sa \
--attach-policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess \
--approve

IAMロールが作成されていることが確認できます。

また、Kubernetes上ではサービスアカウントが作成されていることが確認できます。

kubectl get sa
NAME SECRETS AGE
default 1 95m
s3-reader-sa 1 3m27s

そしてサービスアカウントのアノテーションにIAMロールのARNが追加されていることが確認できます。

kubectl describe sa s3-reader-sa | grep eksctl
Annotations: eks.amazonaws.com/role-arn: arn:aws:iam::<accountid>:role/eksctl-irsa-test-addon-iamserviceaccount-Role1-XXXXXXXX

※コマンドのオプションで渡すとどうしても冗長です。以下のようにyaml化して適用する事も可能です。

irsa.yaml

apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: irsa-test
  region: us-west-2

iam:
  withOIDC: true
  serviceAccounts:
  - metadata:
      name: s3-reader-sa
    attachPolicyARNs:
    - "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
eksctl utils associate-iam-oidc-provider -f irsa.yaml --approve
eksctl create iamserviceaccount -f irsa.yaml --approve

IRSAが適用できたら、先ほどErrorになっていたjobを削除し、

kubectl delete -f testjob.yaml

spec.template.spec.serviceAccountNameに先ほど作成したサービスアカウントの名前を指定します。

apiVersion: batch/v1
kind: Job
metadata:
  name: s3-listonly
spec:
  template:
    spec:
      serviceAccountName: s3-reader-sa
      containers:
      - name: s3-listonly
        image: amazon/aws-cli
        command: ["aws", "s3" , "ls"]
      restartPolicy: Never
  backoffLimit: 0
kubectl apply -f testjob.yaml

すると、Jobが成功していることが確認できます。

kubectl get job
NAME COMPLETIONS DURATION AGE
s3-listonly 1/1 5s 16s
kubectl get pod
NAME READY STATUS RESTARTS AGE
s3-listonly-mfb2r 0/1 Completed 0 30s
kubectl logs s3-listonly-mfb2r

バケットの一覧が表示されます。

ノードからS3へのアクセス確認

SSMのSession Managerでノードに入って同じコマンドを打ったとしても、AccessDeniedとなることが確認できます。

sudo su -
aws s3 ls
An error occurred (AccessDenied) when calling the ListBuckets operation: Access Denied

このようにeksctlを通してIRSAを利用することで、サービスアカウントに対してIAMロールを紐づけるのが容易に実装することができました。

次回はこのIRSAを用いたCluster AutoScalerを試せたらと思います。