ansibleansible-vault

Ansible Roles and vars/vault.yml file


I usually use encrypt_string for encrypting secrets in my ansible roles but I have a new boss and he insists on encrypting the whole file but I cannot not seem to get that to work with an Ansible role.

Here is a simple example that illustrates what my problem is.

bash-3.2$ tree
.
├── playbooks
│   └── run_foo.yml
└── roles
    └── foo
        ├── tasks
        │   └── main.yml
        └── vars
            ├── main.yml
            └── vault.yml

5 directories, 4 files

Here is a playbook that uses the role.

bash-3.2$ cat playbooks/run_foo.yml
---
- name: Run Foo
  hosts: all
  roles:
    - role: ../roles/foo

Here's the role:

bash-3.2$ cat roles/foo/tasks/main.yml
---
# tasks file for foo
- name: Show us debug clear_text
  debug:
    var: clear_text
- name: Show us debug password
  debug:
    var: password

Here is my vars/main.yml file:

bash-3.2$ cat roles/foo/vars/main.yml
---
# vars file for foo
include_vars: vault.yml
clear_text: 12345

Here is my encrypted vault.yml file:

bash-3.2$ cat roles/foo/vars/vault.yml
$ANSIBLE_VAULT;1.1;AES256
64653334323939303461653839353634666162383130326533306234636232656162306661383761
6339376233366638643331373831316638373263663830350a333733653039353939656633376333
31323232666633373633393032396232613830393735396139376333353035633566376465636536
3930633964633861620a633735343066313938633733303538333864353665393062626338356665
63363666636638623964336461346366323565323563316434323439626239633734

This is what it looks like decrypted:

bash-3.2$ ansible-vault view roles/foo/vars/vault.yml
Vault password:
password: hovercraft

Here's what happens when I run the playbook:


bash-3.2$ ansible-playbook -i,localhost playbooks/run_foo.yml --connection=local --ask-vault-pass
Vault password:

PLAY [Run Foo] ******************************************************************************************************************************************************************************

TASK [Gathering Facts] **********************************************************************************************************************************************************************
[WARNING]: error loading fact - please check content
[WARNING]: Platform darwin on host localhost is using the discovered Python interpreter at /Library/Frameworks/Python.framework/Versions/3.6/bin/python3.6, but future installation of
another Python interpreter could change the meaning of that path. See https://docs.ansible.com/ansible/2.10/reference_appendices/interpreter_discovery.html for more information.
ok: [localhost]

TASK [../roles/foo : Show us debug clear_text] **********************************************************************************************************************************************
ok: [localhost] => {
    "clear_text": 12345
}

TASK [../roles/foo : Show us debug password] ************************************************************************************************************************************************
ok: [localhost] => {
    "password": "VARIABLE IS NOT DEFINED!"
}

PLAY RECAP **********************************************************************************************************************************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Is it possible to use a encrypted vault.yml file like my new manager wants?


Solution

  • Put both files into the directory vars/main. All files in vars/main are included automatically. For example,

    shell> tree .
    .
    ├── ansible.cfg
    ├── hosts
    ├── roles
    │   └── foo
    │       ├── tasks
    │       │   └── main.yml
    │       └── vars
    │           └── main
    │               ├── main.yml
    │               └── vault.yml
    └── run_foo.yml
    
    shell> cat roles/foo/tasks/main.yml 
    - debug:
        var: clear_text
    - debug:
        var: password
    
    shell> cat roles/foo/vars/main/main.yml 
    clear_text: 12345
    
    shell> cat roles/foo/vars/main/vault.yml 
    password: hovercraft
    
    shell> cat run_foo.yml 
    - hosts: all
      roles:
        - foo
    

    gives

    shell> cat hosts
    localhost
    
    shell> ansible-playbook run_foo.yml 
    
    PLAY [all] ***********************************************************************************
    
    TASK [foo : debug] ***************************************************************************
    ok: [localhost] => 
      clear_text: 12345
    
    TASK [foo : debug] ***************************************************************************
    ok: [localhost] => 
      password: hovercraft
    
    PLAY RECAP ***********************************************************************************
    localhost: ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    

    You'll get the same result with encrypted vault.yml. Encrypting the whole file is simpler and safer. Instead of the code, you update and encrypt this single file only. In addition to this, a use case is possible where each user maps its own vault.yml overlay.

    Notes

    1. Use the role's directory defaults

    Don't distribute roles with the directory vars/main or file vars/main.yml. The purpose of the role's vars is to override the role's defaults. See Understanding variable precedence. An update of a role will overwrite potential user's customization in vars if you distribute it. In development, ignore vars/main in your version control.

    Put vault.yml into the directory defaults

    shell> cat roles/foo/defaults/main/vault.yml 
    password: hovercraft
    

    and test the password

    shell> cat roles/foo/tasks/main.yml
    - assert:
        that: password != 'hovercraft'
        fail_msg: Change and encrypt password!
      when: assert_password|d(true)|bool
    
    - debug:
        var: clear_text
    - debug:
        var: password
    

    By default, the user'll see

    shell> ansible-playbook run_foo.yml 
    
    PLAY [all] ***********************************************************************************
    
    TASK [foo : assert] **************************************************************************
    fatal: [localhost]: FAILED! => changed=false 
      assertion: password != 'hovercraft'
      evaluated_to: false
      msg: Change and encrypt password!
    

    Turn the assert off for testing

    shell> ansible-playbook run_foo.yml -e assert_password=false
    
    1. Limit the scope of secrets

    You can limit the scope of secrets to the tasks where you need them. For example, put the password to a file

    shell> cat vars/password.yml 
    my secret password
    

    and read it in the tasks

    shell> cat roles/foo/tasks/main.yml 
    - assert:
        that: password != 'hovercraft'
        fail_msg: Change and encrypt password!
      when: assert_password|d(true)|bool
      vars:
        password: "{{ lookup('file', password_path) }}"
    
    - debug:
        var: clear_text
    - debug:
        var: password
      vars:
        password: "{{ lookup('file', password_path) }}"
    

    Provide the path to the file, for example, in the playbook

    shell> cat run_foo.yml 
    - hosts: all
      vars:
        password_path: "{{ playbook_dir }}/vars/password.yml"
      roles:
        - foo
    

    gives

    shell> ansible-playbook run_foo.yml
    
    PLAY [all] ***********************************************************************************
    
    TASK [foo : assert] **************************************************************************
    ok: [localhost] => changed=false 
      msg: All assertions passed
    
    TASK [foo : debug] ***************************************************************************
    ok: [localhost] => 
      clear_text: 12345
    
    TASK [foo : debug] ***************************************************************************
    ok: [localhost] => 
      password: my secret password
    
    PLAY RECAP ***********************************************************************************
    localhost: ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    

    The encryption of the files is transparent to the file lookup. This also solves your other problem to "use grep to find things in the code"

    shell> grep -r password: roles/
    roles/foo/defaults/main/vault.yml:password: hovercraft
    roles/foo/tasks/main.yml:    password: "{{ lookup('file', password_path) }}"
    roles/foo/tasks/main.yml:    password: "{{ lookup('file', password_path) }}"