↓ Skip to main content

How CLI Password Managers Saved Hours of Work and Embarrassment

·1740 words·9 mins·
Table of Contents

What Is This CLI Password Manager You Speak Of?
#

Using pass, the Unix password manager, has completely changed the way I handle API keys and other sensitive environment variables.

As developers and engineers, we usually handle sensitive secrets by throwing them in a .env or config file and calling it a day. However, that is extremely insecure and also prone to data loss. How many times have you DM’d a colleague to ask them for their .env file because you accidentally deleted yours during your weekly shitty GitHub project cleanup?

And how many times have you forgotten to set up a .gitignore file and had to use git-filter-repo to delete the history?

pass solves all that.

How? Git.

I know I’m beating a dead horse each time I talk about how Git solves all our problems, but what do you want me to do, lie? It does. The answer is always Git.

pass uses Git and GPG under the hood. This lets you save your keys in a “private” repository and not worry about them being stolen, because they’ll be encrypted.

So How Do I Do That?
#

First, install pass. I’ll show you how it’s done on Fedora, since that is the correct distro. FIGHT ME!

sudo dnf install pass

The Boring Stuff (GPG Setup)
#

We just generate a GPG key.

gpg --gen-key

This launches an interactive wizard. GPG will ask for your name and email. These are just metadata tied to the key, so use whatever you like; it doesn’t matter. I’m not your mom. After that, it will prompt you to set a passphrase. Do not skip this step: the passphrase is what protects your private key. If someone gets their hands on your private key and it isn’t passphrase-protected, they own everything you’ve encrypted.

Real name: Your Name
Email address: [email protected]
You selected this USER-ID:
    "Your Name <[email protected]>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O

Once your key is generated, you’ll want to set it to never expire so you don’t have to deal with renewal:

I know, I know, key rotation is important, but counterpoint: I don’t want to fucking deal with it.

gpg --edit-key <KeyID>

This drops you into an interactive GPG prompt (gpg>). Here’s what each command does:

  • expire: enters the expiration submenu
  • 0: selects “key does not expire”
  • y: confirms the change when GPG asks “Is this correct?”
  • save: writes the changes to disk and exits the prompt
gpg> expire
Changing expiration time for the primary key.
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Your selection? 0
Key does not expire at all
Is this correct? (y/N) y

gpg> save

Let’s Start the Fun Part (pass Setup)
#

pass init <KeyID>

Then we init the Git repository.

pass git init

Then add your keys.

pass insert llm/deepseek

Important note: Do not use your email as the pass entry name! That is a credential leak, albeit a minor one. If you want to add an email to your password entry, just use pass edit <key name>.

It will then prompt you for your “password”. Just paste your key there.

Alright, How Do I Use Them Now?
#

Environment Variables & the Terminal
#

This is the meat and potatoes of the whole thing: actually using your secrets. The best way I’ve found is using Bash command substitution, the $() syntax, which executes a command and substitutes its output. In our case, it runs pass, decrypts the secret, and inserts the value into our command at runtime.

Here’s an example using export:

export DEEPSEEK_API_KEY=$(pass show llm/deepseek)

And that’s it. Your API key is now available as an environment variable without you having to manually copy and paste it like some kind of caveman.

A quick security note: The secret is still exposed to processes that inherit the environment variable. GPG protects your secrets while they’re stored, not while your applications are using them (it’s the same for .env too don’t worry). Also, if you added multiple lines of metadata to your pass entry, the command above will export those too(just pass into head, awk, sed or whatever if you only need the first line).

Clipboard
#

if you need to send your secrets to that one Guy who refuses to use git and sensible secret management software here is how to copy them into your clipboard without leaking them in your Terminal

pass show llm/deepseek --clip

By default, pass copies the first line of the entry and restores your clipboard after 45 seconds.

Just remember: Your clipboard isn’t a magical secure vault. Some clipboard managers can retain copied values (Sensible DEs like KDE don’t), and sending someone an API key over an insecure channel defeats the whole point of protecting it in the first place.

Use a secure method when possible. And maybe have a conversation with the guy about his engineering choices.

Backups
#

Now we’ll create a remote repository on GitHub using the GitHub CLI (or you can use the website like a chump).

Don’t forget to make it a private repo. Yes, they’re encrypted, but extra security never hurts.

gh repo create <RepositoryName> --private

Then copy the URL of the empty remote repository and set it as the origin of your pass repository.

pass git remote add origin [email protected]:<GitHubUsername>/<RepositoryName>

You aren’t using SSH? First of all: ew. Second of all, here is the HTTPS version, I guess.

pass git remote add origin https://github.com/<GitHubUsername>/<RepositoryName>

I’ll write a guide at a later date on how to use SSH for your Git operations, though it’s not that hard.

