如何使用AnsibleVault保护敏感的Playbook数据

来自菜鸟教程
跳转至:导航、​搜索

介绍

Ansible Vault 是一项允许用户加密 Ansible 项目中的值和数据结构的功能。 这提供了保护成功运行 Ansible 播放所需但不应公开可见的任何敏感数据的能力,例如密码或私钥。 当提供密钥时,Ansible 在运行时自动解密保管库加密的内容。

在本指南中,我们将演示如何使用 Ansible Vault 并探索一些推荐实践以简化其使用。 我们将为 Ansible 控制机器使用 Ubuntu 20.04 服务器。 不需要远程主机。

先决条件

接下来,您将需要一个具有 sudo 权限的非 root 用户的 Ubuntu 20.04 服务器。 您可以按照我们的 Ubuntu 20.04 初始服务器设置指南 创建具有适当权限的用户。

在服务器上,您需要安装和配置 Ansible。 您可以按照我们关于 在 Ubuntu 20.04 上安装 Ansible 的教程来安装相应的软件包。

当您的服务器配置有上述要求时,请继续阅读本指南。

什么是 Ansible Vault?

Ansible Vault 是一种允许将加密内容透明地合并到 Ansible 工作流中的机制。 名为 ansible-vault 的实用程序通过在磁盘上加密机密数据来保护称为 secrets 的机密数据。 为了将这些机密与常规 Ansible 数据集成,ansibleansible-playbook 命令分别用于执行临时任务和结构化剧本,都支持在运行时解密保管库加密的内容。

Vault 以文件级粒度实现,这意味着单个文件要么加密要么未加密。 它使用 AES256 算法为用户提供的密码提供对称加密。 这意味着使用相同的密码来加密和解密内容,这从可用性的角度来看很有帮助。 Ansible 能够识别和解密它在执行剧本或任务时发现的任何 Vault 加密文件。

现在您已经了解了 Vault 是什么,我们可以开始讨论 Ansible 提供的工具以及如何将 Vault 与现有工作流程一起使用。

设置 Ansible Vault 编辑器

在使用 ansible-vault 命令之前,最好指定您喜欢的文本编辑器。 Vault 的一些命令涉及打开编辑器来操作加密文件的内容。 Ansible 将查看 EDITOR 环境变量以找到您喜欢的编辑器。 如果未设置,则默认为 vi

如果您不想使用 vi 编辑器进行编辑,则应在您的环境中设置 EDITOR 变量。

注意: 如果您不小心发现自己在 vi 会话中,您可以按 Esc 键,键入 :q!,然后按 [ X159X]输入。 如果您不熟悉 vi 编辑器,您所做的任何更改都可能是无意的,因此此命令退出而不保存。


要为单个命令设置编辑器,请在命令前加上环境变量分配,如下所示:

EDITOR=nano ansible-vault . . .

要使其持久化,请打开您的 ~/.bashrc 文件:

nano ~/.bashrc

通过将 EDITOR 分配添加到文件末尾来指定您的首选编辑器:

~/.bashrc

export EDITOR=nano

完成后保存并关闭文件。

再次获取文件以将更改读入当前会话:

. ~/.bashrc

显示 EDITOR 变量以检查您的设置是否已应用:

echo $EDITOR
Outputnano

现在您已经建立了首选编辑器,我们可以讨论 ansible-vault 命令可用的操作。

如何使用 ansible-vault 管理敏感文件

ansible-vault 命令是在 Ansible 中管理加密内容的主要界面。 此命令用于最初加密文件,随后用于查看、编辑或解密数据。

创建新的加密文件

要创建使用 Vault 加密的新文件,请使用 ansible-vault create 命令。 传入您要创建的文件的名称。 例如,要创建一个名为 vault.yml 的加密 YAML 文件来存储敏感变量,您可以键入:

ansible-vault create vault.yml

系统将提示您输入并确认密码:

OutputNew Vault password: 
Confirm New Vault password:

确认密码后,Ansible 将立即打开一个编辑窗口,您可以在其中输入所需的内容。

要测试加密功能,请输入一些测试文本:

保险库.yml

Secret information

当您关闭文件时,Ansible 将加密内容。 如果您检查文件,而不是看到您键入的单词,您将看到一个加密块:

cat vault.yml
Output$ANSIBLE_VAULT;1.1;AES256
65316332393532313030636134643235316439336133363531303838376235376635373430336333
3963353630373161356638376361646338353763363434360a363138376163666265336433633664
30336233323664306434626363643731626536643833336638356661396364313666366231616261
3764656365313263620a383666383233626665376364323062393462373266663066366536306163
31643731343666353761633563633634326139396230313734333034653238303166

