I'd been hesitant to adopt a password manager for a number of years, due to a few reasons:
-
I kept moving between computers. I still do. I have two or three laptops and a desktop in the active rotation; the desktop's been through about three generations of hard disks. People know me as someone to whom they can give their "outdated" hardware to get some more use out of it. So keeping a password database up to date between all of them is not really trivial.
-
I have an inherent distrust of "cloud" solutions. These are my passwords; they anchor trust for all my accounts. "Trust me bro" is not a good security policy for something like that. Yes, I know, most of them have reasonable thought and effort put into cryptosystem design, but the point still stands: for starters, you're trusting your vendor to implement that design right, even if it is correct, and--after that--you're investing all your effort and trust into one vendor. I'm sure that's fine for you KeePassXC users out there who can audit the source, but it's more concerning for things like 1Password where you can't. And, besides, the golden rule of auditability: Keep It Simple, Stupid. Anything requiring network is already dubious at best, since "easy reachability" doesn't mean just your ability in particular; anything that neeeeeeds a graphical interface will, if it doesn't detonate straightaway on Sway or work incorrectly with XWayland, still expose itself to a huge surface of code dependencies that can be exploited. Having a "nice REST API" or a CLI bolted on after the fact doesn't "fix" this; simply, less is more when you're dealing with secrets.
-
I remember those passwords anyway, right? That's the whole point!
... well, until recently, at least. I do have about a dozen base passwords with a few derivations apiece, and all of them are pretty long but fairly memorable. But the number of accounts I have has far outpaced how many passwords I can remember, which leads me down the ugly rabbit hole of password reuse (also known as: how a phishing attack can pivot into emptying your bank accounts). There are some tactics you can use to combat this--I recall someone who insisted on inserting the domain name into their password in a regular and memorable way--but this can run afoul of very obtuse password requirements, including the ill-advised maximum length options. (I'm looking at you, Progressive and FCC CORES.) Plus it won't save you if a reasonably attentive attacker can guess your derivation scheme from an example; not all of them will invest the effort, of course, but it doesn't hurt to have good posture.
So I guess I have to revisit (3). Enter pass.
In the effort of keeping it simple, pass is just that. No cloud sync, no
home-rolled database formats, no extra software agents; pass does exactly two
things:
- It owns a directory tree in '~/.password-store`, where the files inside are (encrypted) passwords; and
- It encrypts everything at rest with a GPG key (or keys).
The exact GPG key(s) it uses is determined by the ~/.password-store/.gpg-id
file, which is a line-based plain text file of key IDs. Aside from that, most
everything else is just wrappers around directory operations; pass rm, pass cp, pass mv, pass ls, etc., all do what you expect they might inside the
tree. The more interesting commands are pass show, which is a gpg --decrypt,
pass insert, which is a gpg --encrypt, and pass generate, which is a
decent little password generator which automatically does pass store for you.
pass show is more or less the default, so you can just pass Path/To/password and expect it to be written out to your terminal.
If you're following along, note that, unless you know what you're doing, I recommend a separate GPG key for this. That's easy enough to do:
gpg --gen-key
will walk you through the steps. Just name it appropriately, and give it a
memorable email address (that doesn't need to be authentic!)--it's one of the
easiest ways to identify the key. (For example, mine is pass@hygiea.) When
you're done, give that email address to pass init, or, if you've already made
the directory, just put it on a line by itself in ~/.password-store/.gpg-id.
Once that's all done, you should be able to just pass insert any of your
current passwords. If you're coming from elsewhere, exporting passwords from
your old tool is left as an exercise to the reader.
So that's fine and dandy, but now I've run afoul of (1). Granted, I can (and have) SSH'd into my devices, but the default configuration of pinentry and gpg-agent means you have to do a few calisthenics to unlock the key password. Plus it depends on your box being SSH-able; if you've memorized your IPv4 gateway's address and are fortunate enough to have a NAT you can control at home, then, by all means, keep doing that.
For the rest of us, including when laziness wins over insanity, there's a lovely Go project called syncthing that tries to make file synchronization between devices easy again. Basically, it's a little network daemon with an HTTP interface that sets up a device keypair; you can take the public parts to other devices and, through the magic of ICE and some freely-available signalling servers (or signal-less protocols like mDNS), the two peers find each other. Somewhat like Wireguard, the key is the identity, so possession of the key is sufficient to validate your peers--although you can still give them reasonable names, if you'd like.
Friends and I have already used this for years; it started out with a book
club, but we quickly learned that sync'd directories are well suited to hold
"bare" Git repositories (decentralized Git, the way it was meant to be!).
Perhaps more surprisingly, it's the perfect fit for pass: all you have to do
is add your ~/.password-store as a "shared folder" and share it with your
other devices, and syncthing will silently but eagerly keep all changes
synchronized between them.
That glosses over one important point, though; the other devices, naturally, need your GPG key, the one you've been using to protect those precious passwords. Fortunately, it's not too hard to get that:
gpg --export-secret-keys -a $ID > $file
gpg --import < $file
Replace $ID with your key ID, and put $file wherever. If your key was
encrypted with a password (as it should be!), this exported key is still
protected by the same password, so--as long as that's pretty strong--you could
even email it to yourself. Personally, I just use SSH and the clipboard; it
works, right?
And here's the nice thing: since it's the same key, it'll have the same ID
you already have in ~/.password-store/.gpg-id, which syncthing also
dutifully synchronized. Zero reconfiguration required. Now you can pass insert on one host and, as long as your network is amicable, pass show it on
another in seconds!
Everyone and their mother wants to set up your Google Authenticator codes, don't they? Fortunately, TOTP (and the lesser-used HOTP) are open standards; oath-toolkit is a decent POSIX CLI implementation thereof. Purists might complain that it's not the wisest to keep your OTP secrets and passwords together--probably why the Authenticator App was conceived, back before people used smartphones for everything--but any password manager worth its salt ought to give you OTP codes on demand, it seems.
You can do it differently, of course, but I personally store TOTP secrets--the
string of letters and numbers you sometimes have to ask explicitly for--just
like I would passwords, under the TOTP directory. For example:
pass store TOTP/example.org
And then just paste in the secret. If you do it this way verbatim, the following script will just work for you:
#!/bin/sh
pass show TOTP/"$1" | oathtool --totp -b -
Just pop that in any $PATH directory as pass-otp, set it executable, and
then you can just pass-otp example.org and expect to get 123456 back. Shell
completion is also left as an exercise to the reader.
(This assumes that all the OTP secrets you get are base32 encoded (-b); in
practice, I've yet to see one that isn't, because they're shorter and easier to
transfer that way. Whitespace isn't significant.)
You could set this up as an extension, too, but the paths are distribution- and
package-specific, so I won't recommend them here. Use the Source, Luke; pass
is just a shell script, after all :)
pass has a small but fairly replete set of plugins for things you might want,
like, say, browser plugins. In doing so, you might wonder how to "autofill"
other things, like usernames and email addresses--all of this and more is
covered on their site. (Spoiler: subsequent lines in your password
files!) And, again, these all transparently work with syncthing if you've set
them up as above.
This is one of the reasons I prefer small, robust tools rather than a
kitchen-sink-included, optional-cloud-sync password manager with built in
social media platform: a small, well-designed interface is infinitely
composable--and that includes unexpected, serendipitous composability, like
"passwords are a file tree" with "this tool synchronizes file trees, no
questions asked". That's true of extensions, too; if OTP 2 comes out tomorrow,
all I need is a replacement for oathtool, and I'll be ready for it!