Skip to main content

How to encrypt Bash shell variables with Ansible Vault

Use Ansible Vault to share encrypted Bash environment variables across projects.
Image
Linux terminal

Photo by Markus Spiske on Unsplash

Ansible is a great tool to automate mundane or complex tasks. It also allows code reuse by passing variables to roles or collections. This is important when sharing code across a large base of users.

However, sometimes those variables are credentials that are specific to a user's access or password rather than a service account credential like you might use in Ansible Automation Platform. You wouldn't want to share sensitive credentials with your team, let alone place them into a Git repo for the world to see. Sometimes these variables are derived from an environment file, such as your .bashrc. And they could be things you need to access from the shell, not only in an Ansible playbook.

This is where Ansible Vault comes into play. Ansible Vault is a command-line tool that allows you to encrypt and decrypt any structured data file. In this article, I'll show you how to protect your shell environment variables while making them available for your typical Bash commands and your Ansible playbooks, roles, and collections.

[ Download now: A sysadmin's guide to Bash scripting. ]

Use encrypted environment variables with Ansible

First, create a .bash_vault file in your home directory:

$ touch ~/.bash_vault

Next, append the following code to your .bashrc file:

export EDITOR=vi

BASH_VAULT=${HOME}/.bash_vault

function bash_vault.load(){
  echo "Loading bash vault..."
  source <(ansible-vault view ${BASH_VAULT})
}

function bash_vault.edit(){
  ansible-vault edit ${BASH_VAULT}
}

function bash_vault.reload(){
  echo "Reloading bash vault..."
  source <(ansible-vault view ${BASH_VAULT})
}

After updating your .bashrc, create your secrets in the .bash_vault file. In this example, you want to keep your usernames, passwords, and destinations secret from prying eyes:

APP1_API_USERNAME='mike.wazowski'
APP1_API_PASSWORD='Ey3b4LL!'
APP1_API_URL='www.foo.com'

APP2_API_USERNAME='james.sullivan'
APP2_API_PASSWORD='H41rB4ll!'
APP2_API_URL='www.bar.com'

APP3_API_USERNAME='randall.boggs'
APP3_API_PASSWORD='Ch4m3l0N!'
APP3_API_URL='www.foobar.com'

export APP1_API_USERNAME APP1_API_PASSWORD APP1_API_URL APP2_API_USERNAME APP2_API_PASSWORD APP2_API_URL APP3_API_USERNAME APP3_API_PASSWORD APP3_API_URL

After updating your .bash_vault file, you must encrypt it using ansible-vault:

$ ansible-vault encrypt .bash_vault
New Vault password: ****
Confirm New Vault password: ****
Encryption successful

Now, validate you've encrypted the environment variables:

$ cat .bash_vault

$ANSIBLE_VAULT;1.1;AES256
66623436333962663336333666306230366230336465333865326363666232383365326233333836
6630393461333137633865303134616262353230636163330a386338366663633230343537303335
3635...shortened for brevity....

Reload your .bashrc file and reload the encrypted shell variables stored in .bash_vault using the functions available in your .bashrc file. You will be prompted for your Ansible Vault password:

$ source .bashrc
$ bash_vault.load
Loading bash vault...
Vault password: ****

Write a playbook showcasing how Ansible can access these variables:

- name: Example playbook to show vaulted shell variables
  hosts: localhost
  gather_facts: false
  become: false
  tasks:
  - name: Debug app1 shell variables
    ansible.builtin.debug:
        msg:
          - "{{ lookup('env','APP1_API_USERNAME') }}"
          - "{{ lookup('env','APP1_API_PASSWORD') }}"
          - "{{ lookup('env','APP1_API_URL') }}"

  - name: Debug app2 shell variables
    ansible.builtin.debug:
        msg:
          - "{{ lookup('env','APP2_API_USERNAME') }}"
          - "{{ lookup('env','APP2_API_PASSWORD') }}"
          - "{{ lookup('env','APP2_API_URL') }}"

  - name: Debug app3 shell variables
    ansible.builtin.debug:
        msg:
          - "{{ lookup('env','APP3_API_USERNAME') }}"
          - "{{ lookup('env','APP3_API_PASSWORD') }}"
          - "{{ lookup('env','APP3_API_URL') }}"

[ Write your first playbook in this hands-on interactive lab. ]

Finally, run the playbook to showcase the results:

$ ansible-playbook -i localhost, bash_vault_example.yml 

PLAY [Example playbook to show vaulted shell variables] ************************

TASK [Debug app1 shell variables] **********************************************
ok: [localhost] => {
    "msg": [
        "mike.wazowski",
        "Ey3b4LL!",
        "www.foo.com"
    ]
}

TASK [Debug app2 shell variables] **********************************************
ok: [localhost] => {
    "msg": [
        "james.sullivan",
        "H41rB4ll!",
        "www.bar.com"
    ]
}

TASK [Debug app3 shell variables] **********************************************
ok: [localhost] => {
    "msg": [
        "randall.boggs",
        "Ch4m3l30N!",
        "www.foobar.com"
    ]
}

PLAY RECAP *********************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

Use variables on the command line

But wait, these are shell environment variables, so you can also access them using the command line outside of Ansible.

Typically when users run commands, Bash stores them in command history. You can override this using set +o history or clear it using history -c, but sometimes you need the history to recall previously executed long commands more easily.

By using Ansible Vault-created environment variables, the history shows the variable name but not the variable value. To test this, first execute a command that uses one of the variables:

# Run a curl command with our encrypted shell variables
$ curl -o /dev/null -s -w "%{http_code}\n" ${APP1_API_URL}

# CURL RESULTS
200

Then review the history to check that it shows the variable name and not the variable value:

# Run the history command to check our shell variables
$ history|tail -2|grep -v history

# HISTORY RESULTS
1034 curl -o /dev/null -s -w "%{http_code}\n" ${APP1_API_URL}

Change the vaulted variables

If you need to add, delete, modify, or review your Ansible Vault-created environment variables, call the bash_vault.edit function:

$ bash_vault.edit

Once you've completed the updates, reload the shell variables using the bash_vault.reload function:

$ bash_vault.reload

Finally, when you're done, you can just log out and know that your sensitive shell variables are safe from prying eyes. This is possible because your variables are encrypted at rest.

[ Get started with automation controller in this hands-on interactive lab. ]

Words of caution

Your variables are only as secure as your security practices, so keep the following in mind:

  • Avoid logging your sensitive shell variables.
  • Avoid writing the environment variables to your logs when running tasks.
    • Use no_log=true in your Ansible tasks
    • Avoid using redirects to a log like > or |tee -a somelog

      Here's an example of using no_log=true to protect your sensitive information:
# Protect your creds using no_log
- name: Debug keep passwords private shell variables using nolog
  ansible.builtin.uri:
    url: "{{ destination_url }}"
    user: "{{ lookup('env','APP1_API_USERNAME') }}"
    password: "{{ lookup('env','APP1_API_PASSWORD') }}"
  no_log: true
  • Never reveal your environment variable to stdout.
  • Avoid printing your environment variables to stdout using the env command or echo commands. This is especially important, when:
    • Using a shared resource like Ansible Automation Platform
    • Using a shared system where others have admin access
    • A tool like tlog has been implemented for terminal session logging
# AVOID DOING THIS
$ env|grep APP
APP3_API_USERNAME=randall.boggs
APP1_API_PASSWORD=Ey3b4LL!
APP3_API_URL=www.foobar.com
APP3_API_PASSWORD=Ch4m3l30N!
APP2_API_PASSWORD=H41rB4ll!
APP1_API_URL=www.foo.com
APP2_API_URL=www.bar.com
APP2_API_USERNAME=james.sullivan
APP1_API_USERNAME=mike.wazowski

# AVOID DOING THIS
$ echo $APP1_API_PASSWORD
Ey3b4LL!

Wrap up

Keeping your personal data, such as web tokens, passphrases, and passwords, safe from prying eyes is imperative. In this article, you learned how to encrypt Bash shell variables with Ansible Vault for use inside and outside of Ansible playbooks. This article provides a straightforward solution for keeping your personal credentials and data private using Ansible Vault.

Topics:   Security   Ansible   Bash  

Randy Romero

Randy Romero is a Senior Ansible Consultant and Red Hat Certified Architect at Red Hat where he specializes in IT automation with Ansible and Ansible Automation Platform.  He has experience in the cable, insurance, and loyalty-marketing industries, having performed multiple roles starting with ju More about me

Try Red Hat Enterprise Linux

Download it at no charge from the Red Hat Developer program.