Using Password Store with Saltstack

I’m using Password store for a long time and I love it. It is simple, intuitive and it just works. Recently I started adding some more formulas into my Saltstack and I started searching for a way how to store some password that are going to be set via Salt. It stand to reason to check whether I can use Password Store with Salt somehow. I found that there is pass renderer already part of Salt itself. I checked it out, but in the end, I ended up forking it. Continue reading to find out how, why and what is the result and what is my final setup.

Password Store workflow

If you don’t know about Password Store, let’s cover the basics first. Password Store stores passwords in directories encrypted using your PGP key. Not just yours. You can specify (per directory) who will be able to decrypt the stored passwords. That is the first cool functionality that seems useful with regards to Salt - I can use my personal secure key to encrypt and decrypt everything, but I can let Salt with his key decode the secrets I want to share. Same way, if there would be more people managing my servers, I could share only subsets of secrets with them.

Other great feature is that Password Store works great together with git. That provides an easy way how to share encrypted data across computers. In particular, I decided to separate my personal passwords and Salts passwords. Salt has its own git repository and I imported it as a git submodule to my own git repository with my passwords. This way, I can easily get any password that Salt knows as well as any of my passwords. But even if you don’t want to play with submodules, it provides an easy way how to synchronize and version passwords.

Existing Password Store integration

One cool feature that Salt has is custom renderers. You can write a simple code, that will transform either text or even parsed data into something else. You can combine them, pipe the data through multiple renderers and only the end result is used by Salt.

One of the officially provided renderers is pass. It uses Password Store as a backend. Seems like job done and all I need to do is start using it.

Unfortunately I found some small issues that were limiting its usability. Actually one issue that has two ugly consequences. pass renderer tries to pass every value that you specified in your pillar to the Password Store and decrypt it. Quite some of those values will not be there so you will get a bunch of errors in your log. On the other hand, some of them might be there accidentally. You store there password for one formula using key that makes sense, but in other one you would need to use the same string as a raw value and might forget that it is already a path in pass. So it might expand even something you don’t want expanded. You can’t specify what to try to decrypt and what not.

Apart from that, providing default values seemed like it would be quite complicated and I would need either support in formula or involve yet another renderer.

My solution - atpass

I forked the pass renderer and changed it slightly. It ignores any element except those with values starting with @pass. Those will be expanded if possible. After a key word @pass, it expects the list of possible paths within Password Store separated by spaces. That means that you can’t have paths containing spaces in your Password Store. But you can provide multiple paths and the first one that succeeds is used. If none works, the original value is used. This allows you to specify the default value and override it in specific cases. For example like this:

#!jinja|yaml|atpass

mysql:
  server:
    root_password: "@pass mysql/{{ grains['id'] }}/root mysql/default/root"

As I changed the syntax and the behaviour, I did not send this to the upstream repository, but rather really forked it. If you want to give it a try, you can check out my repo on github. Changes are really minimal (I don’t know Python), but usability is much better in my opinion.