亚马逊AWS官方博客

基于 IAM 的 EKS 权限管理详解(上)

众所周知,Kubernetes 拥有自己的身份验证和授权控制机制,而 Identity and Access Management(IAM)为整个 AWS 生态系统提供了全面的精细化访问控制, Amazon Elastic Kubernetes Service(Amazon EKS)自发布之初就已经支持 AWS IAM principals 作为针对集群进行身份验证的实体。所以在 AWS 上管理托管 EKS 集群的权限时必然会面临 IAM 与 Kubernetes 原生的认证授权机制的融合,主要分为两个方面:1)如何管理 AWS IAM 主体对 EKS 集群的访问;2)如何管理 EKS 集群中的应用访问 AWS 服务的权限。而在去年末 Amazon EKS 推出了两个新功能:Cluster Access Management 和 EKS Pod Identity,极大程度简化了 EKS 权限管理,我们将结合这两个新功能通过两篇博客来来详细探讨基于 IAM 的 EKS 权限管理。

本文是第一篇讨论如何管理集群访问。通常有两种类型的身份可以访问您的 Amazon EKS 集群,IAM principal 和您自己的 OpenID Connect (OIDC) identity provider 中的用户,关于后者不在本文讨论范围内,请参考文档

EKS API Server 如何对 IAM identity 的请求进行身份验证

Kubernetes 支持多种不同的策略来验证对 API server 服务的请求,EKS 使用 webhook token authentication 的方式实现了与 IAM 的集成。当我们创建一个 EKS 集群时,EKS 会在托管控制平面启动一个 aws-iam-authenticator 组件,用于支持集群 API Server 的 webhook 令牌认证。

集群 API Server 启动日志

集群 kubeconfig 文件

然后我们从 kubeconfig 文件开始,通常创建集群后会通过以下命令生成集群的 kubeconfig 文件:

aws eks --region <region-code> update-kubeconfig --name <cluster_name>

获取的 kubeconfig 文件中除了常规的 API server 的 URL 和集群 CA 证书等基本信息外,在 user 字段会包括如下部分:

    exec:
      apiVersion: client.authentication.k8s.io/v1beta1
      args:
      - --region
      - <region>
      - eks
      - get-token
      - --cluster-name
      - <cluster name>
      command: aws

目的是用于获取身份验证 token,等同于如下命令:

aws eks get-token --cluster-name <cluster_name>

手动执行后会输出类似于以下内容:

{
"kind": "ExecCredential",
"apiVersion":  "client.authentication.k8s.io/v1beta1",
"spec": {},
"status": {
"expirationTimestamp": "2024-04-07T08:40:04Z",
"token": "k8s-aws-v1.<base64 code>"
}
}

每个 token 都以 k8s-aws-v1 开头,后面为 Base64 编码,执行:

aws eks get-token --cluster-name <cluster_name> \
| jq -r .status.token | sed 's/k8s-aws-v1.//' | base64 --decode

解码后内容:

https://sts.amazonaws.com/?Action=GetCallerIdentity&Version=2011-06-15&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=XXXXJPFRILKNSRC2W5QA%2F20200219%2Fus-xxxx-1%2Fsts%2Faws4_request&X-Amz-Date=20200219T155427Z&X-Amz-Expires=60&X-Amz-SignedHeaders=host%3Bx-k8s-aws-id&X-Amz-Signature=XXXf8f3285e320ddb5e683a5c9a405301ad76546f24f28111fdad09cf648a393

可以看到这是一个预签名的 AWS STS GetCallerIdentity 请求 URL,用于获取临时安全凭证。详细信息请参阅 GetCallerIdentity

集群认证流程

然后我们再来看看 IAM user/role 使用 kubectl 执行命令时的认证流程:

