Moving a plaintext Ansible password into the vault without ever printing it
Encrypt one value with ansible-vault encrypt_string into its own small vars file, so the big encrypted vault stays untouched, and prove nothing changed by comparing hashes per host.
My inventory had grown a bad habit. Nine hosts (hypervisors, Raspberry Pis, the
router, the NAS) shared one SSH password, and hosts.yml had it in plain text
nine times. One older host had its own root password in there too. The repo is
private, but that’s not a reason.
I wanted the values in Ansible Vault, without seeing them on screen or in a log,
and without touching the existing vault.yaml, which is encrypted as a whole
file.
One value, its own file
A fully encrypted vault file has to be decrypted, edited and re-encrypted to add anything, and every edit rewrites the whole thing. For a single secret it’s easier to put an inline vaulted value into a separate vars file:
read -rs PW
printf '%s' "$PW" | ansible-vault encrypt_string --stdin-name vault_ansible_password \
>> inventory/group_vars/all/ssh.yml
unset PW--stdin-name names the variable and reads the value from stdin, so it never
appears in the command line or the shell history. The file then holds a !vault
block, and hosts.yml refers to it:
ansible_password: "{{ vault_ansible_password }}"For the host with its own password I did the same into its host_vars file.
There, a short script read the value straight out of hosts.yml, piped it into
encrypt_string and rewrote the line, so I never had to type it at all. The
file used CRLF line endings, and the script kept them.
Prove it without looking
How do you check that every host still gets the same password, when you don’t want to print it? Hash it, and print a prefix of the hash:
ansible all -m debug \
-a 'msg={{ ansible_password | hash("sha256") | truncate(16, True, "") }}'I ran that before and after the change and compared the two outputs. Same
hashes on all nine hosts, and a real ansible <host> -m ping afterwards
confirmed that the vault decrypts at run time.
Two smaller things bit me on the way. ansible <name> matches inventory host
and group names, not a hostname: variable, so some hosts had to be addressed by
the name they actually have in the inventory. And ansible-vault only lived in
the project’s virtualenv, so a bare ansible-vault from my PATH didn’t run at all.
What vaulting doesn’t do
The old password is still in the git history, and it still works on every one of those hosts. Encrypting it in the current tree stops new copies, and that’s all it does. The real fix is to rotate it, which is still on my list.