我在管理 debian 盒子的 vagrant 环境中使用 ansible。由于多个 ansible 配置程序具有保管库加密数据(例如数据库根密码),我必须由第一个配置程序输入一次保管库密码。目前,此密码存储在 中/tmp
,由每个 ansible 配置程序中的脚本读取,并由/dev/null
最后一个配置程序覆盖并删除。
Ansible 能够调用返回保管库密码的脚本。所以这个脚本将在单独的 shell 中运行。
如果攻击者成功侵入虚拟机,他就有机会恢复任何临时文件并获得保管库密码。
虽然这些流浪环境将在生产中使用,但我正在寻找一种更安全的方法。我首先想到的是以某种方式读取和写入内存。因此,虚拟机重新启动会擦除内存。我知道上述数据可以以某种方式在磁盘上交换。但我认为获取这些数据比临时文件方法更困难。
编辑
我忘记提及如果配置程序失败会带来安全风险。然后最后一个配置程序将不会被执行,并且临时文件保留在文件系统上。
我提供了一个额外的答案,展示了集成解决方案,仅供对导致此问题的起源问题的解决方案感兴趣的任何人使用。
答案1
要使用以下命令创建 1 MB RAM 磁盘tmpfs
:
mkdir /tmp/ramdisk
mount -t tmpfs -o size=1m myramdisk /tmp/ramdisk
RAM盘不进行交换,但如果需要确保在使用 RAM 盘期间没有发生交换,则可以在使用RAM 盘ramfs
时分别关闭和打开交换tmpfs
:swapoff -a
和。卸载:swapon -a
tmpfs
umount /tmp/ramdisk
也许我错过了一些东西,但我没有看到在 中描述 RAM 磁盘的好处/etc/fstab
;但是,如果需要的话,如下所示的内容应该足以使 RAM 磁盘仅对用户可用root
( mode=0700
):
tmpfs /tmp/ramdisk tmpfs rw,mode=0700,size=1m
答案2
除了 @Christopher 的回答之外,我还想展示如何将解决方案集成到我的 Vagrantfile 中,以便让它与我的 ansible 配置一起工作。
可能有人会偶然发现这一点。
流浪文件
Vagrant.configure("2") do |config|
class AnsibleVaultPassword
def to_s
begin
system 'stty -echo'
print "Enter ansible vault password: "
pass = STDIN.gets.chomp
ensure
system 'stty echo'
end
pass
end
end
config.vm.box = "vendor/box-name"
config.vm.box_version = ">=1.0"
config.vm.box_url = "https://vagrant.example.com/vendor/box-name.json"
config.vm.box_download_insecure = true
config.vm.define "vendor-server-name"
config.vm.provider "virtualbox" do |provider|
provider.name = "vendor-server-name"
end
config.vm.network "private_network", ip: "192.168.0.42"
config.vm.synced_folder ".", "/vagrant", type: "nfs"
config.vm.provision "Provisioning the server `server-name` with the playbook `ansible`", type: "ansible_local" do |provisioner|
provisioner.compatibility_mode = "2.0"
provisioner.playbook = "/vagrant/ansible/ansible.yml"
provisioner.inventory_path = "/vagrant/ansible/inv/integration/hosts"
provisioner.limit = "localhost"
end
config.vm.provision "Requesting the ansible vault password", type: "shell" do |provisioner|
provisioner.env = { "ansibleVaultPassword" => AnsibleVaultPassword.new }
provisioner.inline = <<-END
[[ ! -d "/mnt/ansible-tmp" ]] \
&& mkdir "/mnt/ansible-tmp"
mountpoint -q "/mnt/ansible-tmp" \
|| mount -t tmpfs -o size=512 ansible-tmp "/mnt/ansible-tmp"
/vagrant/env/ansible/scripts/vault-password.sh --save "${ansibleVaultPassword}"
END
end
config.vm.provision "Provisioning the server `server-name` with the playbook `environment`", type: "ansible_local" do |provisioner|
provisioner.compatibility_mode = "2.0"
provisioner.playbook = "/vagrant/ansible/environment.yml"
provisioner.raw_arguments = [ "--vault-id /vagrant/ansible/scripts/vault-password.sh" ]
provisioner.inventory_path = "/vagrant/ansible/inv/integration/hosts"
provisioner.limit = "localhost"
end
config.vm.provision "Provisioning the server `server-name` with the playbook `users`", type: "ansible_local" do |provisioner|
provisioner.compatibility_mode = "2.0"
provisioner.playbook = "/vagrant/ansible/users.yml"
provisioner.raw_arguments = [ "--vault-id /vagrant/ansible/scripts/vault-password.sh" ]
provisioner.inventory_path = "/vagrant/ansible/inv/integration/hosts"
provisioner.limit = "localhost"
end
config.vm.provision "Deleting the ansible vault password", type: "shell" do |provisioner|
provisioner.inline = <<-END
/vagrant/env/ansible/scripts/vault-password.sh --delete
mountpoint -q "/mnt/ansible-tmp" \
&& umount "/mnt/ansible-tmp"
[[ -d "/mnt/ansible-tmp" ]] \
&& rm -r "/mnt/ansible-tmp"
END
end
end
/vagrant/ansible/scripts/vault-password.sh
#!/usr/bin/env bash
passwordFile="/mnt/ansible-tmp/vault-password"
case "${1}" in
"-s" | "--save" )
echo "${2}" > "${passwordFile}"
;;
"-d" | "--delete" )
unlink "${passwordFile}"
;;
* )
cat "${passwordFile}"
;;
esac
虽然它是 Debian VM,但我首先运行 playbook ansible
,添加来自 Ubuntu 的 ansible 存储库,然后安装最新的ansible
.这至少--vault-id
对于为 Vagrant 的调用提供参数是必要的ansible-playbook
。 (在这篇文章Debian Stretch
发表时,ansible 2.2
它并不支持这一论点。)
然后通过 class 请求保管库密码AnsibleVaultPassword
。随后将创建密码tmpfs
,并将密码传递给脚本vault-password.sh
以将其存储在tmpfs
安装上。
vault-password.sh
然后,ansible 将通过每个提供保管库加密数据的 playbook 中的脚本请求密码。
最后会先删除密码,然后tmpfs
再卸载挂载。
我认为这是一个非常安全可靠的解决方案,可以通过临时可擦除内存存储提供 ansible 保险库密码。因此,可以配置正在运行的系统,并且不再需要重新启动。