集群认证和授权流程

  1. 使用 kubectl 执行命令时,客户端会生成预签名的 STS GetCallerIdentity URL,并使用整个 base64 编码的 URL 作为身份验证令牌与请求一起发送到 EKS API server。
  2. EKS API server 利用 Webhook Token Authentication 机制为收到的每个请求调用 aws-iam-authenticator 组件进行 TokenReview。
  3. 如果 token 内容以“k8s-aws-v1”开头,aws-iam-authenticator 会调用 token 正文中嵌入的预签名的 URL,执行 AWS STS GetCallerIdentity 请求,确认用户的 IAM 信息。
  4. 一旦用户的身份通过 AWS IAM 服务的身份验证,会将用户的身份信息返回给 kube-apiserver 从而完成认证过程,至于 aws-iam-authenticator 的处理细节我们会在下一个章节展开。

接下来我们将探讨 EKS API server 如何确定每个传入请求应具有哪些权限以及作为集群管理员应该如何配置具体的访问权限,分为使用 aws-auth ConfigMap 和 Cluster access mangement 两种方式。

使用 aws-auth ConfigMap 方式(deprecated)

在 2023 年 11 月之前,管理 Amazon EKS 集群中管理权限的方式是通过配置集群 kube-system 命名空间中名为 aws-auth 的 ConfigMap,该 ConfigMap 负责将 AWS IAM 身份映射到 Kubernetes 基于角色的访问控制(RBAC)授权从而控制对集群资源的访问。aws-auth ConfigMap 是在创建 Amazon EKS 集群时自动创建的,而且集群创建者的 IAM ARN 信息会被硬编码到集群的 aws-iam-authenticator 配置文件(用户不可访问),并关联 system:masters 的 Kubernetes group 从而让集群创建者拥有集群的最高控制权限。集群创建完成后,管理集群权限的方式就是直接或间接的方式配置 aws-auth ConfigMap 中 IAM principal 和 Kubernetes group 的映射关系,而集群内部具体的 RBAC 权限则需要通过与 API Server 交互进行配置

aws-auth ConfigMap

示例如下:

apiVersion: v1
data:
  mapRoles: |
    - groups:
      - system:bootstrappers
      - system:nodes
      rolearn: arn:aws:iam::<AWS_ACCOUNT_ID>:role/kube-system-<SELF_GENERATED_UUID>
      username: system:node:{{SessionName}}
  mapUsers: | 
    - groups:
      - system:masters
      userarn: arn:aws:iam::<AWS_ACCOUNT_ID>:user/a-user
      username: admin     
kind: ConfigMap
metadata:
  creationTimestamp: "2023-10-22T18:19:30Z"
  name: aws-auth
  namespace: kube-system

在这个 ConfigMap 的 data 字段定义了 IAM principal 和 Kubernetes group 的映射关系,主要配置参数有:

  • groups:要将 IAM 角色映射到的 Kubernetes group。可以是默认组,也可以是在 clusterrolebinding 或 rolebinding 中指定的自定义组。
  • rolearn/userarn:将 AWS IAM role 或者 user 的 ARN 映射到添加的 Kubernetes group。
  • username:Kubernetes 中映射到 AWS IAM role 或者 user 的用户名。这可以是任何自定义名称。

使用 ConfigMap 方式的认证和授权流程

集群认证和授权流程

  1. 集群管理员通过 aws-auth ConfigMap 配置 IAM role/user 与 RBAC group 的映射关系。
  2. 使用 kubectl 执行命令时,客户端会生成预签名的 STS GetCallerIdentity URL,并使用整个 base64 编码的 URL 作为身份验证令牌与请求一起发送到 EKS API server。
  3. EKS API server 利用 Webhook Token Authentication 机制为收到的每个请求调用 aws-iam-authenticator 组件进行 TokenReview。
  4. 如果 token 内容以“k8s-aws-v1” 开头,aws-iam-authenticator 会调用 token 正文中嵌入的预签名的 URL,执行 AWS STS GetCallerIdentity 请求,确认用户的 IAM 信息。
  5. 如果用户的身份通过 AWS IAM 服务的验证,会根据命名空间 kube-system 中的 aws-auth ConfigMap 确定与请求者关联的 Kubernetes group,并返回给 API server 完成认证过程。
  6. API server 使用 RBAC 进行授权,确定请求者是否有执行该具体 Action 的权限。

