Playing with pass on macOS and Windows

A long time ago I had stumbled upon pass (website), thought it looked interesting, made a note to take a look at it later, and then forgot about it.

This week I was thinking I need some sort of a command line password manager to store my various secrets and stuff like that. Not passwords as such, but I have a lot of PowerShell code where I need to add API Keys or Entra ID App Registration client secrets etc., and I wanted some secure way of doing it. Until now I was storing these in separate files to the code, and referring to them in my code by sourcing the file with the secret.

To give an example, I would create a file like mysecret.ps1 with the following:

Then, in the actual code file I’d have a line like this:

That sources the first file, and now I can use $apiKey in the code without actually putting it in the code. It’s not super secret coz anyone getting a hold of my machine can figure out the file with the secret from the code, and thus get the secret; but it’s way better than including the secret in my code itself, especially since the code is often pushed to GitHub or put in shared locations at work. I could pat myself on being half-secure, though not fully there. ☺️

This week I decided to change that. I know password managers like BitWarden and 1Password have CLI options, but I didn’t want to use either of them (for one, the BitWarden CLI is quite a hassle to setup; and for another, most of these are work secrets and I didn’t want to mix them with my personal BitWarden). I came across pass again in some Reddit thread, and this time decided to explore it properly.

(BTW, fun fact that I didn’t know. pass is created by Jason A. Donenfeld, same person who created WireGuard. He’s got a very minimalistic approach to things, which I like).

The coolest thing about pass is how it’s very simple and follows the Unix Philosophy (write programs that do one thing and do it well). It stores the secrets by encrypting them with GPG (so it doesn’t reinvent the wheel there), and there’s no concepts of folders or metadata or whatever – things are pretty much free form.

I don’t want to go too much into the details of how to setup pass, as the official page has all the details (and it’s super simple) but here’s what I did. I started using it on macOS, so let’s start there.

macOS

I created a separate GPG key for use with pass. So let’s start with that.

First, install GPG if not already installed.

Then generate a key.

Follow the wizard. I went with the defaults: ECC (sign and encrypt), Curve 25519, does not expire, and filled in the rest of the details.

Next, I initialized pass using the GPG key I created above.

And that’s pretty much it! Then I can add/ remove/ view secrets…

As you can see it’s pretty free form. I stored an app Id called “appId/0eb79d7c-4cb2-48eb-bf20-a9c91fad17f4” under a “folder” called “appId”. But there’s nothing folderish about it… it’s just a free form place I chose to put it so things are a bit organized. I could have very well put the secret in the top level itself, or even move it after creating to a different folder.

Viewing a secret is super simple too – just pass followed by the path to the secret.

(See the official website for some tips on adding more metadata to the secret apart from just the password).

If the GPG passphrase is needed, pinentry usually pops up.

All these secrets are stored in a folder called .password-store in the home folder. Easy peasy. Each secret is a file of its own, and the “folders” you put them in when creating are also present in the file system.

Using any of these secrets from PowerShell is easy. Simply do:

Windows

I wanted to have this setup on Windows too. There is no pass for Windows natively, so one must use WSL.

In WSL (Ubuntu) I installed pass.

I then exported the GPG key from macOS to here.

I was pretty basic and had the Windows machine opened via RDP, so I just copied the key that was output on macOS, and pasted it into a new file (called pass-private-key.asc in this case) in Windows. Then I did:

Continuing to be pretty basic, I then zipped the .password-store folder on macOS and copied it over to Windows and extracted it in WSL. And that’s it, I now had the same password on Windows too.

Using pass in Windows is straight forward. Just add wsl before the command.

I like to copy paste my code between macOS and Windows, and I didn’t want to keep adding wsl on the Windows side. So I made a batch file called pass.bat and put it in my PATH. (Thanks to ChatGPT for this idea actually! 🤖)

Now I can use the same command between both OSes and code.

macOS again

OK, so how can I keep the passwords in sync between both? My initial idea was about write some code to zip it on the macOS side. I created the following function and added it to my .bash_profile.

This zips the folder and puts it in my Downloads folder and also copies it to the clipboard. ☺️

Windows again

And on the Windows side I added this to .bash_profile in WSL.

So all I do now after making some changes in macOS (which is where I mostly work in), I type passCopy in the terminal (in Bash). Switch to Windows, go to the WSL folder in File Explorer, press Ctrl+V to paste the file, and in WSL terminal I type passExtract.

Not a 100% ideal but better than nothing!

macOS one more time!

Then I realized pass can integration with git. It was there in the man page but I missed that initially. Wow!

So all I do is:

With this in place, all I need to after any changes to pass is also do pass git push.

Windows one more time!

So now I can get rid of the copy pasting I was doing earlier. 😃

What I did is do one final copy and paste between the two OSes – this way the .password-store in Windows too is Git initialized. (I did this also coz I couldn’t find any instructions on how to initialize a new .password-store from Git in Windows. I think the steps would be to clone the git repo to .password-store and then use pass as usual, but I was too lazy to try).

Now I can add/ remove/ manage secrets in either Windows or macOS. Wherever I make changes I must do pass git push after making changes; and on the other side do pass git pull to get it. Sweet!

To make things simpler, I modified the pass.bat file I created above like this:

This pulls the secrets so I always have the latest version. Now I can make a change in macOS, and as long as I didn’t forget to do pass git push after making changes I should pick it up straight away in Windows/ WSL (update: see 1️⃣ below).

One problem with this though, is that I use SSH to connect to GitHub and it kept prompting me for the passphrase of the SSH key. In WSL or Windows this was a one time affair as ssh-agent remembers it, but when running from Windows via wsl pass git pull it kept asking each time.

The fix for that was to add the following in .bash_profile or .bashrc in WSL:

This is needed so the ssh-agent session is shared across multiple WSL instances.

And I modifed pass.bat thus:

This way before pass git pull runs it also pulls the ssh-agent info and both are run in the same bash session. No more passphrase prompts each time! Yay.

And that’s about it for now. Now I have a super simple CLI based password manager I can use on macOS and Windows for the various code I write, without saving secrets in plain text on either machine. 🤘

Updates:

1️⃣ Created a function like this in .bash_profile or .bashrc in macOS. This will push after every operation. Might be an overkill.

And this in PowerShell profile.