我们可以看到 Ansible 用来了解如何处理文件的一些标头信息,然后是加密内容,显示为数字。

加密现有文件

如果您已经有想要使用 Vault 加密的文件,请改用 ansible-vault encrypt 命令。

为了测试,我们可以通过键入以下内容创建一个示例文件:

echo 'unencrypted stuff' > encrypt_me.txt

现在,您可以通过键入以下内容来加密现有文件:

ansible-vault encrypt encrypt_me.txt

同样,系统将提示您提供并确认密码。 之后,一条消息将确认加密:

OutputNew Vault password: 
Confirm New Vault password:
Encryption successful

ansible-vault 不会打开编辑窗口,而是会加密文件的内容并将其写回磁盘,替换未加密的版本。

如果我们检查文件,我们应该会看到类似的加密模式:

cat encrypt_me.txt
Output$ANSIBLE_VAULT;1.1;AES256
66633936653834616130346436353865303665396430383430353366616263323161393639393136
3737316539353434666438373035653132383434303338640a396635313062386464306132313834
34313336313338623537333332356231386438666565616537616538653465333431306638643961
3636663633363562320a613661313966376361396336383864656632376134353039663662666437
39393639343966363565636161316339643033393132626639303332373339376664

如您所见,Ansible 加密现有内容的方式与加密新文件的方式大致相同。

查看加密文件

有时,您可能需要引用保管库加密文件的内容,而无需对其进行编辑或将其写入未加密的文件系统。 ansible-vault view 命令将文件的内容提供给标准输出。 默认情况下,这意味着内容显示在终端中。

将 Vault 加密文件传递给命令:

ansible-vault view vault.yml

您将被要求输入文件的密码。 输入成功后会显示内容:

OutputVault password:
Secret information

如您所见,密码提示混合到文件内容的输出中。 在自动化流程中使用 ansible-vault view 时请记住这一点。

编辑加密文件

当需要编辑加密文件时,使用ansible-vault edit命令:

ansible-vault edit vault.yml

系统将提示您输入文件的密码。 输入后,Ansible 将打开文件一个编辑窗口,您可以在其中进行任何必要的更改。

保存后,新内容将再次使用文件的加密密码加密并写入磁盘。

手动解密加密文件

要解密 Vault 加密文件,请使用 ansible-vault decrypt 命令。

注意: 由于意外将敏感数据提交到项目存储库的可能性增加,仅当您希望从文件中永久删除加密时才建议使用 ansible-vault decrypt 命令。 如果您需要查看或编辑 Vault 加密文件,通常最好分别使用 ansible-vault viewansible-vault edit 命令。


传入加密文件的名称:

ansible-vault decrypt vault.yml

系统将提示您输入文件的加密密码。 输入正确密码后,文件将被解密:

OutputVault password:
Decryption successful

如果您再次查看文件,而不是保管库加密,您应该会看到文件的实际内容:

cat vault.yml
OutputSecret information

您的文件现在在磁盘上未加密。 完成后,请务必删除任何敏感信息或重新加密文件。

更改加密文件的密码

如果您需要更改加密文件的密码,请使用 ansible-vault rekey 命令:

ansible-vault rekey encrypt_me.txt

输入命令时,首先会提示您输入文件的当前密码:

OutputVault password:

输入后,系统会要求您选择并确认新的保管库密码:

OutputVault password:
New Vault password:
Confirm New Vault password:

当您成功确认新密码后,您将收到一条提示重新加密过程成功的消息:

OutputRekey successful

现在应该可以使用新密码访问该文件。 旧密码将不再有效。

使用 Vault 加密文件运行 Ansible

使用 Vault 加密敏感信息后,您可以开始使用 Ansible 的常规工具使用这些文件。 ansibleansible-playbook 命令都知道如何在给定正确密码的情况下解密受保险库保护的文件。 根据您的需要,有几种不同的方法可以为这些命令提供密码。

要继续进行,您将需要一个保管库加密的文件。 您可以通过键入以下内容创建一个:

ansible-vault create secret_key

选择并确认密码。 填写您想要的任何虚拟内容:

密钥

confidential data

保存并关闭文件。

我们还可以创建一个临时的 hosts 文件作为清单:

nano hosts

我们将只添加 Ansible localhost。 为了准备后面的步骤,我们将它放在 [database] 组中:

主机

[database]
localhost ansible_connection=local

