aws eks身份认证体系解读

EKS的几种身份认证策略

  • Service account tokens: service account看做是由k8s的control plane分发给pod的专属的security token(本质是个bearer token),而api server就是通过查看请求中包含的这个security token来识别身份信息的。
  • OpenID connect tokens: 建立在OAuth2.0协议上的身份认证,本质是个jwt token。EKS外部的可信实体(例如google、aws、github)可以定义成OIDC 身份供应商(OIDC IDP),外部用户可以通过OIDC IDP来提供身份认证访问k8s api server
  • Webhook token authentication: 主要用于EKS集群外部用户访问 k8s api server,通过携带有效的token来做身份认证。不依赖K8S内部的service account机制,将token生成委托给外部服务,用户可以自定义token生成。
  • 基于OIDC的IRSA(IAM roles for service accounts): 主要用于EKS数据面访问AWS资源时进行身份认证。允许service account关联一个IAM实体

service account

基本原理

Service Account Tokens是Kubernetes内部的一种身份验证机制。Service Account是Kubernetes的内置对象,它可以用来让Pods访问Kubernetes API。
这种机制的工作方式大致如下:

  1. 当Service Account被创建时,Kubernetes会为它自动创建一个关联的Secret对象。这个Secret包含一个用于身份验证的bearer token。
  2. 当创建Pod时,可以指定Pod运行在某个Service Account的上下文中。这样,Pod就可以访问这个Service Account的token。
  3. Kubernetes的API server会检查来自Pod的API请求中的bearer token,来确认Pod的身份并授权相应的API访问权限。
  4. Pod可以使用这个token来与Kubernetes API server进行通信,获取资源、修改状态等。

在一些场景下,这种机制很有用。例如,你可能有一个需要读取Kubernetes API中信息的应用,这个应用可以在Pod中运行,并使用Service Account的token来访问API。
值得注意的是,Service Account Tokens的权限取决于Service Account被赋予的角色(Role)或集群角色(ClusterRole)。默认情况下,Service Account只有很少的权限,但可以通过RoleBinding或ClusterRoleBinding来增加它的权限。

使用场景

该token主要用于EKS集群内部POD的身份认证,主要是pod和api-server之间的交互 。因为token泄露的话比较不安全,外部用户访问k8s api server一般采用其他两种身份认证方式。

EKS上的处理

这个是K8S内部本身的机制,EKS的处理并没有什么不同。

Webhook token身份认证

基本原理

Webhook Token Authentication 不依赖于 Kubernetes 内置的 Service Account 机制,而是将 token 的验证过程委托给了一个外部的服务。这意味着你可以完全自定义 token 的生成、发放和验证过程,可以实现更多的安全控制和集成更多的身份源。

当 Kubernetes API server 收到一个请求时,如果这个请求包含一个 Bearer Token,且这个 Token 不是 Kubernetes 自己可以识别的(例如不是一个 Service Account Token),那么 API server 就会将这个 Token 发送给配置的 Webhook 服务器进行验证。Webhook 服务器会检查这个 Token,然后返回 Token 是否有效,以及 Token 对应的用户和组信息。

  1. 用户(或客户端程序)首先需要获取一个 Token。这个 Token 可以从任何你选择的身份提供者处获取,例如一个 OAuth2 服务器,或者一个企业的 SSO 系统。
  2. 用户使用这个 Token 来发起对 Kubernetes API 的请求。Token 通常在请求的 HTTP Authorization 头中以 Bearer Token 的形式提供。
  3. Kubernetes API server 收到请求后,将 Token 发送到配置的 Webhook 服务器。
  4. Webhook 服务器验证这个 Token。如果 Token 有效,Webhook 服务器返回 Token 对应的用户和组信息。
  5. Kubernetes API server 根据 Webhook 服务器返回的信息,以及 Kubernetes 的 RBAC 策略,来决定是否允许这个请求。

虽然 Webhook Token Authentication 也使用了 Token,但是它提供了更大的灵活性和控制能力。例如,你可以实现 Token 的自动过期和刷新,可以实现复杂的身份验证流程,可以集成更多的身份源,等等。当然,使用 Webhook Token Authentication 也需要确保 Token 的安全,例如,需要使用 HTTPS 来保护 Token 在网络中的传输,需要使用安全的方式来存储和处理 Token,等等。

使用场景

如果有自定义身份认证的需求,则可以通过webhook token的方式。

EKS上的处理

EKS属于aws云上的资源,aws对齐云上的资源采用其IAM体系管理。因此外部用户需要访问EKS集群做身份认证,必然是需要经过IAM的身份认证的。这种场景显然就比较适合AWS自己去实现一套webhook token的身份认证流程,这个流程包含了IAM身份认证的过程。大体基于webhook token的身份认证流程如下[1]:
image.png

  1. IAM user(需要是创建集群的IAM ROLE/USER)需要使用一个利用IAM服务产生的token发送请求至api server
  2. api server根据webhook选项的配置,将这个请求的authorization token首先转发给IAM Authenticator server
  3. IAM Authenticator Server会把这个token传给AWS STS服务进行身份核实,并传回身份信息认证结果

如何获取IAM服务产生的token

本质这个token也是个bearer token。获取到这个bearer token,外部用户才可以通过API或者kubeconfig文件来访问k8s api server。
如果通过命令行获取,可以采用:

1
aws eks get-token --cluster-name

如果采用代码获取,可以使用GetCallerIdentity

为什么必须使用创建集群的IAM实体使用webhook token?

