Ansible Tips Part 4: Encrypt Sensitive Data

Everyday I pray to Lord Turing that I never see another Production database password in version control again. Unfortunately, I doubt my prayers will be answered because it seems to be an epidemic in Tech to store an application's production configuration file in version control without any thought to security or privacy. So if developers cannot stop themselves from storing passwords in version control, does anyone honestly think a sysadmin will think twice about storing passwords for their Ansible provisioning in version control?

Stepping back for a second, I'll concede that it does make sense to store every file in version control. That way, one can see changes on over time, and it also ensures that repository represents a complete set of knowledge for an application or system. So why not let people store everything in version control? Let's just secure the content.

As of Ansible 1.5, the Vault encrypts files containing sensitive information and allows you to store the result in version control. When Ansible is run against a server and its script contains Vault-encrypted files, you will need to enter the decryption password (or point to a file with the password) and then all decryption happens automatically.

Prior to Ansible Vault, I would create <file>.yml.example files that can be checked into version control, and then gitignore <file>.yml files to prevent passwords from being committed.

Now in my provisioning scripts, I simply create the YAML variable files and encrypt them with Vault. Simple and I love it. To get a sense of what files I encrypt (and this changes depending on the project), here is a list of the common sensitive information:

  • MySQL Root Password: stored in my.cnf
  • MySQL Application User Username: stored in ~<appuser>/.my.cnf
  • MySQL Application User Password: stored in ~<appuser>/.my.cnf
  • Application Variables (passwords and such)
  • SSH Keys
  • …and even the production inventory file (so that if anyone got their hands on my repository, they wouldn't have knowledge of my Production IP addresses).

Example Walkthrough

Here we have a variables file that we want to store sensitive data. It's the root password to a MySQL database.

---
# mysql/vars/main.yml
mysql_root_password: "lazy elsie marley"

Then we ask Ansible to encrypt the contents.

$ ansible-vault encrypt mysql/vars/main.yml
Vault password: <type your password here>
Confirm Vault password: <type your password here>
Encryption successful

If the file doesn't yet exist, you can use create instead of encrypt. The resulting encrypted file is just ASCII text and looks like:

$ cat mysql/vars/main.yml
$ANSIBLE_VAULT;1.1;AES256
65303631633136343137363738373963643766613165623831313465656132383631396130636665
6230363635363938366166613837616439663465383831350a646265333463366565333630353461
61653063363038626235626438333734363433303931343935303138326138323261383565623035
6136323666313763660a616537303934643834613063623632373265383463313330316532336234
38366164653038653462313534316234616364396532393339343337666461306330333466616266
3663383133643766303435643961393338346232343432643036

This file can now be checked into version control without worrying about security leakage. Be sure to note that the file is encrypted or else someone looking at the diffs may not understand why the content is garbled.

$ git add mysql/vars/main.yml
$ git commit -m 'Adding MySQL root password (file is encrypted)'

When running a playbook, tell Ansible to ask for a Vault password.

$ ansible-playbook -i inventories/production_servers --ask-vault-pass playbook.xml
Vault password: <type password here>