My email setup

Genesis

It all started when I graduated from CMU. I had four months before my computer account (and email address) would be terminated. Knowing that this would cause huge amounts of pain, I made the decision that this would be the last email-address-change of my life and used clickngo to buy megacz.com.

Specs

I wanted to host mail and www services on this domain, as well as have a shell account that I could access to do random hacking (not cracking, you dimwit, get it straight). I also wanted to be able to ssh to megacz.com and read my mail from there, so that even if my personal network connection went down, I could find the nearest alternate net connection and still get to my mail. I also wanted my own IP address for megacz.com in case I wanted to run miscellaneous services in the future. Last of all, the host for megacz.com had to be reliable.

The Search

I used eggdrops.com, shellaccounts.com and various other resources to shop for shells. Eventually I settled on tasam.com due to their excellent reputation and long history in the business. I pay $20/month for all the features mentioned above on a FreeBSD box that comes with a highly skilled and patient admin team.

The Setup

Cyrus Server

My first order of business was to get CMU'sn Cyrus Server working so I could use it to read my mail. I've become very attached to this server since it's blazingly fast (due to a heirarchical mailbox format instead of the crufty outdated /var/mail/ format), and it supports pretty much every feature mentioned in the RFC.

Unfortunately, cyrus makes the assumption that you have root on your machine in order to install it. Furthermore, unless you plan on setting up your own kerberos realm or a custom sasl database, it assumes that you're using /etc/passwd as a list of users and passwords. Tasam uses shadow passwords, and cyrus wouldn't be setuid root, so it can't read the passwd file.

So, over the course of four painful days, I hacked cyrus-imapd and cyrus-sasl into a form that could be installed by a non-root user, run as a non-root user, and would accept a login only from a single user (with the password hardcoded into the csource code). I then wrote a perl script that will download, patch, and compile cyrus and sasl, and then install it in your home directory. It will also download inetd and set it up to run imapd on the mmcc port (port 5050), since non-root users can't run services on priviledged ports (<1024). You can find that script here. Just run it for instructions.

Finally, I put the lines:

:0 c
|/home/adam/bin/fixdeliver.pl

in my .procmailrc. This runs all my email through fixdeliver.pl, which strips off the first line (a header that sendmail tacks on for the /var/mail mailbox format) and then pipes the mail into cyrus' “deliver” command. The “c” on the :0 line makes sure that a copy of each mail is ALSO delivered to my /var/mail mailbox, just in case I somehow goof things up with my cyrus configuration.

Mail Client

My admin was kind enough to install emacs for me – sadly the rest of the users on tasam are more-or-less microsoft-loving-gatesworshipers, and none of them ever asked for emacs. Heathens.

I use gnus with the nnimap mode to read imap folders as if they were newsgroups. This affords me an easy-to-use unified mail-and-news setup with built-in mailing list filtering (puts my mailing lists in their own folders), PGP integration, and the full power of emacs when composing my emails.

Inbound mail handling for the megacz.com domain

Now I needed a way to implement mailing lists and redirection for megacz.com. I wanted to give out addresses to my family (as first-name@megacz.com), and additionally create an alias as a mailing list that would send to all four of us. Plus I wanted to be able to give out random accounts to my friends.

The Right Way ™ to do this is to set things up in sendmail. Unfortunately, I don't have root on the machine, so I can't do that. And I don't want to bug the admin every time I need a change. So the admin set it up so that all mail for megacz.com got delivered to my local shell userid, and advised that I use procmail.

I quickly discovered that if you try to match on the headers of an email, you run into a few problems. These problems are detailed in the advanced section of the Procmail FAQ. I strongly recommend that you read at least that section if you're interested in doing stuff like this.

Briefly, here are the problems with using procmail to do stuff like what I was trying to do (also see Why Headers Don't Matter:

  • Headers don't matter. SMTP mailers DO NOT LOOK AT the headers on a message – the only thing they care about is the envelope (MAIL FROM and RCPT TO) – unfortunately, this information is thrown away BEFORE the message is delivered to procmail! So procmail doesn't really know who the mail was intended for – it can only guess at that from the headers.

  • BCC's – if I send mail to foo@bar.com and BCC foo@megacz.com, the email that arrives at megacz.com WILL NOT have the text “foo@megacz.com” anywhere in it! How will megacz.com know who to give the mail to? The same problem occurs with mailing lists.

  • CC's – if I send an email to foo@megacz.com and carbon-copy bar@megacz.com, both copies of the email will match both users, so the email will get forwarded TWICE to each person.

  • Local delivery – if somebody on tasam sends me email, it might not even have enough headers to guess at the person it should go to!

First Attempt

My first attempt at fixing this was the approach that fetchmail uses, which I found here. I had the admin add the following line to his sendmail.cf file:

H?l?Delivered-To: $u

I then put this lines like this in my .procmailrc:

:0
* Delivered-To:.*joe@megacz.com
!real-address-of-joe-megacz@wherever.com

Second Attempt

This worked for a while, until ran into a problem. Sendmail will set $u to the empty string if a message's envelope (MAIL FROM) specifies two users on the same system. This is to prevent a mail sent to one user and BCC'd to another user from arriving with the BCC'd user's userid on it. A more complete description of the problem can be found in this post to comp.mail.sendmail.

So I finally broke down and researched sendmail's mailertables – it's a way to get mail for a given domain to completely bypass sendmail and be forwarded to a program of your choice, including the envelope. To do this, we had to add these two lines to our sendmail.mc file

FEATURE(mailertable)
MAILER(procmail)

Then execute this command (note that it only works if you have an echo command that interprets \t as a tab character when the -e option is enabled.)

cd /etc/mail
echo -e "megacz.com\tprocmail:/home/adam/.procmailrc" | makemap btree mailertable

Finally, you can now filter based on the envelope sender and reciever like this:

SENDER = $1
RECIPT = $2

:0
* RECIPT ?? ^joe$
! -f $SENDER real-address-for-joe@wherever.com

More Recently

I just colocated a machine in Baltimore, so I now have full control over the mail server config. In the interest of simplicity, I took a vanilla RedHat 7 install and:

Added a pair of lines to my /etc/mail/mailertable for each domain I host:

domain.com    procmail:/etc/procmailrcs/procmailrc
.domain.com   procmail:/etc/procmailrcs/procmailrc

NOTE that's a TAB, not a space on those lines!

  • Ran make from /etc/mail

  • Commented out all lines containing “$=w” or “$#local” in /etc/sendmail.cf

  • Add domain.com to /etc/mail/relay-domains

  • Changed the perms on cyrus/bin/deliver so that it was setuid root and executable by “megacz” (is this needed)?

Finally, this is how to get sendmail to invoke procmail.

I'll put a better description in here when I have time (ie never).