NO IMAGE

EKS IRSA 完全活用 IAM Roles for Service Accounts Terraform

NO IMAGE
目次

1. この記事について

fig01: IRSA 全体アーキテクチャ図

EKS で Pod に AWS API 呼び出し権限を付与する標準手段が IRSA (IAM Roles for Service Accounts) である。Worker Node 単位の IAM Role や AWS_ACCESS_KEY_ID をマウントする旧来手法は、最小権限原則に反するうえに credential ローテーション運用が破綻しやすい。一方 IRSA は、Kubernetes ServiceAccount に annotation で IAM Role ARN を紐付け、Pod 起動時に projected token を OIDC Provider 経由で AWS STS に渡し、AssumeRoleWithWebIdentity API で一時資格情報を取得する仕組みであり、ServiceAccount 単位の最小権限設計と短命 credential ローテーションを両立する。しかし日本語記事には「IRSA の概念解説」「eksctl 一発設定」止まりが多く、Terraform で OIDC Provider + Federated IAM Role + kubernetes_service_account を一気通貫自動化し、aws-ebs-csi-driver / aws-load-balancer-controller / external-dns / Fluent Bit の主要 Add-ons IRSA 連携まで網羅し、2024 GA の EKS Pod Identity との比較選定軸を提示した本番運用ガイドが見当たらない。

本記事はその空白を 1 本で解消する。OIDC + AssumeRoleWithWebIdentity 認証フロー解剖から始まり、aws_iam_openid_connect_provider + thumbprint 取得 + Federated IAM Role + kubernetes_service_account の Terraform 完全実装、主要 4 Add-ons の IRSA 設定、EKS Pod Identity との 5 軸比較、CloudTrail / IAM Access Analyzer / kubectl describe sa によるトラブルシュートまでを Terraform 完全 HCL + AWS CLI + コンソール の 3 形式で解説する。読者が「そのままコピー&ペーストで本番 IRSA 基盤を構築できる」ことをゴールとしている。

【シリーズ】EKS 本番運用シリーズ (全3巻)

本記事の対象読者

  • Vol1 で構築した EKS Cluster + Karpenter 基盤の上に IRSA を導入したい中〜上級者
  • Worker Node IAM Role / AWS_ACCESS_KEY_ID の旧来手法から ServiceAccount 単位の最小権限設計に移行したいチーム
  • EKS Pod Identity (2024 GA) と IRSA の選定で迷っているアーキテクト

1-1. なぜ今 IRSA か

IRSA が GA してから時間は経ったが、日本語記事には依然として 4 つの空白がある。

空白①: OIDC + AssumeRoleWithWebIdentity 認証フローの深掘り解説が希少

IRSA の概念解説は多いが、Pod → Projected Volume token → STS AssumeRoleWithWebIdentity → 一時資格情報 取得経路を 1 ステップずつ解剖した記事が国内に少ない。本記事 §3 でこの空白を埋める。

空白②: Terraform 一気通貫自動化の実例が散在

aws_iam_openid_connect_provider + thumbprint 取得 + assume_role_policy + kubernetes_service_account annotation を 1 セットで自動化した Terraform HCL が国内に少ない。本記事 §4 で QG-2 として完全実装する。

空白③: 主要 Add-ons IRSA 設定が散在

aws-ebs-csi-driver / aws-load-balancer-controller / external-dns / Fluent Bit のそれぞれで必要な IAM ポリシー JSON が公式ドキュメント / GitHub Issue / 個別記事に分散している。本記事 §5 で 4 種を 1 章にまとめる。

空白④: EKS Pod Identity (2024 GA) との比較・選定指針がない

2024 年に GA した EKS Pod Identity は IRSA より設定が容易だが、マルチクラスタ運用やトークンライフサイクルで差異がある。本記事 §6 で QG-4 として 5 軸比較マトリクスを提示する。

1-2. 本記事のゴール

本記事を読み終えると以下を単独で実施できる。

ゴール対応章
OIDC + AssumeRoleWithWebIdentity 認証フローの理解§3
OIDC Provider + Federated IAM Role + ServiceAccount の Terraform 完全実装§4
主要 4 Add-ons (EBS CSI / ALB Controller / external-dns / Fluent Bit) の IRSA 連携実装§5
EKS Pod Identity vs IRSA の選定判断§6
CloudTrail / IAM Access Analyzer / kubectl describe sa によるトラブルシュート§7
落とし穴 10 選を回避した本番投入§8

1-3. 差別化6軸

本記事の差別化 6 軸

  1. OIDC + AssumeRoleWithWebIdentity 認証フロー解剖 — Pod → Projected Volume → STS の認証経路を 1 ステップずつ解剖した本番ガイド。国内の IRSA 記事は設定手順止まりが多く、フロー解剖まで踏み込んだ記事が希少
  2. Terraform 一気通貫自動化 — aws_iam_openid_connect_provider + thumbprint 自動取得 + Federated IAM Role + kubernetes_service_account annotation を 1 セットで自動化。手動計算・eksctl 依存なし
  3. 主要 4 Add-ons IRSA 全実装 — aws-ebs-csi-driver / aws-load-balancer-controller / external-dns / Fluent Bit の IAM ポリシー JSON + Terraform をワンストップで提供。Add-ons ごとに散在していた情報を 1 章に集約
  4. EKS Pod Identity (2024 GA) との 5 軸比較 — 設定容易さ / マルチクラスタ / トークンライフサイクル / Terraform 設定量 / 移行コストで比較し、最新手法の採用判断フローと移行手順を提示
  5. Trust Policy 3 パターン完全例 — sub claim 制限あり (本番推奨) / namespace のみ制限 / 制限なし (開発用) の JSON 完全例で「Trust Policy 起因の認証失敗ゼロ」実装を担保
  6. 3 点セット + ECS Task Role との対比 — Terraform / AWS CLI / コンソール 全形式完走で「コピペで動く」を担保。ECS Task Role との対比で既存 ECS 運用者が最速で IRSA を理解できる構成

1-4. 章立て

§Nタイトル概要
§2前提・環境・準備Vol1 同仕様 + IAM Identity Provider 操作権限 + eksctl
§3IRSA の仕組みOIDC + AssumeRoleWithWebIdentity 認証フロー解剖
§4OIDC Provider + Federated IAM Role Terraform 完全実装aws_iam_openid_connect_provider + thumbprint + assume_role_policy + sa annotation
§5主要 Add-ons IRSA 連携実装EBS CSI / ALB Controller / external-dns / Fluent Bit
§6EKS Pod Identity vs IRSA 比較2024 GA の Pod Identity との 5 軸比較 + 移行判断
§7監視・トラブルシュートCloudTrail / IAM Access Analyzer / kubectl describe sa
§8まとめ + Vol3 予告 + 落とし穴 10 選チートシート + シリーズナビ + Vol1 / ECS バックリンク

1-5. 想定環境

ツールバージョン備考
Terraform1.9.xAWS Provider 5.x / kubernetes provider 2.x / helm provider 2.x / tls provider
aws-cliv2 (最新)aws configure 設定済
kubectl1.30aws eks update-kubeconfig
helm3.14helm repo add 可能な状態
eksctl0.x (最新)IRSA 代替手段として使用 (Terraform と二重管理に注意)
OSmacOS / LinuxWSL2 可
AWS リージョンap-northeast-1他リージョンでも同様に動作
前提Vol1 EKS Cluster 稼働中EKS Cluster + Karpenter 本番ガイド 構築済

1-6. この記事のユースケースと読み方

本記事は EKS 本番運用の IRSA 全体を 1 本で完走すること を想定しているが、ユースケース別の読み進め方を整理する。

ユースケース推奨読み方
IRSA を初めて設定する§3 → §4 → §5 → §7 の順に通読
IRSA 設定済みでトラブルシュートのみ§7 にジャンプ → 失敗パターン 5 選を確認
ECS Task Role から IRSA に移行したい§3-3 (対比) → §4 (Terraform)
Pod Identity との選定で迷っている§6 (5 軸比較) を先に確認 → §4 実装へ
Terraform ではなく eksctl で管理したい§4-5 (eksctl 代替手段) を参照
Add-on の IRSA エラーを修正したい§5 の該当 Add-on → §7 のトラブルシュート

注意事項:

  • 本記事のコード例は ap-northeast-1 リージョンを前提とする。他リージョンでは region 変数を適切に変更すること
  • Terraform リソース名 (e.g., aws_eks_cluster.main) は Vol1 のモジュール構成を継承している。異なる命名を使っている場合は適宜読み替えること
  • {ACCOUNT} / {CLUSTER_NAME} / {OIDC_ID} などのプレースホルダーは実環境の値に置き換えること

§3 IRSA の仕組みに進む


2. 前提・環境・準備

2-1. 前提環境

本記事は EKS 本番運用シリーズ Vol1 で構築した EKS Cluster + Karpenter 基盤が稼働中であることを前提とする。Vol1 の aws_eks_cluster には OIDC Issuer URL が自動付与されており、本記事 §4 でその URL を AWS IAM OIDC Provider として登録することで IRSA が機能する。

ローカル環境には以下のツールが設定されていること:

  • aws-cli v2: aws configure 済み、以下の IAM 権限を保有
  • kubectl 1.30: aws eks update-kubeconfig --name {cluster} --region ap-northeast-1 済み
  • helm 3.14: helm repo add が実行可能な状態
  • terraform 1.9.x: AWS / kubernetes / helm / tls / http provider が利用可能
  • eksctl (最新): IRSA の代替手段として使用 (Terraform と二重管理になる点に注意)

デプロイ担当者に必要な IAM 権限:

iam:CreateOpenIDConnectProvider
iam:GetOpenIDConnectProvider
iam:DeleteOpenIDConnectProvider
iam:CreateRole
iam:AttachRolePolicy
iam:CreatePolicy
iam:GetRole
eks:DescribeCluster

2-2. 使用技術スタック

