Table of Contents
AWS EKSのIAM Roles for Service Accountsの仕組みを完全に理解する記事。
IAM Roles for Service Accounts (IRSA)とは
IAM Roles for Service Accounts (IRSA)は、IAMロールをEKSのServiceAccountに紐づける機能。
IRSAを使うには、IAMにOIDCプロバイダの設定をちょろっとして、特定の信頼ポリシーを付けたIAMロールを作っておいて、ServiceAccountのアノテーションにeks.amazonaws.com/role-arn: <IAMロールのARN>
を付けるだけ。
そうしておくと、そのServiceAccountを付けたPodがそのIAMロールの権限でAWSサービスにアクセスできる。
IRSAを構成する要素
Kubernetes側の要素
-
KubernetesのAdmission Controllersの一つ。
Kubernetesのkube-apiserverは、何らかのAPIリクエストを受けたとき、それに対して認証(Authentication)、認可(Authorization)、許可(Admission)の処理を実行する。
Admission Controllersは許可の部分の処理を担当するコンポーネントで、LimitRangeのチェックをしたり、PodSecurityPolicyのチェックをしたりするいろいろなcontrollerで構成されるんだけど、そのなかの一つで、APIオブジェクトをいじる任意の処理を差し込めるcontrollerがMutating Admission Webhook。
KubernetesにMutatingWebhookConfigurationを登録しておくと、そこで指定したAPIリクエストがkube-apiserverに来た時に、指定したServiceやURLにリクエストの内容を送ってくれるので、受け取ったサーバ側でAPIオブジェクトをいじって返してやるとそれが反映される、という寸法。
Service Account Token Volume Projection
PodのServiceAccountを認証するトークン(i.e. ServiceAccountトークン)をPodのボリュームに挿入する、Kubernetesの機能。
PodのボリュームにProjected Volumeを追加して、ソースとしてserviceAccountTokenを指定しておくと、kubeletがPodを起動するときに、kube-apiserverからServiceAccountトークンを取得して挿入してくれる。
Podにはデフォルトで一つService Account Token Volume Projectionがついて、KubernetesのAPIにアクセスする用のServiceAccountトークンが挿入される。このトークンをHTTPの
Authorization
ヘッダにベアラトークンとして入れれば、KubernetesのAPIをPodのServiceAccountとして実行できる。ServiceAccountトークンはOIDCのIDトークンの形式で、発行者(iss)がkube-apiserverのURLになってるので、kube-apiserverのOIDC Discoveryエンドポイント(
/.well-known/openid-configuration
)でJWKSエンドポイントのURL(/openid/v1/jwks
)を取得し、そこから公開鍵を取得し、IDトークンについている署名を検証することができる。 (EKSの場合、issはhttps://oidc.eks.ap-northeast-1.amazonaws.com/id/11223344456677889900AABBCCDDEEFF
みたいな、EKSクラスタ毎のOIDC専用のURLになっている。)
-
AWS側の要素
IAMのOIDC IDプロバイダとWeb IDフェデレーション
Web IDフェデレーションは、AWSのリソースに、OIDC互換な外部のIDプロバイダが認証したユーザでアクセスできるようにする、IAMの機能。
IAM OIDC IDプロバイダというIAMリソースを作成し、issとかそのTLSサーバ証明書のサムプリントを登録しておくと、そのissが発行したIDトークンの内容をIAMが信用してくれるようになる。
-
Web IDフェデレーションしたIDプロバイダが発行したIDトークンを使ってIAMロールを引き受ける、STSのAPI。 実行すると、指定したIAMロールの権限でAWSリソースにアクセスできる、一時的な認証情報(i.e. アクセスキーIDとシークレットアクセスキーとセッショントークン)が返ってくる。
引き受けられるIAMロールの方には、対応するIAM OIDC IDプロバイダを指定する信頼ポリシーを付けておく必要がある。
Amazon EKS Pod Identity Webhook
IRSAの肝。
Amazon EKS Pod Identity Webhook(以下PIdW)はMutating Admission Webhookの実装で、
eks.amazonaws.com/role-arn
が付いたServiceAccountが割り当てられたPodをいじって、Web IDフェデレーションのためのService Account Token Volume Projectionと、環境変数のAWS_WEB_IDENTITY_TOKEN_FILE
とかAWS_ROLE_ARN
とかを挿入する。挿入するService Account Token Volume Projectionでコンテナの
/var/run/secrets/eks.amazonaws.com/serviceaccount/token
にServiceAccountトークンをマウントさせて、AWS_WEB_IDENTITY_TOKEN_FILE
でそのパスを指定してやることで、コンテナ内で動くAWSクライアントがそのトークンを読んで、AWS_ROLE_ARN
で指定されたIAMロールに対してAssumeRoleWithWebIdentityをコールするという寸法。PIdWはEKS環境ではコントロールプレーンで動いているっぽいけど、ノードでPodとして動かすこともできる。
図解IRSA
IRSAを図にするとこんな感じになるはず。 左側がEKSの世界で、右側がIAMの世界。青い実線矢印はHTTPS通信。
処理の流れは、細かい間違いはあるかもしれないけど以下のような感じのはず。
- EKSのkube-apiserverにPodを作るリクエストが来たら、kube-apiserverがその内容をPIdWに送る。
- PIdWは、Podに紐づくServiceAccountを取得し、そのアノテーションに
eks.amazonaws.com/role-arn
が付いてたら、Podの定義をいじってService Account Token Volume Projectionと、環境変数のAWS_WEB_IDENTITY_TOKEN_FILE
とかAWS_ROLE_ARN
とかを挿入する。 - kubeletは挿入されたService Account Token Volume Projectionを見て、それに対応するServiceAccountトークンをkube-apiserverに発行してもらう。
- kubeletは、発行されたServiceAccountトークンをPodにマウントさせる。
- Pod内で起動したコンテナは、
AWS_WEB_IDENTITY_TOKEN_FILE
が指定する場所にマウントされたServiceAccountトークンと、AWS_ROLE_ARN
で指定されたIAMロールのARNを付けて、AssumeRoleWithWebIdentity APIを実行する。 - AssumeRoleWithWebIdentityを呼ばれたSTSは、受け取ったServiceAccountトークンのissクレームに書いてあるURLからOIDC Discovery Documentを取得する。さらに、そのドキュメントの
jwks_uri
に書いてあるURLにアクセスしてJWKSを取得し、そこからトークン署名キーを取得する。 - 取得したトークン署名キーを使ってServiceAccountトークンを検証する。
- STSは次に、ServiceAccountトークンのissとかaudとかをみて、IAMのOIDC ID provider設定によって信頼している発行者からSTSに対して発行されたトークンであることを確認する。
- STSは、ServiceAccountトークンの正当性を完全に確かめられたので、AssumeRoleWithWebIdentityリクエストを認証する。認証されるユーザはServiceAccountトークンのsubクレームに、
system:serviceaccount:kube-system:vault
のように書いてあって、つまりAssumeRoleWithWebIdentityを呼んだPodのServiceAccountになる。STSは最後に、AssumeRoleWithWebIdentityで指定されたIAMロールの信頼ポリシーをみて、そのIAMロールがServiceAccountトークンの発行者を信頼し、それによって認証されたユーザによるAssumeRoleWithWebIdentityを許可していることを確認する。 - ここまで来てやっとAssumeRoleWithWebIdentityリクエストを認可し、アクセスキーIDとかの認証情報を発行し、Podに返す。