Kubernetes 身份管理:身份验证

原创:Marc Boorshtein

编译:小君君(才云)

当你完成 Kubernetes 部署时,你应该如何安全地将它交到开发人员和管理人员手中呢?

与其他复杂的系统一样,Kubernetes 拥有自己的安全模型来与用户、系统进行交互。在本文中,我将介绍 Kubernetes 的一些身份验证方案,并提供有关管理集群访问权限的示例和建议。

身份对 Kubernetes 意味着什么

首先,你需要明白在 Kubernetes 中,“什么是身份”?Kubernetes 与大多数其他系统和应用程序截然不同。它是一组 API,没有 “Web 界面”、没有“登录”、“会话”或“超时”等选项框。每个 API 请求都是唯一且不同的,并且它必须包含 Kubernetes 验证和授权请求所需的所有内容。

也就是说,你要牢记这样一个道理:Kubernetes 的用户不会存在于任何持久状态中。你不需要将 Kubernetes 连接到 LDAP 目录或 Active Directory 中,但是每个请求都必须存在一种方法可以对 Kubernetes  ASSERT(断言)身份。我将 ASSERT 大写,是因为它将在后文变得十分重要。Kubernetes 不会对用户进行身份验证,它验证的是断言。

服务账户  

服务帐户正好与我们刚刚提到的规则相反。Kubernetes 不存储有关用户的信息,但它存储服务帐户的信息(该信息为任何非人的事物)。在 Kubernetes 中,与任何内容交互的东西都会被作为服务帐户运行。例如,如果你要提交一个非常基本的 Pod:

在部署后的 Kubernetes 中,你可以通过运行   kubectl get pod myapp-pod -o yaml   来查看它:

你会发现这里存在   serviceAccount    serviceAccountName  属性,这两者都是 default。此服务帐户由入口控制器链为使用者注入,你可以在 Pod 上设置自己的服务帐户。

服务帐户易于创建和使用,但它们有一些缺点:

  • 服务帐户的令牌是一个很长的字符串,需要被记录下来。如果处理不当,它就有可能会被利用;

  • 授权服务帐户的唯一方法是通过 RBAC 进行绑定;

  • 在服务帐户还在使用期间时,如果一个人泄露了令牌并且未被发觉。它可能会被一直滥用,直到被处理为止。

在日常工作中,如果你的应用程序在一个 Pod 中运行并且需要与 API 服务器通信,你就可以通过挂载到 Pod 上的一个 secret 来检索 Pod 的服务帐户。

通过上面示例 YAML,你会发现一个挂载的卷被添加到了 /var/run/secrets/kubernetes.io/serviceaccount 中,这其中有一个令牌文件包含 Pod 的服务帐户令牌。你需要注意:不要将服务帐户令牌嵌入到集群中运行的 Pod 的 secret 或配置中!因为这样的做法会使得你使用旋转令牌(rotating token)变得更加困难,并且难以管理。

用户账户  

Kubernetes 不连接任何类型的用户存储(至少不直接连接)。这意味着在每个请求中,你必须为 Kubernetes 提供足够的信息以验证调用者的身份。Kubernetes 并不关心你如何建立身份,它只关心它如何证明身份是有效的。

Kubernetes 如何知道你是谁

OpenID Connect  

OpenID Connect 是你用来验证用户身份的选项:

  • OpenID Connect 令牌具有短暂性。当该类令牌被拦截或泄露时,如果攻击者得知该令牌的字符串,那么令牌就没用了;

  • 使用 OpenID Connect 的好处是:Kubernetes 永远不会拥有用户的凭据,它不可能泄漏 Kubernetes 没有的东西;

  • OpenID Connect 提供的用户标识不仅可以提供用户名信息,还可以提供组信息。这使得它们通过 LDAP 目录或外部数据库管理访问变得更容易(你无需为单个用户创建 RBAC 绑定);

  • 通过在 Kubernetes 和身份层之间添加“代理”,你可以更轻松地添加多种类型的身份验证,例如多因素身份验证;

  • 大量的开源 OpenID Connect 可以与 Kubernetes 一起使用。

OpenID Connect Primer  