カテゴリサービス / ツールバージョン
コンテナオーケストレーションAmazon EKS1.30 (Vol1 で構築済)
認証基盤IAM OIDC ProviderEKS クラスタ付属 (自動生成)
IAM 連携手法IRSA (IAM Roles for Service Accounts)
新世代 IAM 連携EKS Pod Identity2024 GA
ストレージ Add-onaws-ebs-csi-driverEKS マネージド Add-on
LB 制御 Add-onaws-load-balancer-controllerhelm chart (最新)
DNS 自動管理external-dnshelm chart (最新)
ログ転送Fluent BitVol1 §7 の深掘り版
IaCTerraform1.9.x
Terraform Providerhashicorp/aws5.x
Terraform Providerhashicorp/kubernetes2.x
Terraform Providerhashicorp/helm2.x
Terraform Providerhashicorp/tls
CLI 補助eksctl0.x (CloudFormation 経由 IRSA 自動作成の代替手段)

2-3. ゴール状態の定義

本記事を完走すると以下の状態になる:

成果物内容
OIDC Provideraws_iam_openid_connect_provider が AWS IAM に登録済
Federated IAM Role × 4aws-ebs-csi-driver / aws-load-balancer-controller / external-dns / Fluent Bit 用
kubernetes_service_account × 4各 Add-on の SA に eks.amazonaws.com/role-arn annotation 付与済
4 Add-ons 稼働確認kubectl get pods -n kube-system で全 Add-on が Running
トラブルシュート環境CloudTrail / IAM Access Analyzer / kubectl で IRSA 認証ログを可視化
Pod Identity 評価済§6 の 5 軸比較を踏まえ、IRSA / Pod Identity の採用判断が完了

2-4. IRSA 用語整理

用語説明
IRSAIAM Roles for Service Accounts。Kubernetes SA に IAM Role を紐付ける EKS 公式手法
OIDC ProviderEKS クラスタ付属の OpenID Connect IdP。AWS IAM に登録して Trust 関係を確立する
AssumeRoleWithWebIdentityAWS STS API。OIDC Token を提示して一時 IAM Credential (TTL 1h) を取得する
Projected Volume Tokenkubelet が Pod に自動マウントする短命 OIDC Token。有効期限 1 時間・自動更新
sub claimJWT Token の Subject 項目。system:serviceaccount:{namespace}:{sa-name} 形式
Federated PrincipalIAM Trust Policy で OIDC Provider ARN を指定するための Principal タイプ
kubernetes_service_accountTerraform kubernetes provider のリソース。SA に annotation を付与する
EKS Pod Identity2024 GA の新手法。OIDC Provider 登録なしで EKS が AWS STS と直接連携 (§6 で比較)
aws-iam-authenticator旧手法。Worker Node IAM Role で全 Pod が同じ権限を持つ非推奨方式

2-5. Vol1 で構築した基盤の確認

本記事 §4 に着手する前に、Vol1 で構築した EKS Cluster の OIDC Issuer URL を確認する:

# OIDC Issuer URL を確認
aws eks describe-cluster \
  --name my-eks-cluster \
  --region ap-northeast-1 \
  --query "cluster.identity.oidc.issuer" \
  --output text
# → https://oidc.eks.ap-northeast-1.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE

# AWS IAM に既存の OIDC Provider が登録済かチェック
aws iam list-open-id-connect-providers \
  --query "OpenIDConnectProviderList[*].Arn" \
  --output table

OIDC Issuer URL は Terraform で aws_eks_cluster.main.identity[0].oidc[0].issuer として参照できる。Vol1 の aws_eks_cluster が既に OIDC 設定を持つため、本記事 §4 ではその値を流用して aws_iam_openid_connect_provider を構築する。

既に OIDC Provider が登録されている場合は import ブロックを使い Terraform state に取り込むか、data source で参照する:

# 既存 OIDC Provider を data source で参照
data "aws_iam_openid_connect_provider" "existing" {
  url = aws_eks_cluster.main.identity[0].oidc[0].issuer
}

3. IRSA の仕組み (OIDC + AssumeRoleWithWebIdentity)

EKS IRSA (IAM Roles for Service Accounts) は、Kubernetes の ServiceAccount と AWS IAM Role を OIDC フェデレーション経由で紐付け、Pod ごとに最小権限の一時 IAM 資格情報を自動付与する仕組みだ。Node 全体に同じ権限を与える旧来の Worker Node IAM Role や、K8s Secret に静的アクセスキーをマウントする手法を完全に置き換える。本章では OIDC Provider・STS・Projected Volume の 3 要素がどう連携するかを解剖し、§4 以降の Terraform 実装の土台を固める。IRSA を正しく理解することで「なぜこの設定が必要か」が明確になり、トラブルシュートの速度も大幅に向上する。

3-1. IRSA 全体アーキテクチャ

IRSA の全体フローは fig01 の通り 4 フェーズで構成される。

フェーズ 1: EKS OIDC Provider の登録

EKS クラスタを作成すると、クラスタ固有の OIDC Issuer URL が払い出される。この URL を AWS IAM に「OpenID Connect Identity Provider」として登録することで、AWS は EKS が発行する JWT の真正性を検証できるようになる。Terraform では aws_iam_openid_connect_provider リソースが担当し、thumbprint_list には TLS 証明書の SHA-1 フィンガープリントを設定する。

# OIDC Issuer URL 確認
aws eks describe-cluster --name my-cluster \
  --query "cluster.identity.oidc.issuer" --output text
# → https://oidc.eks.ap-northeast-1.amazonaws.com/id/ABCDEF1234567890

フェーズ 2: ServiceAccount Token (Projected Volume)

IRSA を有効にした ServiceAccount を参照する Pod が起動すると、kubelet は OIDC 署名付き JWT を生成して Projected Volume としてマウントする。トークンのパスは /var/run/secrets/eks.amazonaws.com/serviceaccount/token。JWT の sub claim には system:serviceaccount:{namespace}:{sa-name} が設定され、aud claim は sts.amazonaws.com に固定される。TTL はデフォルト 1 時間で kubelet が自動ローテーションする。

フェーズ 3: STS AssumeRoleWithWebIdentity

Pod 内の AWS SDK (boto3・AWS SDK for Go など) は起動時に環境変数 AWS_WEB_IDENTITY_TOKEN_FILEAWS_ROLE_ARN を検出し、従来の ~/.aws/credentials より優先して STS の AssumeRoleWithWebIdentity を自動呼び出しする。STS は受け取った JWT を OIDC Provider の公開鍵で検証し、IAM Role の Trust Policy (Federated Principal + sub Condition) と照合する。検証が成功すると TTL 付きの一時 Credential を返す。

フェーズ 4: 一時 IAM Credential の払い出しと AWS API 呼び出し

STS が返す AccessKeyIdSecretAccessKeySessionToken を AWS SDK がメモリにキャッシュし、Expiration の数分前に自動再取得する。アプリコードは通常の boto3.client('s3') など標準 API を呼び出すだけで、資格情報のライフサイクル管理は SDK が完全に担う。Pod が削除されると Projected Volume も破棄されるため、一時 Credential の漏洩リスクは最小化される。

この 4 フェーズの連鎖により、アプリコードへの変更ゼロで Pod 単位の最小権限 IAM 制御が実現する。

IRSA 有効化の設定要件

IRSA の設定は AWS 側と Kubernetes 側の両方にまたがる。どちらか一方が欠けても AccessDenied が発生するため、両側を揃えて管理することが重要だ。

設定場所リソース / 設定値
OIDC Provider 登録AWS IAMaws_iam_openid_connect_provider
IAM Role 作成AWS IAMaws_iam_role (Trust Policy: Federated + sub Condition)
IAM Policy アタッチAWS IAMaws_iam_role_policy_attachment
SA annotationKuberneteseks.amazonaws.com/role-arn
Pod serviceAccountNameKubernetesPod spec の serviceAccountName フィールド

Kubernetes 側では ServiceAccount に以下の annotation を付与する。token-expiration はデフォルト 3600 秒 (1 時間) だが、バッチジョブなど長時間処理の場合は最大 86400 秒 (24 時間) まで延長できる。

apiVersion: v1
kind: ServiceAccount
metadata:
  name: fluent-bit
  namespace: kube-system
  annotations:
 eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/FluentBitRole
 eks.amazonaws.com/token-expiration: "3600"  # JWT TTL (デフォルト 1 時間)

Terraform では kubernetes_service_account リソースで annotation を管理し、AWS IAM リソースと同一モジュールで一元管理する (§4 参照)。

3-2. AssumeRoleWithWebIdentity 認証フロー

fig02: AssumeRoleWithWebIdentity 認証フロー

QG-1: IRSA 認証フロー チェックポイント (OIDC + AssumeRoleWithWebIdentity)

  • OIDC Provider URL = EKS cluster.identity.oidc.issueraws_iam_openid_connect_provider に登録必須
  • IAM Trust Policy: Principal Federated = arn:aws:iam::{account}:oidc-provider/oidc.eks.{region}.amazonaws.com/id/{OIDC_ID}
  • Trust Policy Condition: StringEquals: sub = system:serviceaccount:{namespace}:{sa-name} で Pod スコープを絞る
  • Kubernetes SA annotation: eks.amazonaws.com/role-arn = arn:aws:iam::{account}:role/{role} を付与
  • Pod spec: serviceAccountName を設定すると自動で Projected Volume がマウントされ AWS SDK が環境変数を検出
  • STS AssumeRoleWithWebIdentity は Pod 内 AWS SDK が自動呼出し (boto3 / AWS SDK v2 で追加コード不要)

fig02 は AssumeRoleWithWebIdentity の 6 ステップを時系列で示したものだ。各ステップを詳解する。

Step 1: kubelet による Projected Volume Token 発行

Pod のスケジュールが確定すると kubelet は ServiceAccount の OIDC JWT を生成し、Projected Volume として Pod にマウントする。JWT には以下の claim が含まれる。

フィールド値の例
isshttps://oidc.eks.ap-northeast-1.amazonaws.com/id/ABCDEF1234567890
subsystem:serviceaccount:kube-system:fluent-bit
audsts.amazonaws.com
exp発行から 3600 秒後 (1 時間)

JWT の署名は EKS OIDC Provider の秘密鍵で行われ、STS は OIDC Provider の公開鍵 (JWKS) で検証する。kubelet は TTL 切れの前に新トークンを自動発行するため、Pod のライフサイクル中は常に有効なトークンが存在する。

