Ansible 中的机密信息掩码

Ansible Playbook 经常需要密码和 API 令牌来连接服务器、部署应用程序。这些值也可能出现在任务输出中,例如某条命令返回了包含密码的错误信息。

为了解决这个问题,Ansible 正在加入机密信息掩码功能。它会将输出中已知的机密值替换为 $REDACTED$,同时保留消息的其余内容,方便阅读。任务仍然使用原始值。

这项功能由 Secret Masking API 拉取请求引入,该请求于 2026 年 9 月 10 日合并。截至本文撰写时,它已在 ansible-core 2.22 开发构建版本中提供,但尚未包含在稳定版中。

下面先看一个简单的 Playbook,再介绍我们计划如何在 Semaphore UI 中使用这项功能。

首先,注册一个机密值

Ansible 需要知道哪些值应该被掩码处理。新的 API 会注册来自已知机密来源的值,例如使用 Vault 加密的变量,以及标记为 no_log 的模块参数。

对于普通变量,我们可以使用 register_secret 过滤器。通过 --extra-vars 传入的变量也需要注册;仅仅将它命名为 db_password 并不够。

在这个例子中,我们使用一个虚构的数据库密码。创建名为 masking.yml 的 Playbook:

- name: Try secret masking
  hosts: localhost
  gather_facts: false
  vars:
    db_password: "example-db-password-123456"

  tasks:
    - name: Register the password before using it
      ansible.builtin.set_fact:
        db_password: "{{ db_password | ansible.builtin.register_secret }}"

    - name: Show a message containing the password
      ansible.builtin.debug:
        msg: "Connecting to db.internal with password={{ db_password }}"

第一个任务注册密码。过滤器返回原始值,因此 Playbook 仍然可以用它连接数据库。必须在任何任务输出该值之前完成注册。

使用 ansible-core 2.22 开发构建版本运行 Playbook:

ansible-playbook -i localhost, -c local masking.yml

第二个任务将显示以下消息:

Connecting to db.internal with password=$REDACTED$

这里输出密码仅用于演示掩码效果。在实际的 Playbook 中,应从你的机密信息来源获取密码,并避免主动输出凭据。

与 Semaphore UI 集成

Semaphore UI 已经知道你将哪些变量标记为了机密。我们计划在未来版本中利用这些信息,自动向 Ansible 注册相应的值。

为此,我们将使用 Ansible 的 _SECRETS_INPUT_FILES 选项。该选项通过环境变量 _ANSIBLE_SECRETS_INPUT_FILES 配置,让 Ansible 能够在 Playbook 启动前读取机密值列表。

输入可以采用 YAML 或 JSON 格式。包含示例密码的文档如下:

version: 1
secrets:
  - example-db-password-123456

这个列表告诉 Ansible 要对哪些值进行掩码处理。变量本身仍然单独传递给 Playbook。

计划中的集成将涵盖以下来源的机密值:

  • 变量组(Variable Groups)——保存下来供任务使用的机密值。
  • 问卷(Surveys)——启动任务时输入的机密答案。

对于这些值,你无需在每个 Playbook 中添加使用 register_secret 的任务。

计划中的 Semaphore UI 集成:将机密值传递给 Playbook,同时通过 _SECRETS_INPUT_FILES 单独注册。任务使用真实值,而 Ansible 在任务日志中将其替换为 $REDACTED$。

我们在 ansible-core 2.22 开发构建版本中测试了这种方法。已注册的值在 debug 消息、命令结果、错误消息、详细输出和 Ansible 日志文件中都进行了掩码处理。通过管道传递列表也能正常工作,无需在磁盘上写入单独的机密文件。

该选项目前是内部选项,可能发生变化。Semaphore UI 计划在未来版本中提供支持,并且需要兼容的 Ansible 版本。

示例:部署应用程序

假设我们有一个用于部署应用程序的任务模板(Task Template)。部署脚本使用保存在变量组中的机密 API 令牌。

采用计划中的集成后,任务将按以下流程运行:

  1. Semaphore UI 将令牌传递给 Playbook。
  2. 在执行前,它还会通过 _SECRETS_INPUT_FILES 将令牌提供给 Ansible 的掩码列表。
  3. Playbook 使用真实令牌运行部署脚本。
  4. 如果脚本在输出中包含该令牌,Ansible 会在显示结果时对其进行掩码处理。

例如,令牌过期时,任务日志中可能出现以下消息:

Deployment rejected: service=payments environment=staging token=$REDACTED$ reason=token expired

我们可以看到哪次部署失败以及失败原因,而不必向所有阅读日志的人暴露令牌。通过问卷的机密字段输入的临时令牌也将采用同样的流程。

限制

掩码处理针对的是已注册的值。开发版实现会跳过长度不足四个字符的机密值,而编码后或经过其他转换的值可能需要单独注册。

它也不保护进程参数、Playbook 写入的文件或 Ansible 之外生成的日志中的凭据。对于需要隐藏全部输出的任务,请继续使用 no_log: true

通过这项集成,在 Semaphore UI 中标记为机密的值也将被 Ansible 的掩码系统识别。这有助于避免凭据出现在任务日志中,同时保留排查问题所需的信息。