cover_image

自动化场景下的身份认证与凭据管理,Vault 是如何求解的?聊聊 AppRole 身份认证方案

弗怠 阿里技术
2024年03月08日 00:30

图片



这是2024年的第16篇文章

( 本文阅读时间:18分钟 )



本文介绍了 HashiCorp Vault 的 AppRole 身份认证方案,分析该身份认证方案的整体流程,并简单介绍其在不同场景下的落地方式,最后总结了自己的一些看法。适合了解 Vault 在自动化场景下,对于身份认证和凭据管理提供的解决方案。



01



vault是什么?

如果单提 Vault 大家可能没有什么印象,但聊到它的开发者 HashiCorp 大家可能就比较熟悉了,HashiCorp 的另一个非常有名的开源项目及产品正是 IaC 工具 Terraform。Vault 与 Terraform 相辅相成,都致力于提供自动化场景下的解决方案,因此 Vault 的产品定位之一,正是为自动化流程提供安全访问敏感数据的解决方案。


HashiCorp Vault 是一个企业级开源的密钥管理系统,通过身份认证和身份授权体系来控制、管理和保护对敏感数据的访问,如 API 密钥、密码、证书或其他机密信息。它支持多种云服务和基础设施平台,提供非常丰富的信息安全功能,包括静态和动态秘密管理、数据加密、身份认证和授权等,并提供了基于 UI、CLI 和 HTTP API 的多种操作界面。



02



Vault 的核心概念介绍

2.1认证(Authentication)和授权(Authorization)

「授权」和「认证」是信息安全和访问控制中比较常见的概念和术语,因为英文单词比较接近,所以在缩写时一般会以 authZ 来表示授权(authorization)操作,用 authN 来表示认证(authentication)操作。一般情况下,认证操作会先于授权操作,因为只有当用户证明了其身份后,才能使用该身份进行后续的访问操作。


通常来说,「认证」是确认用户身份的过程,简单理解就是需要验证用户是否是他们所声称的那个访问实体。认证时通常需要用户提供一个或多个凭证来证明其身份,比如密码、动态验证码、临时凭证等。而「授权」则发生在「认证」通过后,是决定已认证用户有权限可以访问什么资源,以及可以执行哪些操作的过程。在该过程中,系统会检查用户请求的资源或进行的操作,是否满足其具有的权限约束。

2.2策略(Policy)

策略是 Vault 中的一种规则集,用于定义身份对 Vault 资源的详细访问控制。通过定义和施加策略,决定了当前身份(用户、应用程序或服务)可以执行哪些操作,可以访问哪些路径,规定了什么样的行为是被允许的,什么样的行为是被禁止的。


键值凭据引擎的策略示例

path "secret/data/{{identity.entity.id}}/*" {  capabilities = ["create", "update", "patch", "read", "delete"]}
path "secret/metadata/{{identity.entity.id}}/*" { capabilities = ["list"]}

当用户完成身份认证后,其获取到的令牌会被施加一系列的策略,这些策略决定了他们认证成功后所具有的访问权限。同时,策略的施加和变更是动态隔离的,这使得当策略发生变更时,用户无需重新进行身份的认证操作,增加了访问权限控制的灵活性。

2.3令牌(Token)

令牌是 Vault 中的一个核心概念及模型,用于身份的认证和授权。Vault 令牌类似于其它系统中的会话令牌或访问令牌,用于标识一次会话,并且存在有效期,其作用是在用户或服务认证成功后,允许持有令牌的客户端访问 Vault 中存储的凭据和其它资源。


当用户或服务通过 Vault 认证方法完成身份认证后,Vault 会为其颁发一个令牌,该令牌会与一系列的策略相关联,这些策略定义了令牌持有者可以执行的操作,以及可以访问的路径。一般来说,令牌具有独立的生命周期,可以在生命周期内对其进行续订,也可以对其执行撤销操作。

2.4 应用角色(AppRole Role)

Role 角色是应用角色认证方案中的关键实体,可以通过将一组策略施加到角色上,从而使其具有相应资源的访问控制权限。Vault 通过预定义权限策略,并将角色和策略分别进行独立管理的方式,提升了系统管控的安全性,限制了通过该角色认证的实体能够访问的资源集合,以及访问时需要满足条件。


