How to encrypt sensitive data in playbooks with Ansible Vault
Ansible Vault is a feature in the Ansible automation engine that allows you to encrypt any data file. This is very useful when you're dealing with sensitive information in your Ansible installation.
You can encrypt any structured data file used in Ansible, including:
ansible_facts
- A variable file in
host_vars
group_vars
directories- Variable files loaded by
vars_files
include_vars
keywords within a playbook- Variable files passed on the command line using the
-e
option followed by the name of the variable file (for example,-e @var_file.yml
)
Ansible Vault allows you to keep sensitive data, such as passwords and keys, in encrypted files, rather than as plain text in playbooks or roles. The ansible-vault
command is used for this purpose.
Restrict a user from viewing content in a playbook
In a previous article, I looked at implementing Jinja2 templates in playbooks to fully automate and manage Apache web servers. In this article, I'll show you how to implement encryption at the individual variable level to restrict a normal user from viewing sensitive content in a playbook. In this example, I will encrypt the ssh_port
variable and its value.
Here's a snippet from a simple playbook:
<---
- hosts: '*'
vars:
ssh_port: 2049
tasks:
- name: Tell SELinux about SSH's New Port
seport:
ports: "{{ ssh_port }}"
proto: tcp
setype: ssh_port_t
state: present
- name: Harden sshd configuration
lineinfile:
dest: /etc/ssh/sshd_config
regexp: "{{item.regexp}}"
line: "{{item.line}}"
state: present
validate: 'sshd -T -f %s'
with_items:
- regexp: "^Port"
line: "Port {{ ssh_port }}"
- regexp: "^PermitRootLogin"
line: "PermitRootLogin no"
- regexp: "^AllowUsers"
line: "AllowUsers ansible-devops"
- regexp: "^PasswordAuthentication"
line: "PasswordAuthentication no"
- regexp: "^AllowAgentForwarding"
line: "AllowAgentForwarding no"
- regexp: "^AllowTcpForwarding"
line: "AllowTcpForwarding no"
- regexp: "^MaxAuthTries"
line: "MaxAuthTries 3"
- regexp: "^MaxSessions"
line: "MaxSessions 6"
- regexp: "^TCPKeepAlive"
line: "TCPKeepAlive no"
- regexp: "^UseDNS"
line: "UseDNS no"
notify: restart sshd
- name: add user ansible-devops
user:
name: ansible-devops
- name: add sudo group rights for deployment user
lineinfile:
dest: /etc/sudoers.d/ansible-devops
regexp: "^ansible-devops"
line: "ansible-devops ALL=(ALL) NOPASSWD: ALL"
state: present
handlers:
- name: restart sshd
service:
name: sshd
state: restarted
Make sure it validates with yamllint
:
$ yamllint ssh-config.yaml || echo "Success"
Success
[ Download now: A system administrator's guide to IT automation. ]
To encrypt a value in a playbook, provide the string you want to encrypt (2049
in this example) along with the key it belongs to (ssh_port
, in this example). Use the --ask-vault-pass
option to be prompted to create a password. The output is very long, so I've truncated it for clarity:
$ ansible-vault encrypt_string --ask-vault-pass '2049' --name 'ssh_port'
New vault password:
Confirm password:
ssh_port: !vault |
$ANSIBLE_VAULT;1.1;AES256
3433313631373[...]3631
Encryption successful
Now copy the result into your playbook. You must copy everything from the key name (ssh_port
) to the end of the long string of numbers containing the encrypted data. It looks a little messy, but in the end, your playbook contains this (this playbook is truncated for brevity):
---
- hosts: '*'
vars:
ssh_port: !vault |
$ANSIBLE_VAULT;1.1;AES256
65633138303133363034333734653734383235353564393264326532376433336137363263353837
6233663762623833393432653438616263396565316365330a376630353530643434653539323834
63386637366462386535636536613032376539633661653462636461613037636131343736623561
6137663533333432390a373563653837386165666633396464343565303766653738656361363237
3234
tasks:
- name: Tell SELinux about SSH's New Port
seport:
ports: "{{ ssh_port }}"
proto: tcp
setype: ssh_port_t
state: present
[...]
Make sure it validates with yamllint
:
$ yamllint ssh-config.yaml || echo "Success"
Success
Run an encrypted playbook
To run a playbook containing an encrypted string, use the ansible-playbook
command, adding the --ask-vault-pass
option. In this example, you can ignore the warnings about valid hosts, because you're just testing an example playbook:
$ ansible-playbook --ask-vault-pass ssh-config.yaml
Vault password:
PLAY [ssh_server] **************************************
TASK [Gathering Facts] **********************************
ok: [localhost]
TASK [Tell SELinux about SSH's New Port] ****************
ok: [localhost]
PLAY RECAP **********************************************
localhost: ok=2 [...] failed=0 skipped=0
Success!
Automate it
The advantage of Ansible is, of course, automation. So that you don't have to manually enter a password every time you want to run a playbook, you can instead use passwords stored in a text file.
Make sure you store password files safely by using disk encryption or a password vault. If you use a thumb drive and your Ansible control node is in an on-premises datacenter, you can implement USBGuard rules and policies for your server to restrict access to rogue thumb drives.
Here's a simple example of a password file called secrets.txt
containing one password:
password123
To run your playbook using this password file instead of manual password entry, use the ansible-playbook
command as usual but with the --vault-id
option referencing the encrypted key (ssh_port
, in this example) along with the name of the password file:
$ ansible-playbook ssh-config.yaml --vault-id ssh_port@secrets.txt
[ Need more on Ansible? Take a free technical overview course from Red Hat. Ansible Essentials: Simplicity in Automation Technical Overview. ]
You can also use multiple passwords by providing multiple vault ID flags. Here's an example password file containing more than one password, assuming that both the ssh_port
and setype
keys in the example YAML file are encrypted with:
ansible-vault encrypt_string --vault-id ssh_port@secrets.txt '2049' --name 'ssh_port'
and
ansible-vault encrypt_string --vault-id setype@secrets.txt 'ssh_port_t' --name 'setype'
respectively.
Here is an example password file (secrets.txt
):
ssh_port: password123
setype: password456
To run the playbook, specify each encrypted key and its password file using the --vault-id
option:
$ ansible-playbook --vault-id ssh_port@secrets.txt \
--vault-id setype@secrets.txt ssh-config.yaml
For more examples, check out the official Ansible documentation on how to use encrypted variables and files and the various ways of passing single and multiple passwords to playbooks.
Better automation through encryption
This article covers basic Ansible Vault usage, and there's much more this feature can do. Using Ansible Vault is an easy way to add encryption to your automation, so use it in your important playbooks to keep your automation workflow safe.
[ Learn more about server and configuration management by downloading Ansible for DevOps. ]
Robert Kimani
Robert is a Linux enthusiast and an open source advocate, currently transitioning into a site reliability engineering (SRE) role. Always striving to learn more, he's pursuing Red Hat Certified Architect - Infrastructure path certification. Besides his love for Linux, he believes in helping others More about me