It took me days to figure this one out, so I'd like to share the knowledge.
This is about how to set up SpamAssassin between Postfix and Dovecot while
keeping a separate system user per mailbox, using
virtual_alias in Postfix.
I recently wanted to add virus and SPAM filtering to my mailserver setup, consisting of Postfix and Dovecot (plus some other policy checking, SPF and DKIM stuff).
Googling this you will find tons of guides on how to set this up, most of them use Amavis to integrate SpamAssassin and ClamAV into the mail chain. I didn't like this approach because it requires a second Postfix instance for back-injection after scanning. I had already integrated ClamAV as milter (using clamav-milter) when coming to SpamAssassin, so I first tried to get along with spamass-milter.
The problem with this approach, as well as with the rest of the guides I found
on the net, is that it assumes mail to be processed and stored with a static
UID/GID, which is not the case for my setup. I had chosen to create a separate
system user account for each mailbox and use
virtual_alias in Postfix to map
addresses to it (maybe I'll explain my reasons in a separate setup post).
The setups described by the guides I found did not play well with this setup,
making SpamAssassin always guess the user to run as from the first part of
the recipient address and fail at
So on the LDA end of the chain I had Dovecot, hooked up to Postfix using
mailbox_command = /usr/bin/dovecot_deliver
in order to have Sieve filtering on the server. SpamAssassin scanning needs to be run as the final system user in order to have user preference files and Bayes databases created in the right place, so I was looking for a way to put it right in between.
Long story short, after a couple of attempts with custom transport definitions
in master.cf, I came up with this extremely simple solution: a custom
wrapper integrated using
(...) mailbox_command = /etc/postfix/spamass-dovecot-pipe.sh
1 2 3
#!/bin/sh exec /usr/bin/spamc -u "$USER" -e /usr/lib/dovecot/deliver -f "$SENDER" -d "$USER"
The key here is that anything defined via
mailbox_command is run by the local
transport under the UID/GID of the final destination, in my case the system account
the current message is delivered to. Luckily, Postfix exposes all required information
as environment variables.