In my recent posts I’ve covered the hardened setup of Vault and covered the basics of using the REST API. As we’ve seen so far, Vault is primarily designed for programmatic interactions from external systems via the API, so lets take a look a favourite of mine; Ansible Tower, which is a prime candidate as a third party system which often has a requirement to call secrets from external systems.
For the purposes of this post, we’ll be using the KV (Key Value) Secrets Engine (one of two supported with Ansible Tower, the other being Signed SSH) and accessing the Vault mc-vault.madcaplaughs.co.uk over TCP port 8200 using HTTPS.
Ansible Tower Credentials – Native Integration
Ansible Tower has two native Credential types for Hashicorp, if we add a new Credential in Tower we can select HashiCorp Vault Secret Lookup, this is the integration with a KV Secret Engine:
Setting up the Credential further, we will need to specify:
- The URL of the Vault Server
- A Token which has appropriate rights to both the Secrets Engine and the API Endpoint /YOUR_SECRET_ENGINE/config, in our case /kv/config
- A definition of if the Secret Engine is KV Version 1 or Version 2
Secrets Engine, But no Secrets?
So now we have an integration with the Secrets Engine, but we don’t actually have individual secrets, this could be problematic.
Within Tower, we will define a custom Credential Type to map custom metadata from the Vault integration and then inject this data in to playbooks when required.
Within Tower, we can browse to Credential Types and add a new YAML structure to define our new Credential, this will be used as a template for any credentials that we look up from this Vault:
Creating this Custom Secret will create a new Credential Type named Hashicorp Vault KV Lookup.
The structures are broken down in detail below:
--# INPUT CONFIGURATION --# The fields define the GUI fields which will be presented, only one is provided --# and it is mandatory, a string named secret which will hold the Secret data initially --# it's unique id is "secret" and it's type is "secret" meaning it will be obscured on --# input, we have also defined it as "required" meaning it has to be entered fields: - id: secret type: string label: Secret secret: true required: - secret --# INJECTOR CONFIGURATION --# Here the "secret" value is converted to a new variable named --# vaultkv_secret, this value can be read in to playbooks extra_vars: vaultkv_secret: '{{ secret }}'
Looking Up an Individual Secret
Now that we have this means of looking up individual secrets, we still need to load them in to Tower so that they can be assigned to Templates. In this example, I’m going to look up a Secret named secret1 and obtain it’s secret data held in a key named password for use in Tower Templates:
When attempting to create the new Credential, the option is present to enter the secret data manually, but we want to look up from the Vault which we have already authenticated with, clicking on the “search” icon next to the Secret field will allow us to perform a lookup action against the Vault:
The lookup will first ask which external system we wish to lookup from (we only have one):
On the next page we need to define metadata for this lookup, and this is where we define data for the lookup:
The TEST button can be used to validate that the configuration is correct and then saved with OK.
Using the Credential in Ansible
Now that a Credential has been passed in to Tower, it can be assigned to any Template in the same manner as any other Credential and used in a playbook, in the below example, I’m using one of the great community modules for Netbox from @FragmentedPacket and @amb1s1 to create a new Device:
--- - name: "Netbox Demo" connection: local hosts: localhost gather_facts: False tasks: - name: Create Netbox Device netbox_device: netbox_url: https://mc-apps.madcaplaughs.co.uk netbox_token: "{{ vaultkv_secret }}" data: name: Test Switch device_type: TLSG105S device_role: Managed Switch site: madcaplaughs state: present ...
The variable being called on line 11 can be read (provided that the Credential secret1-password has been assigned to the Template calling this Playbook) as it will be passed from the Injector Configuration of the customer Credential Type we created earlier.
For a look at how to expand on this design to use multiple Secrets take a look at the follow up post here which covers that scenario.
Hi, I trust you are well. On Ansible tower, I have saved a vault token as a credential, in my playbook I use this command, “vault_secrets: “{{ lookup(‘hashi_vault’, ‘secret={{Secret_Path}} token={{Vault_Token}} url={{Vault_Addr}}’) }}”, to look up the vault secrets and then use them in my playbook.
I would like to assign the token saved as a credential to the variable “{{Vault_Token}}”, I can add the token as a credential in the template, but I cannot reference it within my playbook, hence the lookup returns a “no token specified” error. I would really appreciate your assistance with this, thank you.
Hi,
I assume this is without Tower and just pure Ansible since you’re using the hashi_vault lookup function?
There’s a couple of interesting things that happen in that {{Secret_Path}} variable and it all boils down to how you structure that variable, would really need to see how you’ve defined it, it also totally changes depending if the KV engine is v1 or v2 (in fact I’ve found the lookup function is totally broken for v2 engines).
Feel free to drop me an email with the code or a link to a repo, these comments tend to eat formatting a bit 🙂
Hello Thuthukani Mngomezulu,
Have you found a solution to your problem? I am in the same situation and would appreciate it if you can share how you dealt with it.
Are you aware of any method of updating the vault token in the credential store? Most HashiCorp Vault tokens used in production instances we use are short lived and wouldn’t be valid for more that a few hours.
For this reason we aren’t planning on using the Tower credential feature for Vault, and instead use the ansible collection hashivault and authenticate via LDAP to vault to perform various automation jobs that need access to Vault secrets.
This is a very interesting point. Using a static token is a strange choice and an external auth method is certainly more attractive for real world so we don’t need to issue long lived, insecure tokens.
Let me drop you a line, there’s some thinking to be done on this. See if I can write something up.
Hi Andy,
Thank you very much for this article, its been the only one I have found that works.
Quick question if i may, how I pull multiple secrets from the same path? Say i have /kv/dell/unity and I have two secrets, Username and Password.
How I pull both and assign to different variables in the playbook?
Appreciate your time and thank you again for the awesome blog post.
[…] and assign them to a single Ansible Tower Credential for use in a Playbook. A while ago I looked at the process of integrating Hashicorp Vault with Ansible Tower (a less that perfect process in the first place) but this has repeatedly led to the same question […]
I have the same question as last two, How do I mention two different secrets in a single playbook?
It’s a pretty clunky process, but I’ve detailed it here https://www.tinfoilcipher.co.uk/2021/10/15/ansible-tower-and-hashicorp-vault-looking-up-multiple-secrets/