It is important that you understand what “relaying” is and why “open relays” are a big problem on the internet. Usually Postfix accepts an email only if one of these criteria match:
- the recipient’s email address belongs to a user on your mail server
- the sender is sending the email from within the local network (defined by Postfix’s “mynetworks” setting)
- the sender is authenticated (SMTP supports authenticating with a username and password)
For security reasons the following must not be allowed:
Otherwise a spammer could abuse your system to send millions of spam emails.This would waste your bandwidth, annoy many people and get your server blacklisted quickly. Such a relay that is not checking which emails to accept is called an “open relay“. Fortunately Postfix makes it hard to become an open relay. Setting the smtpd_recipient_restrictions right (as described below) is very important.
Generally you can define the networks that are allowed to relay through your mail server by setting the mynetworks parameter in your main.cf. Usually you set it to your local network so local users do not need to authenticate:
$> postconf -e mynetworks=192.168.50.0/24
But one case of relaying from outside of your network is important in real-life. Imagine you have a user who is currently not connected to your local network but wants to send out an email to the internet through your mail server. According to the above rules the user would not be able to do that because neither are they on your network nor are they sending an email to one of your domains. You need to find a way to make your remote user be trusted by your mail server. The users will need to send their username and password so you know relaying should be permitted. This is what authenticated SMTP is about. And all decent email clients have that feature built in.
Authenticated SMTP with Postfix has always been a pain. It was done through the SASL (Simple Authentication and Security Layer) library that was part of the Cyrus mail server. It was nearly impossible to debug and threw error messages that were gibberish and misleading. Fortunately starting with Postfix 2.3 we can make Postfix ask the Dovecot server to verify the username and password. And since you already configured Dovecot this is really easy now. Postfix just needs some extra configuration:
$> postconf -e smtpd_sasl_type=dovecot $> postconf -e smtpd_sasl_path=private/auth $> postconf -e smtpd_sasl_auth_enable=yes $> postconf -e smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject_unauth_destination
smtpd_sasl_auth_enable enables SMTP authentication altogether. And the smtpd_recipient_restrictions define rules that are checked after the remote user sends the RCPT TO: line during the SMTP dialog. In this case relaying is allowed if:
- permit_mynetworks: the user is in the local network (mynetworks) or
- permit_sasl_authenticated: if the user is authenticated or
- reject_unauth_destination: the mail is destined to a user of a domain that is a local or virtual domain on this system (mydestination, virtual_alias_domains or virtual_mailbox_domains).
There are further restrictions (smtpd_client_restrictions, smtpd_helo_restrictions, smtpd_sender_restrictions) that get checked during the different states of the SMTP dialog (IP connection, HELO/EHLO command, MAIL FROM command) but for now you should put all restrictions into the smtpd_recipient_restrictions.
Try to authenticate during an SMTP session:
$> telnet localhost smtp
The server will let you in:
Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. 220 mailtest ESMTP Postfix (Debian/GNU)
Postfix will present a list of features that are available during the SMTP dialog:
250-mailtest 250-PIPELINING 250-SIZE 10240000 250-VRFY 250-ETRN 250-ENHANCEDSTATUSCODES 250-8BITMIME 250 DSN
Send the authentication string with a Base64-encoded password:
auth plain am9obkBleGFtcGxlLmNvbQBqb2huQGV4YW1wbGUuY29tAHN1bW1lcnN1bg==
The server should accept that authentication:
235 2.0.0 Authentication successful
Disconnect from Postfix:
221 2.0.0 Bye
Authentication works. Well done.
If you have set John’s password to something other than ‘summersun’ you need Base64-encode it yourself. Use:
$> perl -MMIME::Base64 -e \
Now you can test sending email with authentication enabled. To make even your local network untrusted temporarily you can set:
$> postconf -e mynetworks= $> postfix reload
Restart Postfix (/etc/init.d/postfix restart). Fire up your mail program and watch your mail.log (tail -f /var/log/mail.log) while you send an email to a domain on the internet. I recommend you send a test email to email@example.com which is an email address that will just discard your email. If everything worked well your logfile will show:
postfix/smtpd: 1234567890: client=..., sasl_method=PLAIN, firstname.lastname@example.org postfix/cleanup: 2EAE8379CB: message-id=<...> postfix/qmgr: 1234567890: from=<email@example.com>, size=830, nrcpt=1 (queue active) postfix/smtpd: disconnect from ... postfix/smtp: 1234567890: to=<firstname.lastname@example.org>, relay=torf.workaround.org[22.214.171.124]:25, delay=6, delays=0.09/0.08/5.6/0.23, dsn=2.0.0, status=sent (250 OK id=1HsPC3-0008UJ-O5) postfix/qmgr: 2EAE8379CB: removed
Otherwise in case of an error your logfile might look like:
postfix/smtpd: connect from ...[10.20.30.40] postfix/smtpd: warning: ...[10.20.30.40]: SASL PLAIN authentication failed: postfix/smtpd: lost connection after AUTH from ...[10.20.30.40] postfix/smtpd: disconnect from ...[10.20.30.40]
Don’t forget to set mynetworks back to your network definition:
$> postconf -e mynetworks=192.168.50.0/24 $> postfix reload
Your email program may have warned you that the mail server uses an untrusted SSL certificate. When you installed Postfix Debian created a self-signed SSL certificate for you automatically. The default certificate is sufficient for testing but just like you did for Dovecot you can create a custom SSL certificate. The default certificate is stored at /etc/ssl/certs/ssl-cert-snakeoil.pem and the default private key is stored at /etc/ssl/private/ssl-cert-snakeoil.key. This will create a new certificate/key pair:
$> openssl req -new -x509 -days 3650 -nodes -out /etc/ssl/certs/postfix.pem \ -keyout /etc/ssl/private/postfix.pem
Same procedure as above when you created a certificate for Dovecot. Just remember to set the “Common Name” to the fully-qualified hostname. You could as well use the same certificate you created for Dovecot if the server name is the same. In that case just use the files /etc/ssl/certs/dovecot.pem and /etc/ssl/private/dovecot.pem below.
Do not forget to set the permissions on the private key so that no unauthorized people can read it:
$> chmod o= /etc/ssl/private/postfix.pem
You will have to tell Postfix where to find your certificate and private key:
$> postconf -e smtpd_tls_cert_file=/etc/ssl/certs/postfix.pem $> postconf -e smtpd_tls_key_file=/etc/ssl/private/postfix.pem
When you relay through Postfix again you should not get that certificate warning any longer.
By default Postfix will allow that the login data for SMTP authentication is sent in plain text. You better only allow encrypted transmission of the credentials by setting these parameters:
$> postconf -e smtpd_use_tls=yes $> postconf -e smtpd_tls_auth_only=no
What these parameters do is offer (but not require) encryption for SASL authentication to be used so that if you’re using the PLAIN or LOGIN SASL methods your passwords aren’t transmitted in the clear. When the client initially connects to the server, the AUTH command isn’t offered by the server. If the client issues the STARTTLS command and successfully negotiates the TLS connection, the client sends the EHLO command a second time and this time the server offers the AUTH command. This won’t require any kind of encryption from clients who don’t need to authenticate (i.e. servers that connect to send mail to the domains our server is a final destination for, as opposed to our end users attempting to relay mail through it to external domains).
If you truly do want to forbid unencrypted SMTP connections (I do this on ports 587 and 465), you’d want to use either “smtpd_tls_security_level = encrypt” (for STARTTLS, generally on port 587) or “smtpd_tls_wrappermode = yes” (for SSL encryption from the initial connection on, generally on port 465). You could also set “smtpd_tls_security_level = may” so that TLS encryption is offered but it’s not mandatory (so-called opportunistic TLS).
Once you are done you should test if your mail server is safely configured to prevent illegal relaying attempts. On a root shell enter:
$> telnet relay-test.mail-abuse.org
This contacts a very convenient service on the internet which tries to relay emails through your mail server. Give it a moment while it checks your server. If at the end you see something like “System appeared to reject relay attempts” then you are fine.