但使用此方式会有以下问题:

  1. 创建集群的 IAM user 或 role 被赋予了 EKS 集群的 admin 权限,并且无法删除。这种不可撤销的授权允许创建者在集群的生命周期内执行任何管理操作,给安全管理带来挑战。
  2. 管理员使用 Amazon EKS API 创建集群,然后必须切换到 Kubernetes API 来管理 AWS IAM principals 与 Kubernetes 权限的映射,多步骤的过程使授予用户访问 Amazon EKS 集群的方式变得复杂也很难自动化。另外需要调用不同的 API(AWS 和 Kubernetes)来管理访问也增加了错误配置的可能性。

使用 Cluster access management 方式

为了解决以上问题,Amazon EKS 发布了 Cluster access management 功能改进了集群访问管理控制。集群管理员现在可以直接通过 Amazon EKS API 向 AWS IAM principal 授予对 Amazon EKS 集群和 Kubernetes 对象的访问权限,也可以在创建集群后删除集群创建者的集群管理员权限。

引入此功能后,Amazon EKS 支持三种身份验证模式:

Authentication mode Methods
1 ConfigMap only (CONFIG_MAP) aws-auth ConfigMap
2 EKS API and ConfigMap (API_AND_CONFIG_MAP) access entries和aws-auth ConfigMap,前者优先级更高
3 EKS API only (API) access entries

Cluster access management 这一新功能依赖于两个新概念:AccessEntry 和 AccessPolicy。每个 AccessEntry 是一个映射到 IAM principal 的用于认证的 EKS 集群身份,而 Amazon EKS AccessPolicy 是 AWS 托管的预定义的用于关联给 AccessEntry 的 Kubernetes 权限。

AccessEntry

AccessEntry 的默认类型为 Standard,指定唯一的 IAM pricipal(user 或 role)后,可通过关联 AccessPolicy 或者向 AccessEntry 中直接添加 Kubernetes Group 为其分配权限。一个 AccessEntry 可以关联 0个或多个 AccessPolicy,也可以配置 0 个或多个 Kubernetes Group,当同时关联 AccessPolicy 和 Kubernetes Group 时有效权限为二者的并集。

除 Standard 外,AccessEntry 还有 EC2 Linux、EC2 windows 和 Fargate Linux 三种类型用于为 EKS 工作节点分配权限。选择这三种类型时,会自动为 AccessEntry  绑定 system:node 的 Kubernetes group 且无法更改。

AccessPolicy

AccessPolicy 由 AWS 托管预置,用户不可编辑。可通过 aws eks list-access-policies 查看,本文撰写时已有 5 条托管策略可满足绝大多数使用场景,名称及对应的 Kubernetes 权限如下:

管理员通过为 IAM 身份创建 AccessEntry 并灵活关联预置的 AccessPolicy 或自定义的 Kubernetes RBAC 实现精细化的授权管理,也可删除集群创建时自动生成的关联 AmazonEKSClusterAdminPolicy 的 AccessEntry。

将 IAM 身份映射到 AccessPolicy

当 AccessPolicy 提供的权限可以满足需求时,可以直接通过创建 AccessEntry 绑定 AccessPolicy 的方式管理集群权限,详细配置请参考官方文档。此处仅以“需要指定 IAM role 对 app namespace 具备只读权限”的场景举例。

# 创建 AccessEntry
aws eks create-access-entry --cluster-name <CLUSTER_NAME> \ 
--principal-arn arn:aws:iam::<AWS_ACCOUNT_ID>:role/read-only-role

{
    "accessEntry": {
        "clusterName": "<CLUSTER_NAME>",
        "principalArn": "arn:aws:iam::<AWS_ACCOUNT_ID>:role/read-only-role",
        "kubernetesGroups": [],
        "accessEntryArn": "arn:aws:eks:<REGION>:<AWS_ACCOUNT_ID>:access-entry/<CLUSTER_NAME>/role/<AWS_ACCOUNT_ID>/read-only-role/60c75bf6-cfdd-3180-258c-c6f905999edd",
        "createdAt": "2024-04-07T17:39:47.355000+08:00",
        "modifiedAt": "2024-04-07T17:39:47.355000+08:00",
        "tags": {},
        "username": "arn:aws:sts::<AWS_ACCOUNT_ID>:assumed-role/read-only-role/{{SessionName}}",
        "type": "STANDARD"
    }
}