Step 2: AWS SDK による環境変数検出

IRSA を有効にした ServiceAccount を参照する Pod には以下の環境変数が自動注入される。~/.aws/credentials より優先されるため、アプリコードの変更は一切不要だ。

# Pod 内で確認できる環境変数
kubectl exec -it {pod-name} -n {namespace} -- env | grep AWS
# AWS_ROLE_ARN=arn:aws:iam::123456789012:role/FluentBitRole
# AWS_WEB_IDENTITY_TOKEN_FILE=/var/run/secrets/eks.amazonaws.com/serviceaccount/token
# AWS_DEFAULT_REGION=ap-northeast-1

Step 3: STS.AssumeRoleWithWebIdentity 呼び出し

AWS SDK は STS エンドポイントへ WebIdentityTokenRoleArnRoleSessionName を送信する。CLI で等価の呼び出しを確認するには以下を実行する。

aws sts assume-role-with-web-identity \
  --role-arn arn:aws:iam::123456789012:role/FluentBitRole \
  --role-session-name eks-pod-session \
  --web-identity-token "$(cat /var/run/secrets/eks.amazonaws.com/serviceaccount/token)" \
  --duration-seconds 3600

Step 4: STS による OIDC Provider への JWT 検証

STS は JWT の iss field から EKS OIDC Provider の JWKS エンドポイント ({issuer}/.well-known/jwks.json) を取得し、JWT ヘッダの kid に対応する公開鍵で署名を検証する。次に audsts.amazonaws.com であること、exp が未来であることを確認する。

Step 5: IAM Trust Policy の Condition 照合

署名検証が通ると STS は呼び出し元 IAM Role の Trust Policy を確認する。sub Condition の値が JWT の sub claim と一致しない場合 (Namespace や ServiceAccount 名が違う場合など) は即座に AccessDenied が返る。これが Pod 単位の権限分離を実現する核心だ。

{
  "Effect": "Allow",
  "Principal": {
 "Federated": "arn:aws:iam::123456789012:oidc-provider/oidc.eks.ap-northeast-1.amazonaws.com/id/ABCDEF1234567890"
  },
  "Action": "sts:AssumeRoleWithWebIdentity",
  "Condition": {
 "StringEquals": {
"oidc.eks.ap-northeast-1.amazonaws.com/id/ABCDEF1234567890:sub": "system:serviceaccount:kube-system:fluent-bit",
"oidc.eks.ap-northeast-1.amazonaws.com/id/ABCDEF1234567890:aud": "sts.amazonaws.com"
 }
  }
}

Step 6: 一時 Credential の発行と AWS API 呼び出し

全検証が通ると STS は AccessKeyIdSecretAccessKeySessionTokenExpiration を返す。AWS SDK はこれをメモリにキャッシュし、Expiration の数分前に自動で AssumeRoleWithWebIdentity を再呼び出しして Credential を更新する。アプリは通常の boto3.client('s3').put_object(...) など標準 API を呼び出すだけでよく、Credential のライフサイクル管理は SDK が完全に担う。

IRSA が正しく機能しているかは以下のコマンドで確認できる。AssumedRoleId に EKS クラスタ名と SA 名が含まれていれば IRSA 経由で認証されている。

# Pod 内で実行 (IRSA Credential の確認)
kubectl exec -it {pod-name} -n {namespace} -- \
  aws sts get-caller-identity
# {
#"UserId": "AROA...:botocore-session-...",
#"Account": "123456789012",
#"Arn": "arn:aws:sts::123456789012:assumed-role/FluentBitRole/botocore-session-..."
# }

3-3. ECS Task Role との対比

EKS IRSA と ECS Task Role は「コンテナに AWS 権限を付与する」という目的は同じだが、設計思想と粒度が異なる。

観点ECS Task RoleEKS IRSA
権限付与の設定箇所Task Definition taskRoleArnKubernetes ServiceAccount annotation
認証経路ECS Task Metadata Endpoint (IMDSv4)OIDC JWT + STS AssumeRoleWithWebIdentity
スコープ粒度Task (= ECS Service) 単位Namespace + ServiceAccount 単位 (より細粒度)
Terraform リソースaws_ecs_task_definition.task_role_arnaws_iam_role + kubernetes_service_account
一時 Credential の管理ECS エージェントが自動管理STS 発行・SDK が自動更新
IAM Role の共有リスクサービス内の全タスクが同一 Role を使用SA ごとに別 Role を付与可能
設定変更の影響範囲Task Definition 更新 (再デプロイ必要)SA annotation 変更で即時反映

ECS Task Role は Task Definition に taskRoleArn を指定するだけで有効になり設定が簡潔だが、権限の粒度はサービス (Task Definition) 単位に留まる。同一サービス内の複数タスクに異なる権限を付与することはできない。

一方 IRSA は Namespace + ServiceAccount の組み合わせで権限を Pod 単位まで絞れるため、マイクロサービスが多数乱立する大規模クラスタでの最小権限管理に適している。ただし ServiceAccount annotation と IAM Trust Policy の両方を管理する必要があり、設定漏れによる AccessDenied が発生しやすい点には注意が必要だ。

ECS での実装詳細は以下を参照:

3-4. Worker Node IAM Role / アクセスキーマウントとの違い

IRSA 以前の EKS での AWS 権限付与には 2 つの手法が広く使われていたが、いずれも深刻な問題を抱えていた。

旧手法①: Worker Node IAM Role

EC2 ワーカーノードに IAM Role を直接付与し、ノード上の全 Pod がその権限を共有する方法だ。IMDSv1 では任意の Pod からメタデータエンドポイント (169.254.169.254) にアクセスでき、ノードの IAM Credential を取得できてしまう。

問題点:

  • 同一ノード上の全 Pod が同じ IAM 権限を保持 → 最小権限原則違反
  • 不正な Pod (サプライチェーン攻撃・SSRF 等) がノード権限を悪用可能
  • 権限の削除はノード全体に影響するため変更コストが高い
  • Karpenter でノードが動的増減するため権限管理が困難

旧手法②: K8s Secret へのアクセスキーマウント

AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY を K8s Secret に保存し、環境変数として Pod にマウントする方法だ。静的な長期資格情報のため漏洩リスクが高く、CI/CD パイプラインや Pod ログに誤って出力されることも多い。

問題点:

  • 静的な長期資格情報のため漏洩時の影響が長期化
  • ローテーションが手動管理 → 定期ローテーション漏れが多発
  • K8s Secret は base64 エンコードのため etcd 暗号化が必須
  • Secret にアクセスできる権限者全員が資格情報を参照可能

IRSA の優位性

比較軸Worker Node IAM RoleK8s Secret アクセスキーIRSA
権限スコープノード単位任意 (設定次第)Pod/SA 単位
資格情報の種類一時 (EC2 Instance Profile)静的 (長期)一時 (TTL 1h)
ローテーション自動手動自動 (kubelet + SDK)
最小権限原則困難可能だが管理コスト大Pod 単位で実現
漏洩時の影響範囲ノード全体・無期限IAM ユーザー権限・無期限1 Pod・最大 1 時間
IMDSv2 対策必要 (HttpPutResponseHopLimit: 1)不要不要 (STS 直通)

IRSA は一時 Credential (TTL 1h) を Pod 単位で払い出すため、万一トークンが漏洩しても影響範囲は 1 Pod・最大 1 時間に限定される。Worker Node IAM Role と異なり、SSRF 対策として IMDSv2 の HttpPutResponseHopLimit: 1 を強制設定する必要もない。§4 では Trust Policy と Terraform による IRSA の完全実装を解説する。

なお、AWS は 2023 年に IRSA の後継として EKS Pod Identity を発表した。OIDC Provider の手動登録が不要になり、EKS アドオン eks-pod-identity-agent を導入するだけで Pod ごとの IAM Role 付与が可能になる。ただし 2025 年時点では一部リージョンや旧 Kubernetes バージョンでのサポートが限定的なため、本シリーズでは実績の豊富な IRSA を採用している。Pod Identity との詳細比較は §6 で扱う (Vol1 EKS Cluster 構築編 も参照)。


4. OIDC Provider + Federated IAM Role Terraform 完全実装

fig03: OIDC Provider + Federated IAM Role Terraform 構成図

EKS で IRSA を機能させるには OIDC Provider の AWS IAM 登録 → IAM Role の信頼ポリシー (Federated Principal + sub claim 制限) → Kubernetes ServiceAccount へのアノテーション という 3 ステップが必要です。本章ではこれらすべてを Terraform で完全実装し、AWS CLI・コンソール手順・eksctl による代替手段もあわせて解説します。

4-1. aws_iam_openid_connect_provider 構築

QG-2: OIDC Provider + Federated IAM Role Terraform 必須チェックリスト

  • aws_iam_openid_connect_provider: url / client_id_list=[“sts.amazonaws.com”] / thumbprint_list
  • tls_certificate (data source): url = EKS OIDC Issuer URL → thumbprint 自動取得
  • aws_iam_role: assume_role_policy に Federated Principal + Condition sub claim
  • kubernetes_service_account: metadata.annotations[“eks.amazonaws.com/role-arn”]
  • aws_iam_role_policy_attachment: 各 Add-on の IAM ポリシー (EBS CSI / LB Controller / external-dns / Fluent Bit)