完成后保存并关闭文件。

接下来,在当前目录中创建一个 ansible.cfg 文件(如果尚不存在):

nano ansible.cfg

现在,只需添加一个 [defaults] 部分并将 Ansible 指向我们刚刚创建的清单:

ansible.cfg

[defaults]
inventory = ./hosts

准备好后,继续。

使用交互式提示

在运行时解密内容最直接的方法是让 Ansible 提示您输入适当的凭据。 您可以通过将 --ask-vault-pass 添加到任何 ansibleansible-playbook 命令来执行此操作。 Ansible 将提示您输入密码,该密码将用于尝试解密它找到的任何受保险库保护的内容。

例如,如果我们需要将保管库加密文件的内容复制到主机,我们可以使用 copy 模块和 --ask-vault-pass 标志来完成。 如果文件实际上包含敏感数据,您很可能希望通过权限和所有权限制锁定远程主机上的访问。

注意: 我们在本例中使用 localhost 作为目标主机,以尽量减少所需的服务器数量,但结果应该与主机真正远程一样:


ansible --ask-vault-pass -bK -m copy -a 'src=secret_key dest=/tmp/secret_key mode=0600 owner=root group=root' localhost

我们的任务指定文件的所有权应更改为 root,因此需要管理权限。 -bK 标志告诉 Ansible 提示输入目标主机的 sudo 密码,因此您将被要求输入 sudo 密码。 然后将要求您输入 Vault 密码:

OutputBECOME password:
Vault password:

提供密码后,Ansible 将尝试执行任务,并使用它找到的任何加密文件的 Vault 密码。 请记住,执行期间引用的所有文件必须使用相同的密码:

Outputlocalhost | SUCCESS => {
    "changed": true, 
    "checksum": "7a2eb5528c44877da9b0250710cba321bc6dac2d", 
    "dest": "/tmp/secret_key", 
    "gid": 0, 
    "group": "root", 
    "md5sum": "270ac7da333dd1db7d5f7d8307bd6b41", 
    "mode": "0600", 
    "owner": "root", 
    "size": 18, 
    "src": "/home/sammy/.ansible/tmp/ansible-tmp-1480978964.81-196645606972905/source", 
    "state": "file", 
    "uid": 0
}

提示输入密码是安全的,但可能很乏味,尤其是在重复运行时,并且还会阻碍自动化。 值得庆幸的是,这些情况有一些替代方案。

将 Ansible Vault 与密码文件一起使用

如果您不想在每次执行任务时都输入 Vault 密码,您可以将 Vault 密码添加到文件并在执行期间引用该文件。

例如,您可以将密码放在 .vault_pass 文件中,如下所示:

echo 'my_vault_password' > .vault_pass

如果您使用版本控制,请确保将密码文件添加到版本控制软件的忽略文件中,以避免意外提交:

echo '.vault_pass' >> .gitignore

现在,您可以改为引用该文件。 --vault-password-file 标志在命令行上可用。 我们可以通过键入以下内容来完成上一节中的相同任务:

ansible --vault-password-file=.vault_pass -bK -m copy -a 'src=secret_key dest=/tmp/secret_key mode=0600 owner=root group=root' localhost

这次不会提示您输入 Vault 密码。

Outputlocalhost | SUCCESS => {
    "changed": false,
    "checksum": "52d7a243aea83e6b0e478db55a2554a8530358b0",
    "dest": "/tmp/secret_key",
    "gid": 80,
    "group": "root",
    "mode": "0600",
    "owner": "root",
    "path": "/tmp/secret_key",
    "size": 8,
    "state": "file",
    "uid": 0
}

自动读取密码文件

为了完全避免提供标志,您可以使用密码文件的路径设置 ANSIBLE_VAULT_PASSWORD_FILE 环境变量:

export ANSIBLE_VAULT_PASSWORD_FILE=./.vault_pass

您现在应该能够在当前会话没有 --vault-password-file 标志的情况下执行命令:

ansible -bK -m copy -a 'src=secret_key dest=/tmp/secret_key mode=0600 owner=root group=root' localhost

要让 Ansible 了解跨会话的密码文件位置,您可以编辑 ansible.cfg 文件。

打开我们之前创建的本地 ansible.cfg 文件:

nano ansible.cfg

[defaults] 部分中,设置 vault_password_file 设置。 指向密码文件的位置。 这可以是相对路径或绝对路径,具体取决于哪个对您最有用:

ansible.cfg

[defaults]
. . .
vault_password_file = ./.vault_pass