在深入研究如何使用 OpenID Connect 之前,你需要先了解一下该协议:

  • OpenID Connect 是一个建立在 OAuth2 之上的断言生成协议;

  • OAuth2 则是一个用于传输承载令牌授权的协议。

这两点似乎缺少一个词:认证!那是因为 OpenID Connect 不是身份验证协议。它不关心你如何进行身份验证。如果用户使用的是用户名和密码,那么一张智能卡或许看起来真的很值得信赖,但是 OpenID Connect 是一种用于生成、检索和刷新用户断言的协议。用户如何进行身份验证最终取决于 OpenID Connect 的实现。

OAuth2 是一个比较关键的协议,它可以用于传递令牌,但是它没有定义令牌是什么或如何使用。它只是定义了令牌和依赖方之间如何传递令牌。

Kubernetes 如何使用 OpenID Connect

图 1 显示了 Kubernetes 身份验证页面:

图 1 Kubernetes OpenID Connect 流程

以下为该图的基础知识点:

  • 用户可以登录到用户身份的提供程序中;

  • 身份提供者会生成一个   id_token  和一个 refresh_token

  • id_token  可以将用户的身份断言为 Kubernetes 的;

  •   id_token   过期时, refresh_token   用于生成新的 id_token

一个   id_token  是一个 JSON Web Token(JWT),它可以表示:

  • 用户是谁;

  • 用户所属的组(可选);

  • 令牌有效时长;

  • 它包含一个数字签名,用于验证 JWT 是否未被篡改。

用户的 ID 属性 sub 通常是用户的唯一标识符。用户经常会使用 Active Directory 的登录 ID(也称为 samAccountName),还有一些人更喜欢使用电子邮件地址。一般来说,这不是最佳的做法。用户的 ID 应该是唯一的和不可变的。虽然电子邮件地址也是唯一的,但它并不总是不可变的(例如,有时名称会更改)。

JWT 将在从   kubectl  到 Kubernetes 的每个请求上传递。其中 id_token   被称为 “承载令牌”,因为它会授予承载者访问权限而无需任何额外的检查。这意味着,如果 API 调用流程中的系统泄漏此令牌,攻击者就可以滥用该系统了。

因为这些令牌很容易被滥用,所以它们的寿命极短。我推荐它们的使用寿命在一分钟左右。这样,如果当一个令牌被泄露时,即使攻击者知道它是什么,但是令牌也已经过期。使用此类短期令牌,最重要的是在它过期后,系统会配置一个 refresh_token   去更新你的  id_token

kubectl  知道如何通过使用 refresh_token   调用标识提供者的授权服务 URL,并更新  id_token   令牌。

refresh_token   是 Kubernetes 的 API 服务器永远不会使用的令牌,用户可以将它视为 secret。该令牌用于获取新的 JWT,此时可以使用 JWT 中新的  refresh_token 。如果   id_token  的生命周期非常短,则   refresh_token   超时与非活动超时类似,通常为 15-20 分钟。这样,你的 Kubernetes 实现将符合企业中针对不活动超时的策略。使用一个   refresh_token   获取一个新   id_token   内容比使用更长时间更安全,因为这个 refresh_token   意味着以下内容:

  • 它只能使用一次。一旦使用,就会生成一个新的令牌;

  • 它只在用户和身份提供者之间传递,在极少情况下会被泄露;

  • 如果它自己被泄漏了,就不能被用来识别你。因为它是不透明的,所以如果没有额外的信息,攻击者根本不知道该如何处理它。

Kubernetes 仪表板

仪表板没有自己的登录系统。所有这一切都可以使用代表用户的现有令牌。这意味着在仪表板前面放置了一个反向代理,它将 id_token 在每个请求上注入 ,然后反向代理会根据需求刷新令牌。

我应该使用哪个身份提供商

在选择身份提供商时,Kubernetes 实际上只有两个要求:

  • 它必须支持 OpenID Connect 发现;

  • 它提供了一种生成令牌并将其注入到 〜/.kube/config 的机制中。

只要符合以上两个要求,你就可以使用了。因为这种类型的身份提供商,它使你不必手动告诉 Kubernetes 不同的 URL 在哪里,用于签名的密钥是什么 …… 将 Kubernetes 指向所有具有这种信息发现的 URL 就容易多了。因为大多数身份提供商都支持开箱即用(通用标准)。

