How to Keep Your Playbooks Secure Using Ansible Vault

How to Keep Your Playbooks Secure With Ansible Vault

This blog post will dive deep into Ansible Vault as a mechanism for keeping your sensitive values and secrets secure in your playbooks and other Ansible files. We will explore different options for working with encrypted content and password management options. 

If you are new to Ansible or interested in other Ansible concepts, these Ansible tutorials on Spacelift’s blog might be handy. 

What is Ansible Vault

Ansible provides a native way to handle sensitive information with Ansible Vault. Ansible Vault allows users to encrypt secrets and other sensitive data such as API keys, passwords, and other credentials to protect them at rest. 

Instead of storing secrets in plaintext, Ansible Vault gives you a secure, native, and low-effort option to manage sensitive information by leveraging encryption. Using Ansible Vault, encrypted content can be stored in playbooks, variable files, and roles, persisted under source control, and shared without exposure risks. 

Ansible Vault protects your sensitive content only at rest. Users are responsible for not exposing decrypted content in the console or any other way. Check out the no_log option for tasks to hide their output if necessary.

Vault Passwords Management

We must use passwords to encrypt and decrypt sensitive data with Ansible Vault. The passwords are used to create encrypted variables or files, decrypt, and view or edit the encrypted content. 

To supply the password for these operations, you can either leverage the ansible-vault command and get prompted for it, set up a location of a password file in your main configuration definition, ansible.cfg, or integrate with an external secret store solution that holds your passwords. 

As you can imagine, since you have to keep track of your vault passwords, there is a process involved around password management in any of these cases. You might leverage the same password for all your encrypted content for simple cases, small teams, and a few encrypted values. This approach simplifies your password management process since you only have to deal with a single password. Store and handle this password and other sensitive data in your secret management system. 

You might have to create and manage more vault passwords for complex systems and different access levels for different teams and people. For example, you might use a different password per team, per environment, per ansible role, application, directory, or any other pattern that fits your needs. 

You can specify a dedicated vault id for every password in such cases. Whenever you generate newly encrypted content, you can pass the dedicated id with the flag --vault-id.  Store this id along with the password and pass it with the password during decryption. 

We will look into more examples of leveraging vault IDs in the next section on how to encrypt sensitive values. 

As discussed previously, a simple but not really secure option would be to store passwords in a local file. If you decide to go that way, make sure the file has the correct permissions, store the password as a string in a single line, and don’t persist it in any code repositories. 

A more secure and scalable way would be to use an external secret store for storing passwords and fetch them dynamically with the help of a vault password client script. A vault password client script is responsible for connecting to the remote secret store, fetching the secret, and printing the password to standard output. Look at the Storing passwords in third-party tools with vault password client scripts section of the official documentation for details on how to create and use such scripts.

Encrypting Sensitive Info with Ansible Vault

Ansible Vault can assist users with encrypting mainly two types of data, variables and files. This means we can encrypt either whole files or only specific values stored in encrypted variables. 

By encrypting specific variables, our content will be decrypted on demand only when needed. This option allows us to mix encrypted and plaintext content according to our needs inside playbooks and roles. One drawback of this approach is that it works only with variables and that changing or rotating the password might be a bit cumbersome since there isn’t a command that supports this.  

On the other hand, encrypting whole files allows us to encrypt other content (for example, tasks) apart from variables, and then decryption happens when the file is referenced. We can easily use the rekey command to change or rotate the password used for encryption on files, and in general file-level encryption is easier to use and manage.

A disadvantage of this approach is that since we are encrypting the whole content of the files, we are trading away readability and accessibility since we can’t read the file without effort and get an understanding of its contents. One way to bypass this issue could be to keep the names or references of your encrypted content in a separate non-encrypted file. Look at Keep vaulted variables safely visible for more information on the topic. 

Let’s look at some examples next. We will start by encrypting a variable.

Encrypt Ansible Variables

To encrypt a string to use in a variable, you can leverage the ansible-vault encrypt_string command. 

To encrypt the string encrypt_this_string_please named encrypted_string and provide the password via the command line:

ansible vault encrypt_this_string_please

Notice that the produced encrypted content starts with the !vault tag, which indicates to Ansible that this value is encrypted and needs decryption to use. After this tag, Ansible adds a header before the encrypted content that includes the format ID, the vault format version, and the cipher algorithm used for encryption.

