In our work environment we have role-based access for passwords (of course). But as we deploy all systems with Ansible, we could end up that someone with only deploy permission ends up with access to all passwords. It’s obvious that we don’t want that, so I started checking in to Ansible’s ability to have multiple vault passwords.
Ansible Vault IDs
Starting with Ansible 2.4 and above, vault IDs are supported.
Vault IDs help in encrypting different files with different passwords to be referenced inside a playbook. Prior to Ansible 2.4, only one vault password could be used in each Ansible run, forcing to encrypt all files using the same vault password.
First and foremost, Vault IDs need to be pre-created and referenced
(best practice) inside your ansible.cfg
file
[defaults] vault_identity_list = apple@prompt, pear@prompt
In this example there are two vault IDs, called apple
and pear
and
in this configuration Ansible will prompt
for the needed passwords.
It’s also possible to supply the vault password files, like
[defaults] vault_identity_list = apple@~/.vault_apple, pear@~/.vault_pear
As we are using Team Password Manager (TPM) with a heavily modified TPM lookup plugin (we can retrieve passwords with a certificate/key combination, without supplying passwords), we just need to decrypt the certificate and key to a certain location and depending on the certificate/key combination the TPM user has rights to retrieve a certain set of passwords.
Encrypt certificates and keys
In this proof of concept I only show how it’s done, no certificates and keys are shown :-)
I created two files (in the files
directory, named apple_file
and
pear_file
with contents like
This file is encrypted with apple
and
This file is encrypted with pear
These files are encrypted with password for apple
and pear
respectively.
Play with it
Now a playbook should be in place that makes sure the correct certificate/key combination is present in the spot where the TPM plugin expects it.
---
- name: test multiple Ansible vault IDs
hosts: localhost
connection: local
gather_facts: no
vars:
pwd: "{{ lookup('env', 'PWD') }}"
crt: "{{ pwd }}/../results/file_dec"
tasks:
- name: ensure pear test file
copy:
src: files/pear_file
dest: "{{ crt }}"
mode: 0600
decrypt: yes
changed_when: false
ignore_errors: true
register: vault_pear
- name: ensure apple test file
copy:
src: files/apple_file
dest: "{{ crt }}"
mode: 0600
decrypt: yes
changed_when: false
register: vault_apple
ignore_errors: true
when: vault_pear is failed
- name: fail if no decryption was possible
fail:
msg: No certificates could be decrypted
when: vault_pear is failed and vault_apple is failed
This playbook tries to copy the file files/pear_file
to the
destination, decrypting it on the fly. But this could fail, because it
could be possible the user running the playbook does not have full
rights in TPM, but only deploy rights.
If the first task fails, the second task (ensure apple test file
) is
run and the file files/apple_file
is placed where it should be, with
decryption as we go.
If both tasks fail the playbook stops with an error-message.
Encrypting strings
When you don’t want to encrypt complete files, but just strings, you can use
printf "String to encrypt" \
ansible-vault \
encrypt_string \
--encrypt-vault-id=apple \
--stdin-name="enc_string"
With result
New vault password (apple): Confirm new vault password (apple): New vault password (pear): Confirm new vault password (pear): Reading plaintext input from stdin. (ctrl-d to end input) enc_string: !vault | $ANSIBLE_VAULT;1.2;AES256;apple 32336536313562333263613433343439663534643661393965316334303362633134396266396561 6135636436623437376236616339353762373736306431310a623335623835616365666361623764 61323136666438613932396237323361393938366438633363623731656236333166373263616631 6434326461386639660a663263613361343764663737386331356438663866646332333838396361 62303034303537656637343838663135656161656538383031363564303364363539 Encryption successful
As you can see the encryption line contain four fields:
-
$ANSIBLE_VAULT
- indication this is a encrypted file -
1.2
- The vault version which supports vault id -
AES256
- AES cipher in 256bits. -
apple
- vault id in use.
Vault IDs in Tower
Starting with Ansible Tower 3.3 vault IDs are also supported. You can use the vault IDs while creating a credential of type Vault.