亚马逊AWS官方博客
基于 IAM 的 EKS 权限管理详解(下)
在《基于 IAM 的 EKS 权限管理详解(上)》中我们讨论了如何管理集群访问,这篇我们将探讨如何管理 EKS 集群应用访问 AWS 服务的权限。
在 EKS 集群中运行的应用程序通常有访问 AWS 服务或资源的需求,例如 Amazon Simple Storage Service (Amazon S3) 的存储桶或 Amazon DynamoDB 的表,同时还要确保每个应用程序遵循最小特权原则。为了实现这一目标,AWS 在 2019 年发布了 IAM Roles for Service Accounts (IRSA) ,它允许用户将 AWS Identity and Access Management (AWS IAM) 的 role 与 Kubernetes 的 Server Accounts 关联,从而可以为 Pod 配置细粒度的 IAM 权限。IRSA 已被 AWS 客户广泛使用,但为了简化向 EKS 集群中运行的 Pod 授予 AWS 访问权限的过程, AWS 在 reInvent 2023上发布了 EKS Pod Identity,提供了一种新的方式来安全地向 Pod 授予 IAM 权限,这项新功能将于 IRSA 共存。
Kubernetes Service Account Token
无论是与 Kubernetes API Server 通信还是与外部服务比如 AWS API 通信,集群中的应用/Pod都需要使用 Service Account 为其提供身份标识,在 Kubernetes v1.22 后, 默认启用了 BoundServiceAccountTokenVolume
,Pod 启动时 Kubernets 控制平面的 Service Account Admission Controller 会根据 spec.serviceAccountName
字段为 pod 添加serviceAccountToken projected volumes,然后节点 kubelet 会使用 TokenRequest
API 获取一个短期的 JWT token 挂载到Pod。
通常用于向集群内部通过 API Server 进行身份验证的 Service Account Token 被挂载在 Pod 的 /var/run/secrets/kubernetes.io/serviceaccount
目录下,Pod 通过此 JWT (JSON Web Tokens) 与 API Server 进行安全通信。但是在与集群外部服务比如 IAM 交互时会稍有不同,为了方便外部系统可以验证 Kubernetes 颁发的 OIDC token,每个 EKS 集群创建后会提供一个独立的 OpenID Connect (OIDC) issuer URL,提供了 OIDC 配置 (.well-known/openid-configuration) 和公钥(keys)信息,外部系统可通过此公钥对 OIDC JWT 进行验证。
集群内部 Service Account Token 生成过程
在 EKS 上为 Pod 授予 IAM 权限本质都是以 Service Account Token 为身份标识分配 IAM 凭证,我们将从 IRSA 和 Pod Identity 两种方式展开介绍。
IAM Role for Service Account(IRSA)
在 EKS 集群创建时,AWS 会默认为集群托管一个 OIDC issuer URL,使用 IRSA 的前提就是使用该 OIDC issuer URL 在 AWS IAM 中创建一个 OIDC Identity Provider ,从而建立 IAM 对 EKS 集群内颁发的 Service Account Token 的信任关系,进而可以将 IAM 凭证分配给 Pod,此时我们可以简单理解整个 EKS 集群就是用于接入 IAM 系统的外部 Identity Provider,创建步骤请参考官方文档 。
IRSA 的用法
下面使用一个简单的示例来阐释 IRSA 的基本用法:我们将 IAM role my-role-a
通过 Service Accounts my-service-account-a
与集群中的 Pod my-cli-pod-a
关联。为了简化示例,我们不会为 IAM role 绑定任何 Policy。
创建 IAM role
创建 Service Account 并关联 IAM role
使用 Service Account 创建 Pod
IRSA 方式下的 Service Account Token
如果我们去观察 Pod 的 yaml 文件会看到多了一些与 IAM 相关的环境变量和一个名为 aws-iam-token 的 projected volume,这是因为 Pod 启动后 Service Account 的 annotation 触发了 Kubernetes 的 MutatingAdmissionWebhook,在 Pod 资源对象被持久化到 etcd 之前对其进行了修改,实际由托管在 EKS 集群控制平面的 amazon-eks-pod-identity-webhook 实现 。
环境变量中 AWS_ROLE_ARN 为关联 IAM role 的 ARN, AWS_WEB_IDENTITY_TOKEN_FILE 为前文所述的 Service Account Token,以 projected volume 的形式挂载在 Pod的 /var/run/secrets/eks.amazonaws.com/serviceaccount
目录 。
其实从 Pod 的 Service Account Token 的 audience:sts.amazonaws.com
可以看出 Token 是直接用于与 AWS STS API 进行交互,Pod 中的 AWS CLI/AWS SDK 会使用该 Token 和环境变量的 IAM role ARN 向 AWS STS AssumeRoleWithWebIdentity API 请求 IAM 临时角色凭证,SDK 也是类似,我们可以获取 Token 后进行验证:
IRSA 方式下 Pod 获取 IAM credentials
我们来梳理一下使用 IRSA 方式下 Pod 获取 IAM credentials 的主要关键流程:
- EKS/IAM 管理员在 IAM 中创建一个 Role,并在信任关系中配置包含 OIDC Provider issue URL 和 Service Account 名称等限制条件, 限制 IAM role 可被 assume 的范围。
- EKS 管理员为 Kubernetes Service Account 添加 Annotation 关联 IAM role。
- 当使用该 Service Account 的 Pod 启动时,首先 amazon-eks-pod-identity-webhook 会为 Pod Spec 添加 IAM 相关的环境变量和 Projected volume,节点 kubelet 再使用 TokenRequest API 获取一个短期的 JWT 并挂载到
/var/run/secrets/eks.amazonaws.com/serviceaccount
目录。 - Pod 中的应用使用 AWS SDK/CLI ,携带 JWT 和 RoleArn 向 AWS STS AssumeRoleWithWebIdentity API 请求临时凭证。
- STS 会通过集群 OIDC issuer URL 提供的信息对 JWT 进行验证,确认 JWT 由受信的 OIDC Identity Provider 签发后,再将 JWT 解析出的 iss,sub 和 aud 信息与 IAM Role 的 Trust relationship 策略进行匹配,验证 Service Account 是否拥有 Assume Role 的权限,成功后返回 IAM 凭证给 Pod。
可以看到 IRSA 是基于 IAM 服务构建的, 通过在 IAM 角色的信任策略中利用 Principal 和 Condition 字段来提供细粒度的权限管理,这种方式一方面需要 EKS 集群管理员有修改 IAM role 的权限,不利于自动化;另一方面 Pod 无法直接复用 IAM role,增加了管理的复杂度。
EKS Pod Identity
EKS Pod Identity 是另一种相对更为简单的权限管理方式,使用的前提是要在 EKS 集群中部署一个 Amazon EKS Pod Identity Agent,此 Agent 会以 DaemonSet Pod 的形式运行在集群工作节点,并且以 hostNetwork 方式运行提供一个HTTP endpoint,目的是协助应用 Pod 获取临时 IAM 凭证,部署方法请参考官方文档。
EKS Pod Identity 功能发布后新增了一系列 EKS API 简化 Pod 的 IAM 权限管理操作,新增 EKS API 如下:
API | 功能描述 |
CreatePodIdentityAssociation | 创建 Service Account 与 IAM role 之间的关联 |
ListPodIdentityAssociations | 列出集群中的 Amazon EKS Pod Identity 关联。您可以根据关联所在的命名空间或关联使用的 Service Account 过滤列表 |
DescribePodIdentityAssociation | 用于返回 EKS Pod Identity 关联的描述性信息 |
DeletePodIdentityAssociation | 用于删除现有的 pod identity 关联 |
UpdatePodIdentityAssociation | 用于更新现有的 pod identity 关联 |
AssumeRoleForPodIdentity | 被 eks-pod-identity-agent DaemonSet pod 使用,用于将 Service Account token 交换为关联的 IAM role 的 credentials,此 token 为 JWT,包含了 Pod 的 name、namespace 和 Service Account 等信息 |
EKS Pod Identity 的用法
我们使用一个前文相同的示例来阐释 EKS Pod Identity 的基本用法:我们将 IAM role my-role-b
通过 Service Accounts my-service-account-b
与集群中的 Pod my-cli-pod-b
关联,为了简化示例,我们不会为 IAM role 绑定任何 Policy。
创建 IAM role
创建 Service Account 并使用 EKS API 将 IAM role 与之关联
使用 Service Account 创建 Pod
EKS Pod Identity 方式下的 Service Account Token
与 IRSA 一样,amazon-eks-pod-identity-webhook 修改了 Pod spec 注入了一系列 AWS 相关的环境变量和 projected volume,但内容有所变化。环境变量 AWS_CONTAINER_CREDENTIALS_FULL_URI
为 EKS Pod Identity Agent 提供的 HTTP endpoint,AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE
则提供了用于交换 IAM 凭证的 Service Account Token。
这是因为使用 EKS Pod Identity方式时,AWS SDK 和 CLI 会使用内置的 HTTP credential provider 向该工作节点上的 EKS Pod Identity Agent 请求 IAM 凭证,关于AWS SDK 和 CLI 搜索 IAM credential 的详细机制请参考 Container credential provider。
另外 Service Account Token 的 audience 也发生了变化,因为 EKS Pod Identity 方式下获取 IAM 凭证不再需要直接与 STS API 进行交互,而是使用的是 EKS Auth API AssumeRoleForPodIdentity 向所在节点的 Agent 请求,通过以下方式进行验证:
支持 session tags
值得一提的是 EKS Pod Identity 方式下支持为 Pod 获取的临时 IAM 凭证添加 session tags。session tags 是在 AWS STS 中 assume IAM role 或联合用户时传递的键值对属性,在 AWS CloudTrail 中可以看到具体的信息。支持将集群名称、命名空间、服务帐户名称等属性添加到每个 Pod 的临时 IAM 凭证上实现基于属性的访问控制( ABAC ),客户可以在集群内以及集群之间的工作负载之间复用 IAM role 时实施更加严格的安全边界。
例如,当集群内 pod 关联的 IAM role 绑定以下 IAM policy 时,Pod 只能对 S3 桶内含有 eks-cluster-name
标签且标签值为 Pod 所在 EKS 的集群名称的对象执行 s3:GetObject 操作。这样即使多个集群的 Pod 使用相同的 IAM role,也可以通过调整桶内对象 tag 的键值控制 Pod 的访问权限。
EKS Pod Identity 方式下 Pod 获取 IAM credentials
梳理一下使用 EKS Pod Identity 方式下 Pod 获取 IAM credentials 的主要关键流程:
- EKS/IAM 管理员在 IAM 中创建一个允许被
pod.eks.amazonaws.com
assume 的 Role。 - EKS 管理员使用 CreatePodIdentityAssociation API 创建 Kubernetes Service Account 与 IAM role 的关联。
- 当使用该 Service Account 的 Pod 启动时,首先 Pod Identity Webhook 会为 Pod 注入 IAM 相关的环境变量和 Projected volume,节点 kubelet 会使用 TokenRequest API 获取一个短期的 JWT 并挂载到
/var/run/secrets/pods.eks.amazonaws.com/serviceaccount/eks-pod-identity-token
目录。 - Pod 中的应用使用 AWS SDK/CLI ,携带 JWT 向 Pod Identity Agent 提供的 HTTP 端点
http://169.254.170.23/v1/credentials
请求临时凭证。 - Pod Identity Agent 进行 SigV4 签名后,调用 EKS Auth API AssumeRoleForPodIdentity 请求获取 IAM 临时凭证。
- EKS Auth API 解码 JWT 并验证通过后,会通过 EKS Pod Identity API 确认 Service Account 与 IAM role 的关联关系,然后再调用 STS API 获取临时 AWS 凭证最终返回给 Pod。
支持的 SDK 请参考受支持的 AWS SDK 版本,需要注意的是目前 EKS pod identity 还无法为 Windows Amazon EC2 节点和 Fargate 节点上运行的 Pod 以及部分 Amazon EKS add-ons 提供 IAM 凭证,详细限制请参考 EKS Pod Identity restrictions,但未来 AWS 会提供 EKS add-ons 和 Pod Identity API 之间的原生集成。
如何从 IRSA 迁移到 EKS Pod identity
如果需要将现有集群的 IRSA 模式更改为利用 EKS Pod Identity,首先确保您的应用程序使用受支持的 AWS SDK 版本,然后在集群上安装 EKS Pod Identity Agent,您就可以使用新的 EKS 服务主体 pods.eks.amazonaws.com
更新您的 IAM 角色的信任策略 。
为了确保无缝迁移,可以更新现有的 IAM role 策略使其同时信任新的 EKS pods.eks.amazonaws.com
服务主体和 IRSA 需要的 OIDC endpoint,因为当 EKS Pod Identity webhook 注意到 IAM role 信任关系同时配置了两种方式时,它会优先使用 EKS Pod Identity 的方式注入环境变量,然后再通过调用 CreatePodIdentityAssociation API 来创建 Pod 身份关联。一旦建立了关联,所有新的/重新启动的 Pod 都会被注入 EKS pod Identity 方式下的新环境变量。验证一切都按预期工作后,可以从 IAM 角色中删除旧的 OIDC 提供程序信任关联。
EKS Pod Identity 和 IRSA 对比
EKS Pod Identity 和 IRSA 都允许您向 EKS 集群上运行的应用程序授予 AWS IAM 权限,虽然都是通过将 IAM role 关联到 Service Account 来实现 Pod 访问 AWS 服务,但是在根本上是设计理念的不同。IRSA 基于 IAM 构建,权限管理只能通过调整 IAM Role 的信任关系来实现;而 EKS Pod Identity 是完全为 EKS 而设计,通过新增一系列 EKS API 减少了 EKS 集群管理员与 IAM 的交互,二者的主要差异有:
角色绑定:使用 IRSA 时,每个 IAM role 的信任策略都与 Service Account 相关,使用新的 Service Account 与当前 IAM role 绑定时集群管理员需要更新该 IAM role 的信任策略。Pod Identity 只需要对 IAM role 设置一次对服务主体的信任策略,在集群中使用新的 Service Account 绑定当前 IAM role 无需调整信任策略;
角色复用:IRSA 中的角色信任策略必须配置 EKS 集群对应的 IAM OIDC provider 端点,因此即使 IAM role 需要的权限相同也需要为每一个集群创建或调整该角色。Pod Identity 中的同一角色被不同的 Service Account 绑定时,可以通过集群名称、命名空间、Service Account 名称等角色会话标签属性匹配的方式设置资源访问权限,而且同一 role 可以被不同集群使用;
可扩展性:IRSA 受到每个账户 100 个 IAM OIDC provider 的限制,故使用 IRSA 的集群数量最大为 100,IRSA 使用时受 IAM 角色信任策略 2048 个字节长度的限制,添加信任条件时受此限制。Pod Identity 无以上限制;
适用环境:IRSA 实现依靠基础服务 IAM,可以用在 EKS,EKS-Anywhere 甚至是基于 EC2 自托管的 Kubernetes 环境;Pod Identity 只适用于 EKS 。
总结
本文我们结合 IRSA(IAM Roles for Service Accounts)和新推出的 EKS Pod Identity 两种方式,阐述了如何管理 EKS 集群应用访问 AWS 服务的权限;前者基于 IAM 服务构建,细粒度权限管理依靠修改 IAM role 的信任策略,而后者则通过新增一系列 EKS API 简化了在 EKS 上为应用授予 IAM 权限的过程,极大程度降低了集群权限管理复杂度并提高了可扩展性。