应用角色的粒度非常的灵活,可以为一个应用创建一个角色,也可以为一个应用下多个具有相同作用的机器实例创建一个角色,甚至可以为一台机器实例上不同的用户创建不同的角色。通过这种不同粒度的角色划分,能够为角色的管理和维护提供更大的灵活性和安全性。

2.5 角色标识(Role ID)和凭据标识(Secret ID)

Role ID 本文译为「角色标识」,它是 Vault 分配给特定 “角色” 的一个标识符,可以简单理解为账密认证模式中的用户名或账号,其不具有敏感性特征,因此无需进行单独的加密处理。


Secret ID 本文译为「凭据标识」,它是身份认证过程中的凭据标识,通常是由 Vault 自动生成的一串随机字符,可以简单理解为账密认证模式中的密码。凭据标识的用途是配合角色标识实现对角色身份的认证流程,因为其具有敏感性的特征,因此需要通过一些额外的安全手段来保证其安全性,如响应封装等。

2.6 封装的凭据标识(Wrapped Secret ID)

在 Vault 中,封装的凭据标识(Wrapped Secret ID)是通过 Cubbyhole 响应封装(Cubbyhole Response Wrapping)技术来获取的,其本质是一个封装的令牌(Wrapped Token),因此通过对其执行解封(Unwrap)操作,可以获取到原始的 AppRole 凭据标识(Secret ID)。


Cubbyhole 响应封装技术并非是一个仅针对凭据生效的凭据管理方案,而是一个能够对所有请求响应进行封装的通用安全性解决方案,调用方通过在请求时携带指定的配置参数,就能实现对请求响应的封装,从而获取到一个临时的一次性的封装令牌(Wrapped Token)。而原始响应的获取,则需要使用方通过对该封装令牌执行解封操作(Unwrap)才能完成,该操作既可以通过 API 调用来实现,也可以通过 CLI 来实现。从这个角度来看,Vault 的 Cubbyhole 响应封装技术,更像是为用户提供了一个请求响应的暂存密码箱,而密码箱的钥匙就是封装令牌(Wrapped Token)。所以总结来说,该响应封装技术通过引用响应原始值,而非传输响应原始值,最终实现了对原始响应的安全性保护。



03



应用角色认证(AppRole AuthN)是什么

应用角色(AppRole)认证方案是 Vault 提供的一种身份认证机制,是针对 M2M(Machine-to-Machine)和 A2M(App-to- Machine)场景提出的一种身份认证解决方案,专门针对自动化的工作流程而设计,目的是为了提升非人为操作场景下获取凭据的安全性和灵活性,从而使得这些工作负载或自动化工具也能够安全的获取和管理凭据。


应用角色认证方案主要通过角色标识(Role ID)和凭据标识(Secret ID)这两个核心要素来完成认证流程。角色标识相当于用户名,用于标识一个特定的角色,凭据标识相当于密码,用于配合角色标识完成身份的认证流程。当应用或自动化工作尝试获取存储在 Vault 服务端中的凭据时,它可以通过提供角色标识和凭据标识来完成身份认证登陆,从而获取一个用于后续操作所使用的令牌。


需要注意的是,应用角色认证方案不是一个受信任的第三方身份认证器,而是一个受信任的代理系统(trusted broker)。这两者之间的不同之处在于,在应用角色的身份认证中,信任(trust)的责任和关键在于一个安全管理的代理系统,该系统在客户端和 Vault 之间代理身份的验证。



04



应用角色认证的核心原则

缩小身份的爆炸半径

在上文有提到过,Vault 是一个基于身份的秘密管理解决方案,在该方案中,对秘密的访问需要基于客户端已认证的身份才能够完成。因此,通过 Vault 进行身份认证的身份必须是可识别的,并且验证完成后,该身份应当只能访问其关联用户的秘密。同时,永远不应该在 Vault 和凭据终端用户(end-user)之间代理传输凭据,客户端永远不应该有权限访问其它终端用户的凭据。


根据 “最小权限原则” 的指导,应当尽可能缩小和减少身份被施加的策略及权限,从而最大限度降低身份的爆炸半径。


缩短认证的有效时间

