Current Email Solution: gpg-agent, cron, OfflineIMAP, Notmuch, Alot, Vim and msmtp
I'm thinking of maybe replacing some parts in my current email pipeline, so I'm writing down the current state of things here lest I forget once I've made the switch. Hopefully this may also be of help to someone wanting something similar to what I have now.
I'll start by outlining the basic idea, and then I'll go slighly more in-depth about how each part is configured, and the problems that may arise.
Basic Idea
At some point a few years ago I had this idea that I wanted to start using email “for real”, whatever that means. Probably had to do with frequent correspondence, and maybe a sprinkle of mailing list in it too. (Spoiler alert: that never happened.)
As a step in that endeavour, I asked a friend to donate me an email inbox at his server. This is the email address you can see on my About page (). I also read up on how to efficiently deal with email locally on your machine. A common recommendation seemed to be sup – but! at the time that wasn't very actively maintained, which lead me to discover Notmuch.
Some things I like about Notmuch over sup is that Notmuch follows the Unix philosophy better by decoupling interface from indexing from downloading. All Notmuch does is index and search through your email. To get the other functionality associated with an email client (technical term: MUA, or Mail User Agent) you need to glue it together with additional software.
Here are the tasks normally associated with an email client:
- Download new emails (and synchronise "unread" flags and such).
- Filter all stored emails according to criteria.
- Display these relevant emails in a suitable view.
- Provide an editor to compose emails.
- Send away outgoing email to the outgoing mail server.
In a traditional email client, the same program might accomplish all of these. With my Notmuch setup, each of these tasks is accomplished by a separate program, which allows each program to "do one thing and do it well". These are the programs I use:
- Download emails: OfflineIMAP
- Filter emails: Notmuch
- Display emails: Alot
- Compose emails: Vim
- Send emails: msmtp
It works really well, and the reason I may change this is not because I'm discontent with it, but rather because I'm curious about what the grass is like on the other side. The primary change I'm interested in making is switching out Alot with Emacs instead, now that I'm slightly more comfortable with Emacs.
Let's get into details!
OfflineIMAP (and cron and gpg-agent...)
I run OfflineIMAP every five minutes as a cron job. Let's just see
the configuration first. This is my .offlineimaprc
, of course
anonymised.
[general] pythonfile = /home/john/.offlineimap.py accounts = john@example.com [Account john@example.com] localrepository = Personal-local remoterepository = Personal-remote [Repository Personal-local] type = Maildir localfolders = ~/mail/john@example.com [Repository Personal-remote] type = IMAP remotehost = imap.example.com remoteport = 143 remoteuser = john remotepasseval = get_pass('john.imap.example.com')
OfflineIMAP allows you to extend the application with your own Python code,
which is what the pythonfile
declaration says. In this file, I
define the function getpass
:
from subprocess import check_output
import os
def get_pass(mailbox):
path = os.path.join(
os.environ['HOME'],
'.passwords',
'{}.gpg'.format(mailbox)
)
gpg = [
'gpg2', '--batch', '--no-tty',
'--use-agent', '-dq', path
]
return check_output(gpg).strip()
This uses gpg to decrypt the password, stored decrypted under
~/.passwords/john.imap.example.com.gpg
. Why? Next section.
Obervations While Researching
I noticed two things while writing this part of the post. One is that apparently you don't need to run OfflineIMAP as a cron job. It can run as a daemon instead. Another is that the primary OfflineIMAP maintainer seems to have stepped down from developing OfflineIMAP. They are making security fixes and answering to pull requests, but they have themselves moved their own development efforts to a similar replacement project. I have not evaluated this alternative.
gpg-agent
I have an irrational dislike of specifying passphrases in plain text in configurations, so this is an additional twist to my configuration. I encrypt the password with gpg and then ask OfflineIMAP to decrypt it to be able to use it. I don't think that actually makes a difference given what I do next, but it gives me a nice, comfy, false sense of security.
Of course, this means OfflineIMAP will stop to ask me for my encryption passphrase every five minutes when it tries to download new mail, so I have to introduce gpg-agent, which lets me save the passphrase in memory so subsequent calls to gpg can use it without asking me.
I launch gpg-agent with gpg-agent --daemon --write-env-file
which writes out the identifying information of the current agent to
.gpg-agent-info
. In the script that runs OfflineIMAP, I prepare
this information by first calling
source /home/john/.gpg-agent-info
One thing worth noting is that by default, gpg-agent only saves passphrases
in memory for something like five minutes, which would make it literally
useless in this case. To tell it to hold onto keys for longer, I changed my
~/.gnupg/gpg-agent.conf
to contain
default-cache-ttl 34560000 max-cache-ttl 34560000
As you may realise, this means the password I went to lengths to protect is very easy to get a hold of – either by connecting to the gpg-agent and decrypting the password, or perhaps directly by inspecting the memory contents of the machine. I tell myself that the kind of attacker I'm worried about is far more likely to snoop around for password-looking files on the filesystem than they are to attempt to decrypt files with the .gpg extension.
Of course, now that I've publically shared this process … I guess the little security by obscurity I had is gone.
Notmuch
Comparatively speaking, my Notmuch configuration is very spartan.
[database] path=/home/john/mail [user] name=John primary_email=john@example.com [new] tags=unread;inbox; [maildir] synchronize_flags=true
Did you click one of those Notmuch links and read about how great Notmuch is at tagging, managing threads, search, how you can include great spam filtering and so on? Well, I use nothing of that. I just haven't gotten around to starting to use email “for real”, so there hasn't been a need yet.
In the same cron script that runs offlineimap, I also run
notmuch new
to add any new email to my Notmuch database.
Alot
I have a lot of Alot configuration (see what I did there?), but 95% of it is just my theme, i.e. the colours I've picked for each element of the UI. The non-UI parts are these:
editor_cmd = vim [bindings] q = bclose Q = exit [accounts] [[john@example.com]] realname = John address = john@example.com sendmail_command = msmtp --account=john@example.com -t sent_box = maildir:///home/john/mail/john@example.com/Sent draft_box = maildir:///home/john/mail/john@example.com/Drafts [[[abook]]] type = shellcommand command = "notmuch address --output=recipients from:john@example.com" regexp = (\"?(?P<name>.+)\"?)?\s*<(?P<email>.*@.+?)> shellcommand_external_filtering = False
Here you also get a sneak peek on how to choose the editor for composing email, and the command used to send mail.
I'm not sure whether the sent_box
and draft_box
directories work like I want them to (i.e. get uploaded to the IMAP server, if
that's even a thing), but I haven't bothered to look into it either.
Vim
Not much to say here (I'm on a roll!). Although my primary editor is Emacs, I'm a Vim fan. And Vim starts up a hell of a lot faster, so I opted to use Vim to compose email. I'm probably missing out on Emacs' message-mode, one of the reasons I'm thinking of switching.
msmtp (and gpg-agent)
To send email, I use msmtp, which at least at the time was the recommended drop-in replacement for sendmail. Like OfflineIMAP, it needs a password for the outgoing mail server, and like with OfflineIMAP, it can be coerced into taking that password in encrypted form. This is my configuration:
defaults tls on tls_certcheck off account john@example.com tls_starttls on host smtp.example.com port 587 from john@example.com auth on user john passwordeval export $(cat /home/john/.gpg-agent-info) && gpg --use-agent --batch -q -d ~/.passwords/john.smtp.example.com.gpg
As you can see, the passwordeval
directive allows some sort of
shell script to be run to get the password, which in this case is a gpg decryption
command. The same caveats about gpg-agent as in the OfflineIMAP section applies.
The Replacements
But Chris, this all sounds wonderful. Why on earth would you want to switch anything of this out?
I'm not quite sure. I'm always thinking "surely this will get me to use my email more, if I just install this new shiny toy". I know it doesn't work that way. I can't help thinking it though.
The shiny new toy I'm currently looking at is message-mode in Emacs to compose email. I do it very manually at the moment. I also like the prospect of using an Alot replacement that is actually developed by the same people that are making Notmuch. The slow start-up times of Emacs won't actually matter if I'm in Emacs already anyway!
I'm also curious about adding some sort of automatic spam filtering and tagging solution, to actually start playing to the strengths of Notmuch.