Storing passwords is a pretty simple problem in software development, right? Wrong! Storing passwords correctly is pretty complicated. With that said, it’s very simple to just lean on work someone else has done, and the libraries available for your language of choice. In reality, you should never do it yourself.
I’ve used Ansible for a number of years for the provisioning of both my servers and desktops. It’s versatile, it’s simple, it’s powerful, and has a number of great features. Personally, I make all of my “playbooks” public for all for all to see, but provisioning still requires some secrets. This is where Ansible vault comes in, which allows storing encrypted variables in the repository, which is decrypted at runtime using a password. Getting started with ansible vault is out of scope for this post, but there are guides out there.
Ansible vault exists to enable storing secrets safely in a public repository, encrypted using a password. But where does that password live. At some point, the secret needs to be stored somewhere in plaintext. The simplest place to put the file is just in a text file, make sure it’s
.gitignore-d, and have Ansible read from that. But that’s boring, not to mention the maintenance annoyance from rotating passwords and reinstalling my devices.
There are tools like Hashicorp Vault (no relation to Ansible vault), which are designed to store credentials, and provide them to tools as they need them, but that’s quite a large hammer for the problem. I’m already a Bitwarden user, and it already has my vault password in - can I use that?
#Setting up Ansible
Whilst Ansible supports reading the vault password from a file, if said file is executable, Ansible will automatically run it, and use its output as the vault password. For example, if we change our vault password file to be:
#!/bin/sh echo "hunter2"
ansible.cfg to it:
[defaults] vault_password_file = ./vault-pass.sh
Make it executable (
chmod +x), and our vault password will be used exactly as it did before (assuming our password was “hunter2”, which yours shouldn’t be). But, that’s really no better. We’ve gone from Ansible reading a text file with the secret baked in, to Ansible running a bash file with the secret baked in. Where’s the bitwarden integration?
#Setting up Bitwarden
Because Bitwarden does all its encryption client side (which is good), it doesn’t have an API for getting the details of specific items. Instead, Bitwarden has a command-line tool for interfacing with your vault. We can then plug this into our Ansible script to retrieve our password when we need it.
Setting up the CLI is very simple, and has nothing to do with Ansible:
- Install it
- (optional) Set a custom server:
bw config server https://vaultwarden.example.com(vaultwarden is great)
bw login, and follow the prompts. You can ignore the
With the CLI now authenticated, you can interface with your vault all from the terminal. The command we need for this is
bw get password "whatever" will prompt for your master password, and then print out the password for the relevant entry (in this case “whatever”).
#Reading the password
Now, we know how to make Ansible execute a file to get the password, and we know how to get the password out of Bitwarden. Now to stitch them together in the best way possible: a tiny bash script!
#!/bin/bash set -e bw get password "Ansible vault key"
The keen-eyed among you may have noticed something.
bw will prompt for your master password when trying to retrieve credentials (unless you set the session key), but how does Ansible know how to enter it? Well it’s simple: It doesn’t. The Ansible developers have thought ahead yet again, and pass through the relevant file handlers such the bitwarden prompt shows when running Ansible, and correctly takes the prompt - excellent!
$ ansible-playbook main.yml ? Master password: [hidden] PLAY [all] ************************************************** ...
So now, when deploying with Ansible, rather than reading the vault password from a static file, it will prompt for my bitwarden password, pull the vault password from my vault, decrypt the vault, and continue the deployment.
In this setup, Bitwarden will prompt you for your master password every time you run Ansible. For most, that’s not a massive issue, but for some that could get quite annoying.
That thing I mentioned before, the “session key”, this is where that comes in. The session key allows Bitwarden to access its credentials without prompting you for your password each time. It works by setting a
$BW_SESSION environment variable, which future command line invocations can read and unlock the database with.
To configure this, run
bw unlock. This will prompt you for your master password, and then display a session key environment variable to set. If you set this in the same terminal you run Ansible from, it won’t prompt you for your master password any more, as Ansible helpfully passes through all environment variables into the relevant password file script.
If you want a one-liner to set the session key:
export BW_SESSION=$(bw unlock --raw)
#What about the become password?
You probably shouldn’t run Ansible as root, for the same reasons you shouldn’t run many things as root. Given Ansible uses SSH, that would require SSH to be open to
root anyway, which is also a bad idea. Instead, Ansible has
become, which can use
sudo to change user as part of individual tasks and roles.
sudo requires your password before running things as root. It’s possible to bypass this, but I don’t want to do that. Instead, I have Ansible prompt for my sudo password during deployment (using
-K), which it then passes through to
Does the same trick we just used for the vault password work for the “become” password: absolutely!
- Create an additional password file (
become-pass.sh), copied from
- Update the name of the Bitwarden item which contains the required password
- Reference the new file in
And now, Ansible will prompt for your become password file too. Unfortunately, it won’t share session, so will require prompting you again.
At the time of writing, this doesn’t actually work… There’s a bug which prevents this working for the become file. But hopefully that’ll be fixed soon!
#Is this better?
Because Bitwarden stores its vault encrypted until it’s needed, and cached locally, the vault password is never stored in plaintext anywhere. It also means that because the secret is no longer in a (
gitignore-d) file in the repository, there’s less chance of accidentally committing it and publishing all your secrets to the world (which I totally have never done). Additionally, there’s no longer a separate file to sync outside the repository. Just clone the repository, and so long as I have the Bitwarden CLI configured, the credentials will flow.
Unfortunately, I haven’t fully solved my problem. Ansible is made easier, but I still have some
gitignore-d secrets for my Terraform configuration. Terraform doesn’t appear to have any easy integrations quite as nice as this for resolving secrets. Depending on how annoying that gets, Hashicorp Vault may be in my future… If you know of a better solution, please, let me know!
Share this page
Docker containers (like onions) have layers. In your Dockerfile, each new RUN, COPY or ADD line creates a new layer (so do the others, but not ones which affect the filesystem). Each layer contains only the files which changed from the previous layer, which allows layers to be shared between…
You should back up your data, properly! If you’re not, you’re playing a dangerous game with fate. Computers are pretty reliable, but they also go wrong, often. You should always backup your files, but backing up a containerized application isn’t quite as simple. A container is 3 things:ConfigurationVolumesNetworking The point…
View all →