When building systems that send out e-mails to the customers one has to find a way to test what the output looks like without actually sending the mail to the client.
This article describes how to set up such a fake mail server that can be used in the test environment as a drop in replacement for you actual mail gateway.
Incoming e-mails will be stored on the local inboxes based on the FROM address (the TO address is ignored).
This allows you to test multiple applications against the same test mail server assuming that the applications use different FROM address.
I will also show how to configure IMAP access to these test mailboxes.
This guide has been tested on Debian 7.
Postfix
We will use Postfix as our SMTP server and Dovecot for the IMAP support.
apt-get install postfix dovecot-imapd
In the file /etc/postfix/master.cf find a line that looks like this:
smtp unix - - - - - smtp
and replace it with:
smtp unix - - - - - discard
This tells Postfix to send all outgoing mail to discard instead of smtp.
discard will just happily accept whatever is sent to it and will always report successful delivery.
In order to keep a local copy of the the discarded mail for testing add the following line to the file /etc/postfix/main.cf:
sender_bcc_maps = hash:/etc/postfix/sender_bcc
This parameter tells where BCC copies of the mails should be sent based on the FROM address.
Create the /etc/postfix/sender_bcc with contents like this:
user@example.com test1@fake
@example.com fallthrough@fake
and run the command:
postmap /etc/postfix/sender_bcc
At this point the very basic setup is in place and if you restarted the postfix all the relayed e-mails would be dropped and local copies would be stored in mailboxes under /var/mail
To make testing a bit more convenient we will set up virtual mailboxes that will be stored under /var/vmail and can be accessed over IMAP using non-system accounts.
Add the following to the file /etc/postfix/main.cf:
virtual_mailbox_domains = fake
virtual_mailbox_base = /var/vmail
virtual_mailbox_maps = hash:/etc/postfix/virtual_mailbox
virtual_minimum_uid = 100
virtual_uid_maps = static:65534
virtual_gid_maps = static:8
Create the file /etc/postfix/virtual_mailbox
test1@fake test1/
fallthrough@fake test2/
Run postmap on it:
postmap /etc/postfix/virtual_mailbox
Create the mail directory:
mkdir /var/vmail
chown nobody:mail /var/vmail
Now restart Postfix.
postfix reload
At this point mail should be correctly be delivered to Maildir formated directories under /var/vmail for addresses that match entries in /etc/postfix/sender_bcc.
IMAP
Now we will configure access to these local mailboxes over IMAP using Dovecot.
We will use file based auth to keep things as simple as possible.
Create the file /etc/dovecot/conf.d/auth-plain.conf.ext with the following contents:
passdb {
driver = passwd-file
args = username_format=%u /etc/dovecot/passwd
}
userdb {
driver = passwd-file
args = username_format=%u /etc/dovecot/passwd
}
Now open /etc/dovecot/conf.d/10-auth.conf and add the following line at the end of it:
!include auth-plain.conf.ext
Create the file /etc/dovecot/passwd with contents similar to this:
test1@fake:{SSHA}ghZpew7L4psekJyC0MUoVA3Usg0SxAjm:65534:8::/var/vmail/test1::
test2@fake:{SSHA}c9yb4ibK+rpoMBR+OnoMBrNgyjD8KraL:65534:8::/var/vmail/test2::
Password hashes can be created with doveadm like this:
doveadm pw -s SSHA -p yourPassword
Edit the file /etc/dovecot/conf.d/10-mail.conf by replacing
mail_location = mbox:~/mail:INBOX=/var/mail/%u
with
mail_location = maildir:/var/vmail/%n
Finally restart Dovecot with:
/etc/init.d/dovecot restart
Safeguard
To make double sure that no test e-mails will leave this system we will also firewall outgoing SMTP and only allow connections over localhost:
iptables -A OUTPUT -p tcp -d 127.0.0.1 --dport 25 -j ACCEPT
iptables -A OUTPUT -p tcp --dport 25 -j DROP
And to make this change permanent add it into the file /etc/network/interfaces so that it would be read in every time the interface comes up. Mine looks like this:
iface eth0 inet dhcp
up /sbin/iptables -A OUTPUT -p tcp -d 127.0.0.1 --dport 25 -j ACCEPT
up /sbin/iptables -A OUTPUT -p tcp --dport 25 -j DROP