现在,当您运行需要解密的命令时,将不再提示您输入保管库密码。 作为奖励,ansible-vault 不仅会使用文件中的密码来解密任何文件,而且会在使用 ansible-vault createansible-vault encrypt 创建新文件时应用密码。

从环境变量中读取密码

您可能会担心不小心将密码文件提交到存储库。 虽然 Ansible 有一个环境变量来指向密码文件的位置,但它没有用于设置密码本身的环境变量。

但是,如果您的密码文件是可执行的,Ansible 会将其作为脚本运行并使用生成的输出作为密码。 在 GitHub 问题 中,Brian Schwind 建议可以使用以下脚本从环境变量中提取密码。

在编辑器中打开 .vault_pass 文件:

nano .vault_pass

将内容替换为以下脚本:

.vault_pass

#!/usr/bin/env python3

import os
print os.environ['VAULT_PASSWORD']

通过键入以下命令使文件可执行:

chmod +x .vault_pass

然后,您可以设置和导出 VAULT_PASSWORD 环境变量,该变量可用于您当前的会话:

export VAULT_PASSWORD=my_vault_password

您必须在每个 Ansible 会话开始时执行此操作,这听起来可能不方便。 但是,这可以有效地防止意外提交您的 Vault 加密密码,这可能会带来严重的缺陷。

将 Vault 加密变量与常规变量一起使用

虽然 Ansible Vault 可以用于任意文件,但它最常用于保护敏感变量。 我们将通过一个示例向您展示如何将常规变量文件转换为平衡安全性和可用性的配置。

设置示例

对于此示例,我们将假装我们正在配置数据库服务器,而不需要实际安装数据库作为先决条件。

当您之前创建 hosts 文件时,您将 localhost 条目放在名为 database 的组中以准备此步骤。 数据库通常需要敏感和非敏感变量的混合。 这些可以在以组命名的文件中的 group_vars 目录中分配:

mkdir -p group_vars
nano group_vars/database

group_vars/database 文件中,我们将添加一些典型的变量。 一些变量,如 MySQL 端口号,不是秘密的,可以自由共享。 其他变量,如数据库密码,将是保密的:

group_vars/数据库

---
# nonsensitive data
mysql_port: 3306
mysql_host: 10.0.0.3
mysql_user: fred

# sensitive data
mysql_password: supersecretpassword

我们可以使用 Ansible 的 debug 模块和 hostvars 变量来测试主机是否可以使用所有变量:

ansible -m debug -a 'var=hostvars[inventory_hostname]' database
Outputlocalhost | SUCCESS => {
    "hostvars[inventory_hostname]": {
        "ansible_check_mode": false, 
        "ansible_version": {
            "full": "2.2.0.0", 
            "major": 2, 
            "minor": 2, 
            "revision": 0, 
            "string": "2.2.0.0"
        }, 
        "group_names": [
            "database"
        ], 
        "groups": {
            "all": [
                "localhost"
            ], 
            "database": [
                "localhost"
            ], 
            "ungrouped": []
        }, 
        "inventory_dir": "/home/sammy", 
        "inventory_file": "hosts", 
        "inventory_hostname": "localhost", 
        "inventory_hostname_short": "localhost", 
        "mysql_host": "10.0.0.3",
        "mysql_password": "supersecretpassword",
        "mysql_port": 3306,
        "mysql_user": "fred",
        "omit": "__omit_place_holder__1c934a5a224ca1d235ff05eb9bda22044a6fb400", 
        "playbook_dir": "."
    }
}

输出确认我们设置的所有变量都应用于主机。 但是,我们的 group_vars/database 文件当前包含我们所有的变量。 这意味着我们可以不加密,这是因为数据库密码变量的安全问题,或者我们加密所有变量,这会产生可用性和协作问题。

将敏感变量移动到 Ansible Vault

为了解决这个问题,我们需要区分敏感变量和非敏感变量。 我们应该能够加密机密值,同时轻松共享我们的非敏感变量。 为此,我们将在两个文件之间拆分变量。

可以使用变量 directory 代替 Ansible 变量 file 来应用来自多个文件的变量。 我们可以重构我们的配置以利用这种能力。 首先,将现有文件从 database 重命名为 vars。 这将是我们未加密的变量文件:

mv group_vars/database group_vars/vars

接下来,创建一个与旧变量文件同名的目录。 将 vars 文件移到里面:

mkdir group_vars/database
mv group_vars/vars group_vars/database/

