查找指南

本指南并非包含的查找插件及其使用方法的详尽列表,而是旨在解释查找插件在 community.hashi_vault 中的作用以及它们的使用方式,特别是与同名模块相比时。

有关 hashi_vault 查找的详细信息,请参阅详细介绍它的本页

查找和写入

大多数 Ansible 查找执行只读、非破坏性操作。它们在模板中运行,通常返回值,并且在检查模式下运行方式没有差异(即它们执行的操作与正常模式相同,即使这意味着更改某些内容)。但是,一些查找确实会更改状态,有时会执行写入操作。例如,password 查找会将生成的密码写入文件,以充当某种缓存,而 pipe 查找运行任意 shell 命令,因此很容易写入或更改状态。

Vault 中的写入

在 Vault 中执行写入操作不仅仅限于诸如写入密钥值、创建策略或启用新的身份验证方法等显而易见的操作。

例如,任何创建令牌的操作(如任何登录操作)也是写入;令牌在 Vault 中使用存储空间,并且活动令牌过多是导致性能问题的常见原因。

此外,Vault 中的某些值只能在创建时“读取”,因此检索此类值的唯一方法是从创建它的“写入”操作中获取它。一个常见的例子是 AppRole 密钥 ID。

这与 Ansible 和此集合的关系是,我们可能有查找插件,它们要么以不直观的方式执行写入(如 vault_login),要么看起来不适合首先作为查找存在,例如计划的 vault_write 查找。

这样做的原因是,我们通常认为这些操作是逻辑“读取”操作,例如执行登录,并希望在其他表达式中使用其结果。

类似 vault_write 的操作并不总是符合该描述,因为您可以使用它来执行明显的显式写入,例如,您可以使用查找创建一个新策略。但有时在查找语义中使用它可能是合适的,例如在“检索”(实际上是创建)AppRole 的新密钥 ID 时。

在考虑对身份验证方法的内置支持时,除 tokennone 之外的任何身份验证方法都会使每个查找(即使是 vault_read)都变成更改状态并在 Vault 中执行写入的操作。这实际上也适用于许多模块,即使在使用检查模式时也是如此。

如何判断何时使用查找

由于任何查找中都可能进行写入,因此仔细考虑何时使用查找与模块/其他插件非常重要。检查模式对查找没有任何影响,因此在检查模式运行中可能会执行多次写入,但有时您可能希望这样做,例如,如果您通过查找执行 vault_login 来检索令牌以在模块调用中使用,您可能希望在检查模式下仍然执行此操作,以便模块调用可以正确检查它们需要检查的内容。

某些以读取为中心的模块(如 vault_read 模块),在与 tokennone 之外的身份验证方法一起使用时,即使在检查模式下仍会执行内部登录,因此这仍然是另一个需要考虑的因素。

查找和延迟模板

如果使用不当,Ansible 的“延迟”模板会加剧查找执行写入或更改状态的能力。

考虑以下示例

- vars:
    token: "{{ lookup('community.hashi_vault.vault_login', auth_method='userpass', username='user', password='pass') | community.hashi_vault.vault_login_token }}"
    secret: "{{ lookup('community.hashi_vault.vault_read', 'secrets/data/my-secret', token=token) }}"
    value_a: "{{ secret.data.data.a }}"
    value_b: "{{ secret.data.data.b }}"
  ansible.builtin.debug:
    msg: "Secret value A is '{{ value_a }}' while value B is '{{ value_b }}'."

由于模板是递归的并且延迟计算,因此不幸的是,这将不会导致单次登录,而是重用令牌来执行单次密钥读取,然后在字典查找中使用。

相反,对 value_avalue_b 的求值将各自导致对 secret 的单独求值,因此会执行两次查找,并且每次查找都会导致对 token 的单独求值,这将执行两次单独的登录,导致创建两个令牌,并且对完全相同的 secret 执行两次读取。

如果将其与循环结合使用,或者在多个任务中重用变量,您可能会很快地将发送到 Vault 的请求数量以及写入情况下创建的对象数量成倍增加。

对于这种情况,任务可能更好,因为它们在遇到时执行而不会被意外重复,并且它们返回的值是静态的。

- name: login
  community.hashi_vault.vault_login:
    auth_method: userpass
    username: user
    password: pass
  register: login

- name: get secret
  community.hashi_vault.vault_read:
    token: '{{ login | community.hashi_vault.vault_login_token }}'
    path: 'secrets/data/my-secret'
  register: secret

- vars:
    value_a: "{{ secret.data.data.data.a }}"
    value_b: "{{ secret.data.data.data.b }}"
  ansible.builtin.debug:
    msg: "Secret value A is '{{ value_a }}' while value B is '{{ value_b }}'."

即使这个例子更加冗长,它也只会执行一次登录和 secret 查找。这也意味着 secretlogin 变量可以在更多任务中重用,而无需向 Vault 发送额外的请求。

在两个示例中,另一个需要考虑的事情是任务是按主机运行的,因此您可能再次将请求成倍增加。

在查找示例中,所有这些请求都发生在控制器上,而在模块示例中,它们发生在远程主机上,除非 play 或任务的目标是本地。

在这两种情况下,您可能希望按主机发出这些请求,因为查找中涉及的一些变量可能依赖于按主机的值,例如不同的身份验证、不同的 secret 路径,甚至是完全不同的 Vault 服务器,或者在某些访问限制的情况下,您可能需要远程主机而不是控制器来建立连接。

但是,如果您的所有 secret 访问都旨在从控制器进行,并且请求不依赖于主机级别的变量,您可以通过使用 run_once,或者在仅以 localhost 为目标的单独 play 中进行 Vault 调用并使用 ansible.builtin.set_fact,或者通过其他方法,来大量减少您的请求。