After that, just push the keys and you’re done.

pass git push origin main

How Can I Access My Keys From Another Machine?
#

Well, my astute reader, what you need to do is export your keys and place them in a secure place, for example, a password manager like Bitwarden. How can you do that? Here you go:

NAS Backup
#

# Let's make a directory to save the files to
mkdir exported_keys
cd exported_keys
gpg --output public.gpg --armor --export <email>
gpg --output private.gpg --armor --export-secret-key <email>

And then you can move them to your NAS through SCP.

Make sure that your NAS is also encrypted. If it isn’t, you just moved the vulnerability from your personal machine to your NAS. You didn’t plug any holes, you just added more availability. (The keys you exported are not encrypted by pass; they’re the keys that do the encrypting, so they’re only as safe as your GPG passphrase.)

cd ..
scp -r exported_keys nas_username@nas_hostname:destination

Don’t forget to delete the exported files afterwards.

rm -r exported_keys

Password Manager Backup
#

If you’re using a password manager like Bitwarden, you don’t actually need to export your keys to files first.

Note: wl-copy is Wayland-specific. If you’re on X11, use xclip -selection clipboard instead. But if you know what X11 is, why the hell are you reading this guide anyway? You don’t need to, but thanks for the engagement, my fellow basement dweller.

gpg --armor --export <KeyID> | wl-copy

Paste that in your public key field inside your password manager.

Now do the same for your private key:

gpg --armor --export-secret-keys <KeyID> | wl-copy

And there you go. No temporary files, no unnecessary cleanup, and your keys are backed up.

What Do I Do Now?
#

Remember those GPG keys I told you to back up earlier?

Yeah, we’re going to need those now.

Retrieve them from your password manager or encrypted backup and transfer them securely to your new machine.

Assuming you’ve saved them in a secure directory, import them with:

gpg --import /path/to/public.gpg
gpg --import /path/to/private.gpg

If you backed up your keys to password manager instead, just copy the contents of your private-key to your clipboard and pipe them directly into GPG:

wl-paste | gpg --import

You can do the same for your public key, though the private-key export should already contain the corresponding public-key material.

Now verify that your private key was imported successfully:

gpg --list-secret-keys --keyid-format LONG

There you go. No temporary files needed. Apparently pipes really do solve everything. Sorry, Git. You’ve got competition.

Important: Make sure this is the same GPG key you used to initialize your password store. Generating a new key won’t magically decrypt files encrypted with the old one.

I assume my readers don’t need this warning but i’ll say it nonetheless, don’t leave copies of your private key lying around in your Downloads directory. Once you’ve confirmed the import works, clean up any temporary files you’ve created.

Clone the Repo
#

Now for my favorite part.

Git.

Again.

Clone your private GitHub repository directly into the directory where pass expects to find its secrets:

git clone [email protected]:<GitHubUsername>/<RepositoryName>.git ~/.password-store

This assumes ~/.password-store doesn’t already exist. If it does, check what’s in there before replacing anything.

And since we’re dealing with secrets, let’s make sure other local users can’t browse our password store:

chmod -R go-rwx ~/.password-store
That’s It, You’re Done
#

That’s literally it. Just verify that you can decrypt before celebrating.

pass show llm/deepseek > /dev/null && echo "Decryption works!"

If the command succeeds, then congrats, you’re done.

Before Being Done Done
#

One last thing.

When you add or modify secrets, pass automatically commits those changes to your local Git repository, assuming you’ve already initialized Git for the password store.

But Git isn’t going to push those commits by telepathy.

After making changes, run:

pass git push

And when you switch to another machine, pull the latest changes before modifying your secrets:

pass git pull --rebase

Just remember that changes made independently on different machines can still cause Git conflicts.

Conclusion
#

And that’s pretty much it. Your secrets are now encrypted with GPG, version-controlled with Git, and backed up somewhere that isn’t the .env file you accidentally deleted at 3 AM.

No more begging your colleagues for API keys, no more manually rebuilding your environment, and one less reason to question your career choices.

Just remember to back up your GPG private key and keep your passphrase safe. Because if you lose access to either, all those beautifully encrypted secrets are going to stay beautifully encrypted. Forever.

And yes, I managed to solve yet another problem with Git.

You’re welcome.

Abdulaziz Askar
Author
Abdulaziz Askar
Hi, I’m Abdulaziz! I’m a computer science student with a passion for cybersecurity and homelabbing. I spend my time experimenting with tools, breaking things(and sometimes fixing them), and sharing what I’ve learned along the way. When I’m not geeking out over tech, you’ll probably find me stuck in a CTF rabbit hole or questioning why I decided to just try one more project. This blog is where I document the chaos and (hopefully) help others learn from it too.
Share this article