而关于如何从你的登录点(通常是 Web 浏览器)将令牌信息添加到 〜/.kube/config 中,可以有不同的方法。

Web 浏览器注入 

在此模型中,所有内容都集中在你的 Web 浏览器上。你可以通过浏览器进行身份验证,然后获得正确设置 kubectl 客户机的命令。例如,OpenUnison(我们自己的项目)为你提供了一个命令,该命令可用于身份验证后设置集群的配置(图 2)。

图 2 浏览器令牌

你可以使用   kubectl   的内置功能从命令行配置配置文件以完成设置。

这种方法有几个优点:

  • 浏览器具有最多的身份验证选项。除了用户名和密码,你还可以集成 Kerberos、Multi-Factor 等;

  • 你不需要管理复杂的 Kubernetes 配置;

  • 这种方法适用于 stock kubectl 命令。

Kubectl 插件

你可以使用插件扩展   kubectl  命令。使用插件,你可以收集用户的凭据,然后生成令牌。我看到过一些插件可以从 CLI 收集到你的凭证,还有一些插件可以启动浏览器提示你登录。从 CLI 角度来看,这种方法很好,因为它可以让你的 CLI 驱动你的用户体验。这种方法的主要缺点是需要在每个工作站上安装插件。

下载配置

使用此方法,身份提供程序(或自定义应用程序)可以为你提供用于下载的完整生成配置文件。但是,如果你没有将某些内容保存到正确的位置上,就可能会产生支持问题。

选择身份提供者后,请按照其说明进行集成,其中关键项是发现 URL、标识符“claim”以及组的“claim”。

X509 证书 

证书身份验证利用客户端(通常是 kubectl 命令)和 Kubernetes API 服务器之间的 TLS 握手,通过向 API 服务器提供证书来声明身份。除了一个用例之外,这种方法并不是 “最佳实践”:

  • 证书不能在 Kubernetes 中撤销。你需要等到证书过期或重新生成整个集群后才可以撤销;

  • 证书的私钥永远不离开生成它的安全介质。通常情况下,你会一起“获得”密钥对和证书;

  • 使用带证书的组很困难,你需要将它们嵌入到 subject 中。

你可以使用 X509 证书来进行身份验证的情形是:当你在引导集群时,或者在紧急情况下,你的身份提供程序不可用。

大多数发行版都会为每个主服务器部署密钥对。因此,如果你 ssh 进入该主服务器,则可以使用它 kubectl 管理集群。这意味着你需要锁定对主服务器的访问权限。

Webhook  

此方法允许你通过 webhook 集成第三方登录或令牌系统。Kubernetes 不会告诉你 Kubernetes 如何验证身份,而是调用 webhook 并询问“这是谁”。

除非你是云提供商并拥有自己的身份解决方案,否则请勿这样做。几乎我见过的每一个实现都变成了一个糟糕的 OpenID Connect。

带模拟的反向代理  

这里客户端(kubectl 或其他)不直接与 API 服务器通信。它会与反向代理进行通信,然后反向代理会将头部注入请求以表示用户。这通常被当作处理高级身份验证的一种方法,因为从 API 服务器的角度来看,它需要的工作量最少。实施步骤如下:

  • 创建服务帐户;

  • 授权服务帐户进行模拟;

  • 配置反向代理以将服务帐户和模拟标头注入到每个请求中。

这个方案现有的标准可能会更适合用户的需求,因为它易于管理和维护。

如何将身份整合到 Kubernetes 中

要将身份整合到 Kubernetes 中,请遵循以下基本核对清单:

  • 仅将服务帐户用于系统,而不是人员;

  • 建议使用 OpenID Connect。因为它会受到多种系统的审核和支持,包括开源和专有系统;

  • 仅对“紧急情况”使用证书认证。

遵循这些规则,你会发现团队中的开发人员会很乐意记住身份验证的密码,安防人员也会很高兴地遵守合规性的要求。

参考文献:

1.https://www.linuxjournal.com/content/kubernetes-identity-management-authentication