Up until now, we've been storing the username and password for our switches in plain text. I mentioned a couple of times that this isn't a good idea, and now it's time to fix it.
Storing passwords in plain text might be fine in a lab, but in a real network, it's a big problem. Anyone who has access to your project directory can open the file and see the credentials for every device. And once you start storing your playbooks in Git, which you should, those passwords end up in your repository history as well, where they can stay long after you've removed them from the file.
Vault is a feature built into Ansible that lets you encrypt sensitive data, such as passwords, so it can't be read without a vault password. The best part is that Ansible decrypts it automatically when you run a playbook, so your playbooks don't need to change at all.
In this chapter, we'll encrypt the credentials for our switches, look at how to run playbooks with encrypted files, and cover a few different ways to work with Vault day to day.
Encrypting a File
We're continuing from where we left off in the previous chapter, using the same project, inventory, and playbook.
Right now, group_vars/cisco.yml contains both the connection settings and the credentials. We only need to encrypt the credentials, so the first thing we'll do is split them into their own file.
Using a group_vars Directory
So far, each group has had a single file in group_vars, such as cisco.yml. But Ansible also lets you create a directory named after the group instead. Any YAML files inside that directory are loaded for the group, as if they were one file.
Let's remove group_vars/cisco.yml and replace it with a group_vars/cisco/ directory containing two files.
group_vars/
├── cisco/
│ ├── vars.yml
│ └── vault.yml
├── core.yml
└── access.yml
vars.yml holds the normal settings that don't need to be secret.
---
ansible_network_os: cisco.ios.ios
ansible_connection: ansible.netcommon.network_cli
vault.yml holds the credentials, which we'll encrypt shortly.
---
ansible_user: admin
ansible_password: admin
If you had to add the threeansible_becomevariables earlier because your devices log you into user EXEC mode, just leave them invars.ymlas they are. We're only movingansible_userandansible_passwordin this chapter, so you don't need to change anything else. If you'd like to encrypt your enable password too, you can moveansible_become_passwordintovault.ymlas well, but it's completely optional.
The file names don't matter to Ansible, but vars.yml and vault.yml are a common convention, and they make it obvious which file contains the secrets.
Before encrypting anything, let's make sure Ansible is reading both files.
ansible-inventory --host core1
{
"ansible_connection": "ansible.netcommon.network_cli",
"ansible_host": "192.168.202.101",
"ansible_network_os": "cisco.ios.ios",
"ansible_password": "admin",
"ansible_user": "admin",
"vlans": [
{
"name": "users",
"vlan_id": 10
},
{
"name": "voice",
"vlan_id": 20
},
{
"name": "printers",
"vlan_id": 30
},
{
"name": "servers",
"vlan_id": 100
}
]
}
We can see the connection settings from vars.yml and the credentials from vault.yml, so everything is being picked up correctly.
Encrypting vault.yml
To encrypt a file, we use the ansible-vault encrypt command followed by the file name.
ansible-vault encrypt group_vars/cisco/vault.yml
New Vault password:
Confirm New Vault password:
Encryption successful
Ansible asks you to create a vault password. This is the password you'll need every time you want to use or view the encrypted file, so make sure you remember it.
If we look at the file now, the credentials are gone, and we only see encrypted text.
cat group_vars/cisco/vault.yml
$ANSIBLE_VAULT;1.1;AES256
61646236656431626132613631393635623037666665306531326634343630333662396666376466
6265326430393431623332326262323535613836393737310a386566366162643564656162326235
33643862626338653365346630303333363763656133313036313638626536303431363365326666
3732613566613138350a363831663636306263373732653538383034373737636563363263313063
30353833303236333165346539323532616432656435653334633539373666326438653762376631
3736646263313133363731326635313433333733316437623365
The first line tells Ansible that this is a vault-encrypted file, and that it's encrypted using AES256. Everything below that is the encrypted data. Without the vault password, there's no way to read it. vars.yml, on the other hand, is still in plain text, so you can still see and edit the connection settings as normal.
Running a Playbook with Vault
Now, let's try running the playbook just like before.
ansible-playbook create_vlan_vars.yml
PLAY [Configure VLANs] ********************************
[ERROR]: Attempting to decrypt but no vault secrets found.
The playbook fails straight away. Ansible found an encrypted file, but we didn't give it the vault password, so it has no way to decrypt the credentials.
To provide the vault password, add --ask-vault-pass to the command. Ansible will prompt you for the password before running the playbook.
ansible-playbook create_vlan_vars.yml --ask-vault-pass
ansible-playbook create_vlan_vars.yml --ask-vault-pass
Vault password:
PLAY [Configure VLANs] ********************************
TASK [Create VLANs] ***********************************
ok: [access3]
ok: [core1]
ok: [access2]
ok: [core2]
ok: [access1]
PLAY RECAP ********************************************
access1 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
access2 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
access3 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
core1 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
core2 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
This time, the playbook runs as normal. Ansible decrypts vault.yml in memory, uses the credentials to connect to the switches, and the file on disk stays encrypted the whole time.
The same applies to other commands that read the inventory. For example, ansible-inventory --host core1 will also fail unless you add --ask-vault-pass.
Viewing and Editing Encrypted Files
Once a file is encrypted, you can't just open it in your editor, since all you'll see is the encrypted text. Ansible Vault has a few commands that let you work with encrypted files without having to decrypt them on disk.
Viewing an Encrypted File
To see what's inside an encrypted file, use ansible-vault view. Ansible asks for the vault password and then displays the content in your terminal.
ansible-vault view group_vars/cisco/vault.yml
Vault password:
---
ansible_user: admin
ansible_password: admin
The file on disk stays encrypted. Ansible only decrypts it in memory to show it to you.
Editing an Encrypted File
If you need to change something, such as the password, use ansible-vault edit.
ansible-vault edit group_vars/cisco/vault.yml
After entering the vault password, Ansible opens the decrypted content in your editor. Make your changes, save, and close the editor, and Ansible automatically encrypts the file again.
By default, Ansible uses the editor set in your EDITOR environment variable, and if that isn't set, it falls back to vi. If you're not comfortable with vi, you can tell Ansible to use a different editor, such as nano, by adding it in front of the command.
EDITOR=nano ansible-vault edit group_vars/cisco/vault.yml
Changing the Vault Password
If you ever need to change the vault password itself, for example, when someone who knew it leaves the team, use ansible-vault rekey. Ansible asks for the current password, then the new one.
ansible-vault rekey group_vars/cisco/vault.yml
Vault password:
New Vault password:
Confirm New Vault password:
Rekey successful
This only changes the vault password. The content of the file stays the same.
Decrypting a File
Finally, if you want to turn an encrypted file back into plain text, you can use ansible-vault decrypt.
ansible-vault decrypt group_vars/cisco/vault.yml
I rarely use this, since view and edit cover almost everything you need. If you do decrypt a file, just make sure you encrypt it again afterwards, especially before committing anything to Git.
Using a Password File
Typing the vault password every time you run a playbook gets old pretty quickly. To avoid this, you can store the vault password in a file and tell Ansible to read it from there.
Creating the Password File
The password file is just a plain text file with the vault password on a single line. I'm going to create it in my home directory rather than in the project directory. This way, there's no chance of it accidentally ending up in Git along with the rest of the project.
echo 'NOT_THE_REAL_PASSWORD' > ~/.vault_pass
chmod 600 ~/.vault_pass
The chmod 600 command makes sure only your user can read the file. Since this file contains the password that unlocks all your encrypted data, you don't want anyone else on the machine to be able to read it.
Keep in mind that using echo like this saves the password in your shell history. If you want to avoid that, you can create the file using a text editor instead.
Using the Password File
To use the password file, add --vault-password-file followed by the path to the file.
ansible-playbook create_vlan_vars.yml --vault-password-file ~/.vault_pass
PLAY [Configure VLANs] ********************************
TASK [Create VLANs] ***********************************
ok: [core1]
ok: [access3]
ok: [core2]
ok: [access2]
ok: [access1]
PLAY RECAP ********************************************
access1 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
access2 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
access3 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
core1 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
core2 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
This time, Ansible doesn't prompt for a password. It reads it from the file and decrypts vault.yml automatically.
Adding the Password File to ansible.cfg
This is better, but we still have to add --vault-password-file every time. To make it even easier, we can add the path to ansible.cfg.
[defaults]
inventory = inventory.yml
host_key_checking = False
vault_password_file = ~/.vault_pass
Now, we can run the playbook without any vault options at all, just like we did before we encrypted anything.
ansible-playbook create_vlan_vars.yml
PLAY [Configure VLANs] ********************************
TASK [Create VLANs] ***********************************
ok: [access1]
ok: [access2]
ok: [core2]
ok: [access3]
ok: [core1]
PLAY RECAP ********************************************
access1 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
access2 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
access3 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
core1 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
core2 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The same applies to the other ansible-vault commands. For example, ansible-vault view no longer asks for the password either.
ansible-vault view group_vars/cisco/vault.yml
---
ansible_user: admin
ansible_password: admin
Just remember that anyone who has access to your password file can decrypt your vault files. The encrypted files are safe to share or store in Git, but the password file should always stay private.
Encrypting a Single Variable
So far, we've encrypted a whole file. This works well, but it also means you can't see anything in the file without the vault password, even the values that aren't secret. Ansible Vault also lets you encrypt a single value instead, and keep it inside a normal YAML file. The rest of the file stays readable, and only the secret is encrypted.