EKS クラスターは作成時に自動で OIDC Issuer URL (https://oidc.eks.ap-northeast-1.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE) を持ちますが、この URL を AWS IAM に OIDC Provider として登録しなければ AssumeRoleWithWebIdentity は機能しません。tls_certificate データソースで thumbprint を自動取得するのが Terraform のベストプラクティスです。

# thumbprint を OIDC Issuer URL から自動取得
data "tls_certificate" "eks" {
  url = aws_eks_cluster.main.identity[0].oidc[0].issuer
}

# OIDC Provider を AWS IAM に登録
resource "aws_iam_openid_connect_provider" "eks" {
  client_id_list  = ["sts.amazonaws.com"]
  thumbprint_list = [data.tls_certificate.eks.certificates[0].sha1_fingerprint]
  url = aws_eks_cluster.main.identity[0].oidc[0].issuer

  tags = {
 Name  = "${var.cluster_name}-oidc-provider"
 Environment = var.environment
  }
}

# OIDC ID (URL から https:// を除いた文字列) をローカル変数化
locals {
  oidc_id = replace(
 aws_eks_cluster.main.identity[0].oidc[0].issuer,
 "https://",
 ""
  )
}

OIDC Issuer URL は aws eks describe-cluster で確認できます。

# OIDC Issuer URL 確認
aws eks describe-cluster \
  --name my-eks-cluster \
  --region ap-northeast-1 \
  --query "cluster.identity.oidc.issuer" \
  --output text

# 出力例:
# https://oidc.eks.ap-northeast-1.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE

thumbprint_list は TLS 証明書の SHA-1 フィンガープリントです。tls_certificate データソースを使うことで手動計算が不要になり、証明書ローテーション時も Terraform 再実行で自動更新されます。

4-2. Federated IAM Role 作成 (assume_role_policy)

IAM Role の信頼ポリシーに Federated Principal と StringEquals Condition を設定することで、特定の Kubernetes ServiceAccount だけが AssumeRoleWithWebIdentity できるよう制限します。本番環境では パターン1 (namespace + SA 名で厳密に制限) を必ず使用してください。

# パターン1: namespace + ServiceAccount 名で厳密に制限 (本番推奨)
resource "aws_iam_role" "fluent_bit" {
  name = "${var.cluster_name}-fluent-bit-role"

  assume_role_policy = jsonencode({
 Version = "2012-10-17"
 Statement = [{
Effect = "Allow"
Principal = {
  Federated = aws_iam_openid_connect_provider.eks.arn
}
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
  StringEquals = {
 "${local.oidc_id}:sub" = "system:serviceaccount:amazon-cloudwatch:fluent-bit"
 "${local.oidc_id}:aud" = "sts.amazonaws.com"
  }
}
 }]
  })

  tags = {
 Name  = "${var.cluster_name}-fluent-bit-role"
 Environment = var.environment
  }
}

resource "aws_iam_role_policy_attachment" "fluent_bit" {
  role = aws_iam_role.fluent_bit.name
  policy_arn = "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy"
}
# パターン2: namespace のみ制限 (同一 namespace 内の複数 SA に適用)
resource "aws_iam_role" "monitoring_namespace" {
  name = "${var.cluster_name}-monitoring-role"

  assume_role_policy = jsonencode({
 Version = "2012-10-17"
 Statement = [{
Effect = "Allow"
Principal = {
  Federated = aws_iam_openid_connect_provider.eks.arn
}
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
  StringLike = {
 "${local.oidc_id}:sub" = "system:serviceaccount:monitoring:*"
 "${local.oidc_id}:aud" = "sts.amazonaws.com"
  }
}
 }]
  })
}
# パターン3: 制限なし (開発・検証環境のみ。本番では絶対に使用しないこと)
resource "aws_iam_role" "dev_unrestricted" {
  name = "${var.cluster_name}-dev-unrestricted-role"

  assume_role_policy = jsonencode({
 Version = "2012-10-17"
 Statement = [{
Effect = "Allow"
Principal = {
  Federated = aws_iam_openid_connect_provider.eks.arn
}
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
  StringEquals = {
 "${local.oidc_id}:aud" = "sts.amazonaws.com"
  }
}
 }]
  })
}

sub claim の形式は system:serviceaccount:{namespace}:{serviceaccount-name} です。誤った値を設定すると Pod が AccessDenied エラーで AWS API を呼び出せなくなるため、namespace と SA 名を正確に指定してください。

4-3. kubernetes_service_account annotation

Kubernetes ServiceAccount に eks.amazonaws.com/role-arn アノテーションを付与することで、その SA を使う Pod に EKS Pod Identity Webhook が AWS_ROLE_ARNAWS_WEB_IDENTITY_TOKEN_FILE 環境変数を自動挿入します。Terraform の kubernetes プロバイダーで管理するのが推奨です。

resource "kubernetes_service_account" "fluent_bit" {
  metadata {
 name= "fluent-bit"
 namespace = "amazon-cloudwatch"
 annotations = {
"eks.amazonaws.com/role-arn" = aws_iam_role.fluent_bit.arn
 }
 labels = {
"app.kubernetes.io/name"= "fluent-bit"
"app.kubernetes.io/component" = "logging"
 }
  }

  automount_service_account_token = true

  depends_on = [
 aws_iam_openid_connect_provider.eks,
 aws_iam_role.fluent_bit,
  ]
}

アノテーションが正しく付与されているかと IRSA の動作は以下のコマンドで確認できます。

# ServiceAccount のアノテーション確認
kubectl get serviceaccount fluent-bit \
  -n amazon-cloudwatch \
  -o jsonpath='{.metadata.annotations}'

# Pod の環境変数確認 (IRSA が有効なら両変数が表示される)
kubectl exec -n amazon-cloudwatch deploy/fluent-bit \
  -- env | grep -E "AWS_ROLE_ARN|AWS_WEB_IDENTITY_TOKEN_FILE"

4-4. AWS CLI / コンソール手順

Terraform を使わずに OIDC Provider と IAM Role を作成する手順です。既存クラスターへの後付け追加や、Terraform 管理外環境での構築に活用できます。

# Step 1: OIDC Issuer URL 取得
CLUSTER_NAME="my-eks-cluster"
REGION="ap-northeast-1"
OIDC_URL=$(aws eks describe-cluster \
  --name "${CLUSTER_NAME}" \
  --region "${REGION}" \
  --query "cluster.identity.oidc.issuer" \
  --output text)

# Step 2: thumbprint 取得 (openssl で TLS 証明書フィンガープリントを計算)
THUMBPRINT=$(echo | openssl s_client \
  -servername "$(echo "${OIDC_URL}" | awk -F/ '{print $3}')" \
  -connect "$(echo "${OIDC_URL}" | awk -F/ '{print $3}'):443" \
  2>/dev/null | openssl x509 -fingerprint -noout -sha1 \
  | awk -F= '{gsub(/:/, "", $2); print tolower($2)}')

# Step 3: OIDC Provider を AWS IAM に登録
aws iam create-open-id-connect-provider \
  --url "${OIDC_URL}" \
  --client-id-list sts.amazonaws.com \
  --thumbprint-list "${THUMBPRINT}"

コンソール手順:
1. AWS マネジメントコンソール → IAMIdentity providersAdd provider
2. Provider type: OpenID Connect を選択
3. Provider URL: EKS OIDC Issuer URL を入力 → Get thumbprint をクリック
4. Audience: sts.amazonaws.com を入力 → Add provider
5. IAM → RolesCreate roleWeb identity タブを選択 → OIDC Provider を選択
6. Audience: sts.amazonaws.comNext → ポリシーをアタッチ → ロール名を入力して作成

4-5. eksctl による代替手段

eksctl create iamserviceaccount は OIDC Provider 登録・IAM Role 作成・ServiceAccount アノテーション付与を一括で行う便利なコマンドです。ただし内部で CloudFormation Stack を作成するため、Terraform との二重管理には注意が必要です。

# eksctl で IRSA を一括作成
eksctl create iamserviceaccount \
  --cluster="${CLUSTER_NAME}" \
  --namespace=amazon-cloudwatch \
  --name=fluent-bit \
  --attach-policy-arn=arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy \
  --approve \
  --region="${REGION}"

# 作成された CloudFormation Stack の確認
aws cloudformation list-stacks \
  --query "StackSummaries[?contains(StackName, 'eksctl')].StackName" \
  --output table
手法管理方法適用場面
TerraformIaC・GitOps本番環境・再現性重視
AWS CLIスクリプト既存環境への後付け追加
eksctlCloudFormation検証・プロトタイプ

Terraform で構築した EKS クラスターに eksctl を併用すると、IAM Role が CloudFormation と Terraform の二重管理になり terraform destroy 時に競合が発生します。本番環境では Terraform に統一 することを強く推奨します。


5. 主要 Add-ons IRSA 連携実装

fig04: 主要 Add-ons IRSA 連携全体図

EKS クラスターを本番運用する上で、aws-ebs-csi-driver / aws-load-balancer-controller / external-dns / Fluent Bit の 4 Add-ons は必須となる。これらはそれぞれ EBS 動的プロビジョニング・ALB 自動作成・Route53 DNS 管理・CloudWatch Logs 転送を担い、AWS API を直接呼び出すため IRSA による権限付与が必要だ。
Node IAM Role で一括付与する手法では Pod 間の権限分離ができず、単一 Pod の侵害が全 Pod の AWS 権限流出に直結する。IRSA で ServiceAccount 単位に最小権限 IAM Role を紐付けることで、Add-on ごとの権限境界を明確に引くことができる。
デプロイ方式は Add-on によって異なる。aws-ebs-csi-driver は aws_eks_addon でマネージド管理し、aws-load-balancer-controller・external-dns は helm_release でデプロイし、Fluent Bit は DaemonSet として展開する。いずれも IRSA の有効化は ServiceAccount への eks.amazonaws.com/role-arn アノテーション付与で完結する。

5-1. 主要 4 Add-ons IRSA 設定マトリクス

主要 Add-ons IRSA 設定マトリクス

Add-onIAM ポリシーNamespaceSA 名設定方法
aws-ebs-csi-driverAmazonEBSCSIDriverPolicykube-systemebs-csi-controller-saaws_eks_addon + IRSA
aws-load-balancer-controllerAWSLoadBalancerControllerIAMPolicy (公式5KB)kube-systemaws-load-balancer-controllerhelm_release + IRSA
external-dnsRoute53ChangeResourceRecordSetskube-systemexternal-dnshelm_release + IRSA
Fluent BitCloudWatchAgentServerPolicyamazon-cloudwatchfluent-bitDaemonSet + IRSA (Vol1 §7 深掘り)

各 Add-on の IAM ポリシーは性質が異なる。aws-ebs-csi-driver と Fluent Bit は AWS 管理ポリシーを policy_arn で直接参照できる。aws-load-balancer-controller の公式 IAM ポリシーは kubernetes-sigs リポジトリで管理されており、data "http" で動的取得することで手動更新なしに最新版を維持できる。external-dns は Route53 の 3 操作に特化した最小権限カスタムポリシーを jsonencode で定義する。

