Integrating Ansible with Hashicorp Vault
- December
- 15
There are several moving parts of this configuration. We’re using a simple example here; your implementation may have different requirements.
First, vault should be installed and working in a production configuration. For these examples we have the root token in our possession. You may not need this for your deployment if you have the correct permissions already assigned to your local account.
To use these examples you will need hvac python library and community.hashi_vault collection installed. Install hvac library with ‘pip install hvac’. Install the community package with ‘ansible-galaxy collection install community.hashi_vault’.
You can use Hashicorp Vault to store all the sensitive variables you use with ansible. However, you still need to have a credential to authenticate ansible against vault. We will explore using a token or alternatively a username/password against an LDAP (Active Directory) backend for authentication.
When using vault from the command line you can use a token from an environment variable, or you can specify your username and you will need to type your password. Generally, you will only use this to write variables for ansible to read. You can also likely do it through the vault GUI if you have set that up.
When running ansible plays, the token or username and password can be set as environment variables manually or from your .profile or .bashrc if you wish. Obviously, it’s more secure to set them manually for your session and not store them on the server.
Let’s go through storing some key value pairs and setting up a policy to access them. We’ll then show how to retrieve them and use them in an ansible playbook.
Let’s put our server address and root token into the environment.
$ export VAULT_ADDR="https://vault.example.com:8200"
$ export VAULT_TOKEN="hvs.hrlxewcxyxyxyxyxyxy"
Now we’ll create a token that is valid for a year and can be refreshed every 30 days. We will set the default max lease time for tokens.
$ vault write sys/auth/token/tune max_lease_ttl=8760h
Success! Data written to: sys/auth/token/tune
Let’s create a path for some vmware credentials, we’ll use the key-value storage version 2:
$ vault secrets enable -path=apps kv-v2
Success! Enabled the kv-v2 secrets engine at: apps/
Now we’ll put our vcenter username and password in there.
$ vault kv put apps/vmware vcenter_username="[email protected]"
$ vault kv patch apps/vmware vcenter_password="SomeVerySecretPassword"
Check to make sure you can read them okay.
$ vault kv get apps/vmware
== Secret Path ==
apps/data/vmware
======= Metadata =======
Key Value
--- -----
created_time 2022-12-13T22:16:24.625488264Z
custom_metadata
deletion_time n/a
destroyed false
version 3
========== Data ==========
Key Value
--- -----
vcenter_password SomeVerySecretPassword
vcenter_username [email protected]
If you only want one value, use the -field parameter to get it:
$ vault kv get -field vcenter_username apps/vmware
[email protected]
Now that we have a few values stored we need to create a policy to allow access to them. Make up a policy file called app-policy.hcl that looks like this:
path "apps/*"
{
capabilities = ["read"]}
Create a new policy and pull in the file:
$ vault policy write app-reader app-policy.hcl
Success! Uploaded policy: app-reader
Make a token an associate it to the policy:
$ vault token create -display-name app-reader -explicit-max-ttl 8760h -policy app-reader -ttl 720h -renewable
Key Value
--- -----
token hvs.CAESIFLCj9VhI2IHzKeTNtMOJGPVxyxyxyxy
token_accessor RJb1xyxyxyxy
token_duration 720h
token_renewable true
token_policies ["app-reader" "default"]
identity_policies []
policies ["app-reader" "default"]
You can confirm the ability to read the secrets:
$ vault token capabilities hvs.CAESIFLCj9VhI2IHzKeTNtMOJGPVxyxyxyxy
read
Now that secrets are stored, we can read them with a playbook. In this example we will be accessing a VMware vCenter server. The non-sensitive variables are in a file called vars.yml that looks like this:
---
vcenter_hostname: "vcenter.example.com"
vcenter_datacenter: "EXAMPLE"
vcenter_cluster: "Cluster1"
vcenter_datastore: "SAN"
vcenter_validate_certs: false
vcenter_destination_folder: "EXAMPLE"
vcenter_content_library: "example"
vm_template: "linux-ubuntu-20.04lts-v22.11"
vm_state: "poweroff"
ansible_hashi_vault_url: 'https://vault.example.com:8200'
ansible_hashi_vault_auth_method: token
Notice the last two variables defined. They are used by the hashi_vault module.
The last thing to do before running a playbook is to store our token into the expected environment variable like this:
$ export ANSIBLE_HASHI_VAULT_TOKEN=hvs.CAESIFLCj9Vxyxyxyxyxy
We can put together a playbook that uses the credentials from vault. Here is one that powers up some VMs from our inventory:
---
- name: start inactive vms
hosts: localhost
become: false
gather_facts: false
collections:
- community.vmware
pre_tasks:
- include_vars: vars.yml
tasks:
- name: get vcenter credentials from hashicorp vault
set_fact:
vcenter_username: "{{ lookup('hashi_vault', 'secret=apps/data/vmware:vcenter_username') }}"
vcenter_password: "{{ lookup('hashi_vault', 'secret=apps/data/vmware:vcenter_password') }}"
- name: power on
vmware_guest_powerstate:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
name: "{{ item }}"
validate_certs: "{{ vcenter_validate_certs }}"
state: powered-on
loop: "{{ lookup('inventory_hostnames', 'inactive:&ubuntu22', wantlist=True) }}"
As an alternative, a username and password can be used for authentication. In this scenario we will use LDAP (Active Directory) to authenticate the user.
We use our root credential on vault to enable the ldap authentication mechanism on vault:
$ export VAULT_TOKEN=hvs.hrlxewxyxyxyxy
$ vault auth enable ldap
Now let’s configure the ldap to talk to our domain controller. We’ve already built a user called ‘vault’ in active directory so we can bind with that user here. We’re not using any certificates for simplicity, it would be a better idea to use ldaps in production.
$ vault write auth/ldap/config \
url="ldap://dc01.ad.example.com" \
userattr="sAMAccountName" \
userdn="cn=Users,dc=ad,dc=example,dc=com" \
groupdn="cn=Users,dc=ad,dc=example,dc=com" \
groupfilter="(&(objectClass=group)(member={{.UserDN}}))" \
groupattr="memberOf" \
binddn="cn=vault,cn=users,dc=ad,dc=example,dc=com" \
bindpass='FNjRdTTzxyxyxy' \
starttls=false \
userfilter="({{.UserAttr}}={{.Username}})"
Now that we can authenticate to vault via ldap, we can use ldap groups to set user policy. Let’s reuse the policy we built previously and bind it to the vaultusers group:
$ vault write auth/ldap/groups/vaultusers policies=app-reader
Let’s log in and test our access from the CLI. We’ll make sure we have our server location set in the environment first:
$ export VAULT_ADDR=https://vault.example.com:8200/
Now we log in with username/password:
$ vault login -method=ldap username=ansible
Password (will be hidden):
Success! You are now authenticated. The token information displayed below
is already stored in the token helper. You do NOT need to run "vault login"
again. Future Vault requests will automatically use this token.
Key Value
--- -----
token hvs.CAESICz0NC0UNUTW1nyxyxyxyxy
token_accessor jXIisU1vaHRGRkxyxyxyxy
token_duration 768h
token_renewable true
token_policies ["app-reader" "default"]
identity_policies []
policies ["app-reader" "default"]
token_meta_username ansible
Since ‘ansible’ is a member of the ‘vaultusers’ security group in AD, you can see that we have the “app-reader” policy applied (in addition to default). So let’s see if we can read our vcenter credentials:
$ vault kv get apps/vmware
== Secret Path ==
apps/data/vmware
======= Metadata =======
Key Value
--- -----
created_time 2022-12-13T22:16:24.625488264Z
custom_metadata
deletion_time n/a
destroyed false
version 3
========== Data ==========
Key Value
--- -----
vcenter_password SomeVerySecretPassword
vcenter_username [email protected]
Success!
To use username and password credentials in our playbook, instead of setting a token in an environment variable we set our username and password instead:
$ export ANSIBLE_HASHI_VAULT_USERNAME="ansible"
$ export ANSIBLE_HASHI_VAULT_PASSWORD="YouWontEverGuessIt"
We also need to change the line in our vars.yml to specify ldap instead of token auth:
ansible_hashi_vault_auth_method: ldap
Now our playbook will run using username/password against ldap instead of requiring a token.
« Use Netbox as backend for Oxidized | Update Unifi Dream Machine Pro certificates with automation » |