クラウドIDとしてのKubernetes Pod: EKS IRSAとAKS Workload Identityの比較
Kubernetes ServiceAccountがどのようにして真のクラウドIDになるか - AWS IRSAとAzure AKS Workload Identityの背後にあるOIDCフェデレーションの信頼チェーンを1対1でマッピングし、これまでの経験則を覆すような注意点も解説します。
AWSとAzureはどちらも同じ問題を同じ方法で解決します。Podは、Kubernetes ServiceAccountをフェデレーションされたクラウドIDに変えることで、短期間有効なトークンと保存されたシークレットなしで、それ自体としてクラウドAPIに認証します。AWSではこの機能はIRSA (IAM Roles for Service Accounts) と呼ばれ、AzureではMicrosoft Entra Workload Identity (非推奨のaad-pod-identityの後継) と呼ばれます。内部的には、どちらも同じOIDCフェデレーションの仕組みであり、構成要素はほぼ1対1で対応しています。
もし1つの段落だけ読むなら、要するに次のとおりです。クラスターは、各PodのServiceAccountに対してトークンに署名するOpenID Connect発行者となり、クラウドのIDプロバイダーはその発行者を信頼し、トークンを1つのIDにスコープされた実際のクラウド認証情報と交換します。 AWSではそのIDをIAMロールと呼び、信頼ポリシーで制御します。Azureではユーザー割り当てマネージドIDと呼び、フェデレーションID資格情報で制御します。system:serviceaccount:<namespace>:<name>というサブジェクト文字列が、どちらにおいても要となります。ただし、3つの点で違いがあり、注意が必要です。Azureでは「OIDCプロバイダー」が2つの別々のクラスター設定に分割されており、どちらもデフォルトでオフになっています。AzureではさらにPod自体にラベルが必要であり、コンテナイメージのプルは、どちらのクラウドでもワークロードIDとは異なるIDで行われます。
両者が解決する問題
フェデレーション以前は、Podにクラウド権限を与えるには悪い選択肢しかありませんでした。静的なアクセスキーをイメージやSecretに埋め込む(漏洩、ローテーションの苦痛)、またはノード全体に権限を付与し、その上のすべてのPodがそれを継承する(分離なし、過剰なスコープ)といった方法です。AWS側の応急処置としては、kube2iamやkiamのようなノードIMDSインターセプターがありました。Azure側ではaad-pod-identityがありました。これらはすべてノードのIDをプロキシしており、扱いにくく、競合状態が発生しやすいものでした。
フェデレーションは、これを暗号による信頼に置き換えます。kubeletは、PodのServiceAccountにスコープされた、署名付きの短期間有効なOIDCトークンをPodにプロジェクションします。クラウドIdPは、クラスターが公開している公開鍵に対してそのトークンの署名を検証し、トークンの発行者、サブジェクト、オーディエンスが設定した信頼ルールと一致すれば、正確に1つのクラウドIDに対する認証情報を返します。シークレットはどこにも保存されません。プロジェクションされたトークンは自動的にローテーションされ(デフォルトの有効期間は約1時間)、クラスター外では無効です。
共通のメカニズム: OIDCフェデレーション
どちらのクラウドでも、すべてのワークロードID設定は同じ信頼の三角形に基づいています。
- クラスターはOIDC発行者です。
<issuer-url>/.well-known/openid-configurationでディスカバリドキュメントを公開し、ServiceAccountトークンに署名する公開鍵を含むJWKSエンドポイントを公開します。この発行者URLが信頼のアンカーとなります。 - クラウドIdPは、特定の
(subject, audience)ペアに対してその発行者を信頼します。 サブジェクトはどのServiceAccountを識別し、オーディエンスは誰のためのトークンかを識別します。 - ワークロードは、プロジェクションされたトークンをクラウド認証情報と交換します。 SDKはkubeletがプロジェクションしたトークンファイルを読み取り、それをクラウドのトークンエンドポイントに提示し、単一のクラウドIDに対する短期間有効な認証情報を受け取ります。
それ以外のすべては命名です。以下に、行ごとに対応関係を説明します。
1対1のマッピング
- Kubernetes側。 AWSでは、ServiceAccountに
eks.amazonaws.com/role-arn: <role arn>というアノテーションを付けます。Azureでは、azure.workload.identity/client-id: <managed identity client id>というアノテーションを付け、さらにPodにazure.workload.identity/use: "true"というラベルを付けます。どちらもPodをクラウドIDに紐付けますが、Azureでは追加のPodラベルが必要です(詳細は後述)。 - 「OIDCプロバイダー」。 AWSでは、これはIAMにOIDC IDプロバイダーとして一度登録されるEKSクラスターのOIDC発行者です。Azureでは、2つのクラスター設定です。発行者URLと署名鍵を公開するOIDC発行者 (
oidcIssuerProfile.enabled) と、ミューティングアドミッションWebhookであるワークロードIDアドオン (securityProfile.workloadIdentity.enabled) です。両方が有効になっている必要があります。 - ID。 AWS: IAMロール。Azure: ユーザー割り当てマネージドID (UAMI)。これがPodがなるオブジェクトです。
- 信頼ルール。 AWS: IAMロールの信頼ポリシーがOIDCプロバイダーをフェデレーションし、
sub = system:serviceaccount:<ns>:<sa>とaud = sts.amazonaws.comをピン留めします。Azure: UAMI上のフェデレーションID資格情報で、issuer = <cluster OIDC issuer url>、subject = system:serviceaccount:<ns>:<sa>、audience = api://AzureADTokenExchangeを設定します。同じ3つのフィールドですが、設定場所が異なります。 - 権限。 AWS: ロールにアタッチされたIAMポリシー(このS3バケットを読み取る、このECRリポジトリからプルするなど)。Azure: ターゲットリソースに対するRBACロール割り当て(Key Vault Secrets User、Storage Blob Data Reader、AcrPullなど)。これはより深い哲学的な違いです。AWSはポリシーをIDに紐付けますが、Azureはリソースのスコープでロールを付与します。
- 交換。 AWS: SDKはプロジェクションされたトークンで
sts:AssumeRoleWithWebIdentityを呼び出します。Azure: SDKはDefaultAzureCredential/WorkloadIdentityCredentialを介してEntraトークン交換を行い、プロジェクションされたトークンをクライアントアサーションとして提示します。どちらも短期間有効な認証情報を生成し、どちらもコード内にシークレット素材を一切必要としません。
この投稿の残りの部分では、各クラウドをエンドツーエンドで解説し、実際に人々を困らせる違いに焦点を当てます。
ウォークスルー: AWS EKS IRSA、エンドツーエンド
信頼の流れに沿った構成要素は次のとおりです。
1. クラスターOIDC発行者。 すべてのEKSクラスターには、https://oidc.eks.<region>.amazonaws.com/id/<hash>のようなURLで発行者が存在します。IAMにOIDC IDプロバイダーとして一度登録することで、IAMはその発行者が署名したトークンを信頼するようになります。
2. IAMロールとその信頼ポリシー。 信頼ポリシーは、特定のServiceAccountのみがロールを引き受けられるようにするものです。
{
"Effect": "Allow",
"Principal": { "Federated": "arn:aws:iam::<account>:oidc-provider/oidc.eks.<region>.amazonaws.com/id/<hash>" },
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"oidc.eks.<region>.amazonaws.com/id/<hash>:sub": "system:serviceaccount:apps:checkout",
"oidc.eks.<region>.amazonaws.com/id/<hash>:aud": "sts.amazonaws.com"
}
}
}
:subと:audの両方をピン留めしてください。発行者のみをピン留めすると、クラスター内の任意のServiceAccountがロールを引き受けることができてしまい、古典的な過剰信頼のバグにつながります。
3. IAMポリシー。 ロールにアタッチされた通常のIDベースのポリシーは、アプリケーションが実際に必要とする権限(特定のバケットに対するs3:GetObject、ecr:GetDownloadUrlForLayerなど)を付与します。
4. ServiceAccount。 1つのアノテーションを持つプレーンなServiceAccountです。
apiVersion: v1
kind: ServiceAccount
metadata:
name: checkout
namespace: apps
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::<account>:role/checkout
5. プロジェクション。 EKSには、クラスターに組み込まれたPod Identity Webhookが付属しています。Podがアノテーション付きのServiceAccountを使用すると、Webhookがそれを変更します。AWS_ROLE_ARNとAWS_WEB_IDENTITY_TOKEN_FILEという環境変数を注入し、プロジェクションされたトークンボリューム(オーディエンスsts.amazonaws.com、自動ローテーション)をマウントします。Podにラベルを付ける必要はありません。SAアノテーションだけで十分です。
6. 交換。 AWS SDKのデフォルトの認証情報チェーンはAWS_WEB_IDENTITY_TOKEN_FILEを検出し、トークンでsts:AssumeRoleWithWebIdentityを呼び出し、返された短期間有効な認証情報をキャッシュします。コードは単にboto3.client("s3")であり、認証情報の処理は一切ありません。
新しい代替手段: EKS Pod Identity (2023年) は、クラスターごとのIAM OIDCプロバイダーとロールごとの信頼ポリシーの代わりに、クラスター上のエージェントとアソシエーションAPIを通じて同じジョブを実行します。大規模な運用が容易ですが、AWS固有の仕組みです。上記のOIDCフェデレーションモデルはAzureにきれいにマッピングされるため、クラウド間の比較のために頭に入れておくべきモデルです。
ウォークスルー: Azure AKS Workload Identity、エンドツーエンド
同じ信頼の流れですが、Azureの用語を使用します。
1. 2つのクラスター設定。 OIDC発行者 (oidcIssuerProfile.enabled = true) を有効にします。これはhttps://<region>.oic.prod-aks.azure.com/<tenant>/<guid>/のような発行者URLを公開します。そして、ミューティングWebhookをインストールするワークロードIDアドオン (securityProfile.workloadIdentity.enabled = true) を有効にします。どちらも新しいクラスターではデフォルトでオフになっており、最初の設定が有効になるまでクラスターのoidcIssuerUrlはnullです。これらを有効にすることは、再作成ではなくインプレース更新です。
2. マネージドID。 ユーザー割り当てマネージドIDを作成します。(フェデレーション資格情報は、システム割り当てIDではなく、ユーザー割り当てIDにのみアタッチされます。)そのクライアントIDがServiceAccountから参照されます。
3. フェデレーションID資格情報。 これは信頼ルールであり、UAMIの子オブジェクトです。
issuer: https://<region>.oic.prod-aks.azure.com/<tenant>/<guid>/
subject: system:serviceaccount:apps:checkout
audience: api://AzureADTokenExchange
AWSと同じサブジェクトの文法と、異なる固定オーディエンスに注意してください。
4. RBAC。 UAMIのプリンシパルに、ターゲットリソースで必要なロールを付与します。Key Vaultに対するKey Vault Secrets User、ストレージアカウントに対するStorage Blob Data Reader、アプリケーション自体がレジストリデータプレーンを呼び出す場合はレジストリに対するAcrPullなどです。IDにはポリシー文書はなく、アクセスは各リソースのスコープでのロール割り当てによって行われます。
5. ServiceAccountとPodラベル。 ServiceAccountはクライアントIDアノテーションを持ち、-AWSのユーザーが忘れがちな部分ですが-Podにもラベルを付ける必要があります。
apiVersion: v1
kind: ServiceAccount
metadata:
name: checkout
namespace: apps
annotations:
azure.workload.identity/client-id: <uami-client-id>
---
# in the Deployment's pod template:
metadata:
labels:
azure.workload.identity/use: "true" # required on the POD, not just the SA
spec:
serviceAccountName: checkout
6. プロジェクション。 アドオンのWebhookは、azure.workload.identity/use: "true"ラベルを持つPodのみを変更します。これが起動すると、AZURE_CLIENT_ID、AZURE_TENANT_ID、AZURE_FEDERATED_TOKEN_FILE、AZURE_AUTHORITY_HOSTを注入し、オーディエンスapi://AzureADTokenExchangeを持つトークンをプロジェクションします。
7. 交換。 DefaultAzureCredential()(または明示的なWorkloadIdentityCredential)はこれらの環境変数を読み取り、プロジェクションされたトークンをクライアントアサーションとしてEntra IDに提示し、ターゲットリソースへのアクセストークンを受け取ります。Key Vaultの読み取りは、Pythonではazure.identity.DefaultAzureCredential()とSecretClient、TypeScriptではnew DefaultAzureCredential()と@azure/keyvault-secretsで行われ、ここでもコード内にシークレットは一切ありません。
サブジェクト文字列が要です(両方のクラウドで)
両方のクラウドで正確に一致する必要がある唯一の値は、トークンのサブジェクトです。
system:serviceaccount:<namespace>:<serviceaccount-name>
kubeletは、Podの実際の名前空間とServiceAccountに基づいてこれをトークンに刻印します。信頼ルール(AWSではIAM信頼ポリシー、Azureではフェデレーション資格情報)は、受け入れる文字列をピン留めします。アプリケーションがapps名前空間でcheckout ServiceAccountの下で実行される場合、信頼ルールはsystem:serviceaccount:apps:checkoutと、文字通り一致している必要があります。「誰も認証できない」という最も一般的な失敗は、ここでの不一致です。チャートが異なる名前空間にデプロイされた、ServiceAccountがアプリケーション名ではなくリリース名になった、または誰かがdefault ServiceAccountを想定した、といったケースです。フェデレーションがサイレントに失敗した場合、まずサブジェクトを確認してください。
オーディエンスはもう1つのピン留めされたフィールドであり、クラウドごとに固定の定数です。AWSではsts.amazonaws.com、Azureではapi://AzureADTokenExchangeです。これを変更することはめったにありませんが、信頼ルールとプロジェクションされたトークンはこれについて一致している必要があります。
実際に困る4つの違い
1. Azureは「OIDCプロバイダー」を2つの設定に分割しており、どちらもデフォルトでオフです。 EKSではPod ID Webhookがクラスターに組み込まれており、一度だけのステップはIAMにOIDCプロバイダーを登録することだけです。AKSでは、OIDC発行者とワークロードIDアドオンを個別に有効にする必要があります。アドオンを忘れると、発行者はトークンを公開しますが、Podに何もプロジェクションされないため、アプリケーションはAZURE_*環境変数を見つけられず、DefaultAzureCredentialは静かに次の認証情報ソースにフォールバックします。両方を有効にし、発行者URLが存在するまでフェデレーション資格情報を作成できないことを覚えておいてください。
2. Azureでは、ServiceAccountのアノテーションだけでなく、Podにもラベルが必要です。 AKS Webhookは、Pod上のazure.workload.identity/use: "true"ラベルをキーとして動作します(ServiceAccountアノテーションだけでは不十分です)。IRSAのWebhookはServiceAccountアノテーションをキーとして動作し、Podラベルは必要ありません。これはAzure側で最も一般的な落とし穴です。ServiceAccountは完璧に見えるのに、Podテンプレートでラベルを忘れてしまい、トークンがプロジェクションされないというケースです。
3. イメージのプルは、ワークロードIDとは異なるIDで行われます - 両方のクラウドで。 イメージのプルはノード/kubeletのIDの仕事です。AKSではkubelet IDがAcrPullを持ち、EKSではノードグループのインスタンスロールがAmazonEC2ContainerRegistryReadOnlyを持ちます(またはプルシークレットを使用します)。ワークロードIDは、アプリケーションコードがクラウドAPIを呼び出す場合(シークレットの読み取り、バケットのリスト、レジストリのデータプレーンの呼び出し)にのみ必要です。クラウドSDKと一切通信しない純粋なHTTPサービスは、ワークロードIDをまったく必要としません。逆に、ワークロードIDにAcrPullを付与しても、イメージのプルには何の効果もありません。なぜなら、プルはアプリケーション(とそのフェデレーションされたトークン)が実行される前に発生するからです。
4. 権限の付与方法が異なります。 AWSはIAMポリシーをロールに紐付けます。権限はIDと共に移動します。AzureはRBACロール割り当てをターゲットリソースのスコープで付与します。付与はアクセスされるものに存在し、IDには存在しません。最終的な状態(このPodはそのVaultを読み取れる)は同じですが、監査のために確認する場所が異なります。AWSでは、ロールにアタッチされたポリシーを読み取ります。Azureでは、リソースのロール割り当て(またはIDのスコープを横断した割り当て)をリストします。これは、両方のクラウドにおけるIAMとRBACのより広範な違いを反映しています。
注入される環境変数の様子
どちらのWebhookも、環境変数とプロジェクションされたファイルを介してSDKが必要とするすべてを渡すため、アプリケーションコードはシークレットやクラウド固有の認証情報ロジックを参照しません。
- AWS:
AWS_ROLE_ARN、AWS_WEB_IDENTITY_TOKEN_FILE、AWS_REGION。デフォルトの認証情報チェーンがAssumeRoleWithWebIdentityを呼び出します。 - Azure:
AZURE_CLIENT_ID、AZURE_TENANT_ID、AZURE_FEDERATED_TOKEN_FILE、AZURE_AUTHORITY_HOST。DefaultAzureCredentialがEntra交換を実行します。
どちらの場合も、プロジェクションされたトークンファイルは短期間有効で、kubeletによって自動的にローテーションされます。SDKはそれを再読み取りし、クラウド認証情報を透過的に更新します。ローテーションするものも、保存するものも、漏洩するものもありません。
両方のクラウドで機能するデバッグチェックリスト
Podが「認証できない」場合、信頼チェーンを順に確認してください。
- クラスター発行者は有効か? OIDC発行者URLが存在することを確認します(AKS: 両方の設定がtrue。EKS: IAM OIDCプロバイダーが登録されている)。発行者がなければ、信頼のアンカーもありません。
- プロジェクションは行われているか? Podにexecして、トークンファイルと環境変数(
AWS_WEB_IDENTITY_TOKEN_FILE/AZURE_FEDERATED_TOKEN_FILE)を確認します。Azureで不足している場合、通常はPodラベルがないか、アドオンがオフになっていることを意味します。 - サブジェクトは一致するか? 信頼ルールのサブジェクトを
system:serviceaccount:<actual-namespace>:<actual-sa>と比較します。これが最も頻繁な失敗です。 - オーディエンスは一致するか?
sts.amazonaws.com/api://AzureADTokenExchangeが両側で一致しているか。 - 権限は? IDが解決された後でのみ確認します。ロール上のIAMポリシー、またはターゲットリソース上のRBACロール割り当てです。ここで空の結果は、認証失敗ではなく認可失敗です。これは有用な区別であり、フェデレーション自体は機能していることを示します。
この内容を掘り下げる認定資格
ワークロードIDは、Kubernetes、クラウドIAM、OIDCフェデレーションが交差する場所に位置するため、3つの試験トラックにわたって登場します。すでに一方のクラウドの資格を持っている場合、もう一方のクラウドでの同じ概念はすぐに理解できるでしょう。
AWS側では:
- AWS Certified Solutions Architect - Associate (SAA-C03) - IAMロール、EKS、およびIDがクラウド権限を取得する方法。
- AWS Certified Security - Specialty (SCS-C03) - IAM信頼ポリシー、OIDCフェデレーション、および最小権限のスコープ設定(まさにIRSA信頼チェーン)。
Azure側では:
- Microsoft Azure Administrator Associate (AZ-104) - AKS、マネージドID、およびRBACロール割り当て。
- Microsoft Azure Security Engineer Associate (AZ-500) - Entra ID、マネージドID、フェデレーション資格情報、およびRBACの詳細。
Kubernetes側では:
- CNCF Certified Kubernetes Administrator (CKA) - ServiceAccounts、プロジェクションされたトークン、およびPodスペック。
- CNCF Certified Kubernetes Security Specialist (CKS) - ServiceAccountトークンのセキュリティ、最小権限、および静的シークレットの露出の削減。
まとめ
IRSAとAKS Workload Identityは、異なる用語をまとった同じアイデアです。クラスターはServiceAccountに対して短期間有効なOIDCトークンに署名し、クラウドIdPは1つの(subject, audience)に対してその発行者を信頼し、Podはトークンを単一のクラウドID(AWSではIAMロール、AzureではマネージドID)にスコープされた認証情報と交換します。信頼の三角形を一度学べば、その翻訳は機械的です。ロールはマネージドIDに、信頼ポリシーはフェデレーション資格情報に、アタッチされたポリシーはRBAC割り当てに、AssumeRoleWithWebIdentityはEntraトークン交換になります。Azureの2つのクラスター設定、Azureで必須のPodラベル、そしてイメージのプルがまったく異なるIDで行われるという3つの点を念頭に置けば、これまでランダムに見えていた拒否が、首尾一貫した意図的な設計として理解できるようになるでしょう。