デプロイ方式の違いが IRSA 設定方法に直結する。EKS マネージドアドオンの aws-ebs-csi-driveraws_eks_addonservice_account_role_arn 引数を指定するだけで EKS API が ServiceAccount への eks.amazonaws.com/role-arn アノテーション付与を自動化する。Helm Chart でデプロイする aws-load-balancer-controllerexternal-dnsserviceAccount.annotations への明示的な設定が必要だ。Fluent Bit DaemonSet は kubernetes_service_account リソースで Terraform から ServiceAccount を直接作成しアノテーションを付与する方法が管理しやすい。

IRSA が正常に機能しているかを確認するには kubectl describe serviceaccount <sa-name> -n <namespace> でアノテーションを確認後、kubectl exec から aws sts get-caller-identity を実行して期待する IAM Role ARN が返ることを検証する。AccessDeniedException が発生する場合は Trust Policy の StringEquals 条件の namespace または ServiceAccount 名のタイポを疑うこと。

5-2. aws-ebs-csi-driver IRSA 設定

aws-ebs-csi-driver は EKS マネージドアドオンとして提供されており、aws_eks_addon と IRSA を組み合わせることで完全な Terraform 管理が実現できる。service_account_role_arn 引数を指定するだけで EKS 側が ebs-csi-controller-sa ServiceAccount へのアノテーション付与を自動化する。

resource "aws_iam_role" "ebs_csi" {
  name = "AmazonEKS_EBS_CSI_DriverRole"

  assume_role_policy = jsonencode({
 Version = "2012-10-17"
 Statement = [{
Effect = "Allow"
Principal = { Federated = aws_iam_openid_connect_provider.eks.arn }
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
  StringEquals = {
 "${local.oidc_id}:sub" = "system:serviceaccount:kube-system:ebs-csi-controller-sa"
 "${local.oidc_id}:aud" = "sts.amazonaws.com"
  }
}
 }]
  })

  tags = {
 Name  = "${var.cluster_name}-ebs-csi-role"
 Environment = var.environment
  }
}

resource "aws_iam_role_policy_attachment" "ebs_csi" {
  policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy"
  role = aws_iam_role.ebs_csi.name
}

resource "aws_eks_addon" "ebs_csi" {
  cluster_name = aws_eks_cluster.main.name
  addon_name= "aws-ebs-csi-driver"
  service_account_role_arn = aws_iam_role.ebs_csi.arn

  addon_version= "v1.28.0-eksbuild.1"
  resolve_conflicts_on_update = "PRESERVE"

  depends_on = [aws_iam_role_policy_attachment.ebs_csi]
}

addon_versionaws eks describe-addon-versions --addon-name aws-ebs-csi-driver で利用可能なバージョンを確認してから固定することを推奨する。resolve_conflicts_on_update = "PRESERVE" を指定するとアドオンアップデート時にカスタム設定を保持できる。PVC でデフォルトの gp3 StorageClass を利用する場合は kubernetes_storage_class リソースで storageclass.kubernetes.io/is-default-class: "true" アノテーションを付与した StorageClass を別途 Terraform で定義する。

5-3. aws-load-balancer-controller IRSA 設定

aws-load-balancer-controller は ALB Ingress と NLB Service の自動プロビジョニングを担う Helm Chart ベースのコントローラーだ。公式 IAM ポリシー JSON は約 5KB あり随時更新されるため、data "http" で GitHub から動的取得するパターンが推奨される。

data "http" "lbc_policy" {
  url = "https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/main/docs/install/iam_policy.json"
}

resource "aws_iam_policy" "lbc" {
  name= "AWSLoadBalancerControllerIAMPolicy"
  policy = data.http.lbc_policy.response_body
}

resource "aws_iam_role" "lbc" {
  name = "AmazonEKSLoadBalancerControllerRole"

  assume_role_policy = jsonencode({
 Version = "2012-10-17"
 Statement = [{
Effect = "Allow"
Principal = { Federated = aws_iam_openid_connect_provider.eks.arn }
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
  StringEquals = {
 "${local.oidc_id}:sub" = "system:serviceaccount:kube-system:aws-load-balancer-controller"
 "${local.oidc_id}:aud" = "sts.amazonaws.com"
  }
}
 }]
  })
}

resource "aws_iam_role_policy_attachment" "lbc" {
  policy_arn = aws_iam_policy.lbc.arn
  role = aws_iam_role.lbc.name
}

resource "helm_release" "lbc" {
  name = "aws-load-balancer-controller"
  repository = "https://aws.github.io/eks-charts"
  chart= "aws-load-balancer-controller"
  namespace  = "kube-system"
  version = "1.7.2"

  set {
 name  = "clusterName"
 value = aws_eks_cluster.main.name
  }

  set {
 name  = "serviceAccount.create"
 value = "true"
  }

  set {
 name  = "serviceAccount.annotations.eks\\.amazonaws\\.com/role-arn"
 value = aws_iam_role.lbc.arn
  }

  depends_on = [aws_iam_role_policy_attachment.lbc]
}

data "http" を使用する場合は required_providers ブロックに hashicorp/http プロバイダーを追加する必要がある。静的管理が好ましい場合はポリシー JSON をローカルファイルとして保持し file() 関数で読み込む方法もある。ALB Ingress Controller を使った Path-based / Host-based ルーティングの完全実装は Vol3 で詳解する予定だ。

5-4. external-dns IRSA 設定

external-dns は Kubernetes Service/Ingress の external-dns.alpha.kubernetes.io/hostname アノテーションを監視し、Route53 の DNS レコードを自動管理するコントローラーだ。Route53 への書き込みに必要な最小権限の IAM ポリシーを作成して IRSA で付与する。

resource "aws_iam_policy" "external_dns" {
  name = "ExternalDNSPolicy"

  policy = jsonencode({
 Version = "2012-10-17"
 Statement = [
{
  Effect= "Allow"
  Action= ["route53:ChangeResourceRecordSets"]
  Resource = ["arn:aws:route53:::hostedzone/*"]
},
{
  Effect = "Allow"
  Action = [
 "route53:ListHostedZones",
 "route53:ListResourceRecordSets",
 "route53:ListTagsForResource"
  ]
  Resource = ["*"]
}
 ]
  })
}

resource "aws_iam_role" "external_dns" {
  name = "ExternalDNSRole"

  assume_role_policy = jsonencode({
 Version = "2012-10-17"
 Statement = [{
Effect = "Allow"
Principal = { Federated = aws_iam_openid_connect_provider.eks.arn }
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
  StringEquals = {
 "${local.oidc_id}:sub" = "system:serviceaccount:kube-system:external-dns"
 "${local.oidc_id}:aud" = "sts.amazonaws.com"
  }
}
 }]
  })
}

resource "aws_iam_role_policy_attachment" "external_dns" {
  policy_arn = aws_iam_policy.external_dns.arn
  role = aws_iam_role.external_dns.name
}

resource "helm_release" "external_dns" {
  name = "external-dns"
  repository = "https://kubernetes-sigs.github.io/external-dns"
  chart= "external-dns"
  namespace  = "kube-system"
  version = "1.14.4"

  set {
 name  = "provider"
 value = "aws"
  }

  set {
 name  = "serviceAccount.annotations.eks\\.amazonaws\\.com/role-arn"
 value = aws_iam_role.external_dns.arn
  }

  set {
 name  = "domainFilters[0]"
 value = var.domain_name
  }

  depends_on = [aws_iam_role_policy_attachment.external_dns]
}

domainFilters で管理対象ドメインを絞り込むことで他の Hosted Zone のレコードを誤変更するリスクを排除できる。route53:ListTagsForResource は Hosted Zone のタグ参照に必要で、これがないと ExternalDNS 起動ログに Warning が出ることがある。

5-5. Fluent Bit IRSA 設定 (Vol1 §7 深掘り)

Vol1 §7 では CloudWatch Add-on (amazon-cloudwatch-observability) で IRSA の概略のみを示したが、本章では Fluent Bit DaemonSet の IRSA 設定を Terraform で完全実装する。Fluent Bit は amazon-cloudwatch Namespace の fluent-bit ServiceAccount で動作し、CloudWatchAgentServerPolicy を付与することで CloudWatch Logs へのログ転送が可能となる。

resource "aws_iam_role" "fluent_bit" {
  name = "FluentBitRole"

  assume_role_policy = jsonencode({
 Version = "2012-10-17"
 Statement = [{
Effect = "Allow"
Principal = { Federated = aws_iam_openid_connect_provider.eks.arn }
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
  StringEquals = {
 "${local.oidc_id}:sub" = "system:serviceaccount:amazon-cloudwatch:fluent-bit"
 "${local.oidc_id}:aud" = "sts.amazonaws.com"
  }
}
 }]
  })
}

resource "aws_iam_role_policy_attachment" "fluent_bit_cw" {
  policy_arn = "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy"
  role = aws_iam_role.fluent_bit.name
}

resource "kubernetes_service_account" "fluent_bit" {
  metadata {
 name= "fluent-bit"
 namespace = "amazon-cloudwatch"
 annotations = {
"eks.amazonaws.com/role-arn" = aws_iam_role.fluent_bit.arn
 }
  }

  depends_on = [aws_iam_role_policy_attachment.fluent_bit_cw]
}

Helm Chart でデプロイする場合は values.yaml でアノテーションを設定できる:

serviceAccount:
  create: true
  annotations:
 eks.amazonaws.com/role-arn: "arn:aws:iam::123456789012:role/FluentBitRole"

kubectl patch でアノテーションを後付けする方法もあるが、Terraform 管理外となるため kubernetes_service_account リソースによる宣言的管理を推奨する。DaemonSet の Pod を kubectl rollout restart daemonset/fluent-bit -n amazon-cloudwatch で再起動することで新しい IRSA 設定が適用される。


6. EKS Pod Identity vs IRSA 比較

fig05: EKS Pod Identity vs IRSA 比較マップ

