查找指南

本指南并非包含的查找插件及其使用方法的全面列表,而是旨在解释查找插件在 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,这将执行两次单独的登录,从而导致创建两个令牌,并对完全相同的秘密执行两次读取。

如果你将此与循环结合使用,或者在多个任务中重用变量,则可以非常快速地倍增对 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 }}'."

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

在两个示例中,还需要考虑的另一点是,任务是按主机运行的,因此您可能会再次增加请求次数。

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

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

但是,如果您的所有密钥访问都打算从控制器进行,并且请求不依赖于主机级别的变量,那么您可以通过使用 run_once、或者在一个单独的剧本中只针对 localhost 执行 Vault 调用并使用 ansible.builtin.set_fact 或通过其他方法,来潜在地大幅减少请求次数。