BlogAboutPGPLightningApps

Gmail with Mutt, GPG, Pass, and your Yubikey

Philihp Busby,9 min read

I use Mutt for most of my gmail needs for a number of reasons, including spam is usually HTML-only, while (actually important) emails written by humans always have a readable text-only format. My hubris also assures me that I’m reasonably insulated from email worms .

Configuring Mutt out of the box requires you to put your plaintext password in a ~/.muttrc file, which is asking for all sorts of problems. The following is how I use a hardware Yubikey to protect my Gmail password, and then sync that password across machines.

Yubikey setup with GnuPG

If you don’t already have one, get a Yubikey. Here are some options:

While waiting for this to arrive, you can continue with a GPG key on your hard drive; just make sure you protect it with a passphrase, which GPG will strongly encourage, because otherwise there’s no point.

Migrating your existing key to the card

If you’ve created your key already, migrate it to the card. Most people just have one master key, and one encryption subkey; if you run gpg --list-secret-keys, you may see something like this:

/Users/philihp/.gnupg/pubring.kbx --------------------------------- sec rsa4096/0x5B640B9F9600F122 2016-02-29 [SC] [expires: 2021-02-07] 427E032939DB40F29D03D80F5B640B9F9600F122 uid [ultimate] Philihp Busby <philihp@gmail.com> ssb rsa4096/0x0D86EF2BF0DA842E 2016-02-29 [E] [expires: 2021-02-07]

In the brackets in the 4th column, you can see [SC] for the master key meaning it is meant for the “Signing” usage and the “Certification” usage, and [E] for the subkey meaning it is meant for “Encryption”. I think it’s not a bad idea to create another subkey for “Authentication” or add that usage to an existing key , but important: , but there are two important points:

To move these to your Yubikey, run the command

gpg --edit-key 5B640B9F9600F122

where 5B640B9F9600F122 is your key… it’s actually my key; I don’t know your key. You could tell me it, though, I’d love to know if this helped you. So if you run that, you’ll be dropped into another console

gpg (GnuPG) 2.2.20; Copyright (C) 2020 Free Software Foundation, Inc. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Secret key is available. ... ... gpg>

Useful commands here: help, for common commands; list to show your key, key N, to select a subkey where N is the index number of the key starting with 1, and keytocard to move the selected key to the card.

!> If you don’t have a key selected, keytocard will move the master key.

  1. keytocard without a key selected to move your master key into the Signing slot of your Yubikey.
gpg> keytocard Really move the primary key? (y/N) y Please select where to store the key: (1) Signature key (3) Authentication key Your selection? 1 Replace existing key? (y/N) y
  1. key 1 and keytocard to move your encryption subkey into the Encryption slot of your Yubikey
gpg> key 1 ... sec rsa4096/0x5B640B9F9600F122 ... ssb* rsa4096/0x0D86EF2BF0DA842E ... ssb rsa4096/0xFD8194C54A63DBD5 ... gpg> keytocard Please select where to store the key: (2) Encryption key Your selection? 2
  1. If you have another subkey with Authentication, or you added that usage to your first key using change-usage, move this into the Authentication slot of your Yubikey.
  2. save to save your key.

Now running

gpg --list-secret-keys

Should display something like

sec> rsa4096/0x5B640B9F9600F122 2016-02-29 [SC] [expires: 2021-02-07] 427E032939DB40F29D03D80F5B640B9F9600F122 Card serial no. = 0006 09123456 uid [ultimate] Philihp Busby <philihp@gmail.com> ssb> rsa4096/0x0D86EF2BF0DA842E 2016-02-29 [E] [expires: 2021-02-07] ssb> rsa4096/0xFD8194C54A63DBD5 2020-06-13 [A] [expires: 2021-02-07]

Some things that changed here:

Similarly, if you run

gpg --card-status

You should see something like

❯ gpg --card-status Reader ...........: Yubico YubiKey FIDO CCID Application ID ...: D2760001240102010006091234560000 Application type .: OpenPGP Version ..........: 2.1 Manufacturer .....: Yubico Serial number ....: 09123456 Name of cardholder: Philihp Busby Language prefs ...: en Salutation .......: URL of public key : https://philihp.com/pgp.asc Login data .......: philihp Signature PIN ....: forced Key attributes ...: rsa4096 rsa4096 rsa4096 Max. PIN lengths .: 127 127 127 PIN retry counter : 3 0 3 Signature counter : 42 Signature key ....: 427E 0329 39DB 40F2 9D03 D80F 5B64 0B9F 9600 F122 created ....: 2016-02-29 07:34:57 Encryption key....: C54A 7C6F 8B38 A89F 3102 4BAB 0D86 EF2B F0DA 842E created ....: 2016-02-29 07:34:57 Authentication key: D5CB FB11 287E 0B3A 287D F591 FD81 94C5 4A63 DBD5 created ....: 2020-06-13 04:53:06 General key info..: pub rsa4096/0x5B640B9F9600F122 2016-02-29 Philihp Busby <philihp@gmail.com> sec> rsa4096/0x5B640B9F9600F122 created: 2016-02-29 expires: 2021-06-12 card-no: 0006 09123456 ssb> rsa4096/0x0D86EF2BF0DA842E created: 2016-02-29 expires: 2021-02-07 card-no: 0006 09123456 ssb> rsa4096/0xFD8194C54A63DBD5 created: 2016-11-08 expires: 2021-02-07 card-no: 0006 09123456

