Ansible with multiple vault ID's


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.

Tower with vault IDs

See also