Synchronize your 2FA Gmail with mbsync

12. 02. 2021 | 21. 01. 2024 | Jakub Kadlčík | EN fedora emacs mbsync email gmail howto

In comparison to graphical email applications, configuring the command-line clients can be a needlessly painful experience. Not because of the client configuration itself but rather finding out the proper IMAP settings for your account. Personally, I spent more hours on moving my mail into Emacs (and previously into Mutt), than I care to admit. And in the end, it turned out that the only real obstacle was figuring out, how to get the freaking synchronization with Gmail working. Let me share my findings to potentially ease the pain for you.

TL;DR: Jump to the last section Gmail with 2FA

The obligatory intro on email clients

If you use, or ever tried to use some of the mainstream email clients such as Thunderbird, Evolution, Geary, or some mobile phone client (I am not a mobile phone nerd, so I don’t know any of them. I just have some Gmail thingy there), you might have formed an idea what an email client is supposed to do - download your messages, index them, filter them, and let you interactively work with them. And of course, allowing you to send messages as well. There is nothing groundbreaking about this, that’s just how we use email, and therefore what we expect email clients to do.

Command-line (or rather text-based) email clients do much less and delegate a lot of work to a series of other small tools (aka the Unix philosophy). Typically, we have a separate program for simply downloading the mail from a server. There are many options, such as mbsync from isync package, OfflineIMAP, getmail, etc. Their only job is to download the mail and save it (one message per file) to your disk. This can be useful on its own, e.g. for backing-up your messages in case the email provider shuts down its business.

Optionally, it’s up to us to configure a spam filter either on the server-side (e.g. in Gmail settings) or on the client by using procmail, Bogofilter, or SpamAssasin. The next building block is indexing. This is generally not true for everybody but after years of countless mailing list subscriptions, many of us have our inboxes flooded with thousands or hundreds of thousands of messages. Directory-based storages just ain’t gonna cut it. We need some kind of a real database with indexes. For this, we can use tools like Mu, or Notmuch.

Finally, we are getting to the fun part, which is displaying, reading, and interacting with email. This is the most discussed and tutorial-covered link in the chain. And while exciting, and hacker-ish looking on screenshots, the configuration revolves mainly around color schemes and key bindings, which is quite intuitive and also not that big of a deal when using some default settings. Anyway, we can pick from the following frontends - Mu4e, Notmuch, Mutt, NeoMutt, Gnus, Alpine, and probably from a bunch of less-known clients as well. For sending email messages, one would expect to also utilize some specialized tool (and it certainly is possible) but usually, it is handled by the frontend program itself.

As you noticed, there is a lot of small, interchangeable utilities in play. Some of them optional, some of them even more optional. The categories I presented are not strictly defined and some tools overlap across multiple categories. It’s up to you to choose and put those lego blocks together and build your own email setup.

Today we are going to focus on the very first category, which is downloading email messages to your computer. We are going to utilize mbsync command, and we are going to set it up for your enterprise 2FA Gmail account. Since the result is a bunch of downloaded files (which is not very impressive), and every cool blog post should have at least one screenshot, I am going to jump a few steps forward and show you how the end-game might look like once you manage to successfully download your mail and open it in Emacs.

Mu4e configuration by @rougier

This beautiful configuration is not mine. The screenshot is taken from the rougier/mu4e-dashboard repository.

A less hostile provider than Gmail

First, please install the isync package, so we can get this out of our way. Use the package manager provided by your GNU/Linux distribution, on Fedora I would do

dnf install isync

Configuring multiple accounts in mbsync is trivial, and Gmail IMAP is a mess. Therefore, I would recommend setting up an account from a different email provider first (that is if you have the option) and learn how mbsync works. Let’s create the ~/.mbsyncrc configuration file and insert the following settings.

IMAPStore foo-remote
Host imap.foo.com
SSLType IMAPS
User frostyx@foo.com
Pass supercomplicatedpassword

MaildirStore foo-local
Path ~/Mail/foo/
Inbox ~/Mail/foo/INBOX
Subfolders Verbatim

Channel foo
Master :foo-remote:
Slave :foo-local:
Create Both
Expunge Both
Patterns *
SyncState *

