Ansible Vault Integration
Introduction
When developing automation workflows with Ansible, you'll frequently need to handle sensitive information like API keys, passwords, and certificates. Storing these credentials in plaintext within your playbooks or inventory files creates serious security risks. This is where Ansible Vault comes into play.
Ansible Vault is a feature that allows you to encrypt any structured data file or variable used by Ansible. By integrating Vault into your workflows, you can securely store sensitive information while maintaining the ability to use this data in your automation tasks.
In this guide, we'll explore how to integrate Ansible Vault with your existing Ansible workflows, allowing you to maintain security without sacrificing automation capabilities.
Understanding Ansible Vault Integration
Ansible Vault can be integrated into your workflows in several ways:
- Encrypting entire files - Secure complete YAML files containing sensitive data
- Encrypting specific variables - Protect individual variables while leaving others in plaintext
- Using encrypted variables in playbooks - Reference encrypted content seamlessly in your automation tasks
- Managing vault passwords - Strategies for handling vault passwords securely
Let's explore each integration approach.
Encrypting Entire Files
The most straightforward integration is encrypting entire files containing sensitive information. This approach is ideal for files like group_vars/all.yml
or host_vars/database_servers.yml
that contain multiple secrets.
Creating an Encrypted File
To create a new encrypted file:
ansible-vault create credentials.yml
This command prompts for a password and opens an editor where you can enter your data:
# Example content for credentials.yml
db_username: admin
db_password: supersecretpassword123
api_key: a72nd92nsa72nd92n82ns2nd0n2na
After saving and closing the editor, your file is now encrypted. If you view the file contents:
cat credentials.yml
You'll see encrypted content that looks similar to:
$ANSIBLE_VAULT;1.1;AES256
32613638323336636465383730643465373834633634323061303538656466623364333161356631
39613034333632373264636264353637376464373666633437346338313962666535333761633765
30643535633634376633633339333138343436356138383962663463343633373231313064663135
6662323433613762330a623434303030323333393366386433353637626536393135646566376637
3934
Using Encrypted Files in Playbooks
To use this encrypted file in a playbook:
---
- name: Database setup
hosts: db_servers
vars_files:
- credentials.yml # Ansible will automatically decrypt this file
tasks:
- name: Create database user
mysql_user:
name: "{{ db_username }}"
password: "{{ db_password }}"
priv: '*.*:ALL'
state: present
When running this playbook, you'll need to provide the vault password:
ansible-playbook database_setup.yml --ask-vault-pass
Encrypting Specific Variables
Sometimes you only need to encrypt specific variables while keeping others in plaintext. This approach provides better readability while still securing sensitive information.
Creating Encrypted Strings
To encrypt just a single string:
ansible-vault encrypt_string 'supersecretpassword123' --name 'db_password'
This command will output an encrypted string that you can paste into your YAML files:
db_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
63386438343933613466366164313234633764383066366566643162386331646566383962666665
3130353036646536303731376333363761333966353739380a323031343035366639663561653037
38643863373833336132363435633339383966306538316438663538316234633561333637373633
6535333865326464350a343934633432313735636361396635386661356333303837383861323232
6536
Integrating Encrypted Variables
You can now add this encrypted variable directly to your playbook or inventory files:
---
- name: Configure application
hosts: app_servers
vars:
app_user: appuser
app_path: /var/www/app
db_username: dbuser
db_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
63386438343933613466366164313234633764383066366566643162386331646566383962666665
3130353036646536303731376333363761333966353739380a323031343035366639663561653037
38643863373833336132363435633339383966306538316438663538316234633561333637373633
6535333865326464350a343934633432313735636361396635386661356333303837383861323232
6536
tasks:
- name: Update application config
template:
src: config.j2
dest: "{{ app_path }}/config.php"
This allows you to maintain most of your playbook in readable plaintext while still securing the sensitive password.
Managing Multiple Vault Passwords
As your infrastructure scales, you might need different vault passwords for different environments or teams. Ansible supports this through password files and IDs.
Creating Password Files
Instead of typing passwords, you can store them in files (which should be secured with appropriate file permissions):
echo "development_password" > .dev_vault_pass
echo "production_password" > .prod_vault_pass
chmod 600 .dev_vault_pass .prod_vault_pass
Using Vault IDs
You can label your encrypted content with vault IDs to differentiate between environments:
# Encrypt with development ID
ansible-vault encrypt_string --vault-id [email protected]_vault_pass 'dev_db_password' --name 'db_password'
# Encrypt with production ID
ansible-vault encrypt_string --vault-id [email protected]_vault_pass 'prod_db_password' --name 'db_password'
These encrypted strings will be labeled with their respective IDs:
# vars/development.yml
db_password: !vault |
$ANSIBLE_VAULT;1.2;AES256;dev
63386438343933613466366164313234633764383066366566643162386331646566383962666665
3130353036646536303731376333363761333966353739380a323031343035366639663561653037
38643863373833336132363435633339383966306538316438663538316234633561333637373633
6535333865326464350a343934633432313735636361396635386661356333303837383861323232
6536
# vars/production.yml
db_password: !vault |
$ANSIBLE_VAULT;1.2;AES256;prod
83754658749387484938292929292876876543234567898765434567887654345678876545678234
7654345678765434567897654324567876543234567898765434566544567876542345787654348
98765434567876543234567876545678765434568765434567887654345678987654345678876545
8765434567895436789765456789087654321678965434565432234567765434567876543456798
6667
Running Playbooks with Vault IDs
When running playbooks, specify the appropriate vault ID:
# For development
ansible-playbook deploy.yml --vault-id [email protected]_vault_pass
# For production
ansible-playbook deploy.yml --vault-id [email protected]_vault_pass
This allows you to maintain separate secrets for different environments while using the same playbooks.
Real-World Integration Example
Let's walk through a complete example of integrating Ansible Vault into a web application deployment workflow.
Project Structure
project/
├── ansible.cfg
├── inventory/
│ ├── development
│ └── production
├── group_vars/
│ ├── all/
│ │ ├── main.yml
│ │ └── vault.yml # Encrypted file
│ ├── webservers/
│ │ ├── main.yml
│ │ └── vault.yml # Encrypted file
│ └── dbservers/
│ ├── main.yml
│ └── vault.yml # Encrypted file
├── roles/
│ ├── common/
│ ├── webserver/
│ └── database/
└── playbooks/
├── deploy.yml
└── backup.yml
Encrypting Group Variables
We'll encrypt each vault.yml
file:
# Encrypt global secrets
ansible-vault encrypt group_vars/all/vault.yml
# Encrypt webserver secrets
ansible-vault encrypt group_vars/webservers/vault.yml
# Encrypt database secrets
ansible-vault encrypt group_vars/dbservers/vault.yml
Contents of Vault Files
# group_vars/all/vault.yml
---
vault_ssh_private_key: |
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA...
-----END RSA PRIVATE KEY-----
# group_vars/webservers/vault.yml
---
vault_ssl_cert_key: |
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkq...
-----END PRIVATE KEY-----
vault_api_tokens:
payment_gateway: "t0p53cr3t4p1k3y"
monitoring: "m0n1t0r1ngk3y"
# group_vars/dbservers/vault.yml
---
vault_db_root_password: "r00tp4ssw0rd"
vault_app_db_password: "4ppdb_s3cr3t"
Referencing Encrypted Variables
In your non-encrypted variable files, reference the vault variables:
# group_vars/all/main.yml
---
ssh_private_key: "{{ vault_ssh_private_key }}"
# group_vars/webservers/main.yml
---
ssl_cert_key: "{{ vault_ssl_cert_key }}"
api_tokens: "{{ vault_api_tokens }}"
# group_vars/dbservers/main.yml
---
db_root_password: "{{ vault_db_root_password }}"
app_db_password: "{{ vault_app_db_password }}"
This pattern of indirection keeps your playbooks clean and allows you to explicitly see which variables come from vault files.
Using in Playbooks
---
- name: Deploy Web Application
hosts: webservers
become: yes
tasks:
- name: Install SSL certificate
copy:
content: "{{ ssl_cert_key }}"
dest: /etc/ssl/private/app.key
owner: root
group: root
mode: '0600'
- name: Configure payment gateway
template:
src: payment_config.j2
dest: /var/www/app/config/payment.php
vars:
payment_api_key: "{{ api_tokens.payment_gateway }}"
Running the Secured Playbook
ansible-playbook -i inventory/production playbooks/deploy.yml --ask-vault-pass
Using Vault in CI/CD Pipelines
Integrating Ansible Vault with CI/CD pipelines requires special consideration since you can't interactively enter passwords.
Using Password Files
Create a secure vault password file and add it to your CI/CD environment (using secure environment storage mechanisms).
# In your CI/CD pipeline
echo "$VAULT_PASSWORD" > .vault_pass
chmod 600 .vault_pass
ansible-playbook deploy.yml --vault-password-file .vault_pass
# Clean up after deployment
shred -u .vault_pass
Using Environment Variables
Alternatively, use the ansible-vault
Python script to read the password from an environment variable:
# Create a vault password script
cat > vault-pass.py << 'EOF'
#!/usr/bin/env python
import os
print(os.environ.get('ANSIBLE_VAULT_PASSWORD', ''))
EOF
chmod +x vault-pass.py
# In your CI/CD pipeline, set the environment variable
export ANSIBLE_VAULT_PASSWORD='your_vault_password'
# Run the playbook
ansible-playbook deploy.yml --vault-password-file vault-pass.py
Best Practices for Vault Integration
Here are some recommended practices for effectively integrating Ansible Vault:
- Don't commit vault passwords - Never store vault passwords in version control
- Use separate vault files - Keep encrypted content in separate files from plaintext content
- Use variable indirection - Reference vault variables from regular variables
- Rotate vault passwords - Periodically change vault passwords for better security
- Use different vault IDs - Maintain separate vault passwords for development and production
- Limit vault access - Only provide vault passwords to team members who need them
Troubleshooting Vault Integration
Common Issues and Solutions
-
"Decryption failed" error
- Ensure you're using the correct vault password
- Check if the file was encrypted with a different vault ID
-
"ERROR! Attempting to decrypt but no vault secrets found"
- You didn't provide a vault password when running the playbook
- Solution: Add
--ask-vault-pass
or--vault-password-file
-
Encrypted content appears corrupted
- The encrypted content was modified (even adding/removing a space will break it)
- Solution: Re-encrypt the content
-
Need to view encrypted content
- Use
ansible-vault view credentials.yml
to safely view the content
- Use
-
Need to edit encrypted content
- Use
ansible-vault edit credentials.yml
to make changes
- Use
Debugging Vault Integration
If you're having issues with vault integration, you can increase Ansible's verbosity:
ansible-playbook deploy.yml --ask-vault-pass -vvv
This provides detailed information about the decryption process and where things might be failing.
Summary
Ansible Vault integration provides a robust way to secure sensitive data while maintaining the automation benefits of Ansible. By encrypting files or individual variables, you can ensure that passwords, keys, and other secrets remain protected in your infrastructure code.
Key takeaways:
- Encrypt entire files with
ansible-vault create/encrypt
- Encrypt individual strings with
ansible-vault encrypt_string
- Use variable indirection for clean playbook design
- Leverage vault IDs for different environments
- Follow best practices for secure vault password management
By properly integrating Ansible Vault into your workflows, you can ensure that your automation is both powerful and secure.
Additional Resources
Exercises
-
Create an encrypted file containing database credentials and use it in a playbook that sets up a database server.
-
Encrypt individual strings for API keys and include them in an existing inventory file.
-
Set up a project with separate vault IDs for development and production environments.
-
Create a CI/CD pipeline that uses Ansible Vault for deploying an application securely.
-
Practice rotating vault passwords by re-encrypting files with a new password.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)