If you create the encrypted variable with a vault ID, this is also included in the generated content. If you are interested in more information, check out the format of files created with Ansible Vault section of the official docs.

You can also select the password source. For example, let’s use a file named password_file as our password source.

To avoid persisting the secret value in your shell history, don’t pass directly with the command, but opt to be prompted for a string to encrypt like this:

ansible-vault encrypt_string --vault-id dev@password_file --stdin-name 'encrypted_string'

You can find more command options by using the --help flag. 

ansible-vault encrypt_string --help

Encrypt Ansible Files

To encrypt whole files that include structured data, you can leverage the ansible-vault create or the ansible-vault encrypt commands. Different files that can be encrypted include group, host, defaults, roles, and other custom variable files, tasks files, handlers files, binary, or other arbitrary files.

To create a new encrypted file, encrypted_file.yml, with vault ID dev, and get prompted for the password, use this command: 

ansible-vault create --vault-id dev@prompt encrypted_file.yml

This will open your configured editor to provide the content to be encrypted. The same considerations discussed earlier for storing and using Ansible Vault passwords also apply here. 

Next, let’s see an example of encrypting an existing file. This operation is as simple as running this command:

ansible-vault encrypt file_to_be_encrypted.yml

Working with Encrypted Content

In the previous section, we explained the process and went over several options for encrypting different content with Ansible Vault. Now, let’s look at how we can utilize and work with encrypted content. 

One of the most common operations for a user is to be able to view the encrypted content if necessary. You can leverage the debug module to view the plaintext value of encrypted content. 

Let’s create a simple encrypted variable and pass the password via the prompt:

Next, we store this variable in a file named file_with_stored_encrypted_variable.yml.

To view the encrypted value, use the debug module and pass the location of the password as we have seen earlier: 

ansible localhost -m ansible.builtin.debug -a var="encrypted_variable" -e "@file_with_stored_encrypted_variable.yml" --vault-password-file password_file

localhost | SUCCESS => {
    "changed": false,
    "encrypted_variable": "encrypt_this"

Viewing the contents of encrypted files is quite simple with the ansible-vault view command:

ansible-vault view encrypted_file.yml

Another handy option when working with encrypted files is the ability to edit the encrypted content with the ansible-vault edit command:

ansible-vault edit encrypted_file.yml

This command prompts the user for the password, decrypts the content to a temporary file, allows the user to edit the encrypted content with the configured editor, and finally saves and re-encrypts the file. 

To permanently decrypt an encrypted file, use the ansible-vault decrypt command:

ansible-vault decrypt encrypted_file.yml

Another common operation is to rotate or change the password used for encryption. As briefly discussed in the previous section, this can be achieved with the ansible-vault rekey command. 

To change the password for an encrypted file, use the below command, and pass the current and the new password when prompted:

ansible-vault rekey encrypted_file.yml

When you need to decrypt content from a playbook with encrypted variables that have been created with different passwords and vault IDs, pass multiple --vault-id flags when executing the playbook. For example:

ansible-playbook --vault-id prod@prod_password_file --vault-id dev@dev_password_file --test@test_password_file playbook.yml

In case you use a specific vault ID or a password source more frequently than others, you can define default options and avoid passing them every time. Leverage DEFAULT_VAULT_DENY_LIST and DEFAULT_VAULT_PASSWORD_FILE to set this up.

Key Points

In this blog post, we explored different options for encrypting sensitive information by leveraging Ansible Vault. We explained Ansible Vault’s main functionality and discussed various options for handling sensitive content, passwords, and files. Lastly, we went over examples and use cases of using encrypted content. 

You can also explore how Spacelift’s vibrant ecosystem and excellent GitOps flow can greatly assist you in managing and orchestrating Ansible. By introducing Spacelift on top of Ansible, you can then easily create custom workflows based on pull requests and apply any necessary compliance checks for your organization.

Thank you for reading and I hope you enjoyed this as much as I did!

Automation and Collaboration Layer for Infrastructure as Code

Spacelift is a flexible orchestration solution for IaC development. It delivers enhanced collaboration, automation and controls to simplify and accelerate the provisioning of cloud based infrastructures.

Start free trial