其中 kubernetesGroups 字段是一个列表,1 个 AccessEntry 可以关联 0 个或多个 Kubernetes Group

# 关联AccessPolicy:
aws eks associate-access-policy --cluster-name <CLUSTER_NAME> \
--principal-arn arn:aws:iam::<AWS_ACCOUNT_ID>:role/read-only-role \
--policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSAdminPolicy \
--access-scope type=namespace,namespaces=app

{
 "clusterName": "<CLUSTER_NAME>",
 "principalArn": "arn:aws:iam::<AWS_ACCOUNT_ID>:role/read-only-role",
 "associatedAccessPolicy": {
 "policyArn": "arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy",
 "accessScope": {
 "type": "namespace",
 "namespaces": [
 "app"
 ]
 },
 "associatedAt": "2024-04-07T17:40:59.813000+08:00",
 "modifiedAt": "2024-04-07T17:40:59.813000+08:00"
 }
}
# 切换到read-only-role进行验证
$ kubectl auth can-i get pods -n app
yes
$ kubectl auth can-i get svc -n app
yes
$ kubectl auth can-i get svc -n default

这里演示了为 IAM role 授予对指定 namespace 的只读权限,只需创建 AccessEntry 绑定目标 IAM role,再关联 AccessPolicy 并限制作用于指定 namespace 即可,当然也可以根据实际需求指定 Cluster 级别甚至绑定多个 AccessPolicy。这种方式相对简单直观,更重要的是可以直接使用 AWS API 统一管理 IAM 身份与 Kubernetes 集群权限的映射关系。

将 IAM 身份映射到 Kubernetes groups

当 AccessPolicy 提供的权限不能满足权限需求时,需要通过 Kubernetes RBAC 自定义权限,我们以“指定 IAM role 对 app namespace 仅具备 Pod 资源只读权限”的场景举例。

# 首先删除之前创建的AccessEntry
aws eks delete-access-entry --cluster-name <CLUSTER_NAME> \
--principal-arn arn:aws:iam::<AWS_ACCOUNT_ID>:role/read-only-role

# 创建自定义RBAC权限
 kubectl create role pod-reader --verb=get,list,watch --resource=pods -n app

kubectl create rolebinding pod-reader \
  --role=pod-reader \
  --group=pod-reader \
  --namespace app
  
# 重新创建 AccessEntry
aws eks create-access-entry \
  --cluster-name <CLUSTER_NAME> \
  --principal-arn "arn:aws:iam::<AWS_ACCOUNT_ID>:role/read-only-role" \
  --kubernetes-group pod-reader

{
    "accessEntry": {
        "clusterName": "<CLUSTER_NAME>",
        "principalArn": "arn:aws:iam::<AWS_ACCOUNT_ID>:role/read-only-role",
        "kubernetesGroups": [
            "pod-reader"
        ],
        "accessEntryArn": "arn:aws:eks:<REGION>:<AWS_ACCOUNT_ID>:access-entry/<CLUSTER_NAME>/role/<AWS_ACCOUNT_ID>/read-only-role/32c75c1e-4a88-b561-d07b-0f98054726f3",
        "createdAt": "2024-04-07T19:06:01.935000+08:00",
        "modifiedAt": "2024-04-07T19:06:01.935000+08:00",
        "tags": {},
        "username": "arn:aws:sts::<AWS_ACCOUNT_ID>:assumed-role/read-only-role/{{SessionName}}",
        "type": "STANDARD"
    }
}

# 查看当前IAM role关联的AccessPolicy
aws eks list-associated-access-policies --cluster-name <CLUSTER_NAME> \
--principal-arn arn:aws:iam::<AWS_ACCOUNT_ID>:role/read-only-role 

{
    "associatedAccessPolicies": [],
    "clusterName": "<CLUSTER_NAME>",
    "principalArn": "arn:aws:iam::<AWS_ACCOUNT_ID>:role/read-only-role"
}
# 可以看到此时未关联任何AccessPolicy

# 切换到read-only-role进行验证
$ kubectl auth can-i get pods -n app
yes
$ kubectl auth can-i get svc -n app
no
$ kubectl auth can-i get svc -n default
no

