Bitwarden is a password management service (like LastPass or 1Password). It's unique in that it is built entirely on open source software. In addition to the the web UI and mobile apps that you would expect, Bitwarden also provides a command-line tool for interacting with the your password store.

At $WORK(-ish) we're looking into Bitwarden because we want a password sharing and management solution that was better than dropping files into directories on remote hosts or sharing things over Slack. At the same time, we are also thinking about bringing more automation to our operational environment, possibly by making more extensive use of Ansible. It looked like all the pieces were available to use Bitwarden as a credential storage mechanism for Ansible playbooks, so I set out to write a lookup plugin to implement the integration...

...only to find that I was not the first person to have this idea; Matt Stofko beat me to it. While it worked, the directory structure of Matt's repository made it difficult to integrate into an existing Ansible project. It was also missing some convenience features I wanted to see, so I have submitted a pull request that makes several changes to the module.

You can find my fork of the Bitwarden lookup plugin at https://github.com/larsks/ansible-modules-bitwarden.

Make it installable

By re-arranging the repository to following the standard Ansible role structure, it is now possible to install it either a submodule of your own git repository, or to install it using the ansible-galaxy tool:

ansible-galaxy install git+https://github.com/larsks/ansible-modules-bitwarden

This command would place the role in $HOME/.ansible/roles, where it will be available to any playbooks you run on your system.

Add explicit support for custom fields

While it was possible to access custom fields by fetching the complete JSON representation of an item in Bitwarden and then querying the resulting document, it wasn't particularly graceful. I've added explicit support for looking up custom fields. Whereas the normal lookup will the specific keys that Bitwarden supports in the bw get:

lookup('bitwarden', 'Google', field=username)

...adding custom_field=True causes the lookup to be performed against the list of custom fields:

lookup('bitwarden', 'Google', field=mycustomfield, custom_field=true)

Add support for the sync operation

The Bitwarden CLI operates by keeping a local cache of your credentials. This means that if you have just modified an item through the web ui (or mobile app), you may still be querying stale data when querying Bitwarden through the CLI. The bw sync command refreshes the local cache.

You can add sync=true to the lookup to have Ansible run bw sync before querying Bitwarden for data:

lookup('bitwarden', 'Google', field=username, sync=true)

Using the lookup module in practice

We're using TripleO to deploy OpenStack. TripleO requires as input to the deployment process a number of parameters, including various credentials. For example, to set the password that will be assigned to the Keystone admin user, one would pass in a file that looks something like:

---
parameter_defaults:
  AdminPassword: "secret.password.goes.here"

Because our deployment configuration is public, we don't want to store credentials there. We've been copying around a credentials file that lives outside the repository, but that's not a great solution.

Using the Bitwarden lookup module, we can replace the above with:

---
parameter_defaults:
  AdminPassword: "{{ lookup('bitwarden', 'keystone admin') }}"

With this change, we can use Ansible to query Bitwarden to get the Keystone admin password and generate as output a file with the passwords included.

Using the custom field support, we can include metadata associated with a credential in the same place as the credential itself. To configure access to a remote Ceph installation, we need to provide a client key and cluster id. By putting the cluster id in a custom field, we can do something like this:

CephClientKey: "{{ lookup('bitwarden', 'ceph client key') }}"
CephClusterFSID: "{{ ((lookup('bitwarden', 'ceph client key', field='clusterid', custom_field=true) }}"

An example playbook

Before you can run a playbook making use of the Bitwarden lookup module, you need to install the Bitwarden CLI. This is as simple as grabbing an appropriate binary and dropping it somewhere in your $PATH. I've been doing this:

$ curl -L 'https://vault.bitwarden.com/download/?app=cli&platform=linux' |
  funzip > $HOME/bin/bw
$ chmod 755 $HOME/bin/bw

For the following example, assume that we have a template named no-passwords-here.yml matching the earlier example:

---
parameter_defaults:
  AdminPassword: "{{ lookup('bitwarden', 'keystone admin') }}"

We can generate a version of the file named yes-passwords-here.yml that includes the actual passwords by running the following playbook:

---
- hosts: localhost

  # we need to include the role in order to make the lookup plugin
  # available.
  roles:
    - ansible-modules-bitwarden

  tasks:
    - name: inject passwords into a file
      template:
        src: ./no-passwords-here.yml
        dest: ./yes-passwords-here.yml

To actually run the playbook, we need to be authenticated to Bitwarden first. That means:

  1. Run bw login (or bw unlock) to log in and get a session key.
  2. Set the BW_SESSION environment variable to this value.
  3. Run the playbook.

The above tasks would look something like this:

bash$ bw login
? Email address: lars@redhat.com
? Master password: [hidden]
You are logged in!

To unlock your vault, set your session key to the `BW_SESSION`
environment variable. ex:
$ export BW_SESSION="..."
[...]
bash$ export BW_SESSION="..."
bash$ ansible-playbook inject-passwords.yml

Safely restarting an OpenStack server with Ansible

Wed 24 January 2018 by Lars Kellogg-Stedman Tags ansible openstack

The other day on #ansible, someone was looking for a way to safely shut down a Nova server, wait for it to stop, and then start it up again using the openstack cli. The first part seemed easy:

- hosts: myserver
  tasks:
    - name: shut down the server
      command: poweroff
      become: true …
read more

Ansible for Infrastructure Testing

At $JOB we often find ourselves at customer sites where we see the same set of basic problems that we have previously encountered elsewhere ("your clocks aren't in sync" or "your filesystem is full" or "you haven't installed a critical update", etc). We would like a simple tool that could …

read more

Deploying an HA OpenStack development environment with tripleo-quickstart

Fri 19 February 2016 by Lars Kellogg-Stedman Tags openstack tripleo rdo ansible

In this article I would like to introduce tripleo-quickstart, a tool that will automatically provision a virtual environment and then use TripleO to deploy an HA OpenStack on top of it.

Introducing Tripleo-Quickstart

The goal of the Tripleo-Quickstart project is to replace the instack-virt-setup tool for quickly setting up virtual …

read more

A systemd-nspawn connection driver for Ansible

Mon 08 February 2016 by Lars Kellogg-Stedman Tags ansible systemd

I wrote earlier about systemd-nspawn, and how it can take much of the fiddly work out of setting up functional chroot environments. I'm a regular Ansible user, and I wanted to be able to apply some of those techniques to my playbooks.

Ansible already has a chroot module, of course …

read more