当 Vault 认证实体的身份时,如果身份认证通过,Vault 会为该实体颁发一个令牌。因为客户端与 Vault 的所有后续交互,都需要使用该令牌来进行身份的认证,因此该令牌应该得到安全的处理,保证其仅具有有限的生命周期,只要能够满足其进行秘密访问操作的操作周期即可(或理解为令牌应该只在需要访问它授权访问的凭据时有效)。


标识仅在最终用户系统处相遇

从受信代理到 Vault 的身份认证期间,角色标识(Role ID)和凭据标识(Secret ID)只在需要使用密钥的最终用户系统(end-user system)上才会一起出现,这个原则是整套身份认证方案的核心。



05



方案是如何实现的?

图片

图1 - Vault 应用角色授权流程(引用自 Vault Blog)

5.1 下发角色标识到应用

Step 1:启用应用角色身份认证方法

正如核心概念中所介绍的,身份认证用于执行身份的认证操作,负责将一个身份和一组访问控制策略分配给用户。Vault 采用显示维护的方案对身份认证方式进行管理,所以当我们需要在系统中使用某种非默认启用的身份认证方式时,需要先对该身份认证方式进行启用操作。


认证方式的启用操作可以通过 API 或 CLI 来完成,并可以通过 path 参数来指定启用的具体路径,不同的路径对应着不同的认证实例,这使得管理员可以为不同的用户或用例,启用不同的认证策略和认证规则,如果不指定该参数,则使用默认的路径名。对于应用角色的认证方式来说,该启用操作可以通过执行以下命令来完成。


Vault 启用 AppRole 认证方式的命令

vault auth enable approle

Step 2:创建应用角色并施加授权策略

完成认证方式的启用后,需要为对应的应用或实例创建关联的角色,并施加适当的策略。在应用角色认证方式中,角色是一个核心的身份模型,它可以用于划分一组实例,这些实例可以是同一个应用下的全部实例,也可以是有语意的部分实例。角色可以被授予一组策略来控制其访问权限,它被授予的权限将用于管控当前应用或实例有权限访问哪些凭据。


角色是由管理员进行定义的,Vault 不感知角色的具体业务含义。因此,当两个应用或实例具有相同的角色时,它们将有相同的权限,能够去访问相同的凭证。所以从安全性的角度考虑,设置角色及其策略时应遵循 “最小权限原则”,尽可能小的划分每个应用或实例具有的角色和权限。当两个不同的应用均需访问相同的公共凭证时,可以通过划分 “公共策略” 和 “特性策略” 的方式,实现权限策略的区分和管理。


除此之外,Vault 还提供了其它的安全措施来保证应用角色的使用安全。比如,可以通过在创建应用角色时设置其凭证标识的有效期、访问次数和源 CIDR 限制等,来保证角色凭据标识(Secret ID)的安全性。


Step 3 ~ Step 5:获取并下发角色标识到应用中

第 3 步和第 4 步为管理员手动获取角色标识(Role ID),并将其下发到制品或静态配置中的过程。这里的制品一般是指待部署的镜像,而静态配置一般是指静态的配置文件。


而第 5 步则是角色标识从制品或静态配置,流转到动态应用运行时的过程。一般是指在通过可信平台进行配置、构建和发布后,动态运行的应用将能够从静态的配置中读取到该角色标识。

5.2 下发封装的凭据标识到应用

Step 6 ~ Step 7:获取临时的封装凭据标识

这一部分的工作是由可信的发布平台或可信的配置中心来完成的,因此在后续的所有操作之前,可信平台要先完成自身的可信认证,向 Vault 证明自己的合法性,这一流程在上图中没有体现,但会在下面 CI 流水线的场景中进行分析。当可信平台完成了自身的可信认证后,将会获取到具有指定权限策略的令牌,可信平台能够使用该令牌完成后续封装凭证的请求。


可信平台通过向 Vault 服务请求获取一个封装的临时凭据标识(Wrapped Secret ID),然后将其下发给应用进行使用。这里会发现可信平台请求并下发给应用的并非是凭据标识(Secret ID),而是一个封装的凭证标识(Wrapped Secret ID),其采用的是上述的  Cubbyhole 响应封装技术,当使用者需要获取到被封装的原始请求时,需要向 Vault 服务发起对该封装响应的解封(Unwrap)请求。按照 Vault 自身的设计来看,主要是为了实现以下几个目的:


(1)隐藏凭据标识

通过下发封装标识而非真实的凭据标识,能够避免凭据标识在多个中间系统间进行流转,从而降低可能发生泄漏的节点数量,提升凭据传输流程的安全性。


(2)缩短有效时间

通过 Vault 的响应封装机制,能够实现对 “响应有效期” 和 “凭据有效期” 的独立管理,从而能够在缩短敏感数据响应暴露时间的同时,保证凭据自身的有效期不受影响。


(3)防止数据篡改

响应封装机制具有的 “临时性” 和 “一次性” 为整条链路的安全性提供了进一步的保障,通过对封装凭据标识的解封结果进行校验,能够在封装标识被恶意非法解封后,得到及时的反馈。


Step 8:下发封装凭据标识到应用中

在获取到封装凭据标识后,可信平台需要将该标识下发到运行时应用中。在 CI 流水线场景下,该流程可以由发布平台将封装凭据标识设置为环境变量,而后应用启动时从环境变量中进行读取的方式来完成。在容器化场景中,可以通过容器 Volume 挂载的方式来完成该流程。


但不管是通过哪种方式进行下发,都需要保证它不是跟角色标识(Role ID)在同一渠道进行下发的,因为只有通过遵守这样的约定,才能实现 Vault 的安全性假设,即角色标识(Role ID)和凭据标识(Secret ID)应在且仅在最终需要使用凭据的用户处才会相遇。同时,应用应保证当封装的凭据标识使用完成后,及时对其进行清理,避免意外的泄漏。

5.3 应用请求客户端令牌

Step 9:解封获取凭据标识

完成前面的步骤后,应用此时已经同时具备了角色标识(Role ID)和封装的凭据标识(Wrapped Secret ID),满足了请求获取原始凭据标识(Secret ID)的条件。


聚焦到运行时应用,此时它需要完成的第一步就是「立即」将可信平台下发给它的封装凭据标识,还原为可直接使用的凭据标识(Secret ID)。在前面的步骤中,我们通过对 Cubbyhole 响应封装技术的简要介绍,知道如果应用想要获取到原始的响应,就需要对封装响应执行解封(Unwrap)操作,因此这一步就是对 “封装的凭据标识” 执行解封操作,从而获取到原始的凭据标识的流程。


这里应用需要尽快向 Vault 发起解封请求(封装凭据标识的异常解封情况,只有在第二次解封时才能获知),并且该操作应在封装失效前执行(封装标识是存在有效期的),而且只能执行一次(封装标识是一次性的)。一方面,如果此时解封失败,则意味着封装令牌可能已经发生了非法的拦截及消费,需要立即进行排查。另一方面,解封后应对解封响应中的创建路径进行校验,防止凭据发生非法的重封装操作。


Step 10 ~ Step 11:获取客户端令牌

完成解封操作获取到原始的凭据标识(Secret ID),此时应用已经同时具备了角色标识(Role ID)和凭据标识(Secret ID),满足了请求获取客户端令牌(Token)的前提条件。应用通过请求 Vault 服务端,使用角色标识和凭据标识完成登陆后,即可获取到后续请求操作所使用的令牌(Token),该令牌会具有应用后续请求所需凭据的权限策略。


Step 12:请求获取凭据

此时应用已经持有了访问令牌(Token),并且该令牌应具有相应的凭据操作权限(访问应用所需的凭证集合),此时应用就可以使用该令牌完成后续向 Vault 的凭据请求操作了。

5.4 流程总结

受信代理和应用与 Vault 的四次交互

在整个受信链路建立的过程中,受信代理和应用总共与 Vault 发生了 4 次交互操作,只有在全部的认证交互完成后,应用才能获取到用于后续请求使用的访问令牌。上述流程省略了受信代理(受信平台)的认证流程,这一流程在下面的 CI 流水线场景中会进行具体的介绍。总结来说,这 4 次交互可以概括为:


  1. 获取受信代理令牌:受信代理向 Vault 请求原始访问令牌,用于颁发后续应用使用的封装凭据标识;

  2. 生成封装凭据标识:受信代理根据应用的角色名称信息,向 Vault 请求对应的封装凭据标识;

  3. 解封获取凭据标识:应用使用受信代理下发的封装凭据标识,向 Vault 发起解封操作,兑换真实的凭据标识;

  4. 兑换应用访问令牌:应用使用预置的角色标识和解封获取到的凭据标识,向 Vault 兑换后续访问使用的令牌;


