Ansible Automation Platform and Hashicorp Vault

A lot of organizations use Ansible Automation Platform (AAP) to orchestrate their infrastructure and Hashicorp Vault to manage their secrets. But how do they work together? Sometimes we @PuzzleITC don’t work out things on our own but in cooperation with our customers. These insights resulted from a collaboration with Beni Keller who worked at Schwyzer Kantonalbank.

Both AAP and Hashicorp Vault are well-known and well-established tools in enterprise environments. AAP provides native integration for Hashicorp Vault. But which features already work out of the box and in which use-cases do we need to put in some extra effort?

Note: In the below text, we use the term „Controller“ as synonym of Ansible Automation Controller. The Controller is the part of the AAP that is actually running Ansible through Execution Environments and the direct successor or Ansible Tower. Read more about the topic in this previous blogpost (in german).


Ansible without Controller

Combining Hashicorp Vault secrets with Ansible is no rocket science. There is a well-tested collection community.hashi_vault that provides modules and plugins to access and interact with a Hashicorp Vault server. It’s straight forward to use these modules with just plain Ansible. The following example shows how we could print out the value of a Vault secret using the hashi_vault lookup plugin and a token:

- ansible.builtin.debug:
    msg: "{{ lookup('community.hashi_vault.hashi_vault', \
'secret=secret/puzzle/prod/root:rootpw_crypted \
token=a95537b0-b1fe-3f22-8bc4-07df59a68aa5
url=https://vault.puzzle.ch:8200'
) }}"

This works, but since the token is sensitive information, we don’t want to write it unencrypted in our playbooks. The „hashi_vault lookup“ lookup plugin can make use of some environment and Ansible varibles. So if we set ANSIBLE_HASHI_VAULT_TOKEN and ANSIBLE_HASHI_VAULT_URL as environment variables or ansible_hashi_vault_token and ansible_hashi_vault_url as Ansible variables in place, we can even slim down our task from above to:

- ansible.builtin.debug:
    msg: "{{ lookup('community.hashi_vault.hashi_vault', \
      'secret=secret/puzzle/prod/root:rootpw_crypted  }}"

With proper environment or Ansible variables in place, this works just as well. If you prefer to use an approle rather than a token (in Hashi Vault), the environment variables ANSIBLE_HASHI_VAULT_SECRET_ID and ANSIBLE_HASHI_VAULT_ROLE_ID and their respective lowercase Ansible variable pendents are your friend.

Up to Ansible 2.5 the hashi_vault lookup plugin used the variable names VAULT_ADDR, VAULT_TOKEN, VAULT_ROLE_ID and VAULT_SECRET_ID without any equivalent Ansible variable. If you are still using Ansible 2.5 or below you have to set these variable. Make sure to set the new ones, too. Otherwise you’re bound to run into troubles upon upgrading Ansible.

What if we don’t use ansible-playbook or ansible-navigator on the command line though, but run our playbooks through Ansible Automation Platform? How can we let our Controller know about these environment variables?

Using the Controller

The AAP documentation shows that we can define credentials with the type HashiCorp Vault Secret Lookup. But beware! While this allows to retrieve for example an ssh key stored in a Hashicorp Vault in order to access a managed host and run a playbook, you cannot fetch secrets from within a playbook run in this way. Custom credential types, however, lets us inject authentication information from environment variables and Ansible variables into our Controller. So let us create a custom credentials type and use it to access our secrets in Hashicorp Vault!

 


Now for the tricky stuff! The VAULT_* variables in the hashi_vault lookup plugin were removed from Ansible versions above 2.5, so for a future-proof solution, we need to work out a different way. Alas, the new supported environment variables of the plugin are named as ANSIBLE_HASHI_VAULT_*, and interfere with the internal AAP/AWX namespace: AAP blocks external variables if they carry an ANSIBLE_* prefix. There’s an open debate whether this a good thing, but fortunately the folks behind the collection community.hashi_vault are very constructive and provided a workaround for the issue. This workaround not only sets the environment variables, but also similar extra vars that pass through AAP/AWX just fine.

Create our own custom credential

So let’s create our own custom credential type named Custom Hashi Vault Token or Custom Hashi Vault AppRole if you use approles:

We set the content of Input Field to:

---
fields:
  - id: vault_url
    type: string
    label: Vault URL
  - id: vault_token
    type: string
    label: Vault Token
    secret: true
required:
  - vault_url
  - vault_token

Or in the case of approles:

---
fields:
  - id: vault_url
    type: string
    label: Vault URL
  - id: role_id
    type: string
    label: App Role ID
  - id: secret_id
    type: string
    label: App Role Secret ID
    secret: true
required:
  - vault_url
  - role_id
  - secret_id"

The content of the field Injector Configuration has to look as follows. Note that with this content, we support the old style VAULT_* environments variables of the hashi_vault lookup plugin as well as the new style ansible_hashi_vault_* Ansible vars. This ensures, that we can use the credentials in the Controller no matter what version of the hashi_vault plugin we are using:

---
env:
  VAULT_ADDR: '{{ vault_url }}'
  VAULT_TOKEN: '{{ vault_token }}'
  VAULT_AUTH_METHOD: token
extra_vars:
  ansible_hashi_vault_url: '{{ vault_url }}'
  ansible_hashi_vault_token: '{{ vault_token }}'
  ansible_hashi_vault_auth_method: token

If you use the approle rather than the token method:

---
env:
  VAULT_ADDR: '{{ vault_url }}'
  VAULT_ROLE_ID: '{{ role_id }}'
  VAULT_SECRET_ID: '{{ secret_id }}'
  VAULT_AUTH_METHOD: approle
extra_vars:
  ansible_hashi_vault_url: '{{ vault_url }}'
  ansible_hashi_vault_role_id: '{{ role_id }}'
  ansible_hashi_vault_secret_id: '{{ secret_id }}'
  ansible_hashi_vault_auth_method: approle


The final step is to create our own Credential from the newly created credentials type Custom Hashi Vault AppRole (you would follow the same logic for one of the type Custom Hashi Vault Token):

In Type Details, we can now enter the values for our three variables defined in the Input Field of the custom credential. These values are passed to the environment variables VAULT_* and extra vars ansible_hashi_vault_* by the injector configuration. This means the hashi_vault plugin has all the information at its hands to access our Hashicorp Vault.

With everything set, we can now use tasks in our playbooks run from the Controller that directly access secrets in our HashiCorps Vault. Yeah!

- ansible.builtin.debug:
    msg: "Confidential number of beers drunk at the last Puzzle party: \
          {{ lookup('community.hashi_vault.hashi_vault', \
          'secret=secret/puzzle/party/numbers_of:beers_crypted  }}"

 

 

Kommentare sind geschlossen.