Ansible Vault

Ansible provides an automated means of performing nearly any administrative function against a list of any number of RHEL or CentOS servers in your managed environment. As with local administration, some tasks, such as viewing a configuration file, require only user level permissions, when other administration tasks, such as user creation, require elevated permissions. Where it is necessary to run a given module, such as the yum module for installing software, as root the playbook author must let Ansible know escalation will be needed at playbook runtime by specifying the become directive in either the playbook, the ansible.cfg file, or at the command line. This directive will require some pretty scary things from a security perspective. Either the playbook user will need to know the sudo password or the user will need to have been given carte blanche in the /etc/sudoers file as a user who can run commands requiring root without specifying a password.

Fortunately, the Ansible developers provided a means of keeping secrets via Ansible Vault. To quote the project’s documentation page, “Ansible Vault is a feature of ansible that allows you to keep sensitive data such as passwords or keys in encrypted files”. It does so by placing protected data into a password protected AES256 encrypted file which decrypts to only to memory at runtime. This allows the playbook user to have all the power of root at playbook runtime while maintaining the confidentiality of root’s password.

Let’s take a look at how to go about setting up a vaulted password file and creating a playbook that uses it.

First, here’s a look at the [privilege_escalation] section of my control server’s ansible.cfg, as you can see I’ve kept the defaults by leaving each of the four directives commented out:

    [privilege_escalation]
    #become=True
    #become_method=sudo
    #become_user=root
    #become_ask_pass=False

As you can see from the snippet, Ansible will not automatically assume I want to escalate via become for each playbook execution. I prefer to supply the become directive on a per-task basis.

Next, well create the vaulted password file in the same directory that holds our inventory file. It is just another YAML file with a single key:value pair that specifies what my sudo password will be:

    ansible-vault create pwvault.yml

The ansible-vault create will first prompt for a password and then drops us into a vi session where anything written in will be encrypted with a password. It should go without saying that no recovery method exists for this password. If the password is lost, the data is lost. You’ve been warned.

Once in the vi session, press i for insert and type the following one-liner replacing supersecretsudopassword with your own root, or sudoer password:

    sudo_password: supersecretsudopassword

Next, press escape to leave insert mode, colon to enter command mode, and wq to write out the file and leave the editor.

Now if you attempt to view the file without decrypting the file you’ll see something resembling the following:

    $ANSIBLE_VAULT;1.1;AES256
    64633337353337353766626336633730373565393537336566643935306138396566653332613539
    3966326136343435666566376232303662656366306334620a383361333334653235383533376265
    6138306461646333316133353538343338633031313135386634336536

However, if you need to view the contents of the file decrypted, simply use the following command:

    ansible-vault view pwvault.yml

Next, we’ll need to tell ansible where to look for this encrypted password when it’s needed. Let’s say for instance that I have 100 RHEL7.3 Workstations which were provisioned with a common root password. For ease of administration I have listed each of these workstations by hostname in my inventory file under the group heading [workstations]. In the same directory as my inventory file I create the directory group_vars, and inside that directory I create workstations.yml which will hold any standing variables that I want my playbooks to know about regarding hosts inside the workstations group. I open up workstations.yml in vi and include the following:

    ---
    ansible_become_pass: "{{ sudo_password }}"

As you have probably already guessed, we’re getting the work done here with simple variable substitution, the only difference being that this particular variable is kept a secret until we decrypt it at playbook runtime.

Now, let’s write a playbook that requires privilege escalation.

Going back to my 100 managed workstations. Did I mention that those workstations belong to the webdevs? They’re real nerds and want to check that their content made it onto the webserver without leaving the command line. My kind of nerds. So we’ll be installing the elinks text browser for them so that they can just say elinks https://myawesomecompanysdomain.com after each commit. To do that we’ll write a playbook named install_elinks.yml that looks like this:

    ---
    - name: install the elinks text-based www browser using yum
      hosts: workstations
      tasks:
        - name: Installing elinks
          yum:
             name: elinks
             state: present
          become: True

This yum task will definitely require escalation since it’s installing software. As you can see, I’ve included my become directive at the task level.

Now there’s nothing left but to call the play. We’ll do this with a couple of extra arguments:

    ansible-playbook --ask-vault-pass -e '@pwvault.yml' install_elinks.yml

On run, you’ll first see a prompt for the Vault password as a result of the –ask-vault-pass command line argument we specified. This will be the password we created when running the ansible-vault create pwvault.yml command earlier. The extra variables provided courtesy of the -e switch looks in the path containing pwvault.yml to get the {{ sudo_password }} variable called for in our group_vars/workstations.yml file.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.