EKS の IAM 権限委任は 2024 年に大きな転換点を迎えた。AWS が正式 GA とした EKS Pod Identity は、OIDC Provider を不要にしてよりシンプルな権限委任を実現する新メカニズムだ。本章では IRSA と Pod Identity の仕組みの違いを 5 軸で整理し、既存環境の移行判断フロー・落とし穴まで徹底解説する。

6-1. EKS Pod Identity 概要 (2024 GA)

EKS Pod Identity は 2023 年 11 月に発表され、2024 年に EKS 1.24 以上で正式 GA となった AWS の新しい IAM 権限委任メカニズムだ。

IRSA との根本的な違い

IRSA が OIDC Provider → Federated Trust Policy → AssumeRoleWithWebIdentity というパスをたどるのに対し、Pod Identity は各ノードで稼働する EKS Pod Identity Agent (DaemonSet) を経由して直接 STS AssumeRole を実行する。

IRSA フロー:
  Pod → kube-apiserver (projected OIDC token) → STS AssumeRoleWithWebIdentity → IAM Role
  (OIDC Provider の thumbprint 検証が必要)

Pod Identity フロー:
  Pod → EKS Pod Identity Agent (DaemonSet, ノードごと) → STS AssumeRole → IAM Role
  (OIDC Provider 不要・Service Principal のみ)

IRSA は OIDC Provider の管理・thumbprint の定期更新・Trust Policy への sub claim 埋め込みが必要なため、SA が増えると運用コストが積み上がる。Pod Identity はそれらの煩雑さをすべて排除した。

EKS Pod Identity Agent Add-on の有効化

Pod Identity を利用するには、まず EKS Pod Identity Agent Add-on をクラスタに追加する。

# EKS Pod Identity Agent Add-on を有効化 (EKS 1.24 以上が前提)
aws eks create-addon \
  --cluster-name my-eks-cluster \
  --addon-name eks-pod-identity-agent \
  --addon-version v1.3.2-eksbuild.2 \
  --region ap-northeast-1

# Add-on の状態確認 (ACTIVE になるまで待機)
aws eks describe-addon \
  --cluster-name my-eks-cluster \
  --addon-name eks-pod-identity-agent \
  --query 'addon.status'

Add-on が ACTIVE になると、各ノードに eks-pod-identity-agent DaemonSet が展開される。

Pod Identity Association の作成と Trust Policy

# Pod Identity Association を作成 (SA ごとに 1 コマンドで完結)
aws eks create-pod-identity-association \
  --cluster-name my-eks-cluster \
  --namespace amazon-cloudwatch \
  --service-account fluent-bit \
  --role-arn arn:aws:iam::123456789012:role/FluentBitRole \
  --region ap-northeast-1

IAM Role の Trust Policy は IRSA に比べて大幅にシンプルになる。

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

IRSA の Trust Policy には OIDC Provider の URL と sub claim (system:serviceaccount:namespace:sa-name) を SA ごとに個別に埋め込む必要があったが、Pod Identity では pods.eks.amazonaws.com という Service Principal だけで十分だ。

Terraform での実装

# EKS Pod Identity Agent Add-on
resource "aws_eks_addon" "pod_identity_agent" {
  cluster_name  = aws_eks_cluster.main.name
  addon_name = "eks-pod-identity-agent"
  addon_version = "v1.3.2-eksbuild.2"
}