我们现在有一个 database 组的变量目录而不是单个文件,并且我们有一个未加密的变量文件。 由于我们将加密我们的敏感变量,我们应该将它们从我们未加密的文件中删除。 编辑 group_vars/database/vars 文件以删除机密数据:

nano group_vars/database/vars

在这种情况下,我们要删除 mysql_password 变量。 该文件现在应该如下所示:

group_vars/数据库/vars

---
# nonsensitive data
mysql_port: 3306
mysql_host: 10.0.0.3
mysql_user: fred

接下来,在与未加密的 vars 文件并存的目录中创建一个 Vault 加密文件:

ansible-vault create group_vars/database/vault

在此文件中,定义以前在 vars 文件中的敏感变量。 使用相同的变量名,但添加字符串 vault_ 以指示这些变量是在受保险库保护的文件中定义的:

group_vars/数据库/保险库

---
vault_mysql_password: supersecretpassword

完成后保存并关闭文件。

生成的目录结构如下所示:

.
├── . . .
├── group_vars/
│   └── database/
│       ├── vars
│       └── vault
└── . . .

此时,变量是分开的,只有机密数据被加密。 这是安全的,但我们的实现影响了我们的可用性。 虽然我们的目标是保护敏感的 ,但我们也无意中降低了对实际变量名称的可见性。 不清楚在不引用多个文件的情况下分配了哪些变量,虽然您可能希望在协作时限制对机密数据的访问,但您仍可能希望共享变量名称。

为了解决这个问题,Ansible 项目通常推荐一种稍微不同的方法。

从未加密的变量中引用 Vault 变量

当我们将敏感数据移至受保险库保护的文件时,我们在变量名称前加上 vault_mysql_password 变为 vault_mysql_password)。 我们可以将原始变量名称 (mysql_password) 添加回未加密的文件中。 我们可以使用 Jinja2 模板语句从我们未加密的变量文件中引用加密的变量名称,而不是直接将这些设置为敏感值。 这样,您可以通过引用单个文件来查看所有定义的变量,但机密值将保留在加密文件中。

为了演示,再次打开未加密的变量文件:

nano group_vars/database/vars

再次添加 mysql_password 变量。 这一次,使用 Jinja2 模板来引用受保险库保护的文件中定义的变量:

group_vars/数据库/vars

---
# nonsensitive data
mysql_port: 3306
mysql_host: 10.0.0.3
mysql_user: fred

# sensitive data
mysql_password: "{{ vault_mysql_password }}"

mysql_password 变量将设置为在 Vault 文件中定义的 vault_mysql_password 变量的值。

使用此方法,您可以通过查看 group_vars/database/vars 文件了解将应用于 database 组中的主机的所有变量。 敏感部分将被 Jinja2 模板遮挡。 group_vars/database/vault 仅在需要查看或更改值本身时才需要打开。

您可以检查以确保所有 mysql_* 变量仍然使用与上次相同的方法正确应用。

注意: 如果您的 Vault 密码没有通过密码文件自动应用,请将 --ask-vault-pass 标志添加到以下命令中。


ansible -m debug -a 'var=hostvars[inventory_hostname]' database
Outputlocalhost | SUCCESS => {
    "hostvars[inventory_hostname]": {
        "ansible_check_mode": false, 
        "ansible_version": {
            "full": "2.2.0.0", 
            "major": 2, 
            "minor": 2, 
            "revision": 0, 
            "string": "2.2.0.0"
        }, 
        "group_names": [
            "database"
        ], 
        "groups": {
            "all": [
                "localhost"
            ], 
            "database": [
                "localhost"
            ], 
            "ungrouped": []
        }, 
        "inventory_dir": "/home/sammy/vault", 
        "inventory_file": "./hosts", 
        "inventory_hostname": "localhost", 
        "inventory_hostname_short": "localhost", 
        "mysql_host": "10.0.0.3",
        "mysql_password": "supersecretpassword",
        "mysql_port": 3306,
        "mysql_user": "fred",
        "omit": "__omit_place_holder__6dd15dda7eddafe98b6226226c7298934f666fc8", 
        "playbook_dir": ".", 
        "vault_mysql_password": "supersecretpassword"
    }
}

vault_mysql_passwordmysql_password 均可访问。 这种复制是无害的,不会影响您对本系统的使用。

结论

您的项目应该具有成功安装和配置复杂系统所需的所有信息。 但是,某些配置数据根据定义是敏感的,不应公开。 在本指南中,我们演示了 Ansible Vault 如何加密机密信息,以便您可以将所有配置数据保存在一个位置,而不会影响安全性。