Starting spamd (and other related fun) using Server.app on OS X

2013/11/03

Categories: GeekStuff

Since my previous blog post on this topic was so useful to me, but slightly obsolete, I’m posting updates. I recently had occasion to rebuild my mail server, and decided to bite the bullet and go with Mavericks plus the new Server.app.

The new Server.app thing is a pretty pathetic replacement for the classic OS X Server, but most of the same stuff can be found under the hood. Eventually.

The SpamAssassin implementation is buried in, and invoked by, amavisd, which means that if you want to run spamc directly, you have to do Something Else. The basic idea from before remains; create a launchd job for the spamd program.

But, there’s changes, because the new system puts server stuff inside Server.app, in Server.app/Contents/ServerRoot. (If you are interested in this post, you probably already know that applications are just directories and can contain all sorts of things, like a partial root filesystem image in this case.) So my new org.spamassassin.spamd.plist looks like this:

<?xml version="1.0" encoding="UTF-8"?>                                                              
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"                                       
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">                                                   
<plist version="1.0">                                                                               
<dict>                                                                                              
        <key>Label</key>                                                                            
        <string>org.spamassassin.spamd</string>                                                     
        <key>OnDemand</key>                                                                         
        <false/>                                                                                    
        <key>Program</key>                                                                          
        <string>/Applications/Server.app/Contents/ServerRoot/usr/bin/spamd</string>                 
        <key>ProgramArguments</key>                                                                 
        <array>                                                                                     
                <string>-D</string>                                                                 
        </array>                                                                                    
        <key>RunOnLoad</key>                                                                        
        <true/>                                                                                     
</dict>                                                                                             
</plist>

But that turns out not to entirely solve my problem. What I’m trying to do is have a local IMAP server so that I can have fetchmail grab mail from a couple of sources, accumulate it in a single place, and so on. And I want it to be subject to SpamAssassin rules. And because of reasons, I want to also hand it off to procmail for some other filtering. And instead of having procmail run everything through spamc, I can just rely on the fact that local delivery goes through the mail server, and thus through amavisd, right?

Almost right. There’s some configuration changes needed (in /Library/Server/Mail/Config/amavisd/amavisd.conf, which is of course the first place you would look.) First, I wanted SpamAssassin tagging on everything, even boring stuff, so I changed the minimum score needed to produce tags:

$sa_tag_level_deflt  = -999.0;  # add spam info headers if at, or above that level

I would like to point out that “deflt”, here, is a really great example of an abbreviation which does not save enough space to help. I would have called this $sa_tag_minimum_score maybe.

But amavisd is also clever enough not to do tagging on anything that it isn’t delivering locally. Unfortunately, for historical reasons, I am receiving mail for more than one domain, so the To: lines in my email don’t always match. There’s lots of ways to set the list of domains you want, but what I want is to always match everything, because I don’t care what the address is, this mailer only does local delivery and thus everything is always local, period. Amusingly, “period” turns out to be the magic word:

@local_domains_maps = ['.'];

This tells amavisd that everything is local delivery and should get properly processed.

And while I’m at it, the place for custom SpamAssassin rules is local.cf, found in /Library/Server/Mail/Config/spamassassin. Only it’s not there, just a local.cf.default, but you can just create a local.cf and stuff seems to work.

And, speaking of things that took me a while to find: The amavisd job description lives in Server.app/Contents/ServerRoot/System/Library/LaunchDaemons, which is a fascinating bit of history: Normally, /System/Library is used only for the base operating system, while installed apps put their stuff in /Library. But the parts of Server.app which aren’t really intended to be tweaked or modified just live in the place under ServerRoot that they would have been in an OS X Server filesystem. Interesting.

By the way, I’d also like to state for the record that mail server failures are a big hassle.