How we manage passwords using Ansible
Ansible is the core of our quick deployment infrastructure (which I’ve detailed here), meaning that we use it to run thousands of servers and automated processes. All of that infrastructure also needs password management, but at our scale, Ansible Vault doesn’t quite fulfill our needs. I’d like to share our solution for group password and permission management on Ansible.
The problem with Ansible Vault is that it has poor support for group work and doesn’t have convenient user, permission and value management options. We always try to keep our infrastructure 100% IaC (Infrastructure as Code), so splitting passwords or adding manual actions wasn’t really an option for us.
We’ve grown fond of Team Password Manager (TPM) over time, but it was its integration functionality that made it the perfect fit for our solution. We were able to integrate TPM to Ansible, solving our group work, permission, and password organization issues. This also took password management off of DevOP’s hands by using encrypted vault files that solve merge requests and search problems in GitLab.
Here are the key changes we made to ensure that our many admins and developers could work securely and conveniently:
- We moved all passwords and sensitive information from Ansible Vault and GitLab Variables to TPM;
- We reworked all application configuration files to the jinja format;
These changes helped us fully separate the server configuration and application development processes. It also made the deployment process fully automated and developer-controlled.
Here are a few examples of how we use the TPM ansible module in group_vars and deployment playbooks.
Group_vars YAML example
elasticsearch_users:
- { user: "writer", password: "{{ {'id':1517,'auth':'auth:deploy'} | nordsec.team_password_manager.get_password }}", roles: [ "writeonly" ] }
- { user: "reader", password: "{{ {'id':1518,'auth':'auth:deploy'} | nordsec.team_password_manager.get_password }}", roles: [ "readonly" ] }
Playbook example
How to pull different values for different environments.
- name: Set tpm passwords
set_fact:
site_map_ids:
production:
projects:
- 930
- 963
passwords:
- 16383
staging:
projects:
- 947
- 1163
passwords:
- 16382
Preparing get request config:
- name: Prepare tpm config
set_fact:
tpm_config:
auth: "auth:deploy"
get_passwords_from_projects: "{{ site_map_ids[deploy_environment]['projects']|default([]) }}"
get_passwords: "{{ site_map_ids[deploy_environment]['passwords']|default([]) }}"
The warmup process:
- name: Get passwords from TPM (warmup)
tpm_module: "{{ tpm_config | combine({'update_cache': True}) }}"
connection: local
run_once: True
when: inventory_hostname == ansible_play_hosts_all[0]
Getting all values from the cache:
- name: Get passwords from TPM
tpm_module: "{{ tpm_config }}"
connection: local
register: tpm_pass_list
Results with variable names with can be used in application configurations and can easily be replaced.
"msg": {
"changed": false,
"failed": false,
"tpm": {
"IDENTITY_EMAIL_PARAM_AES_KEY": "xxxxxxxxxx",
"AUTH_API_CLIENT_ID": "xxxxxxxxxx",
"AUTH_API_CLIENT_SECRET": "xxxxxxxxxx",
...
"RABBITMQ_API_PASSWORD": "xxxxxxxxxx"
}
}
}
You’ll have to complete the implementation based on your own specific needs and technical capabilities, but these are some of the building blocks that were essential for our implementation (you can find it all on GitHub). Now, all of our admin and dev teams across all of our infrastructure enjoy easy and dependable password management.
Want to read more like this?
Get the latest news and tips from NordVPN.