A more complicated version of this snippet can be found on ArchWiki and countless blogs (1, 2, 3, 4, …), so I am not going to thoroughly describe the settings here. I would by paraphrasing the manpage anyway. Let’s just vaguely say, that the first section describes how to access your email account provided by some third-party. The second section describes how to store the downloaded messages on our computer. Finally, the last section configures what messages should be synchronized between those two, he said while pretending to understand it.

Replace all foo symbols in the snippet with some short label for your email account (e.g. personal, work, test, …) and properly set the Host, User, and Pass values in the first section. Then you should be able to successfully run the following command

# Use the short email label you chose before
mbsync -V foo

There is not going to be any successful message in the output. Don’t panic, seeing a bunch of Opening and Synchronizing... lines means that it works fine. To make sure, list the mail directory

ls -1 ~/Mail

Because we don’t want to store our super-secret email passwords in plaintext, we should stash them into some keychain and let mbsync how to get them. Don’t even think about using the gpg command, that’s more complicated than rocket science. Use pass instead!

# Initialize the keychain, you will run this just once in your life
pass init <gpg-id or email>

# Insert the password for your email account
pass insert email/frostyx@foo.com

# And print it to the terminal to make sure it was stored properly
pass email/frostyx@foo.com

This is all you need to know about pass (although it has some amazing features such as storing passwords to git, which is worth checking out). Now, remove the Pass line from your ~/.mbsyncrc config and use this one instead.

PassCmd "pass email/frostyx@foo.com"

Make sure that mbsync -V foo still works, even when using a password from the keychain.

Gmail IMAP sucks

Before moving to the Gmail configuration, I would like to make a disclaimer - Gmail IMAP support has limitations, idiosyncrasies, and to be scientifically precise, it just sucks. Please have patience.

All of these are minor inconveniences, that can be workarounded, solved, or lived with. It’s just something to keep in mind.

Gmail with a plain password

Armored with bulletproof patience and unending determination, we shall continue to mbsync configuration for Gmail. Now, if you think “perhaps I should try a personal Gmail account without 2FA, it is going to be easier”, please smother the idea in this instant. It is not possible. Please repeat after me, it is not possible. I refused to believe, tried over and over, failed miserably, over and over, and ended up configuring 2FA anyway. Please save yourself the time and the pain.

Log-in to your Gmail account and click to your profile picture in the top-right. Then continue to “Manage your Google Account”. In the left menu, click on “Security”, and turn on the 2-Step Verification.

Now we need to generate an App Password. Click on “2-Step Verification”, then at the bottom of the page, you will find “App passwords” section. When opening it, you will see this.

Use whatever app name you want and click “Create”.

After closing this page, you won’t be able to display it again, so please write down the generated password. We will put it to the keychain in a minute.

Isn’t 2FA like a 2FA

Just a small digression in case you skipped the previous section because you already have 2FA configured. The Google documentation about App Passwords explicitly says:

App Passwords can only be used with accounts that have 2-Step Verification turned on.

The statement is correct but it can be a bit misleading. In fact, this happened to be one of the biggest pain-points for me. My corporate email uses Single-Sign-On authentication through Kerberos. The login page looks like this.

As you can see, my password consists of a PIN, which is a persistent passphrase, followed by a TOKEN - a randomly generated short string from a small device, that I wear in my wallet. In my view, this counts as a 2-Step verified account and should pass the constraint for using App Passwords. This is absolutely not true though. Please follow the same exact steps that we did in the Gmail with a plain password section.

Gmail with 2FA

We finally managed to jump through all the necessary hoops and tweaked our Google account. Now we can get back to the mbsync configuration. Let’s take the snippet from the Less hostile provider than Gmail section and make just a couple of changes.

IMAPStore gmail-remote
Host imap.gmail.com
SSLType IMAPS
AuthMechs LOGIN
User foo@gmail.com
PassCmd "pass email/foo@gmail.com"

MaildirStore gmail-local
Path ~/Mail/gmail/
Inbox ~/Mail/gmail/INBOX
Subfolders Verbatim

Channel gmail
Master :gmail-remote:
Slave :gmail-local:
Create Both
Expunge Both
Patterns * !"[Gmail]/All Mail" !"[Gmail]/Important" !"[Gmail]/Starred" !"[Gmail]/Bin"
SyncState *

The only fields that you need to modify are User and PassCmd. Your password is the generated App Password that we transcribed from the yellow stripe in the previous section - Gmail with a plain password. Don’t forget to stash it to the pass keychain.

pass insert email/foo@gmail.com