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。
这种机制的工作方式大致如下:
- 当Service Account被创建时,Kubernetes会为它自动创建一个关联的Secret对象。这个Secret包含一个用于身份验证的bearer token。
- 当创建Pod时,可以指定Pod运行在某个Service Account的上下文中。这样,Pod就可以访问这个Service Account的token。
- Kubernetes的API server会检查来自Pod的API请求中的bearer token,来确认Pod的身份并授权相应的API访问权限。
- 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 对应的用户和组信息。
- 用户(或客户端程序)首先需要获取一个 Token。这个 Token 可以从任何你选择的身份提供者处获取,例如一个 OAuth2 服务器,或者一个企业的 SSO 系统。
- 用户使用这个 Token 来发起对 Kubernetes API 的请求。Token 通常在请求的 HTTP Authorization 头中以 Bearer Token 的形式提供。
- Kubernetes API server 收到请求后,将 Token 发送到配置的 Webhook 服务器。
- Webhook 服务器验证这个 Token。如果 Token 有效,Webhook 服务器返回 Token 对应的用户和组信息。
- 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]:
- IAM user(需要是创建集群的IAM ROLE/USER)需要使用一个利用IAM服务产生的token发送请求至api server
- api server根据webhook选项的配置,将这个请求的authorization token首先转发给IAM Authenticator server
- 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 | 创建 Amazon EKS 集群时,将为创建集群的 IAM 主体自动授予 Amazon EKS 控制面板 |
EKS集群初始化的时候,会初始化一个aws-auth的configMap,只有这个configMap包含的IAM实体才有真正的K8S API访问权限(创建集群的IAM实体除外)。这个权限控制由上图中的 [AWS IAM Authenticator Server]来实现。
OpenID connect tokens
基本原理
这个也是K8S本身提供的机制。大体的流程如下:
使用场景
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 的身份验证协议,它允许客户端在进行身份验证时,可以请求和接收有关用户的信息。
具体过程如下:
- 当你在 EKS 集群中创建一个服务帐户,并为其关联一个 IAM 角色(通过annotation执行,后续访问AWS资源assume role会采用该角色)时,EKS 会将这个关联关系保存到该服务帐户的 Kubernetes Secret 中。这个 Secret 中会包含一个包含服务帐户信息的 JSON Web Token (JWT)。
- 当一个 Pod 使用这个服务帐户进行运行时,Kubernetes 会将这个 Secret 挂载到 Pod 中的一个特定位置。Pod 中的应用可以读取这个 JWT。
- 当 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的原因)
- 当这个 JWT 到达 AWS STS时,AWS STS会将 JWT 发送到 EKS 集群的 OIDC 身份提供者进行验证。OIDC 身份提供者会验证 JWT 的签名,以确保它是由 EKS 集群颁发的。
- 在 JWT 验证通过后,AWS 会根据 JWT 中的服务帐户信息,确定与之关联的 IAM 角色,允许service account 关联的POD扮演相应的AWS角色,执行aws API调用。
通过这种方式,你可以将特定的 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格式问题引发的身份认证失败问题,就是通过这边提供的日志排查和定位的。
参考资料
- [1] 深入浅出的谈谈Amazon EKS的身份认证处理
- [2] How do I allow an additional AWS user to gain access to EKS?
- [3] error: You must be logged in to the server (Unauthorized)
- [4] [aws] Enabling IAM principal access to your cluster
- [5] An Illustrated Guide to OAuth and OpenID Connect
- [6] K8S官方文档-身份认证
- [7] 通过 OpenID Connect 身份提供商对集群的用户进行身份验证
- [8] [aws] 服务账户的 IAM 角色
- [9] Understanding IAM roles for service accounts, IRSA, on AWS EKS.