令牌
警告:由于令牌的结构被认为是非公开的,它的实现结构没有相关文档,也不保证稳定不变。出于这些原因,请不要在代码或自动化脚本中解析令牌,因为它可能会令牌结构的破坏性变更而出错。
令牌是 Vault 中身份验证的核心方法。可以直接使用已有的令牌,也可以使用身份验证方法根据外部身份动态生成令牌。
如果读者从本书开篇读到这里,那么可能会注意到 vault server -dev
(或者 vault operator init
初始化的用于正式场合的服务器)命令输出的 root token
。这是 Vault 的第一种身份验证方法。它也是唯一无法禁用的身份验证方法。
正如身份验证章节中所述,所有外部身份验证机制(例如 GitHub)最终都映射到动态创建的令牌上。这些动态创建的令牌与普通的手动创建的令牌具有相同的属性。
在 Vault 中,令牌映射了一组信息。映射到令牌的最重要的信息是一组一个或多个“策略”。这些策略控制着令牌持有者可以在 Vault 中做什么。其他映射的信息包括了一些只读的元数据,例如创建时间、上次更新时间等,并且这些元数据会被记录到审计日志当中。
令牌类型
从 Vault 1.0 开始,有两种类型的令牌:service
令牌和 batch
令牌。本章后续部分将叙述它们之间的差异,但我们可以先略过,了解一下其他有关令牌的概念。以下部分中的功能均适用于 service
令牌,稍后将讨论它们对 batch
令牌的适用性。
令牌存储
通常在文档或帮助中,会引用“令牌存储”。这是一个特殊的后端组件,负责创建和存储令牌,并且不能被禁用。它也是唯一没有登录功能的身份验证方法——所有其他操作都需要一个已存在的经过身份验证的令牌。
根令牌
根(Root)令牌是附加了根策略的令牌。根令牌可以在 Vault 中为所欲为。为所欲为!此外,它们是 Vault 中唯一可以设置为永不过期而无需任何续约的令牌类型。这导致了 Vault 中创建根令牌的流程被有意设计的复杂而困难;实际上只有三种方法可以创建根令牌:
- 使用
vault operator init
命令时创建的根令牌:此令牌没有过期时间 - 使用根令牌来创建一个新的根令牌:一个带有过期时间的根令牌无法创建一个无过期时间的根令牌
- 使用
vault operator generate-root
命令创建一个新的根令牌(我们在之前的篇章中有过叙述)
根令牌在开发过程中很有用,但在生产环境中应该非常小心地保存。事实上,Vault 官方建议根令牌仅用于在创建集群时进行初始设置(通常设置足够的身份验证方法和策略,以允许管理员获取更多的受限制的令牌)或在紧急情况下使用,并在使用完成后应立即吊销。如果需要新的根令牌,可以使用 operator generate-root
命令或相关的 API 来即时生成一个。
在多人监视的状态下使用根令牌进行操作是很好的安全实践。通过这种方式,多个人员可以验证使用根令牌执行的任务,并且在这些任务完成后立即吊销令牌。
令牌的层次结构以及孤儿令牌
通常当令牌的持有者创建新的令牌时,这些新令牌将被创建为原令牌的子令牌;而这些子令牌创建的令牌将是它们的子令牌。当父令牌被吊销时,它的所有子令牌——以及它们创建的所有租约——也会被吊销。这确保用户无法通过简单地生成一个永无止境的子令牌树来逃避吊销。
有时我们不希望这种级联吊销,因此具有适当访问权限的用户可以创建 orphan
令牌。这些令牌没有父令牌——它们是自身令牌树的根。可以用以下方法创建孤儿令牌:
- 通过对
auth/token/create-orphan
路径执行write
操作 - 在对
auth/token/create
路径有sudo
或是root
访问权限的前提下,将no_parent
参数设置为true
- 通过设置令牌存储角色
- 通过其他非
-token
机制的身份验证方法登录
具有适当权限的用户还可以使用 auth/token/revoke-orphan
端点,可以吊销指定的令牌的同时不吊销它的子令牌,而是将令牌的直接子令牌设置为孤儿。谨慎使用!
令牌访问器(Accessor)
创建令牌时,还会创建并返回令牌访问器。此访问器是一个作为对令牌引用的值,只能用于执行以下有限的操作:
- 查询一个令牌的信息(不包括实际的令牌 ID)
- 查询一个令牌对指定路径的权限
- 续约令牌
- 吊销令牌
执行以上操作时使用的是当前使用的令牌,而不是访问器对应的令牌,所以以上操作的执行者必须拥有相关的权限。
有许多有用的工作流程被设计成使用令牌访问器。举例来说,代表另一个服务(例如 Nomad 调度器)创建令牌的服务可以存储与特定任务 ID 相关的访问器。任务完成后,可以立即使用访问器来吊销授予任务的令牌及其所有租约,从而限制入侵者发现和使用它们的机会。
可以将审计设备设置为不混淆审计日志中的令牌访问器。这使得我们可以在紧急情况下快速吊销令牌。然而,这也意味着获取到审计日志的攻击者可以进行大规模拒绝服务攻击。
最后,枚举令牌的唯一方法是通过 auth/token/accessors
命令,它将返回令牌访问器的列表。虽然这是一个危险的操作(因为列出所有访问器意味着它们可以用来吊销所有令牌),但它也提供了一种审计和吊销当前活动令牌集的方法。
令牌有效期,周期性令牌以及显式的最大有效期限制
每个非根令牌都有一个与之关联的有效期 (TTL),这是自令牌的创建时间或上次续约时间(以较近者为准)以来的当前有效期。(根令牌可能有关联的 TTL,但 TTL 也可能为 0,表示令牌永不过期)。当前 TTL 到期后,令牌将不再起作用——令牌及其相关联的租约将被吊销。
如果令牌是可续约的,则可以要求 Vault 使用 vault token renew
命令或适当的更新端点来延长令牌有效期,这时候要考虑各种因素。命令的结果取决于令牌是否是周期性令牌(可由 root
或 sudo
用户、令牌存储角色或某些身份验证方法创建),是否附加了显式的最大 TTL 限制,或者两者都没有。
通常情况
通常情况下,如果是一个没有显式最大 TTL 限制的非周期性令牌,令牌自创建以来的存活时长会与最大 TTL 进行比较。这个最大 TTL 值是动态生成的,可以随着续约而改变,所以在查询令牌信息时无法显示该值。它基于多种因素的组合:
- 系统默认最大 TTL 是 32 天,然而这可以在 Vault 配置文件中进行修改
- 可以为指定挂载点设置最大 TTL。该值允许覆盖系统级别的最大 TTL —— 可以更长也可以更短,挂载点上的配置拥有更高优先级
- 签发令牌的身份验证方法给出的建议值。这可以基于个别角色、个别组或个别用户进行配置。该值允许小于挂载点上配置的最大 TTL(或者,如果未设置,则为系统最大 TTL),但不允许更长。
请注意,第二条和第三条中的值可能会在任何时间发生更改,这就是为什么在续约时即时读取当前值来最终确定当前允许的最大 TTL 原因,这也是为什么客户端始终要检查从更新操作返回的 TTL 在允许的范围内的一个重要原因;如果有效期未能成功延展,则令牌的 TTL 可能无法扩展到超过其当前值,那么客户端可能需要重新进行身份验证并获取新令牌。但是,除非直接使用相关命令,Vault 永远不会在返回的 TTL 到期之前吊销令牌。
显式的最大 TTL
令牌可以设置一个显式的最大 TTL。该值是令牌生命周期的硬限制——无论一般情况下的三种条件中的值是什么,令牌都不能超过这个明确设置的值,甚至周期性令牌 TTL 也无法摆脱它的限制。
周期性令牌
在某些情况下,吊销令牌会很麻烦——例如,如果长时间运行的服务需要长期维护它的 SQL 连接池。在这种情况下,可以使用定期令牌。可以通过以下几种方式创建定期令牌:
- 使用有
sudo
权限的令牌,或是根令牌操作auto/token/create
端点 - 使用令牌存储角色
- 使用支持周期令牌的身份验证方法来签发周期性令牌,例如 AppRole
在签发时,周期性令牌的 TTL 将等于设置的周期。每次续约时,TTL 都会重置回这个配置的时长,只要令牌在每个时间段内成功续约,它就永远不会过期。在根令牌之外,它目前是 Vault 中唯一一种具有无限生命周期的令牌。
周期性令牌的设计理念是,使得系统和服务可以很容易地频繁地反复执行某项操作——例如,每两小时,甚至每五分钟。因此,只要系统主动续约此令牌——换句话说,只要系统还活着——系统就可以继续使用令牌和任何相关的租约。但是,如果系统在此期间停止续约(例如,如果它被关闭),令牌将相对较快地过期。设置一个较短的周期是一种很好的做法,同时,一般来说给人类提供周期性的令牌是没有什么益处。
使用周期性令牌有几项重要的事项:
- 如果使用令牌存储角色创建周期性令牌,那么当前角色设置的周期值将会被用作续约周期
- 如果一个令牌同时设置了周期以及显式最大 TTL,那么它工作起来就如同一个普通的周期性令牌,但是在到达最大 TTL 时间后会被吊销。
绑定 CIDR 的令牌
某些令牌能够绑定一组限制允许使用它们的客户端 IP 范围的 CIDR。该设置会影响除了无有效期限制的根令牌(TTL 为零的令牌)之外的所有令牌。如果根令牌有过期时间,它也会受到 CIDR 绑定的影响。
令牌类型详解
目前有两种令牌
服务令牌
服务令牌是用户通常认为的“普通” 的 Vault 令牌。它们支持所有功能,例如续订、吊销、创建子令牌等。它们对应了重量级的创建和跟踪流程。
批量令牌
批量令牌是加密的二进制数据,携带了足够的信息供来用于 Vault 操作,但它们不需要 Vault 服务的磁盘存储来记录跟踪它们。因此,它们非常轻量级和可扩展,但缺乏服务令牌的大部分灵活性和功能。
令牌类型对比
下表描述了服务令牌与批量令牌的不同之处:
服务令牌 | 批量令牌 | |
---|---|---|
可以是根令牌吗? | 可以 | 不可以 |
可以续约吗? | 可以 | 不可以 |
可以是周期性令牌吗? | 可以 | 不可以 |
可以设置显式最大 TTL 吗? | 可以 | 不可以(只能使用固定 TTL) |
拥有访问器吗? | 有 | 没有 |
拥有 Cubbyhole 吗? | 有 | 没有 |
会随着父令牌被级联吊销吗? | 会 | 停止工作 |
创建的动态机密关联到谁? | 自身 | 父令牌(如果不是孤儿令牌) |
可以跨性能复制集群使用吗? | 不能 | 能(如果是孤儿令牌) |
创建成本 | 高:创建每个令牌都要写入多次存储 | 低:创建令牌不需要访问存储 |
服务令牌与批量了令牌创建的租约的处理
服务令牌租约
服务令牌创建的租约(包括子令牌创建的租约)会与服务令牌一起被追踪,一旦令牌被吊销,则租约都会被吊销。
批量令牌租约
批量令牌创建的租约受限于批令牌剩余的 TTL,如果批量令牌不是孤儿令牌的,则由父令牌代为跟踪。当批量令牌的 TTL 到期时,或者当批量令牌的父令牌被吊销(此时批量令牌也会被 Vault 拒绝访问)时,它们将被吊销。
可以推论出,批量令牌可以跨性能复制集群使用,但前提是它们是孤儿令牌,因为非孤儿令牌将无法确保父令牌的有效性。