两种凭据标识

两种凭据标识是指 “封装的凭据标识(Wrapped Secret ID)” 和 “真实的凭据标识(Secret ID)”,它们的生命周期和配置彼此隔离,使用场景和解决问题也很不相同。“封装的凭据标识” 是一个短期有效的一次性标识,主要是为了解决 “凭据标识” 在传输过程中的安全性问题,本质上是一个基于响应封装技术构建的 “封装令牌(Wrapping Token)”。而真实的凭据标识则用于执行角色的身份认证,通过与角色标识的配合使用,完成角色的身份认证流程。



06



具体落地场景

6.1 CI 流水线场景:通过环境变量实现凭据标识的下发

图片

图2 - Vault 应用角色认证方式在 CI 流水线下的最佳实践

(引用自 Vault Blog)


CI Worker 初始化获取访问令牌

在 CI 流水线的应用角色认证场景下,作为核心的受信代理系统(trusted broker),CI Worker 承载着将凭据标识下发至 CI Job 的职责,并且作为整套授权认证系统的核心节点,CI Worker 理论上会经手所有下发到 CI Job 的凭据标识,因此它是整套认证系统的核心节点。


在整个使用流程中,CI Worker 的初始化是整个流程的起点,其核心步骤对应着上图中的第 1 步和第 2 步,即向 Vault 服务器进行身份认证,从而获取到认证后颁发的令牌。这一步骤是整个受信链的第一环,当 Vault 完成对 CI Worker 的身份认证后,标志着 Vault 已经信任了该 CI Worker,允许将信任链向下传递。


在 Vault 的官方文档中,官方推荐基于平台级的认证方式完成对 CI Worker 的身份认证,这里的平台级认证方法,一般来说就是基于可信的三方平台完成认证。但另一方面,在该文章的 “Vault returns a token” 部分,官方也表达了对 Vault 下发令牌到 CI Worker 这个流程安全性的放心,甚至表示管理员可以直接将 CI Worker 需要使用的令牌预先下发到 Worker 中,或者干脆直接硬编码角色标识和凭据标识即可。


而这种坦荡的前提是,开发者能够按照最佳实践来对角色的权限进行管理。在最佳实践中,Vault 向 CI Worker 颁发的令牌,应当仅具有获取封装凭据标识(Wrapped Secret ID)的权限(权限示例如下),这使得当仅具有 CI Worker 的令牌时,是无法独立完成 CI Job 中秘密的解密操作的。


获取封装凭据标识的策略示例

path "auth/approle/role/+/secret*" {  capabilities = [ "create", "read", "update" ]  min_wrapping_ttl = "100s"  max_wrapping_ttl = "300s"}

CI Worker 请求封装的凭据标识

CI Worker 完成初始化后,它已经获取到了具有后续操作权限的令牌,有权向 Vault 服务器请求获取待 CI Job 所需使用的被封装的凭据标识(Wrapped Secret ID)。这里对应着上图中的第 3 步和第 4 步,CI Worker 根据 CI Job 的角色,向 Vault 请求获取其对应的封装凭据标识。


这里需要注意两个概念,一个是 “封装的凭据标识”,通过上文的介绍可知,封装凭据标识的含义是为凭据标识增加了一个有效期,使得只有在有效期内,才能通过 `vault unwrap` 命令完成凭据的解封。这样的操作流程符合 Vault 的三个基本原则中的第二个,即尽可能缩短认证的有效时间,保证权限的闭环。


另一个概念是 “角色”,当 CI Worker 向 Vault 请求获取封装的凭据标识时,它所仅知的两个信息只有 CI 任务标识(CI Job ID)和应用角色的角色名称(Approle RoleName),此时 CI Worker 不知道任何关于 CI Job 的角色标识(Role ID)信息。因此,CI Worker 仅能通过应用角色的角色名称(Role Name),来向 Vault 请求封装的凭据标识。