当预置的 AccessPolicy 无法满足权限需求时,我们可以通过 Kubernetes 原生的 RBAC 机制自定义权限再关联到 AccessEntry 中,当然也可以在使用 AccessPolicy 的基础上再关联 Kubernetes Group。这种方式允许我们灵活地根据业务需求,对  IAM principal 分配精细化的 Kubernetes 资源级别的权限。

使用 Cluster Access Management 方式的认证和授权流程

熟悉了 Cluster Access Management 的基础概念和使用后我们来梳理一下背后的认证和授权逻辑,在发布 Cluster Access Management 后,EKS 集群会在控制平面托管一个授权的组件 eks-authorizer,用于实现 authorization webhook,当 Kubernetes 配置了多个授权模块时,会按顺序使用每个模块。EKS 集群的授权链的顺序为:Node、RBAC、Webhook。

集群 API Server 启动日志

集群认证和授权流程

  1. 集群管理员通过 EKS AccessEntry API 配置 IAM role/user 与集群权限的映射关系。其中 AccessEntry 的信息包括 kubernetets group 关联的信息会同步给 iam-auth-authenticator,与 acess policy 的关联的信息会同步给 eks-authorizer
  2. 使用 kubectl 执行命令时,客户端会生成预签名的 STS GetCallerIdentity URL,并使用整个 base64 编码的 URL 作为身份验证令牌与请求一起发送到 EKS API server。
  3. EKS API server 利用 Webhook Token Authentication 机制为收到的每个请求调用 aws-iam-authenticator 组件进行 TokenReview。
  4. 如果 token 内容以“k8s-aws-v1”开头,aws-iam-authenticator 会调用 token 正文中嵌入的预签名的 URL,执行 AWS STS GetCallerIdentity 请求,确认用户的 IAM 信息。
  5. 如果用户的身份通过 AWS IAM 服务的身份验证,会根据 AccessEntry 的信息确定与请求者关联的 accessEntryArn 和 Kubernetes group(如果有),返回给 API server 完成认证过程。
  6. EKS API server 首先使用 RBAC 进行授权,如果通过则执行请求操作;否则再使用 eks-authorizer 进行授权。

 aws-auth ConfigMap 和 AccessEntry 同时配置

当 AccessEntry 和 aws-auth ConfigMap 中引用同一 AWS principal 时,API Server 仅考虑通过 AccessEntry 授予的权限,忽略 aws-auth ConfigMap 的配置。 例如,即使 aws-auth ConfigMap 授予管理员权限,但如果 AccessEntry 只授予了只读访问权限,则该 principal 的有效权限仅为只读。

总结

本文我们结合 aws-auth ConfigMap 和 Cluster Access Management 两种方式阐述了基于 IAM 的 Amazon EKS 集群访问权限管理,配置 aws-auth ConfigMap 的方式已被新引入的 Cluster Access Management 功能所取代,后者引入了 AccessEntry 和 AccessPolicy 两个核心概念。集群管理员现在可以直接通过 EKS API 实现更加安全、灵活且易于自动化的集群访问权限管理。如果您需要将 aws-auth ConfigMap 迁移至 AccessEntry,请确保先将您的集群更新为使用 API_AND_CONFIG_MAP 身份验证模式,然后再根据文档完成迁移。

下一篇我们继续探讨如何管理 EKS 集群应用访问 AWS 服务的权限。

参考链接

https://aws.amazon.com/cn/blogs/containers/a-deep-dive-into-simplified-amazon-eks-access-management-controls/

https://aws.github.io/aws-eks-best-practices/security/docs/iam/

本篇作者

胡凯

亚马逊云科技解决方案架构师,负责基于亚马逊云科技的云计算方案架构的咨询和设计,致力于亚马逊云科技云服务在企业的应用和推广。曾任职于 Rancher Labs,在容器及云原生领域拥有丰富的实践经验。

杜晨曦

亚马逊云科技解决方案架构师,负责基于亚马逊云科技云计算方案架构的咨询和设计,在国内推广亚马逊云科技云平台技术和各种解决方案。