Modeled after http://www.adomas.org/2006/08/postfix-dovecot/, but updated to Debian Wheezy. It’s mostly a documentation for myself, but if Google brought you here, maybe it’s useful to you?
1. Install postfix
apt-get install postfix heirloom-mailx
With these two installed, try receiving mail at root@FQDN
. It should appear
in /var/mail/root
. Most likely you need to add your FQDN in /etc/postfix/main.cf
by updating
the mydestination
variable to something like:
mydestination = yrden.de, localhost.customer.vlinux.de, localhost
Similarly, try sending mail
echo "test" | mail -s testsubject old_mail_address
on the new server, to see if everything works. I’ve found it to be very helpful to have a
tail -f /var/log/mail.log
window open, so I can see error messages right as they appear.
2. Create self-signed certificate
openssl req -new -x509 -days 3650 -nodes \
-out /etc/ssl/certs/mail.yrden.de.pem \
-keyout /etc/ssl/private/mail.yrden.de.key
Everything is optional, except “Common Name”. It should be the FQDN as announced in the DNS.
In my case, that’s mail.yrden.de
.
3. TLS on Postfix
Again, edit /etc/postfix/main.cf
and modify the following lines. They are present in the
default file, but use the snake-oil certificate.
smtpd_tls_cert_file=/etc/ssl/certs/mail.yrden.de.pem
smtpd_tls_key_file=/etc/ssl/private/mail.yrden.de.key
Restart Postfix: postfix reload
4. SMTP auth with Postfix
Setting up SASL
apt-get install sasl2-bin libsasl2-2
Modify /etc/default/saslauthd
so that START and OPTIONS settings look like
START=yes
# …
OPTIONS="-c -m /var/spool/postfix/var/run/saslauthd"
Next, create the appropriate directories
cd /var/spool/postfix/
mkdir -p /var/spool/postfix/var/run/saslauthd
chown -R root.sasl /var/spool/postfix/var/run/saslauthd
and allow postfix to access the saslauthd:
adduser postfix sasl
Restart saslauthd now: service saslauthd restart
. It should have created
additional sockets in the specified directory. It should look similar to:
$ ps -ef | grep saslauthd
root 24996 1 0 10:03 ? 00:00:00 /usr/sbin/saslauthd -a pam -c -m /var/run/saslauthd -n 5
root 24997 24996 0 10:03 ? 00:00:00 /usr/sbin/saslauthd -a pam -c -m /var/run/saslauthd -n 5
root 24999 24996 0 10:03 ? 00:00:00 /usr/sbin/saslauthd -a pam -c -m /var/run/saslauthd -n 5
root 25000 24996 0 10:03 ? 00:00:00 /usr/sbin/saslauthd -a pam -c -m /var/run/saslauthd -n 5
root 25001 24996 0 10:03 ? 00:00:00 /usr/sbin/saslauthd -a pam -c -m /var/run/saslauthd -n 5
root 26174 1 0 10:50 ? 00:00:00 /usr/sbin/saslauthd -a pam -c -m /var/spool/postfix/var/run/saslauthd -n 5
root 26175 26174 0 10:50 ? 00:00:00 /usr/sbin/saslauthd -a pam -c -m /var/spool/postfix/var/run/saslauthd -n 5
root 26176 26174 0 10:50 ? 00:00:00 /usr/sbin/saslauthd -a pam -c -m /var/spool/postfix/var/run/saslauthd -n 5
root 26177 26174 0 10:50 ? 00:00:00 /usr/sbin/saslauthd -a pam -c -m /var/spool/postfix/var/run/saslauthd -n 5
root 26178 26174 0 10:50 ? 00:00:00 /usr/sbin/saslauthd -a pam -c -m /var/spool/postfix/var/run/saslauthd -n 5
$ ls -lh /var/spool/postfix/var/run/saslauthd
total 972K
-rw------- 1 root root 0 2013-07-31 10:50 cache.flock
-rw------- 1 root root 963K 2013-07-31 10:52 cache.mmap
srwxrwxrwx 1 root root 0 2013-07-31 10:50 mux
-rw------- 1 root root 0 2013-07-31 10:50 mux.accept
-rw------- 1 root root 6 2013-07-31 10:50 saslauthd.pid
Setting up Postfix
Directory /etc/postfix/sasl
should exist by now. Create a file /etc/postfix/sasl/smtpd.conf
with content pwcheck_method: saslauthd
:
echo "pwcheck_method: saslauthd" > /etc/postfix/sasl/smtpd.conf
Append the following to /etc/postfix/main.cf
and reload postfix:
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain = $myhostname
broken_sasl_auth_clients = yes
smtpd_recipient_restrictions =
reject_non_fqdn_recipient,
reject_unknown_recipient_domain,
permit_sasl_authenticated,
permit_mynetworks,
reject_unauth_destination
Testing the setup
It should now be possible to auth yourself against the SMTP server. First, generate the auth string, which we will require in a moment. Replace username and password with an existing one on your server:
perl -MMIME::Base64 -e 'print encode_base64("username\0username\0password");'
# example:
perl -MMIME::Base64 -e 'print encode_base64("shibe\0shibe\0muchsecurewow");'
Next, try telnet localhost 25
. It should look something like this:
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 vsrv79318.customer.vlinux.de ESMTP Postfix (Debian/GNU)
EHLO mail.yrden.de ## type this
250-vsrv79318.customer.vlinux.de
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-STARTTLS
250-AUTH DIGEST-MD5 NTLM CRAM-MD5 LOGIN PLAIN
250-AUTH=DIGEST-MD5 NTLM CRAM-MD5 LOGIN PLAIN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN
AUTH PLAIN <<base64 string created above>> ## type this
235 2.7.0 Authentication successful
QUIT ## type this
221 2.0.0 Bye
If you get a warning in /var/log/mail
similar to
warning: SASL authentication failure:
cannot connect to saslauthd server: Permission denied
you have not added user postfix
to the sasl
group.
Don’t continue until you receive an “Authentication successful” message. Restart
the service and have a look at /var/log/mail.log
for hints.
Make misconfiguration harder
By default postfix will also accept connections and auths without any encryption.
You can set postfix to ignore such requests, so you will notice if you misconfigured
your client. Add the following lines to /etc/postfix/main.cf
and set them
approrpiately:
smtpd_tls_auth_only = yes
smtpd_tls_security_level = may
The latter is stricter than the former. Setting smtpd_tls_security_level = encrypt
implies smtpd_tls_auth_only = yes
. According to the Postfix documentation the latter
also replaces the obsolete parameters
smtpd_use_tls = yes
smtpd_enforce_tls = yes
(deprecated in Postfix 2.3 and later). Important: in previous versions I had
set this to encrypt
instead of may
. This requires the sending server to
support TLS as well. However, certain servers don’t and thus the mail delivery
fails. One famous (and sad) example is GitHub.
5. Installing Dovecot
apt-get install dovecot-imapd
You may want to enable some workarounds for broken clients, namely Thunderbird.
Do so by adjusting the imap_client_workarounds
in /etc/dovecot/conf.d/20-imap.conf
to
imap_client_workarounds = tb-extra-mailbox-sep tb-lsub-flags
Next, restart dovecot (service dovecot restart
) and see if IMAP works:
mutt -f imap://username@hostname/
Obviously, mutt will complain about certificate errors, but you should be able to see the test mails you sent yourself earlier.
6. SSL in Dovecot
Edit /etc/dovecot/conf.d/10-ssl.conf
and update the following lines so
that they point to the certificates created earlier. E.g.
ssl_cert = </etc/ssl/certs/mail.yrden.de.pem
ssl_key = </etc/ssl/private/mail.yrden.de.key
Most likely you also want to enforce TLS, so that you can’t misconfigure your client to use plain auth. So, set
ssl = required
in the same file and uncomment
disable_plaintext_auth = yes
in /etc/dovecot/conf.d/10-auth.conf
. Note that this will still allow
PLAIN
to be used, but only over an SSL connection or from local
adresses.
7. Changing to Maildirs
I wanted to be able to automatically sort into folders. Maildirs seem to be better equipped for that. So let’s change the format to Maildirs.
The Debian Wiki says to run this command:
postconf -e "home_mailbox = Maildir/"
However you can also simply edit /etc/postfix/main.cf
. The mails will now be
stored in ~user/Maildir
(e.g. /root/Maildir
for root). Again, test that
you can receive mail properly, either by using mutt -f ~/Maildir
or by
doing some cat
-magic.
If this doesn’t work out of the box, ensure the Maildir directory is created and has the correct permissions. If it doesn’t, change into the user’s home directory and run:
cd ~user
mkdir -p Maildir
mkdir -p Maildir/cur
mkdir -p Maildir/new
mkdir -p Maildir/tmp
chown -R user:user Maildir
Dovecot tries to auto-detect where your mails are stored by default, but in Debian it doesn’t auto-detect Maildirs. Furthermore, it is recommended to set the location manually anyway, so it works for new users as well. See the Dovecot wiki for more information.
To set the location edit /etc/dovecot/conf.d/10-mail.conf
, find the mail_location =
line and set it to
mail_location = maildir:~/Maildir
8. Testing everything in Thunderbird
Now, restart Dovecot and Postfix once more and test the setup in Thunderbird. If you didn’t use any strange names, Thunderbird will be able to auto detect everything.
If you can’t access your inbox the /var/log/mail.log
shows something like
Error: Opening INBOX failed: Mailbox doesn't exist: INBOX
ensure that you
have created the Maildir folders and set the mail_location
to the correct
path in Dovecot’s configuration (see step 7).
Your basic setup is now complete. You should be able to send and receive mails. Everything else that follows are add-ons to your current setup.
9. Aliases
Plussing
Postfix supports “plussing” by default, i.e. mails to user+anything@ will be
put into user’s inbox. This is a fairly effective way to sort mail and detect
who sends you spam. However, more often than I’d like adress validation schemes
do not allow +
. Therefore, I changed the character to -
by editing recipient_delimiter =
in /etc/postfix/main.cfg
.
Real Aliases
You might want to have adresses for your first, last and nickname. All should end up in the
same inbox. To do so, edit /etc/aliases
and add a new line for each aliases you want to
have. The format is
newalias: existing_inbox_user_name
After you saved the file, run newaliases
so that Postfix picks up the changes.
10. Mail filtering with Sieve
Setting up Sieve
apt-get install dovecot-sieve
Enable the plugin by uncommenting and changing this line in
# /etc/dovecot/conf.d/15-lda.conf
mail_plugins = $mail_plugins sieve
Instruct Postfix to hand all mail to Dovecot, so sieve can do its magic. Add this to:
# /etc/postfix/main.cf
mailbox_command = /usr/lib/dovecot/deliver
With these changes, mails to root
now need to be directed to a real user. See section 9 for details.
If you changed the recipient delimiter in Postfix, also update:
# /etc/dovecot/conf.d/90-sieve.conf
recipient_delimiter = -
Restart dovecot and postfix: service dovecot restart; service postfix restart
General hints about filters
Tutorials on the file format:
Test your filters by storing a mail with all headers in a file. Next, run this command:
sieve-test ~/.dovecot.sieve ~/stored-mail.txt
Example filter to sort away own mail
I’ve setup my clients to send a blind-copy to one of my addresses in order to keep track of what I’ve sent. This usually works better than having the client copy the message to the Sent-folder because it only uses my upstream once.
If you haven’t changed the default configuration, the filters will be put into ~user/.dovecot.sieve
and the log will be available at ~user/.dovecot.sieve.log
. This section assumes you are
logged in as the user. If you work as root, ensure you have the correct permissions and
owners on all touched files.
First, ensure the target folder exists:
mkdir -p ~/Maildir/.Sent
Next, edit ~/.dovecot.sieve
to look like:
require ["fileinto", "imap4flags"];
if address :is "Delivered-To" "MAIL-ADDRESS-YOU-BCC-TO" {
setflag "\\Seen";
fileinto "Sent";
stop;
}
The require line loads plugins needed for this recipe. These are shipped by default, so you don’t need to install additional packages.
“address” is a special command that extracts the mail address from a header
(e.g. consider headers like “Stefan Breunig some@mail.address”). So the
if matches all mails whose Delivered-To
header has the mail address MAIL-ADDRESS-YOU-BCC-TO
.
The actions applied to the matched mails are pretty self explanatory:
setflag
sets imap flags, in this case it marks the mail as read.fileinto
moves the mail into the given folder. Note that you need to omit the leading dot, even if the path is~/Maildir/.Sent
stop
tells Sieve to not apply any further filters.
11. Fail2ban
fail2ban is a daemon that monitors log files for failed logins. If too many occur, that IP address
is banned for a certain amount of time. By default an exponential back-off scheme is used. You can
read details in /etc/fail2ban/jail.conf
, although you should put your configuration into /etc/fail2ban/jail.local
.
In my case, /etc/fail2ban/jail.local
looks like this:
[ssh]
enabled = true
[postfix]
enabled = true
[sasl]
enabled = true
[dovecot]
enabled = true
12. SpamAssassin
Setting up SpamAssassin
apt-get install spamassassin spamc
Set user, group and permissions:
groupadd spamd
useradd -g spamd -s /bin/false -d /var/log/spamassassin spamd
mkdir /var/log/spamassassin
chown spamd:spamd /var/log/spamassassin
Next, edit /etc/default/spamassassin
so that:
ENABLED=1
# …
SAHOME="/var/log/spamassassin/"
OPTIONS="--create-prefs --max-children 2 --username spamd -H ${SAHOME} -s ${SAHOME}spamd.log"
Start SpamAssassin to see if there are any errors in the configuration files: service spamassassin start
.
Setting up Postfix
In order to pipe mail through SpamAssassin, simply edit /etc/postfix/main.cf
so that:
mailbox_command = /usr/bin/spamc -e /usr/lib/dovecot/deliver
To test SpamAssassin works, send yourself a test mail. It should have several new headers:
X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on
vsrv79318.customer.vlinux.de
X-Spam-Level:
X-Spam-Status: No, score=-0.7 required=5.0 tests=RCVD_IN_DNSWL_LOW,SPF_PASS,
TVD_SPACE_RATIO autolearn=unavailable version=3.3.2
Notice the autolearn=unavailable
. The most likely reason is that SpamAssassin can’t
write to the user’s home directory to store files. To fix this, run:
mkdir -P ~USER/.spamassassin
chown -R USER:spamd .spamassassin
chmod -R g+w .spamassassin
autolearn should work now and the warning messages in /var/log/spamassassin/spamd.log
should be gone.
Training the filter
If you have some spam available move it to a separate folder (e.g. Junk in ~/Maildir/.Junk
).
Next, run this for spam:
sa-learn --spam -u spamd --dir /home/USERNAME/Maildir/.Junk/* -D
and this for ham:
sa-learn --ham -u spamd --dir /home/USERNAME/Maildir/.INBOX/* -D
Move spam to junk folder
If you have setup sieve above, you can automatically move spam to the junk folder by using this recipe:
if exists "X-Spam-Flag" {
if header :contains "X-Spam-Flag" "NO" {
} else {
setflag "\\Seen";
fileinto "Junk";
stop;
}
}
13. More Anti-Spam measures
Some mail can be rejected right away, for instance:
- sender does not follow RFC standard
- sender domain does not exist
Update your /etc/postfix/main.cf
to read:
smtpd_helo_required = yes
smtpd_helo_restrictions =
permit_mynetworks,
reject_invalid_helo_hostname,
reject_non_fqdn_helo_hostname
smtpd_sender_restrictions =
permit_mynetworks,
reject_non_fqdn_sender,
reject_unknown_sender_domain
smtpd_recipient_restrictions =
reject_non_fqdn_recipient,
reject_unknown_recipient_domain,
permit_sasl_authenticated,
permit_mynetworks,
reject_unauth_destination
Finally, don’t forget to postfix reload
. If you just copied and pasted these lines in, postfix may warn you that
you overwrite previous configuration entries. They should match each other, so only keep one!
14. IPv6
To enable IPv6 in Postfix, set inet_protocols = all
in /etc/postfix/main.cf
. You can also set
a specific address via smtp_bind_address6 = 1234:DEAD:BEEF:8::1234
. Update
/etc/network/interfaces
accordingly and also set the AAAA record for the smtp.FQDN
in your DNS entries.
Google decided to block all mails delivered via IPv6 if there is no rDNS entry for your v6 IP to your MX record (mail.yrden.de in my case). Depending on your hosting solution, you may need to setup a bind server yourself. Most likely you can add rDNS entries yourself in the web interface provided by your hoster though. If in doubt, ask them first.
To enable IPv6 in Dovecot, uncomment #listen = *, ::
in /etc/dovecot/dovecot.conf
. Like above,
update your interfaces file and set the AAAA record for imap.FQDN
.
Restart both services.
15. Enabling Forward Secrecy
To enable Forward Secrecy you need to add a Diffie-Hellman key to your private key file created in step 2. To do so, run:
openssl dhparam -rand - 2048 >> /etc/ssl/private/mail.yrden.de.key
Postfix
Update your /etc/postfix/main.cf
file to read:
smtpd_tls_ciphers = high
smtpd_tls_exclude_ciphers = aNULL, MD5, DES, 3DES, DES-CBC3-SHA, RC4-SHA, AES256-SHA, AES128-SHA
smtp_tls_protocols = !SSLv2, !SSLv3, TLSv1
smtpd_tls_mandatory_protocols = TLSv1
smtp_tls_note_starttls_offer = yes
smtpd_tls_received_header = yes
smtpd_tls_eecdh_grade = strong
tls_preempt_cipherlist = yes
smtpd_tls_loglevel = 1
smtp_tls_loglevel = 1
The logging is obviously optional, but it helps to see if your client is properly configured to use modern encryption.
Dovecot
Dovecot automatically uses the modern ciphers with clients that support it (e.g. Thunderbird). Unfortunately many
mobile clients (e.g. Kaiten Mail) behave badly and choose old ciphers even though they support new ones. To alleviate
this, you can configure Dovecot to not use the old ciphers by updating /etc/dovecot/conf.d/10-ssl.conf
:
ssl_cipher_list = ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AES:RSA+3DES:!ADH:!AECDH:!MD5:!DSS
If you want to log the ciphers used for Dovecot as well, modify /etc/dovecot/conf.d/10-logging.conf
so that the
login_log_format_elements
option reads (note the added %k
in the end):
login_log_format_elements = "user=<%u> method=%m rip=%r lip=%l mpid=%e %c %k"
Testing
After restarting both Postfix and Dovecot, run these commands on your server:
openssl s_client -starttls smtp -connect localhost:25
openssl s_client -starttls imap -connect localhost:143
Both should output the following “or better”:
SSL-Session:
Protocol : TLSv1.2
Cipher : ECDHE-RSA-AES256-GCM-SHA384
If you enabled logging, the same ciphers should appear in your /var/log/mail.log
file as well.
Conclusion
Setting up mail is just like building legos: You never find the pieces you need and it takes much longer than anticipated. Good luck!