这也是为什么 Vault 对 CI Worker 的令牌安全性如此之放心,因为 CI Worker 永远不会获取到角色标识(Role ID),仅能获取到封装后的凭据标识(Wrapped Secret ID)。按照 Vault 自己的叙述来说,这种安全机制的核心原则是,从代理到 Vault 的身份认证期间,角色标识和凭据标识仅在需要使用密钥的最终用户系统(end-user system)上才会一起出现。


具体的请求命令如下所示,wrap-ttl 是为本次申请的 “凭据标识” 设置了一个有效期,从而生成一个 “封装的凭据标识”,在当前示例中该有效期为 120 秒,而具体请求路径中的 my-role 则是本次请求的角色名称(Role Name),而非角色标识(Role ID)。


请求封装的凭据标识的命令示例

vault write -wrap-ttl=120s -f auth/approle/role/my-role/secret-id

CI Worker 将封装的凭据标识下发给 CI Job

第 5 步中,CI Worker 从 Vault 获取到封装的凭据标识,并会在完成 CI Job 的创建后,将封装的凭据标识以环境变量的形式下发到 CI Job 中,这样 CI Job 就可以从环境变量中获取该值了。请求封装的凭据标识及执行下发命令的示例如下所示,以 CI Job 的名称作为角色名称,向 Vault 请求其对应的封装凭据标识(有效期为 300 秒),然后再将其设置为任务的环境变量 WRAPPED_SID。


请求封装凭据标识,并通过环境变量将其下发示例

environment {   WRAPPED_SID = """$s{sh(                    returnStdout: true,                    Script: ‘curl --header "X-Vault-Token: $VAULT_TOKEN"       --header "X-Vault-Namespace: ${PROJ_NAME}_namespace"       --header "X-Vault-Wrap-Ttl: 300s"         $VAULT_ADDR/v1/auth/approle/role/$JOB_NAME/secret-id’         | jq -r '.wrap_info.token'                 )}"""  }

CI Job 解封被封装的凭据标识

CI Job 启动后,将会执行封装凭据标识的解封(Unwrap)操作,即将具有生命周期且被封装的凭据标识(Wrapped Secret ID)还原为真实有效的凭据标识(Secret ID),该步骤可以通过 `vault unwrap` 命令或其对应的 API 请求来完成,对应上面流程图中的第 6 步和第 7 步。需要注意的是,每个封装的凭据标识仅能被执行一次解封操作,以此保证整个流程的安全性。


CI Job 获取访问令牌

在完成封装凭据标识的解封操作后,CI Job 就获取到了真实有效的凭据标识(Secret ID),同时因为角色标识(Role ID)天然是由 CI Job 进行保管的,所以当前运行时已经同时具备了 Secret ID 和 Role ID,可以向 Vault 换取后续请求使用的访问令牌(Token),这一流程对应上图中的第 8 步和第 9 步。


执行完这一步骤后,整个认证的可信链路已经建立完成。可以发现,在整个流程中,正如 Vault 所说,封装的凭据标识(Wrapped Secret ID)由 CI Worker 动态下发到 CI Job 中,并在解封后才能获取到真实有效的凭据标识(Secret ID),而角色标识(Role ID)则只有 CI Job 自己可知,从而保证了角色标识和凭据标识仅在需要使用凭据的最终用户系统(当前为 CI Job)上才会一起出现,极大的保证了密钥的安全性。


CI Job 使用访问令牌执行凭据访问操作

最后,正如图中第 10 步所示,当 CI Job 通过角色标识和凭据标识换取到访问令牌后,该任务后续的请求操作均会基于该令牌来执行,其具有的访问权限也由该令牌进行控制。

6.2 容器化场景:通过容器挂载卷来下发凭据标识

应用获取凭据标识

容器化场景下的落地方案与 CI 流水线场景下的落地方案整体类似,只不过是将凭据标识的下发从环境变量替换为了容器挂载卷。整个流程如下图 3.1 和图 3.2 所示,图 3.1 为应用启动获取凭据标识的流程,图 3.2 为应用换取访问令牌并最终发起凭据请求的流程。下面概括一下图 3.1 中的流程:


  1. 在应用启动前完成角色标识(Role ID)的预置;

  2. 流水线向 Vault 服务器请求获取封装后的凭据标识(Wrapped Secret ID);

  3. 流水线将封装的凭据标识写入到能够映射至容器挂载卷的存储中;

  4. 应用启动时从容器挂载卷中读取封装的凭据标识;

  5. 应用向 Vault 服务器发起对封装凭据标识的解封请求(Unwrap);

  6. Vault 服务器将解封后的真实凭据标识(Secret ID)返回给应用。


