Postfix Configuration

NOTE: This page is for Postfix 2.11.x which is now officially end of life, with 2.11.11 being the last release in that branch.

LibreLAMP will be updating to Postfix 3.2.x branch soon, and there are many configuration changes.

This page will be updated when that update happens.


The needs of an SMTP server can vary quite extensively, it would be very difficult to attempt to cover all of them here.

For the specific needs of your deployment, I suggest you read the official documentation at http://www.postfix.org/documentation.html.

The purpose of this page is to assist in setting up a basic Postfix server that will deliver mail to local user accounts and that allows users with accounts on the system to authenticate over TLS from a remote Message User Agent (MUA) and send e-mail.

For more advanced configuration, see the previously mentioned Postfix documentation.

For the purposes of this article, we will configure Postfix to accept mail sent to user address at the domain example.org with the postfix running on a server with the hostname mail.example.org.

In this case, the hostname of the mail server happens to be a sub-domain of the e-mail address domain, but that is frequently not the case. The two do not need to be related, as long as the MX record for the e-mail address domain points to the hostname we are running the Postfix server on, that is where other MTAs will connect to deliver messages sent to addresses at the domain.

It is important to understand that in the context of this article, domain refers to the part of an e-mail address after the @ while hostname refers to the actual host the server is running on.

To reduce the odds of mail sent from your system being flagged as spam, make sure the reverse DNS lookup of your IP address matches the hostname the mail server is running on.

Finally it is recommended that you run a DNSSEC enforcing recursive (caching) nameserver on the mail server. This nameserver should only listen on the localhost, it should not listen on public interfaces. Unbound is a good choice. Make sure the server is configured to use it for your DNS needs.


Postfix Basic Configuration

The standard port for an SMTP server is TCP port 25. This section will help you get Postfix running on that port.

However it is imperative that you also set up the TLS instructions in the section that follows. Not just for the security reasons, but for availability. Many SMTP servers running on Port 25 are spam relays, and for this reason many Internet Service Providers block access to Port 25. The solution is to use the submission Port 587 for sending e-mail from an e-mail client, and that requires TLS.

The file we need to configure for basic mail services is /etc/postfix/main.cf. It is always good to make a time-stamped backup of that file whenever making edits:

cd /etc/postfix
cp -p main.cf main.cf-20181216.bak

Now it is safe to edit the file.

Host and Domain Name

Find the section of the configuration file that reads

# INTERNET HOST AND DOMAIN NAMES

First line after the end of the comment block, add the line

myhostname = mail.example.org

That needs to be the Fully Qualified Domain Name that will be used in your DNS zone file as the MX record.

The very end of the next comment block will have a line that should read

#mydomain = domain.tld

After that line, add the domain that will appear after the @ in e-mail addresses:

mydomain = example.org

Finally, the end of the next comment block reads:

#myorigin = $mydomain

un-comment that line so that it now reads:

myorigin = $mydomain

Misc Settings

By default, Postfix is only listening on the localhost. That is how it should behave when its only job is to send messages between users on the same host. To run it as a mail server we can connect to from the outside world, we need to change the inet_interfaces setting. Comment out the line that reads:

inet_interfaces = localhost

Then add the following line:

inet_interfaces = all

By default, Postfix will only accept e-mail destined for the host defined earlier with the $myhostname definition and for the localhost. Usually (and in our example scenario) $myhostname and $mydomain differ. We need it to accept e-mail for $mydomain as well. Assuming they are actually different on your setup, comment out the line that reads:

mydestination = $myhostname, localhost.$mydomain, localhost

After the comment block it appears in, add the following line:

mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain

No one likes an open relay. Well, okay, spammers like an open relay, but no one else does.

At least initially, you should make sure that Postfix only ‘trusts’ the local machine to relay mail. Find the line that reads:

#mynetworks_style = host

Un-comment it so that it now reads:

mynetworks_style = host

