Using Ansible Vault
The purpose of this lab is to show how to use Ansible Vault to protect sensitive data, like passwords, when working with Ansible.
vTeam Specialization Program
Pure Storage nominated me recently to join the Pure Storage vTeam Specialization program for New Stack. The idea behind the program is, to create an active community within Pure Storage. Allowing Puritans to learn and develop their skills and grow into a subject matter experts.
The program consists of training and lab exercises that are focussed on developing experience in the New Stack space (Kubernetes, Ansible, Open Stack and more).
Since I think there are more people out there how want to learn more about New Stack, I will blog my progress in a series of lab exercises.
Lab instructions
The purpose of this lab is to use of Ansible Vault to protect sensitive data in Ansible. This can for example be used to protect the API tokens used to access FlashBlade and FlashArray.
Name: | Ansible Vault |
Description: | Create a role to manage a FA or FB using Vault |
Objective: | Be able to use vault to hide API tokens |
Task #1: | Create a vault |
Task #2: | Create a password file |
Task #3: | Enter an API token into the vault |
Task #4: | Use the encrypted vault key in a simple playbook |
Task #5: | Run playbook to get info from the array |
Intro to Ansible Vault
Ansible Vault is designed to protect sensitive data against exposure. So basically encrypting usernames, passwords and other sensitive data. It uses Vaults for that. Within a Vault you can store the same content as a regular yaml file.
To use Ansible Vault in a playbook there is a Best Practice from Ansible that suggests to use host_vars and group_vars. So before we dive into using Ansible Vault, lets first quickly explore host_vars and group_vars.
Using host_vars and group_vars
As we have seen in an earlier blog, we can use inventories to define our hosts and groups to use in our playbook. Once these hosts and/or groups have been defined, you can automatically load variables when you target a specific host or group.
To do this, create a directory host_vars
or group_vars
in the directory where your playbook is located. Now in these directories you can create files with the same names as the hosts or groups.
In my case I’ll be running a playbook against localhost
to run the purefa_info
module. So I create a file called host_vars/localhost
and add the following content (I’ve obfuscated the API token for obvious reasons):
--- fa1_url: 192.168.10.12 fa1_api_token: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx fa2_url: 192.168.10.15 fa2_api_token: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Reference our host_vars from a playbook
Next I create a simple playbook containing the following:
- name: Ansible test script hosts: localhost gather_facts: true collections: - purestorage.flasharray tasks: - name: Get info purefa_info: gather_subset: - minimum fa_url: "{{ fa1_url }}" api_token: "{{ fa1_api_token }}" register: array_info - name: Show info debug: msg: "{{ array_info }}"
A simple playbook that runs two tasks, the purefa_info
module to collect data from FlashArray and the debug
module to show me the data that was collected. However you’ll notice that I have not defined any variables, but I do reference the fa1_url
and fa1_api_token
variables in the playbook. These variables get loaded automatically from the host_vars
that I’ve defined.
If you run the playbook, you’ll that it executes just fine. So that is the preferred way to access variables in Ansible, if not located in the playbook itself.
Now that we’ve seen how to use variables the correct way, let’s go ahead an put them into a Vault!
Creating a Vault
The first step in using Ansible Vault is to create an actual Vault. Which raises the question what a Vault is? Basically a Vault is a file or variable that has been encrypted by Ansible Vault. Any Vault is encrypted and decrypted as a whole so you generally will have multiple Vaults for multiple hosts, groups or environments.
For this lab we’ll be creating a Vault that will store our FlashBlade and FlashArray IP addresses and API tokens for our localhost
. For this we’ll remove the existing localhost
file and recreate it using the following commands:
rm host_vars/localhost ansible-vault create host_vars/localhost
The command will prompt for a password to be entered twice, which is used to encrypt our Vault. Once we’ve entered the password, an editor is opened, so that we can add content to our new Vault.
For this lab we will use the exact same text that we did earlier to create our host_vars/localhost
file. Now save the file.
If we open the file with a regular file editor, we’ll see the first line as $ANSIBLE_VAULT;1.1;AES256
and after that a random sequence of numbers, which if the encrypted data.
The data in the Vault is now no longer visible to users who do not have the password. To modify the contents of the Vault, we can use the following command:
ansible-vault edit host_vars/localhost
After we enter our password, we can change the contents using our editor.
Using our Vault in our playbook
Since we’ve used the same file name as we did before, the Vault will automatically be loaded when we run our Ansible playbook. However we need to specify a password to descrypt the Vault, so running the same command:
ansible-playbook purefa_info.yaml
Will return an error:
ERROR! Attempting to decrypt but no vault secrets found
So we need to add the parameter --ask-vault-pass
as so:
ansible-playbook purefa_info.yaml --ask-vault-pass
Now when we run the playbook, Ansible will prompt us for the Vault password and once entered our playbook continues in the same way as before when we weren’t using Vault.
Showing Vault variable in vars
In some cases you’ll be combining regular variable with variables stored in the Vault. In this case it might make sense to show all the configurable variables in a single file, while the sensitive variables are stored in the Vault. For this Ansible states that the best practice is to use a slightly different setup.
First remove the Vault we’ve created before and create a directory for the host to use (in our case still localhost
):
rm host_vars/localhost mkdir -p host_vars/localhost
Now we create our Vault, where we will now only store our API tokens:
ansible-vault create host_vars/localhost/vault
And we will add the following lines (obviously you’ll want to enter the actual API tokens here). Note that we changed the variable name from fa1_api_token
to vault_fa1_api_token
here.
--- vault_fa1_api_token: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx vault_fa2_api_token: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Next we want to create our plain text variables file:
nano host_vars/localhost/vars
And we add the following lines:
--- fa1_url: 10.225.112.80 fa1_api_token: "{{ vault_fa1_api_token }}" fa2_url: 10.225.112.180 fa2_api_token: "{{ vault_fa2_api_token }}"
The fa1_url
and fa2_url
are just regular variables that are visible to anyone with access to the playbook. However the fa1_api_token
point to the vault_fa1_api_token
using Jinja2. This way the variable will resolve by looking up the variable that is stored in the Vault.
We can test our playbook using the exact same command as before:
ansible-playbook purefa_info.yaml --ask-vault-pass
The advantage of this approach that anyone can see the variables that have been defined for the playbook. However to actually access the sensitive information (in this case the API tokens), you’ll need to access the Ansible Vault using the password.
Using a Vault password file
In addition to the --ask-vault-pass
parameter that we’ve seen so far, Ansible also allows us to store out Vault password in a file.
To use a password file, just create a file and put the password in the file in plain text. I will use ~/vault-passwd
. Now we can use:
ansible-playbook purefa_info.yaml --vault-password-file ~/vault-passwd
While this method saves you from having to enter your password everytime you run Ansible, you need to make sure that the password is kept very secure, since it’s stored in plain text in the password file.
I would suggest to use strict permissions, in the same sense as used for you SSH private key, so:
chmod 600 ~/vault-passwd
Which will only allows your user access to the password file.
Conclusion
This concludes this lab, where we’ve gone through a first introduction into Ansible Vault.