图片图3.1 - 容器化场景下应用获取凭据标识的流程示意图


请求客户端令牌并查询凭据

图 3.2 的前提是应用已经同时获取到了角色标识(Role ID)和凭据标识(Secret ID),而后才能使用它们向 Vault 服务器发起访问令牌的兑换请求,下图的流程描述如下:


  1. 应用通过携带角色标识和凭据标识向 Vault 服务器发起登陆请求,获取访问令牌;

  2. Vault 服务器验证通过后,将角色对应的访问令牌返回给应用;

  3. 获取到访问令牌后,应用对容器挂载卷中的封装凭据标识进行清理;

  4. 后续应用可以该使用访问令牌发起对其有权限凭据的访问请求。


图片

图3.2 - 容器化场景下应用请求客户端令牌并查询凭据的流程示意图



07



方案的一些思考

7.1 方案有哪些优点

安全性

整套方案学习下来,最惊叹的就是 Vault 对于安全性的极致追求,这可能会让某些追求稳定性的开发者抓狂,而究其原因,主要因为它是基于中心化的单主方案来设计的,同时还伴随着一系列的凭证过期风险和令牌一次性消费风险。但作为一款基于身份的密钥管理系统,其对于安全性的把控基本做到了极致。从方案的三大基本核心原则来看,不管是缩小身份爆炸半径,还是缩短认证有效时间,都在尽可能降低密钥泄漏的风险。


通过角色标识和凭据标识的组合认证,提供了基本的双重凭证认证机制。而后通过细粒度的访问控制,使得管理者可以对每个角色分配不同的策略,从而精准控制应用程序的权限,实现最小权限原则,并给予角色和凭据标识单独的安全访问控制能力,如提供凭据标识有效时间限制,以及受信 CIDR 源限制等。最终通过提供凭据标识和访问令牌的定期轮转技术,最大限度降低了长期使用同一凭证可能带来的安全性风险。


可审计

本文虽然没有着重介绍方案的审计功能,但通过学习 Vault 的审计模块设计,可以发现「操作可审计」是深深嵌入 Vault 的整个产品设计中的。客户端在身份认证完成后的所有操作,均需基于认证后获取到的访问令牌来进行,通过访问令牌的请求记录,可以精确的追溯到该客户端的每一次请求操作。而这样优雅的审计方案及可审计性,主要还是得益于 Vault 的整个对外界面设计,不管是 UI 还是 CLI 还是 HTTP API 都是基于同一套 Open API 来实现的,因此客户端的每个操作都可以分解为对某个路径执行的某种 HTTP 操作。


灵活性

方案的灵活性体现在多个方面。一方面对于角色配置的管理,Vault 提供了众多的可配置项,如可以设置当前角色可登陆的 IP 地址块,也可以设置角色凭据标识的有效期,还可以设置角色访问令牌的最大访问次数等等。另一方面,对于访问控制策略的配置也提供了较大的灵活性和通用性,与可审计中提到的观点相同,因为 Vault 是基于标准的 HTTP REST API 协议来设计其对外接口的,因此对于策略的设计可以做到十分的优雅且通用,仅需使用资源路径和操作类型就可以指定对一个操作的访问控制,所见即所得。


无状态

与传统的基于机器指纹进行身份认证不同,Vault 采用的是基于动态令牌结合静态标识的方式来完成身份的认证,其间经过两次的凭证转换,即从封装的凭据标识到原始的凭据标识,再从原始的凭据标识到最终的访问令牌。


通过基于可信代理平台下发短期有效的一次性封装令牌,再结合应用自身静态的角色标识的方式,Vault 实现了身份认证的无状态化,即在身份认证期间无需再单独依赖其它的中心化数据中心,来完成机器指纹的有状态校验。这在一定程度上提升了方案的灵活性和稳定性,避免了因多处数据不一致而导致身份认证失败的问题。