That will allow scripts on the local machine to send mail when they need to without needing to authenticate, while at the same time still keeping the mail server closed to outside users who have not authenticated themselves.

Note that if you plan to use the server as a mail relay for applications running on another machine, you will probably need to manually specify a list of hosts that are allowed to relay mail. See the Postfix documentation for more information.

Test the Configuration

That should be enough to receive mail sent to a valid username@example.org address. The user has to either be an account on the system, or an alias listed in the /etc/aliases file.

Make sure the hostname you specified as $myhostname is specified in the MX record for $mydomain.

Make sure to have a very basic command line mail client installed on the server. I like to use alpine for this purpose.

A good primer on using alpine can be found at http://umanitoba.ca/computing/ist/software/unix/alpine-howto.html.

If you are running the RHEL/CentOS 7 firewall, open up TCP port 25:

firewall-cmd --zone=public --add-port=25/tcp --permanent
firewall-cmd --reload

Reload Postfix:

/usr/sbin/postfix reload

Using your regular e-mail client, attempt to send an e-mail to a valid username at that host. Then log in to that user account, you should see the message using the command line mail client.

Configure /etc/aliases

When we want to set up TLS we will need to get a TLS certificate for the host we have defined with $myhostname.

If $myhostname either is the same as $mydomain or is a sub-domain of $mydomain then we need to be able to receive an e-mail sent to an administrative account on this server to validate to the Certificate Authority that we own the domain. I personally like to use the admin account for this (e.g. admin@example.org).

We could just create a user account named admin but I recommend against it, that user name is the target of brute force attacks. Instead I recommend setting up an alias so that mail sent to that user name ends up in the mail box of another user.

At the bottom of the /etc/aliases file as distributed by RHEL/CentOS 7 is the following:

# Person who should get root's mail
#root:          marc

After that comment block, add the following:

# Custom Aliases
admin:          root
root:           alice

Of course replace alice with the username of your non-root account.

Then issue the command /bin/newaliases so that the actual aliases database is updated.

Send an e-mail from an e-mail account on a different domain to the admin account, and make sure the account you pointed the root alias to receives it.

TLS Support

TLS support is critical. Without it, any message sent from your server to another server and any message received by your server from another server can be read in plain text or modified by anyone sniffing the network. It is also critical for user authentication when users want to use the server to send e-mail from a client on another system, so that the login credentials are not sent in plain text. That would be very bad.

To use TLS you will need an x.509 certificate. For SMTP servers that human users will connect to, I recommend against using a self-signed certificate. E-mail clients will often rightfully give the end user a very scary message with self-signed certificates. For SMTP servers that only non-human users connect to, a self-signed TLS certificate is fine but I do recommend you use DANE to secure the certificate.

Creating a private key and obtaining a signed certificate is the identical process described in the Apache Configutation Instructions. Just make sure when generating the CSR that for the Common Name field you use the same FQDN you defined in Postfix as $myhostname (e.g. mail.example.org).

Basic TLS Directives

Add the following at the end of your /etc/postfix/main.cf file:

# TLS Options
smtpd_tls_security_level = may
smtpd_tls_auth_only = yes
smtpd_tls_key_file  =     /etc/pki/tls/private/mail.example.org-20181216.key
smtpd_tls_cert_file =       /etc/pki/tls/certs/mail.example.org-20181216.crt
smtpd_tls_CAfile    = /etc/pki/tls/certs/mail.example.org-chain-20181216.crt
# protocols / ciphers
smtpd_tls_exclude_ciphers = IDEA
smtpd_tls_mandatory_ciphers = HIGH
smtpd_tls_dh1024_param_file = /etc/pki/tls/dh2048.pem
#
smtp_tls_security_level = may

What these particular options do:

smtpd_tls_security_level
Use of this options enables TLS. RFC 2487 requires that publicly referenced SMTP servers must accept connections from other servers that are not TLS enabled. Setting this to may as opposed to encrypt ensures we are compliant. If the mail server is a relay and not a final destination, then it is okay to set it to encrypt.
smtpd_tls_auth_only
By default, Postfix does not require TLS when users use remote authentication to connect to the server and send e-mail. That is dangerous, authentication should always be encrypted. Setting this to yes requires encryption with authentication.
smtpd_tls_key_file
The location on the file system of our private TLS key.
smtpd_tls_cert_file
The location on the file system of our signed x.509 certificate.
smtpd_tls_CAfile
The location on the file system of the Certificate Authority intermediary certificate(s).
smtpd_tls_exclude_ciphers
By default, Postfix already requires ciphers that are considered to be at least MEDIUM quality. This directive allows us to exclude remaining ciphers that have features we do not want.
smtpd_tls_mandatory_ciphers
That directive only applies when encryption is mandatory, which is how we will be setting up the Submission Port 587. That is where users will connect from e-mail clients with authentication, so we set it to require ciphers that meat the rating of HIGH to prevent weaker ciphers from being used.
smtpd_tls_dh1024_param_file
This parameter is needed for DHE ciphers. The name is for historic reasons. The default LibreLAMP configuration uses a 2048-bit DH group, which is the current best practices recommendation. The 2048 parameters are re-generated once a day in LibreLAMP.
smtp_tls_security_level
This makes sure Postfix will relay a message without TLS if the receiving SMTP server does not support it.

There are many other TLS options you may wish to investigate. See the man 5 postconf documentation for more information.


Now we need to edit the /etc/postfix/master.cf file to turn on submission.

Find the commented out line near the top that reads:

#submission inet n       -       n       -       -       smtpd

Un-comment it and add a line below it so it looks like the following:

submission inet n       -       n       -       -       smtpd
  -o smtpd_tls_security_level=encrypt

That turns on submission (Port 587) and the -o smtpd_tls_security_level over-rides the value in main.cf when submission is being used.

For connections over port 587, encryption is now required which kicks in the smtpd_tls_mandatory_ciphers setting and only the HIGH quality ciphers are allowed for the connection. This protects the users from a cipher down-grade attack to weaker ciphers that could result in theft of authentication data.

Simple Authentication and Security Layer

SASL is the mechanism by which remote Message User Agents (e-mail clients) authenticate with Postfix to send mail through through the system.

Traditionally Port 25 was used for both MUAs connecting to send mail and for other MTAs to connect to for relaying mail.

It now is preferred to restrict MUAs to Port 587 only, leaving Port 25 for MTAs. The issue has to do with the quality of encryption ciphers that must be supported.

To be RFC compliant, an SMTP server has to accept connections from other Message Transfer Agents that do not use TLS. This does not put the server at risk, but it does reduce the privacy of the communication between the sender of the communication and the receiver of the communication. It is not ideal for such a connection to take place.

If the two Message Transfer Agents do not have a cipher in common, encrypted communication can not happen and plain text will be used instead. From a privacy point of view, communication between the two MTAs using a cipher that does not support Forward Secrecy or using a cipher with known weaknesses such as RC4 is still better than the communication taking place using plain text. So it is best to be somewhat liberal in what ciphers are available when communication takes place on Port 25.

For Mail Users Agents that need to authenticate themselves, however, there is a security risk to a server with liberal cipher availability. If the authentication information a user transmits is compromised, bad things can result.

The solution to the dilemma is to allow a liberal cipher selection on Port 25 but a much stricter cipher selection on Port 587, and configure Postfix to only use SASL on Port 587.

There are two SASL libraries available to postfix, Cyrus and Dovecot. Which one is best for you depends upon many factors, but for the sake of simplicity we are going to use the Cyrus libraries for SASL.

Cyrus SASL

First, we need to install, enable, and start the saslauthd daemon:

yum -y install cyrus-sasl cyrus-sasl-plain

systemctl enable saslauthd.service

systemctl start saslauthd