Alternatively, creating a new key on the card

If you don’t have a GPG key, or you don’t want to reuse it, this is simpler.

gpg --edit-card

Should bring you into a console for tinkering with the card. The help command will show you most of the common commands anyone should need. Most of the fun stuff is enabled with the admin command, which you should do.

gpg/card> admin Admin commands are allowed

From there, you might want to use the factory-reset command, or at least know it’s there. passwd will let you change the PIN (default: 123456), or the Admin PIN (default: 12345678).

Run the following two commands:

gpg/card> admin Admin commands are allowed gpg/card> generate

It will ask you if you want an off-key backup, which it’s not a bad idea to do, and take the file in ~/.gnupg/sk_????????????????.gpg and the similar file in ~/.gnupg/openpgp-revocs.d/*, and copy those to a reliable and secure location. Treat these like you would treat your passport. You can get another, but you really don’t want to because you will lose all of your visas and history.

Test it out

You should now be able to sign something like a git commit… Configure it like this (with your own key, of course)

git config --global commit.gpgsign true git config --global user.signingkey 427E032939DB40F29D03D80F5B640B9F9600F122 git config --global log.showSignature true

Then go commit somewhere; it should ask you for a pin. Then git log and you can see your signature.

commit a7558903018908258386c9cdabca70e47c6aed24 (HEAD -> master, origin/master) gpg: Signature made Fri Jun 12 03:26:18 2020 GMT gpg: using RSA key D5CBFB11287E0B3A287DF591FD8194C54A63DBD5 gpg: Good signature from "Philihp Busby <philihp@gmail.com>" [ultimate] Author: Philihp Busby <philihp@gmail.com> Date: Fri Jun 12 03:26:18 2020 +0000 Committing like a pro!

The UI you get for PIN entry is a ncurses-driven text-based PIN entry. This can cause issues  with other command line programs, or weird behavior if you commit with a GUI.

I like to use a GUI-driven PIN entry as another layer of being sure I’m not being spoofed into unlocking my Yubikey; setting that up is as simple as

brew install pinentry-mac echo "pinentry-program /usr/local/bin/pinentry-mac" >> ~/.gnupg/gpg-agent.conf

and then kill any existing gpg-agent process.

Configuring Passwordstore

Now that that’s sorted, it’s time to setup Passwordstore! Passwordstore  is a 719  line shell script that fulfills a lot of the same functions as any commercial password manager. It’s a posterchild for the Unix philosophy  by delegating encryption bits to GPG, and delegating the syncing of filesystems with nonlinear history to git. When decrypting a password, it gives it back to you in STDOUT, so you can pipe it as well. And it’s short, so if that’s not enough for you, or you’re wondering how it works (as I once did when I wanted to use it in a team setting, which it does very well), you can read it and get a pretty good grasp on it. I love it. Setting it up is pretty easy. On macOS, just run

brew install pass

and then

pass init 427E032939DB40F29D03D80F5B640B9F9600F122

This will create a folder ~/.password-store, with a file .gpg-id of with your ID in it. If you want other people (perhaps people on your team) to be able to decrypt the files here too, add them as a new line.

Now go to your Google Account and create an App Password . You should be given a 16 character lowercase password. Tell that to Pass with

pass insert gmail.com/philihp@gmail.com

Then if you go to ~/.password-store/gmail.com/ you should see a file philihp@gmail.com.gpg. All this is is a text file with your password, encrypted. Test out decrypting it with:

gpg --decrypt ~/.password-store/gmail.com/philihp@gmail.com.gpg

Another way to get that is with

pass gmail.com/philihp@gmail.com

or if you were going to paste this somewhere and didn’t want it displayed to the screen,

pass -c gmail.com/philihp@gmail.com

Cool. Now you can programmatically request your own password from the command line. Now if we had a highly configurable email client, we could tell it a command that goes and gets your password so it can login and show you your email.

If you want to sync this with another machine, you can run

pass git init pass git remote add origin philihp@dolores.local:password-store.git pass git push

And on your other machines, instead of reconfiguring this, you can just say

git clone philihp@dolores.local:password-store.git ~/.password-store

Configuring Mutt to use Passwordstore

Install mutt (or neomutt ) with

brew install mutt

Then configure it by creating a file ~/.muttrc with

set realname = "Philihp Busby" set from = "philihp@gmail.com" set use_from = yes set envelope_from = yes set smtp_url = "smtps://philihp@gmail.com@smtp.gmail.com:465/" set smtp_pass = `pass show gmail.com/philihp@gmail.com` set imap_user = "philihp@gmail.com" set imap_pass = `pass show gmail.com/philihp@gmail.com` set folder = "imaps://imap.gmail.com:993" set spoolfile = "+INBOX" set ssl_force_tls = yes

!> Notice the back-ticks on the smtp_pass and imap_pass. That’s important, because it tells mutt that rather than “this is the password”, it says to execute that and it will give you back the password.

Now startup mutt with

mutt

If doesn’t manage to get your gmail password with this command, it will ask you for it.

Conclusion

I hope I didn’t expose myself by writing this, and admittedly this is a very unique snowflake for email; most people just use a browser on gmail.com. This setup has evolved over a couple years, and works very well for me.

There are some more things you can configure, which are not simple with other setups:

GitHub · Bluesky · LinkedIn · Instagram · KeybaseRSS

Built from ecfc45b2 CC BY 4.0 — with love from San Francisco.