7.2 能存在的一些问题

复杂度和学习成本

整体感受下来,Vault 所有解决方案的学习成本相对都还是比较高的,这一点我觉得可以理解为是 “高灵活性” 和 “高可配置性” 带来的一个必然结果。一方面,因为 Vault 提供了较多的实体模型,如身份、别名、实体、策略、令牌和令牌访问器等等,每个实体模型都会有自己的操作点,再加上众多的认证方式和机密引擎,使得整个方案的学习成本还是相对较高的。另一方面,因为存在着众多的实体模型,对于这些实体进行有效的管理和维护,也需要承担较高的成本。


比如按照 Vault 的设计,通常情况下一个默认策略是直接施加到某个访问令牌上的,但你也可以给该令牌的实体施加一个策略,从而实现令牌策略的叠加,甚至你还可以给身份实体所在的身份组施加一个策略,再将该策略叠加到前两者之上,甚至的甚至你还可以实现组策略的继承。此时,灵活度拉满,但复杂度也同时拉满了。


性能和扩展性

由于 Vault 是基于 Raft 共识协议实现的单主写入架构,所以当系统面对较大的请求量时,尤其是在密集的写入操作时,可能会遇到性能瓶颈。这个问题在应用角色认证方案中可能会比较明显,因为在该方案中存在着大量的写入操作。在开源的基础方案中,单主节点架构会导致水平扩展的作用降低,因为通过增加更多的备用节点,并不会提升系统的写入性能,仅能提升系统的读取性能和冗余度。


一方面,虽然 Vault 提供了高可用的集群部署和数据中心之间的复制能力,但设置和管理这些复杂的架构还是会存在较大的挑战,对于数据同步和确保数据一致性来说,在网络质量较差或跨区域部署时,往往可能会突然出现一些非预期的异常问题。另一方面,因为方案提供了较多的扩展特性,如凭据和令牌的生命周期管理,租约的续订等,随着数据规模的增大,维护和同步这些状态信息的复杂度和成本可能会越来越高,甚至对系统的基本功能产生影响。


稳定性和健壮性

从技术架构的角度来说,单主节点架构可能存在的一个问题是对稳定性的影响。由于整个集群中仅有主节点能够处理写入请求,所以当主节点宕机时,需要进行故障转移,选举新的主节点,而这个过程可能会带来短暂的服务中断,这对于需要执行高频写入和刷新操作的密钥管理服务来说,存在较大的风险。


出于安全性的设计,方案中存在大量的实体生命周期维护,实体需要通过不断的续约,来保证自身凭证的有效性,这可能会放大稳定性的风险。同时,也是出于安全性的考量,使用方也不应对获取到的凭据标识和令牌进行落盘处理,而这往往会造成自动化系统对代理系统的强依赖,使得未经代理系统进行的操作终将失败(如紧急情况下直接通过机器脚本重启应用),但在大型微服务系统中,这样的操作可能是很常见的。


封装凭据标识的一次有效性,也可能会对系统在异常情况下的健壮性产生影响,使得整个系统的容错性降低。比如一次性的封装凭据标识解封后,凭据标识出现丢失的情况,此时因为封装的凭据标识已经被消费过,所以将无法再次执行解封操作,这时就只能通过由可信代理重新下发新的封装凭据标识来解决。

参考文档

[01] What is Vault?
https://developer.hashicorp.com/vault/docs/what-is-vault

[02] AppRole auth method

https://developer.hashicorp.com/vault/docs/auth/approle

[03] Vault Tokens

https://developer.hashicorp.com/vault/docs/concepts/tokens

[04] Policies
https://developer.hashicorp.com/vault/docs/concepts/policies

[05] How (and Why) to Use AppRole Correctly in HashiCorp Vault

https://www.hashicorp.com/blog/how-and-why-to-use-approle-correctly-in-hashicorp-vault

[06] Recommended pattern for Vault AppRole use

https://developer.hashicorp.com/vault/tutorials/recommended-patterns/pattern-approle

[07] Cubbyhole response wrapping

https://developer.hashicorp.com/vault/tutorials/secrets-management/cubbyhole-response-wrapping

图片

图片
欢迎留言一起参与讨论~

继续滑动看下一个
阿里技术
向上滑动看下一个