There are options other than using the saslauthd daemon, many ways to skin a cat.

Edit the /etc/postfix/main.cf file and at the end of the file, add the following section:

# SASL Options
smtpd_sasl_type = cyrus
smtpd_sasl_path = smtpd
smtpd_sasl_auth_enable = no
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain = $myhostname

What these particular options do:

smtpd_sasl_type
This defines whether we are uses the cyrus or dovecot libraries.
smtpd_sasl_path
This is specific to the engine being used. It defines the name of the configuration file for the SASL engine. In the case of cyrus a .conf is appended to the end automatically. The actual file is located at /etc/sasl2/smtpd.conf if you want to look at it. The defaults are usually fine.
smtpd_sasl_auth_enable
This defines whether or not SASL authentication is turned on. We want it turned off in the main.cf file so that it is not available on Port 25. We will specifically enable it on Port 587 later.
smtpd_sasl_security_options
This restricts what authentication mechanisms are offered to the client. We do not want to allow anonymous login.
smtpd_sasl_local_domain
The local SASL authentication realm. Usually we want this to be $myhostname.

Now we need to turn on SASL for Port 587. Edit the file /etc/postfix/master.cf and find the part we edited earlier:

submission inet n       -       n       -       -       smtpd
  -o smtpd_tls_security_level=encrypt

Add a line directly after it so it looks like this:

submission inet n       -       n       -       -       smtpd
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes

That tells Postfix that on the submission port, override the default set in the main.cf so that SASL authentication will be available on Port 587.

After making the edits, reload the Postfix configuration:

postfix reload

If you are running the RHEL/CentOS 7 firewall you need to poke a hole in it for TCP port 587:

/bin/firewall-cmd --zone=public --add-port=587/tcp --permanent
/bin/firewall-cmd --reload

Users with login accounts on your server can now safely authenticate to send mail through the server.

Basic Spam Filtering

The following additions to the main.cf are quite useful in reducing spam:

# Basic Spam Reduction
strict_rfc821_envelopes = yes
disable_vrfy_command = yes
unknown_address_reject_code  = 554
unknown_hostname_reject_code = 554
unknown_client_reject_code   = 554
smtpd_helo_required = yes

#  HELO Restrictions
smtpd_helo_restrictions =
         permit_mynetworks,
         reject_non_fqdn_helo_hostname,
         reject_invalid_helo_hostname,
         permit

#  Sender Restrictions
smtpd_sender_restrictions =
         permit_mynetworks,
         reject_non_fqdn_sender,
         reject_unknown_sender_domain,
         permit

#  Relay Restrictions
smtpd_relay_restrictions =
         permit_mynetworks,
         permit_sasl_authenticated,
         reject_unauth_destination

#  Recipient Restrictions
smtpd_recipient_restrictions =
         permit_mynetworks,
         permit_sasl_authenticated,
         check_client_access hash:${config_directory}/rbl_override,
         permit_dnswl_client list.dnswl.org=127.0.[0..255].[1..3],
         reject_rbl_client zen.spamhaus.org,
         permit

What these additional directives do:

strict_rfc821_envelopes
When set to yes, Postfix rejects e-mail that is sent with an envelope that is not RFC 821 compliant. Such messages are almost always spam. False positives are possible but only if the sender is using a very poorly written e-mail client.
disable_vrfy_command
The SMTP VRFY command allows other systems to verify whether or not a user exists on the system. Spammers like to use it to verify e-mail addresses exist before adding the address to their spam list. Setting this to yes prevents them from easily verifying a particular user account exists.
unknown_*_reject_code
Setting these to 554 just tells the sending MTA that the transaction failed without being specific as to why it failed, making it more difficult for them to confirm if an address is valid or not.
smtpd_helo_required
Well written applications will always send a HELO or EHLO before sending its message through the server. Spam bots frequently do not. Setting this to yes rejects message attempts from clients that do not send it.
smtpd_helo_restrictions
It is a really good idea to read the Postfix documentation to fine tune it to your needs. The options shown should generally be considered safe. They reject messages from clients that do not identify themselves with a proper Fully Qualified Domain Name. I would caution against using reject_unknown_sender_domain as sometimes legitimate servers have temporary DNS issues that would cause their messages to be rejected.
smtpd_sender_restrictions
This defines a set of restrictions based upon what the client uses for the MAIL FROM command. This is another area that legitimate SMTP clients get right but that spammers frequently get wrong. In this case, we can use reject_unknown_sender_domain because when triggered the sending client will be sent a try again later message, and it would be improper for the server to accept messages if that domain does not resolve because that is where bounce messages need to be sent.
smtpd_relay_restrictions
This directive restricts who can use the SMTP server to relay messages. We need to specifically allow hosts specified in $mynetworks and clients that authenticated through SASL but reject any other messages sent to hosts that our server is not the destination for.
smtpd_recipient_restrictions
Please refer to the subsection below.

smtpd_recipient_restrictions

This is the heart and soul of the internal Postfix spam filtering capabilities.

The name of this directive is a little confusing. The options do not filter based upon who the recipient is, but rather in the context of the RCPT TO command. The Postfix documentation for this directive can be found in the man 5 postconf documentation.

First we declare permit_mynetworks and permit_sasl_authenticated. Any message sent from a host defined in $mynetworks or that has authenticated through SASL will not be rejected.

Messages that meet those criteria are allowed and the processing of this directive halts. Messages that do not meet that criteria but for which the RCPT TO command indicates local delivery continue to be processed, and are messages that are then checked against whitelists and blacklists to identify spam.

The spam filtering that takes place here is not content based spam filtering, for that you need an external application like Apache SpamAssassin. The spam filtering that takes place here is based upon whether or not the IP address of the MTA sending the message has been identified as a spammer. Rejecting these messages within Postfix reduces the number of messages that have to go through content-based spam filtering by eliminating easily identified spam, reducing the processing power used for content-based spam filtering.

Local Whitelist Table

There are some IP addresses that I never want to be rejected. These include my customers, my hosting provider, my domain name registrar, and previous false positives I have identified from /var/log/maillog.

You can also blacklist an IP address in the local table, but when I blacklist an IP address I prefer to do it with the RHEL/CentOS 7 firewall so that the offending MTA can not even connect.

The local table is defined with the option check_client_access hash:${config_directory}/nameOfTable where nameOfTable refers to a file name in the /etc/postfix directory. I like to name the file rbl_override.

The file takes the following format:

#comments start with a pound hash
# mail.somewhere.net
192.168.3.12 OK
# mx.elsewhere.net
192.168.17.4 OK

After creating the file and every time you make a change to it, use the postmap command to turn it into a lookup table Postfix can use:

/usr/sbin/postmap /etc/postfix/rbl_override

Now when a message arrives from an MTA at one of the IP address listed as OK in that file, Postfix will not reject it as spam even if it ends up on one of the blacklists we use.

DNS Whitelist

A DNS whitelist is a whitelist of IP addresses that are trusted to not be spammers, accessible through queries on the DNS system.

Using a DNS whitelist servers two purposes:

  1. It reduces false positives that occur when a legitimate mail server is erroneously added to a DNS blacklist. Blacklists have to respond fast to spammers as they frequently change IP addresses, a lot of them involve automated additions, and automated additions means mistakes.
  2. It reduces the number of DNS queries your server has to make. It is common to use five or six or more different DNS blacklists. When a message is legitimate, that is five or six or more different DNS queries. However if it is a whitelisted IP address, those queries to the blacklists do not take place. That is easier on your server and on the blacklist DNS servers.

The DNS whitelist that I like to use is the one provided by dnswl.org. Commercial or high volume use requires a license. Low volume non-commercial use is free. See the website for details.

To use the whitelist within smtpd_recipient_restrictions use the permit_dnswl_client option:

permit_dnswl_client list.dnswl.org=127.0.[0..255].[1..3]

For alternative methods of implementing the whitelist, see https://www.dnswl.org/?page_id=15.

DNS Blacklist

Messages that have gotten past the local whitelist table and the DNS whitelist can now be checked against DNS blacklist(s) and rejected if they match.

At this point in the fight against spam, rejecting the message is better than putting it in a spam folder. The problem is the sheer volume of spam that currently plagues the Internet results in far too much spam in the user’s spam folder if spam from known spamming MTAs is not rejected. Estimates are that anywhere from 70% to 90% of e-mail messages sent are spam. That results in a very low signal to noise ratio when the user checks the spam folder for mis-identified spam. The spam folder should be for messages identified as potentially being spam based upon content, with messages sent from identified spam MTAs rejected so they do not flood the spam folder.

You probably will want to use more than one blacklist. So-called ‘snow-show spammers’ frequently change IP addresses, it often takes some time before they are identified by all the lists. The configuration example shown only uses one for the purpose of brevity. The one that it does show is the Spamhaus Zen blacklist. It is free for low-volume non-commercial use. Please see the terms at the link provided.

To use the whitelist within smtpd_recipient_restrictions use the reject_rbl_client option:

reject_rbl_client zen.spamhaus.org

If a match is made, the message is rejected with identification sent with the reject telling the sender why the message was rejected. In the event the sender of the message is a legitimate sender, the sender will know from the reject that their message was not delivered.

Other blacklists can be added in a similar fashion. I recommend against adding them just because they exist, that results in un-necessary DNS queries.

When spam gets past the spam filters, I look at the message headers for the IP address of the MTA it came from. I then look up at that IP address at MX Toolbox.

This often shows me what blacklists would have caught it. When I see a particular blacklist frequently coming up, I look into adding that blacklist to the lists I use.

Do not forget the permit after the last blacklist so that messages that pass all the blacklist tests are permitted.

Beyond Basic Filtering

The above techniques will considerably reduce the amount of spam that reaches your users. Unfortunately a considerable amount of spam will still reach your users.

The spam reducing methods mentioned so far only filter out spam that does not conform to the standards or is being sent from an IP address that is already known to be a spam relay.

Professional spammers often send messages without errors, and they change the IP address they send from frequently to avoid the above filtering techniques.

You probably will need to run a content-based spam filter. They can really bog down a CPU so it is best to filter messages after Postfix has rejected the obvious spam.

Please avoid third-party cloud filtering. It exposes personal e-mails sent to your users to a third party, and may violate the privacy laws in some countries.

By default, a lot of content-based spam filters also check DNS based blacklists. Disable that, it is a waste of resources, let Postfix handle that. All we want is the content-based filtering.

Ideally, the content-based spam filter will just create a spam score that is added to the message as a header. The message than can be placed into the user’s spam folder by the IMAP/POP3 server, preferably taking into account user-supplied white and black lists.

If your server allows attachments, you should also provide up to date malware scanning of attachments. This also should be run after Postfix has rejected obvious spam.

There are many different content-based spam checkers out there, some commercial and some free. I do not personally have reason to recommend a particular product. SpamAssassin appears to be the most popular free one, but I do not believe it can claim to be the best.

DKIM Message Signing

DKIM is a mechanism for validating that message came from a particular domain and has not been modified in transit.

When your MTA receives an outgoing message from a user on your domain, it uses a specific private key to add a digital signature to that message.

Other mail servers can then check that the signature matches what it should to help eliminate forged e-mail, and when e-mail clients receive an e-mail claiming to be from your domain they can make sure the e-mail is properly signed by your DKIM key.

DKIM is an important tool in the fight against forged e-mail headers.

To set up DKIM with Postfix on RHEL/CentOS 7, please follow the instructions at SteveJenkins.com.

Steve maintains the OpenDKIM packages for the EPEL package repository. His instructions are solid.