# IAM Role (OIDC Provider 不要)
resource "aws_iam_role" "fluent_bit" {
  name = "FluentBitRole"

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

# Pod Identity Association
resource "aws_eks_pod_identity_association" "fluent_bit" {
  cluster_name = aws_eks_cluster.main.name
  namespace = "amazon-cloudwatch"
  service_account = "fluent-bit"
  role_arn  = aws_iam_role.fluent_bit.arn

  depends_on = [aws_eks_addon.pod_identity_agent]
}

IRSA の実装と比較すると、aws_iam_openid_connect_provider リソースと kubernetes_service_account annotation 設定が丸ごと不要になる。

6-2. 5 軸比較マトリクス

QG-4: EKS Pod Identity vs IRSA 5 軸比較マトリクス

観点IRSAEKS Pod Identity (2024 GA)
OIDC Provider必要 (aws_iam_openid_connect_provider + thumbprint 定期更新)不要 (EKS Pod Identity Agent Add-on のみ)
Trust PolicyFederated + sub claim 条件 (SA ごとに個別設定・ミスが認証失敗の主因)シンプル (Service Principal: pods.eks.amazonaws.com のみ)
EKS バージョン1.13+ (旧クラスタでも利用可)1.24+ (EKS Pod Identity Agent Add-on 必須)
Terraform 設定量aws_iam_role + aws_iam_openid_connect_provider + kubernetes_service_account (annotation) の 3 リソース以上aws_eks_pod_identity_association + aws_iam_role の 2 リソースで完結
移行コスト・推奨度既存環境で安定稼働中 → 継続も可 (IRSA は廃止予定なし)新規採用強く推奨・既存は Add-on 単位で段階移行が安全

各軸の詳細を以下に補足する。

OIDC Provider 管理コスト

IRSA では OIDC Provider の CA thumbprint を管理する必要がある。AWS が CA ルート証明書を更新した際 (2023 年に実績あり) に thumbprint 更新を怠ると、全 SA の IRSA 認証が一斉失敗する重大障害につながる。Pod Identity はこのリスクが根本からなくなる。

Trust Policy の複雑さとミス率

IRSA の Trust Policy に埋め込む sub claim 条件 (oidc.eks.ap-northeast-1.amazonaws.com/id/XXXX:sub) は Namespace 名や SA 名のタイポが認証失敗の最大原因となる。SA が 20 個を超えるとこの管理コストが無視できなくなる。Pod Identity では SA ごとの Trust Policy 変更が不要で、Association の追加・削除だけで権限変更が完結する。

EKS バージョン制約と互換性

EKS 1.23 以前のクラスタは Pod Identity を利用できない。クラスタを 1.24 以上にアップグレードしてから EKS Pod Identity Agent Add-on を有効化する手順が必要だ。なお IRSA は現時点で廃止予定はなく、1.13 以上であれば引き続き利用できる。

Terraform での設定量比較

IRSA 設定では最低でも aws_iam_roleaws_iam_openid_connect_providerkubernetes_service_account の 3 リソースが必要で、SA が増えるたびに Trust Policy の Statement が肥大化する。Pod Identity は aws_eks_pod_identity_association を SA ごとに 1 リソース追加するだけで済み、Trust Policy の変更は一切不要だ。

移行コストと段階移行戦略

IRSA と Pod Identity は同一クラスタで共存できる。新規 Add-on (AWS Load Balancer Controller など) から Pod Identity を採用し、既存の IRSA SA はそのまま運用継続するハイブリッド戦略が最も安全だ。ただし同一 SA に両方を設定した場合の優先順位に注意が必要だ (§6-4 参照)。

6-3. 移行判断フロー

既存 IRSA 環境から Pod Identity への移行は、段階的なアプローチが安全だ。以下の判断ツリーを参考に移行タイミングを決定する。

移行判断ツリー

新規クラスタ作成?
  └── YES: EKS 1.24+?
├── YES → Pod Identity 強く推奨 (OIDC Provider 不要で管理コスト最小)
└── NO  → IRSA (クラスタ 1.24 アップグレード後に移行検討)
  └── NO (既存クラスタ・IRSA 運用中):
  IRSA SA が 10 個以上?
 ├── YES: Trust Policy 管理が複雑 → Pod Identity 段階移行推奨
 └── NO:  マルチクラスタ (3 クラスタ以上)?
  ├── YES → OIDC Provider 管理コスト高 → Pod Identity 優先移行
  └── NO  → 安定稼働中であれば当面 IRSA 継続も可

段階移行の推奨手順

# Step 1: クラスタバージョン確認 (1.24 以上が必須)
aws eks describe-cluster \
  --name my-eks-cluster \
  --query 'cluster.version' \
  --output text

# Step 2: EKS Pod Identity Agent Add-on を有効化
aws eks create-addon \
  --cluster-name my-eks-cluster \
  --addon-name eks-pod-identity-agent \
  --region ap-northeast-1

# Step 3: Add-on の ACTIVE 確認
aws eks describe-addon \
  --cluster-name my-eks-cluster \
  --addon-name eks-pod-identity-agent \
  --query 'addon.status'

# Step 4: 新規 SA から Pod Identity を採用 (既存 IRSA はそのまま)
aws eks create-pod-identity-association \
  --cluster-name my-eks-cluster \
  --namespace new-app \
  --service-account new-app-sa \
  --role-arn arn:aws:iam::123456789012:role/NewAppRole

# Step 5: 既存 IRSA SA の段階移行 (1 SA ずつ検証しながら切替)
# 5a: IAM Role の Trust Policy を Pod Identity 用に更新
aws iam update-assume-role-policy \
  --role-name ExistingAppRole \
  --policy-document file://pod-identity-trust-policy.json

# 5b: Pod Identity Association を作成
aws eks create-pod-identity-association \
  --cluster-name my-eks-cluster \
  --namespace existing-app \
  --service-account existing-app-sa \
  --role-arn arn:aws:iam::123456789012:role/ExistingAppRole

# 5c: Service Account annotation を削除 (切替完了後)
kubectl annotate serviceaccount -n existing-app existing-app-sa \
  eks.amazonaws.com/role-arn-

# 5d: Pod を再起動して Pod Identity を適用
kubectl rollout restart deployment -n existing-app existing-app

移行後は aws eks list-pod-identity-associations --cluster-name my-eks-cluster で Association 一覧を確認し、旧 IRSA annotation が残っていないことを kubectl で検証すること。

6-4. 併存設定の挙動と落とし穴

IRSA と Pod Identity の優先順位

同一 ServiceAccount に IRSA (annotation) と Pod Identity (Association) の両方が設定されている場合、Pod Identity が優先される。EKS Pod Identity Agent が credentials を先にインジェクトするためだ。

この優先順位を把握せずに Pod Identity Association を作成すると、既存 IRSA ロールのポリシーが突然適用されなくなりアクセス障害が起きる。移行は必ず 1 SA ずつ計画的に行い、本番環境では事前にステージングで動作確認すること。

落とし穴 4 点

  1. Pod Identity Agent Add-on 未インストールで Silent Fail: Add-on なしで Pod Identity Association を作成しても Pod は権限なしで起動する。IRSA にフォールバックもしない。必ず Add-on を先に ACTIVE 状態にしてから Association を作成すること。

  2. IRSA と Pod Identity 両方設定した SA での意図しないロール切替: Pod Identity が優先されるため、古い IRSA ロールのポリシー (S3 アクセス権など) が適用されなくなる。移行期間中は IAM ポリシーの差分に注意し、Pod Identity 用ロールに必要な権限がすべて揃っていることを確認してから切替を実施すること。

  3. EKS 1.23 以下での Pod Identity 利用不可: クラスタバージョンを確認せず Add-on を有効化しようとするとエラーになる。aws eks describe-cluster --name my-cluster --query 'cluster.version' でバージョン確認してから作業すること。EKS 1.23 以下では IRSA を継続利用すること。

  4. Trust Policy の Service Principal ミス: pods.eks.amazonaws.com ではなく eks.amazonaws.com と書くと AssumeRole が失敗する。Trust Policy 設定後は aws iam simulate-principal-policy で検証し、Pod Identity Agent が正しく AssumeRole できることを確認すること。

ECS との連携アーキテクチャへの発展

EKS + IRSA / Pod Identity で実装したコンテナ単位の最小権限付与という思想は、ECS Fargate の Task Role 設計にも共通する。ECS CICD の実装や ecspresso による設定管理については以下の関連記事も参照してほしい。


7. 監視・トラブルシュート

fig06: IRSA トラブルシュート判断フロー

IRSA の本番運用で発生する障害の多くは認証設定の誤りに起因する。本章では CloudTrail / IAM Access Analyzer / kubectl の 3 つの観点から障害を調査する方法を解説する。

代表的な失敗パターン 5 選と原因切り分けフロー、Pod Identity 移行時のトラブルシュートを体系的にカバーし、本番稼働後のインシデント対応に直接活用できる内容を目指す。

障害発生時は「OIDC 設定→Trust Policy→ServiceAccount annotation→Pod spec」の順に確認すると原因の特定が速い。本章のチェックリスト (QG-5) を手元に置きながら進めると効果的である。

7-2. CloudTrail 活用

CloudTrail は AssumeRoleWithWebIdentity イベントを記録するため、IRSA 認証の成功・失敗を追跡するのに最も有用なツールである。

# AssumeRoleWithWebIdentity イベントを抽出 (過去1時間)
aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=AssumeRoleWithWebIdentity \
  --start-time "$(date -u -v-1H +%Y-%m-%dT%H:%M:%SZ)" \
  --region ap-northeast-1 \
  --query "Events[*].{User:Username,Role:Resources[0].ResourceName,Time:EventTime}" \
  --output table

# 失敗イベントのみ抽出 (ErrorCode が存在するもの)
aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=AssumeRoleWithWebIdentity \
  --start-time "$(date -u -v-1H +%Y-%m-%dT%H:%M:%SZ)" \
  --region ap-northeast-1 \
  --query "Events[?contains(CloudTrailEvent, 'errorCode')].{Time:EventTime,Error:CloudTrailEvent}" \
  --output text | head -50

代表的なエラーコードと原因:

エラーコード原因対策
AccessDeniedTrust Policy の Condition が一致しないsub claim (namespace/SA名) を確認
InvalidIdentityTokenOIDC token の発行者 (iss) が不一致OIDC Provider URL を確認
ExpiredTokenExceptiontoken TTL 超過kubelet が自動更新しない場合は Pod 再起動

CloudWatch Logs Insights でのクエリ (CloudTrail を CloudWatch Logs 転送している場合):

fields @timestamp, errorCode, userIdentity.sessionContext.sessionIssuer.arn
| filter eventName = "AssumeRoleWithWebIdentity" and ispresent(errorCode)
| sort @timestamp desc
| limit 20

本番環境では上記 Logs Insights クエリをベースに CloudWatch Metric Filter + Alarm を設定し、IRSA 認証失敗が一定数を超えたら SNS 通知する運用が推奨される。

7-3. IAM Access Analyzer 活用

IAM Access Analyzer は Trust Policy の外部エンティティ参照 (Federated Principal) を自動検出し、意図しないアクセスを可視化する。

# Access Analyzer の作成 (リージョン単位)
aws accessanalyzer create-analyzer \
  --analyzer-name eks-irsa-analyzer \
  --type ACCOUNT \
  --region ap-northeast-1

# 検出結果 (findings) を確認
aws accessanalyzer list-findings \
  --analyzer-name arn:aws:access-analyzer:ap-northeast-1:{ACCOUNT}:analyzer/eks-irsa-analyzer \
  --query "findings[?resourceType=='AWS::IAM::Role'].{Resource:resource,Action:action,Status:status}" \
  --output table

Findings の読み方:

  • ACTIVE の IRSA Role が検出された場合 → Trust Policy の Federated Principal が正しいか確認
  • RESOLVED → 過去の問題が修正済みであることを示す
  • Trust Policy に Condition (sub claim 制限) がないと「過剰権限」として検出される可能性あり

本番環境では Trust Policy の Condition に StringEquals で namespace と SA 名を明示し、ワイルドカード (system:serviceaccount:{namespace}:*) を使わないことが Access Analyzer の ACTIVE findings ゼロを達成する最短経路である。

なお、Access Analyzer は無料で使用できる (アナライザー作成コストなし)。複数リージョンで EKS クラスターを運用する場合はリージョンごとにアナライザーを作成し、定期的に findings を確認する運用が推奨される。

7-4. kubectl コマンド集

# ServiceAccount に annotation が付与されているか確認
kubectl describe sa fluent-bit -n amazon-cloudwatch
# Annotations: eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/FluentBitRole

# ServiceAccount の完全 YAML 確認
kubectl get sa fluent-bit -n amazon-cloudwatch -o yaml

# Pod 内の環境変数で IRSA が機能しているか確認
kubectl exec -it {pod-name} -n amazon-cloudwatch -- env | grep AWS
# AWS_ROLE_ARN=arn:aws:iam::123456789012:role/FluentBitRole
# AWS_WEB_IDENTITY_TOKEN_FILE=/var/run/secrets/eks.amazonaws.com/serviceaccount/token

# Token ファイルの内容確認 (JWT デコード)
kubectl exec -it {pod-name} -n amazon-cloudwatch -- \
  cat /var/run/secrets/eks.amazonaws.com/serviceaccount/token | \
  cut -d. -f2 | base64 -d 2>/dev/null | python3 -m json.tool | grep -E '"sub"|"iss"'

# Pod の projected volume マウント確認
kubectl describe pod {pod-name} -n amazon-cloudwatch | grep -A5 "Volumes:"

上記コマンドの確認ポイント:

  • describe saeks.amazonaws.com/role-arn annotation がなければ IRSA は機能しない
  • env | grep AWSAWS_ROLE_ARN が表示されなければ kubelet が projected token を注入していない
  • JWT の sub フィールドが system:serviceaccount:{namespace}:{sa-name} と一致しているか確認
  • Volumes:aws-iam-token (projected) が存在しない場合は SA の annotation を再設定して Pod を再起動

7-5. 代表失敗パターン 5 選

QG-5: IRSA トラブルシュートチェックリスト

  • ① OIDC thumbprint 不一致 → tls_certificate data source を使っているか確認
  • ② Trust Policy sub claim typo → system:serviceaccount:{namespace}:{sa-name} を完全一致確認
  • ③ ServiceAccount annotation 漏れ → kubectl describe sa {name} で eks.amazonaws.com/role-arn 確認
  • ④ namespace 制限ミス → Trust Policy の Condition StringEquals の namespace を確認
  • ⑤ projected token 未マウント → Pod spec の serviceAccountName が正しいか確認
  • ⑥ Pod Identity と IRSA の併存 → pod-identity-association が優先される点に注意

パターン①: OIDC thumbprint 不一致

症状: InvalidIdentityToken エラー。原因: thumbprint が古い値のまま。
対策: tls_certificate data source を使い thumbprint を自動取得する。手動で計算した場合は証明書ローテーション後に更新が必要。

# 現在登録されている thumbprint 確認
aws iam get-open-id-connect-provider \
  --open-id-connect-provider-arn arn:aws:iam::{ACCOUNT}:oidc-provider/oidc.eks.ap-northeast-1.amazonaws.com/id/{OIDC_ID} \
  --query "ThumbprintList"

パターン②: Trust Policy sub claim typo

症状: AccessDenied / sts:AssumeRoleWithWebIdentity 拒否。
原因: system:serviceaccount:{namespace}:{sa-name} の namespace またはSA名が間違っている。
対策: kubectl describe sa で SA 名と namespace を確認してから Trust Policy を設定。

# Trust Policy の Condition を確認
aws iam get-role \
  --role-name FluentBitRole \
  --query "Role.AssumeRolePolicyDocument.Statement[*].Condition"

パターン③: ServiceAccount annotation 漏れ

症状: Pod 内で AWS_ROLE_ARN 環境変数が設定されない。
原因: eks.amazonaws.com/role-arn annotation が SA に付与されていない。
対策: kubectl annotate sa または Terraform kubernetes_service_accountannotations を確認。

# annotation を付与 (既存 SA への追加)
kubectl annotate sa {sa-name} \
  -n {namespace} \
  eks.amazonaws.com/role-arn=arn:aws:iam::{ACCOUNT}:role/{RoleName} \
  --overwrite

パターン④: namespace 制限ミス

症状: 意図しない SA が IAM Role を AssumeRole できてしまう。
原因: Trust Policy の Condition.StringEquals に namespace 制限のみで SA 名を指定していない (system:serviceaccount:{namespace}:* になっている)。
対策: 本番環境では必ず system:serviceaccount:{namespace}:{sa-name} で SA 名まで制限する。

# Trust Policy の sub claim 確認
aws iam get-role \
  --role-name {RoleName} \
  --query "Role.AssumeRolePolicyDocument.Statement[*].Condition.StringEquals"
# "oidc.eks.*.amazonaws.com/id/*:sub" に "system:serviceaccount:{ns}:{sa}" があるか確認

パターン⑤: projected token 未マウント

症状: Pod 内で AWS_WEB_IDENTITY_TOKEN_FILE が設定されるがファイルが存在しない。
原因: Pod spec の serviceAccountName が IRSA annotation 付き SA と一致していない。
対策: kubectl describe podVolumes セクションで aws-iam-token projected volume が存在するか確認。

# projected volume の存在確認
kubectl describe pod {pod-name} -n {namespace} | grep -A10 "Volumes:"
# aws-iam-token が表示されれば正常

7-6. Pod Identity 移行時のトラブルシュート

EKS Pod Identity から IRSA への移行 (または §6 で比較した逆方向の移行) 時に発生しやすい問題:

問題原因対策
pod-identity-association 反映遅延EKS コントロールプレーンの設定伝播に数秒〜数十秒かかるPod 再起動で新しい token を取得させる
EKS Pod Identity Agent 未起動eks-pod-identity-agent DaemonSet が未デプロイkubectl get pods -n kube-system で確認
IRSA と Pod Identity の競合同一 SA に両方設定Pod Identity が優先される仕様 (§6 参照)。切替後は IRSA annotation を削除
# Pod Identity Agent の稼働確認
kubectl get daemonset eks-pod-identity-agent -n kube-system

# pod-identity-association の確認
aws eks list-pod-identity-associations \
  --cluster-name my-eks-cluster \
  --region ap-northeast-1

# IRSA → Pod Identity 切替時の annotation 削除
kubectl annotate sa {sa-name} -n {namespace} \
  eks.amazonaws.com/role-arn- \
  --overwrite

Vol1 EKS Cluster + Karpenter 本番ガイド でも CloudWatch / Fluent Bit の監視を扱っている。IRSA の認証ログと組み合わせることで、Pod が AWS API を正常に呼び出しているかをエンドツーエンドで確認できる。

移行後の動作確認には kubectl execenv | grep AWS を実行し、AWS_ROLE_ARN が新しい Pod Identity の ARN に切り替わっていることを必ず確認する。IRSA と Pod Identity を併用している過渡期は CloudTrail で AssumeRoleWithWebIdentity と AssumeRole (Pod Identity) の両イベントを監視し、意図しない認証経路が残っていないか確認することを推奨する。


8. まとめ + Vol3 予告 + 落とし穴 10 選

8-1. 本記事のチートシート

本記事で構築した IRSA 基盤の確認コマンドチートシート:

確認項目コマンド
OIDC Issuer URLaws eks describe-cluster --name {cluster} --query "cluster.identity.oidc.issuer" --output text
OIDC Provider 登録aws iam list-open-id-connect-providers --query "OpenIDConnectProviderList[*].Arn"
Trust Policy 確認aws iam get-role --role-name {role} --query "Role.AssumeRolePolicyDocument"
SA annotation 確認kubectl describe sa {sa-name} -n {namespace}
Pod IRSA 動作確認kubectl exec -it {pod} -- env \| grep AWS_ROLE_ARN
Token ファイル確認kubectl exec -it {pod} -- cat $AWS_WEB_IDENTITY_TOKEN_FILE \| cut -d. -f2 \| base64 -d 2>/dev/null
Add-on Pod 状態kubectl get pods -n kube-system -l app.kubernetes.io/name={addon-name}
CloudTrail 認証ログaws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=AssumeRoleWithWebIdentity
IAM Access Analyzeraws accessanalyzer list-findings --analyzer-name {name} --output table
Pod Identity 確認aws eks list-pod-identity-associations --cluster-name {cluster}

Terraform リソース構成まとめ:

リソース用途
data.tls_certificate.eksOIDC Issuer URL から thumbprint を自動取得
aws_iam_openid_connect_providerOIDC Provider を AWS IAM に登録
locals.oidc_idhttps:// を除いた OIDC ID (Trust Policy の Condition で使用)
aws_iam_role (Federated)IRSA 用 IAM Role (assume_role_policy に Federated Principal + sub claim)
aws_iam_role_policy_attachment各 Add-on に必要な IAM ポリシーをアタッチ
kubernetes_service_accountSA に eks.amazonaws.com/role-arn annotation を付与

8-2. 本番投入の落とし穴 10 選

EKS IRSA 本番投入 — 落とし穴 10 選

  1. OIDC Provider 重複作成エラー — Vol1 で既に aws_iam_openid_connect_provider を作成済の場合、Terraform apply で「already exists」エラー。terraform import または data source で既存 Provider を参照する
  2. thumbprint 自動更新漏れ — ACM 証明書ローテーション後に thumbprint が変わると InvalidIdentityToken エラー。tls_certificate data source を使い Terraform apply のたびに自動更新する
  3. Trust Policy sub claim typosystem:serviceaccount:{namespace}:{sa-name} の namespace または SA 名が 1 文字でも違うと AccessDeniedkubectl describe sa で SA 名と namespace を事前確認する
  4. namespace 制限漏れで権限スコープ拡大 — Condition に sub: system:serviceaccount:{namespace}:* の wildcard を使うと namespace 内の全 SA が AssumeRole 可能。本番は SA 名まで完全一致で制限する
  5. ServiceAccount annotation 漏れeks.amazonaws.com/role-arn annotation が SA に付与されていないと AWS_ROLE_ARN 環境変数が設定されない。kubernetes_service_account リソースの annotations ブロックを確認する
  6. projected token 未マウント — Pod spec の serviceAccountName が IRSA annotation 付き SA と一致しない場合、token が Volume マウントされない。kubectl describe podVolumesaws-iam-token projected volume を確認する
  7. aws-load-balancer-controller IAM ポリシーの旧バージョン使用 — GitHub の IAM ポリシー JSON が更新されていることがあり、古いバージョンのポリシーでは権限不足でコントローラーが動作しない。data "http" で常に最新 JSON を取得する
  8. external-dns の Route53 権限不足route53:ListResourceRecordSets が漏れると DNS レコード差分確認ができず同期が停止する。3 つの権限 (ChangeResourceRecordSets / ListHostedZones / ListResourceRecordSets) を必ず付与する
  9. Fluent Bit の CreateLogGroup 権限漏れ — CloudWatch Logs にロググループが存在しない場合、logs:CreateLogGroup がないと起動時にエラーになる ([Vol1 §7](https://www.watchittrend.com/eks-cluster-karpenter-production/) の教訓)。CloudWatchAgentServerPolicy に含まれるが カスタムポリシーの場合は明示的に付与する
  10. Pod Identity と IRSA の併存で挙動誤解 — 同一 SA に eks.amazonaws.com/role-arn annotation と Pod Identity Association を両方設定すると Pod Identity が優先される。切替後は annotation を削除し kubectl annotate sa {name} eks.amazonaws.com/role-arn- で明示的に除去する

落とし穴別の詳細と対策コマンド:

①③ OIDC Provider 重複 / thumbprint 問題の対処

既存 Provider の import:

# 既存 OIDC Provider の ARN を取得
aws iam list-open-id-connect-providers --query "OpenIDConnectProviderList[*].Arn"

# Terraform state に取り込む
terraform import aws_iam_openid_connect_provider.eks \
  arn:aws:iam::{ACCOUNT}:oidc-provider/oidc.eks.ap-northeast-1.amazonaws.com/id/{OIDC_ID}

# thumbprint の現在値を確認
aws iam get-open-id-connect-provider \
  --open-id-connect-provider-arn arn:aws:iam::{ACCOUNT}:oidc-provider/oidc.eks... \
  --query "ThumbprintList"

tls_certificate data source を使っていれば terraform apply のたびに thumbprint が自動更新される。手動で計算した thumbprint は証明書ローテーション後に陳腐化するため非推奨。

③ Trust Policy sub claim の正確な確認方法

# SA の完全名と namespace を確認
kubectl get sa -A | grep {addon-name}
kubectl describe sa {sa-name} -n {namespace} | grep Annotations

# Trust Policy の sub claim を確認
aws iam get-role --role-name {RoleName} \
  --query "Role.AssumeRolePolicyDocument.Statement[0].Condition"
# → StringEquals: {"oidc.eks.../id/...:sub": "system:serviceaccount:{ns}:{sa}"}

⑤ ServiceAccount annotation が効かない場合の確認手順

# SA に annotation があるか確認
kubectl get sa {sa-name} -n {namespace} \
  -o jsonpath='{.metadata.annotations}'

# annotation がない場合は kubectl patch で付与
kubectl annotate sa {sa-name} -n {namespace} \
  eks.amazonaws.com/role-arn=arn:aws:iam::{ACCOUNT}:role/{RoleName} \
  --overwrite

# Pod を再起動して新しい token を取得
kubectl rollout restart deployment/{deployment-name} -n {namespace}

8-3. Vol3 予告: ALB Ingress + Argo CD GitOps 編

本 Vol2 で構築した IRSA 基盤の上に、Vol3 では ALB Ingress Controller の本番設計Argo CD GitOps デプロイパイプライン を構築する。

Vol3 の主要コンテンツ:

  • ALB Ingress Controller 本番設計: 本 Vol2 §5 で IRSA 設定済の aws-load-balancer-controller を活用した Ingress リソース設計。Path ベースルーティング / SSL ターミネーション / WAF 連携 / Target Group Binding を Terraform + Helm で完全実装
  • Argo CD GitOps パイプライン: Git リポジトリを Single Source of Truth とした宣言型デプロイ。Application / AppProject / RBAC / Sync Policy を設定し、EKS への継続的デリバリーを自動化
  • Argo CD × IRSA 連携: Argo CD が AWS リソース (ECR / Secrets Manager / Parameter Store) にアクセスする際の IRSA 設定パターン
  • 本番監視: Argo CD の同期状態を CloudWatch Metrics / Alerts で可視化

Vol3 で ALB Ingress Controller と Argo CD を組み合わせることで、「EKS Cluster (Vol1) → IRSA 権限設計 (Vol2) → 本番グレードの Ingress + GitOps (Vol3)」という EKS 本番運用の 3 本柱が完成する。

【シリーズ】EKS 本番運用シリーズ — 次巻予告

Vol3 ALB Ingress + Argo CD GitOps 完全実装ガイド (完結巻)

8-4. 関連記事