17. 项目签名与验证
项目签名与验证提供了对项目目录中的文件进行签名,然后验证其内容是否以任何方式发生更改,或文件是否意外地添加到项目或从项目中删除的功能。为此,需要一个用于签名的私钥和一个用于验证的匹配公钥。
对于项目维护人员,执行内容签名的支持方式是使用名为 ansible-sign
的实用程序,通过附带的命令行界面 (CLI) 使用它。
CLI 的目标是简化使用诸如 GNU Privacy Guard (GPG) 之类的加密技术的应用,以验证项目中指定的文件是否以任何方式被篡改。目前,GPG 是唯一支持的签名和验证方式。
AWX 用于验证签名的内容。在将匹配的公钥与签名的项目关联后,AWX 将验证签名期间包含的文件是否已更改,以及是否意外添加或删除了文件。如果签名无效或文件已更改,则项目将无法更新,并且任何使用该项目的作业都将无法启动。项目的验证状态确保仅在作业中运行安全且未被篡改的内容。
假设存储库已配置为进行签名和验证(见下文),更改项目的通常工作流程如下
用户已经设置了一个项目存储库,并且想要对文件进行更改。
用户进行更改,运行
ansible-sign project gpg-sign /path/to/project
,这将更新校验和清单并对其进行签名。用户将更改、更新的校验和清单和签名提交到存储库。
当用户同步项目时,AWX(在此情况下已配置)会提取新的更改,检查 AWX 中与项目关联的公钥是否与用于对校验和清单进行签名的私钥匹配(这可以防止对校验和清单本身进行篡改),然后重新计算清单中每个文件的校验和以确保校验和匹配(从而确保没有文件更改)。它还将查看以确保所有文件都被考虑在内:这些文件必须包含在下面讨论的
MANIFEST.in
文件中或被排除在该文件中;如果文件意外添加或删除,则验证将失败。
17.1. 先决条件
需要有效的 GPG 公钥/私钥对才能对内容进行签名。有关详细信息,请参阅 如何创建 GPG 密钥对。
访问 GnuPG 文档 以了解有关 GPG 密钥的更多信息。
您可以使用以下命令验证您是否拥有有效的 GPG 密钥对,并且该密钥对位于您的默认 GnuPG 密钥环中
$ gpg --list-secret-keys
如果上述命令没有输出,或者只有一行输出,内容为
trustdb was created
,那么您的默认密钥环中没有私钥。在这种情况下,请参阅 如何创建 GPG 密钥对 了解如何在继续操作之前创建新的密钥对。如果它产生了其他输出,那么您拥有有效的私钥,并且可以继续使用ansible-sign
。
17.2. 将 GPG 密钥添加到 AWX
为了在 AWX 中使用 GPG 密钥进行内容签名和验证,您必须使用以下命令在 CLI 中添加它
$ gpg --list-keys
$ gpg --export --armour <key fingerprint> > my_public_key.asc
在 AWX 用户界面中,从左侧导航菜单中点击 **凭据**,然后点击 **添加** 按钮。
为新的凭据提供一个有意义的名称(例如,“基础设施团队公共 GPG 密钥”)。
在 **凭据类型** 字段中,选择 **GPG 公钥**。
点击 **浏览** 以查找并选择公钥文件(例如,
my_public_key.asc
)。完成后,点击 **保存**。
现在可以在 项目 中选择此凭据,并且将在未来的项目同步中自动进行内容验证。
注意
使用项目缓存 SCM 超时来控制您希望 AWX 多久重新验证一次签名的内容。当项目配置为在启动时更新(任何配置为使用该项目的作业模板的启动)时,您可以启用缓存超时设置,该设置会告诉它在上次更新后的 N 秒后更新。如果验证运行过于频繁,则可以通过在项目的选项详细信息窗格的 **缓存超时** 字段中指定时间来减慢项目更新的频率。
17.3. 访问 ansible-sign
CLI 实用程序
该 ansible-sign
实用程序提供了用户签署和验证项目是否已签署的选项。
运行以下命令安装
ansible-sign
$ dnf install ansible-sign
验证
ansible-sign
是否已成功安装
$ ansible-sign --version
将显示类似于以下内容的输出(版本号可能不同)
ansible-sign 0.1
这表明您已成功安装 ansible-sign
。
17.4. 签名您的项目
顾名思义,对项目进行签名涉及 Ansible 项目目录。有关项目目录结构的更复杂示例,请参阅 Ansible 文档。
以下示例项目具有非常简单的结构。一个清单文件和两个位于 playbooks 目录下的小型剧本
$ cd sample-project/
$ tree -a .
.
├── inventory
└── playbooks
└── get_uptime.yml
└── hello.yml
1 directory, 3 files
注意
本节中使用的命令假设您的工作目录是项目的根目录。作为一项规则,ansible-sign project
命令始终将项目根目录作为其最后一个参数,因此,我们使用 .
来表示当前工作目录。
ansible-sign
通过获取项目中所有受保护文件的校验和 (SHA256),将这些校验和编译成校验和清单文件,最后对该清单文件进行签名来保护内容免遭篡改。
对内容进行签名的第一步是创建一个文件,告诉 ansible-sign
哪些文件需要保护。该文件应命名为 MANIFEST.in
并且位于项目根目录中。
在内部,ansible-sign
使用 Python distlib 库的 distlib.manifest
模块,因此 MANIFEST.in
必须遵循该库指定的语法。有关 MANIFEST.in
文件指令的说明,请参见 Python 打包用户指南。
在示例项目中,包含两个指令,导致 MANIFEST.in
文件如下所示
include inventory
recursive-include playbooks *.yml
有了这个文件,就可以生成校验和清单文件并对其进行签名。这两步都在一个 ansible-sign
命令中完成
$ ansible-sign project gpg-sign .
[OK ] GPG signing successful!
[NOTE ] Checksum manifest: ./.ansible-sign/sha256sum.txt
[NOTE ] GPG summary: signature created
现在,项目已签名。
请注意,gpg-sign
子命令位于 project
子命令下。对于对项目内容进行签名,每个命令都将以 ansible-sign project
开头。如上所述,作为一项规则,每个 ansible-sign project
命令都将项目根目录作为其最后一个参数。
如前所述,ansible-sign
默认使用您的默认密钥环并查找它可以找到的第一个可用私钥来对您的项目进行签名。您可以使用 --fingerprint
选项指定一个特定的私钥,甚至可以使用 --gnupg-home
选项指定一个完全独立的 GPG 主目录。
注意
如果您正在使用桌面环境,GnuPG 将自动提示您输入私钥的密码。如果此功能无法正常工作,或者您在没有桌面环境的情况下工作(例如,通过 SSH),您可以在上述命令中 gpg-sign
后使用 -p/--prompt-passphrase
标志,这将导致 ansible-sign
提示输入密码。
查看项目目录的结构后,请注意,创建了一个新的 .ansible-sign
目录。此目录包含校验和清单及其分离的 GPG 签名。
$ tree -a .
.
├── .ansible-sign
│ ├── sha256sum.txt
│ └── sha256sum.txt.sig
├── inventory
├── MANIFEST.in
└── playbooks
├── get_uptime.yml
└── hello.yml
17.5. 验证您的项目
如果您想验证签名的 Ansible 项目是否被修改过,可以使用 ansible-sign
检查签名是否有效,以及文件的校验和是否与校验和清单中的预期一致。特别是,ansible-sign project gpg-verify
命令可用于自动验证这两个条件。
$ ansible-sign project gpg-verify .
[OK ] GPG signature verification succeeded.
[OK ] Checksum validation succeeded.
注意
默认情况下,ansible-sign
会使用您的默认 GPG 密钥环查找匹配的公钥。您可以使用 --keyring
选项指定密钥环文件,或使用 --gnugpg-home
选项指定不同的 GPG 主目录。
如果验证因任何原因失败,将显示信息以帮助您调试原因。可以通过在命令中 ansible-sign
后面立即传递全局 --debug
标志来启用更多详细程度。
注意
当 GPG 凭据在项目中使用时,内容验证将在将来的项目同步中自动进行。
17.6. 自动化签名
在高度可信的 CI 环境(例如 OpenShift、Jenkins 等)中,可以自动化签名过程。例如,您可以将您的 GPG 私钥存储在选择的 CI 平台中作为秘密,并将该私钥导入 CI 环境中的 GnuPG。然后,您可以在正常的 CI 工作流程/容器/环境中执行上述签名工作流程。
使用 GPG 签名项目时,可以将环境变量 ANSIBLE_SIGN_GPG_PASSPHRASE
设置为签名密钥的密码。这可以在 CI 管道中注入(并屏蔽/保护)。
根据具体情况,ansible-sign
在签名和验证期间将返回不同的退出代码。这在 CI 和自动化环境中也很有用,因为 CI 环境可以根据失败情况采取不同的行动(例如,对某些错误发送警报,但对其他错误则静默失败)。
以下是目前 ansible-sign
中使用的退出代码,这些代码可以被认为是稳定的
退出代码 |
近似含义 |
示例场景 |
---|---|---|
0 |
成功 |
|
1 |
一般故障 |
|
2 |
校验和验证失败 |
|
3 |
签名验证失败 |
|
4 |
签名过程失败 |
|