上面也提到必须使用创建EKS集群的IAM实体来获取bearer token。在AWS 的EKS集群上,会涉及3个IAM实体:

  • 创建EKS集群的IAM实体
  • EKS集群本身使用的集群 IAM实体(cluster role)
  • EKS集群内部节点使用的 IAM实体(node role)

我们必须使用创建EKS集群的IAM实体来生成bearer token,负责是无法正常访问EKS集群的。AWS Authenticator server会对创建集群的IAM实体放行,而对于其他IAM实体,则都要通过configMap中的aws-auth来做身份认证。
官方文档关于这边的描述如下:

1
2
3
4
5
6
7
创建 Amazon EKS 集群时,将为创建集群的 IAM 主体自动授予 Amazon EKS 控制面板
中基于集群角色的访问控制(RBAC)配置中的 system:masters 权限。
该主体不会显示在任何可见配置中,因此请确保跟踪最初创建集群的主体。
要授予其他 IAM 主体与集群进行交互的能力,
请编辑 Kubernetes 中的 aws-auth ConfigMap,
创建 Kubernetes rolebinding 或 clusterrolebinding,
名为 aws-auth ConfigMap 中指定的 group。

EKS集群初始化的时候,会初始化一个aws-auth的configMap,只有这个configMap包含的IAM实体才有真正的K8S API访问权限(创建集群的IAM实体除外)。这个权限控制由上图中的 [AWS IAM Authenticator Server]来实现。

OpenID connect tokens

基本原理

这个也是K8S本身提供的机制。大体的流程如下:
image.png

使用场景

OIDC IDP可以避免用户分发账号、密码[5],通过一个可信的身份认证供应商来做身份认证。一般是一些标准化的服务商会提供OIDC IDP的方式来访问K8S集群,比如aws、google这类比较标准的云服务厂商。

EKS上的处理

K8S提供了OIDC相关能力的兼容,用户可以自己利用像keycloak[5]这样的产品自己创建OIDC IDP。大致步骤是:

  • 创建一个OIDC issuer: 有个公网的endpoint
  • 创建一个OIDC client(也称为被授予方): OIDC 客户端使用 OIDC 提供者的身份验证流程来验证用户的身份。它与 OIDC 提供者交互,获取包含用户身份验证结果的 ID 令牌。
  • k8s上配置关联issuer和client

AWS的EKS本身提供了内置的OIDC IDP的支持[7],也兼容一些像PingIdentity这样的合作伙伴。底下本质原理是一样的,只不过走AWS OIDC IDP,aws的OIDC IDP会去帮忙做身份认证。

值得注意的事,OIDC IDP仅仅负责的是身份认证,如果需要对应身份的IAM实体具备K8S数据面的权限,需要走k8s RBAC的授权体系,需要配置role、clusterrole和做一些role binding.

IRSA(IAM roles for service accounts)

基本原理

IRSA 的工作原理是通过 OpenID Connect (OIDC) 身份提供者来实现的。OIDC 是一个基于 OAuth 2.0 的身份验证协议,它允许客户端在进行身份验证时,可以请求和接收有关用户的信息。
具体过程如下:

  1. 当你在 EKS 集群中创建一个服务帐户,并为其关联一个 IAM 角色(通过annotation执行,后续访问AWS资源assume role会采用该角色)时,EKS 会将这个关联关系保存到该服务帐户的 Kubernetes Secret 中。这个 Secret 中会包含一个包含服务帐户信息的 JSON Web Token (JWT)。

image.png

  1. 当一个 Pod 使用这个服务帐户进行运行时,Kubernetes 会将这个 Secret 挂载到 Pod 中的一个特定位置。Pod 中的应用可以读取这个 JWT。

image.png

  1. 当 Pod 中的应用想要调用 AWS 的 API 时,它可以使用这个 JWT 作为凭证。AWS 的 SDK 和 CLI 都会自动在这个特定位置查找 JWT,并在调用 AWS API 时使用它。调用API时,都会向STS服务发起assume role请求来执行具体的API操作,这都会使用service account JWT token。(这也是为什么我们EKS配置IRSA使用的OIDC受众为sts.amazon.com的原因)

image.png

  1. 当这个 JWT 到达 AWS STS时,AWS STS会将 JWT 发送到 EKS 集群的 OIDC 身份提供者进行验证。OIDC 身份提供者会验证 JWT 的签名,以确保它是由 EKS 集群颁发的。
  2. 在 JWT 验证通过后,AWS 会根据 JWT 中的服务帐户信息,确定与之关联的 IAM 角色,允许service account 关联的POD扮演相应的AWS角色,执行aws API调用。

image.png

通过这种方式,你可以将特定的 AWS 权限精细地授予 Kubernetes 中的工作负载,而不需要在 EC2 实例上使用更宽松的 IAM 角色,或者在你的应用代码中硬编码 AWS 凭证。这不仅提高了安全性,也使得权限管理更加灵活和细粒度。

EKS的AWS权限问题诊断技巧

默认情况下,访问EKS权限给到的错误都是比较粗糙的,根本难以确定其真实的原因,如下是一个case。基本上我们从这种简略信息只能得到的信息是“没有权限”,但是很多情况都会造成这种现象。

1
You must be logged into the server (Unauthorized)

在EKS上如果需要排查这类问题,可以在EKS管理页面开启Authenticator日志,这样在CloudWatch服务对应日志组中,我们可以看到EKS具体的的权限拦截日志详情。其中比较重要的是查看aws authenticator server的日志。我之前遇到过一个bearer token格式问题引发的身份认证失败问题,就是通过这边提供的日志排查和定位的。
image.png

参考资料