OIDC 协议及其在 Kubernetes 中的运用

2019/7/9 posted in  Kubernetes

K8s 中的认证机制大多都是用 ServiceAccount 来做的,虽然 K8s 有 User 的概念,但没有一种资源与“人”对应,所以在 K8s 里做用户管理还是很困难的。好在 K8s 对于用户管理提供了另一种方式,即对接 OIDC 协议。本篇文章我们就来了解一下什么是 OIDC 协议,及其在 K8s 中的运用。

OIDC 协议

OpenID Connect 协议(OIDC 协议)是基于 OAuth 2.0 协议的身份认证标准协议,在 OAuth 2.0 上构建了一个身份层。在介绍 OIDC 协议之前,我们先来看看 OAuth 2.0 协议。

OAuth 2.0 协议

OAuth 2.0 协议主要适用的场景是第三方应用登录,比如我们新登录一个网站,就可以不注册账号,而使用 Github 账号登录。

OAuth 2.0 协议在客户端和服务器之间定义了一个授权层,客户端只需要根据获取的 token 来认证登录即可,不需要存储密码信息。具体流程如下:

这一套流程不难理解,重点在于相比普通的账号密码登录,多了一层以 code 换取 token 的步骤。

这一步看似十分多余,然而保证了安全性,因为从 OAuth Server 跳转到 Client 这一步,code 需要放置在 URL 参数中,若直接将 token 放在 URL 参数中传给 Client,相当于把钥匙公开给整个互联网。所以在 OAuth 2.0 协议中,将 token 传输放在了后面一步,即将 token 放置在 Response body 中。

OIDC 协议的流程

总的来说,OAuth 2.0 协议只提供了授权认证,并没有身份认证的功能,而这一缺陷就由 OIDC 协议补上了。OIDC 协议又构建了一个身份层,具体流程如下:

可以看到,当 Client A 要求登录时,OIDC Server 会检测到该用户没有登录,从而启动类似 OAuth 流程的登录过程;而当同一个用户在 Client B 要求登录时,OIDC Server 会检测到该用户已经登录了,继而开始后面的流程。

而 OIDC 的登录过程与 OAuth 相比,最主要的扩展就是提供了 ID Token,下面我们进一步来熟悉一下 ID Token。

ID Token

ID Token 是一个安全令牌,其数据格式满足 JWT 格式,在 JWT 的 Payload 中由服务器提供一组用户信息。其主要信息包括:

  1. iss(Issuer Identifier):必须。提供认证信息者的唯一标识。一般是一个 https 的 url(不包含q uerystring 和 fragment 部分);
  2. sub(Subject Identifier):必须。iss 提供的用户标识,在 iss 范围内唯一,它有时也会被客户端用来标识唯一的用户。最长为 255 个 ASCII 字符;
  3. aud(Audiences):必须。标识 ID Token 的受众。必须包含 OAuth2 的 client_id;
  4. exp(Expiration time):必须。过期时间,超过此时间的 ID Token 会作废;
  5. iat(Issued At Time):必须。JWT 的构建时间;
  6. auth_time(AuthenticationTime):用户完成认证的时间;
  7. nonce:客户端发送请求的时候提供的随机字符串,用来减缓重放攻击,也可以来关联 ID Token 和客户端本身的 Session 信息;
  8. acr(Authentication Context Class Reference):可选。表示一个认证上下文引用值,可以用来标识认证上下文类;
  9. amr(Authentication Methods References):可选。表示一组认证方法;
  10. azp(Authorized party):可选。结合 aud 使用。只有在被认证的一方和受众(aud)不一致时才使用此值,一般情况下很少使用。

除了上述这些,ID Token 的用户信息还可以包含其他信息,由服务器端配置。

另外 ID Token 必须进行 JWS 签名和 JWE 加密,从而保证认证的完整性、不可否认性以及可选的保密性。

K8s 中使用 OIDC 的原理

在了解了 OIDC 协议的流程及原理后,我们再来看 K8s 中使用 OIDC 的原理。

首先先来看看 K8s 中使用 OIDC 的流程:

可以看到,APIServer 本身与 OIDC Server(即 Identity Provider)并没有太多交互,需要我们自己获取到 ID Token 后,将其写入 Kubectl 的配置,由 Kubectl 使用 ID Token 来与 APIServer 交互。

此外,除了 ID Token,我们还需要将 refresh Token 提供给 Kubectl,而 refresh Token 则是用来在 ID Token 过期后自动重新获取 ID Token 的。

APIServer 拿到 ID Token 后,有以下几个步骤:

  1. 检查其是否 JWT 格式;
  2. 判断 Token 是否过期(根据 iat 和 exp);
  3. 该用户对此操作是否有权限。

而 APIServer 如何根据 ID Token 知道是哪个用户的呢?这也需要我们在 APIServer 的配置中指定 ID Token 中的对应字段。

另外需要注意的是,虽然在 OIDC Server 中可以做到用户的权限管理,但由上述过程我们也可以发现,K8s 并不认 OIDC Server 的权限管理。因为 K8s 已经有一套非常完善的 RBAC 体系,我们将权限控制管理的步骤留在 K8s 集群内就可以了。