Postfix Email Server

Postfix is one half of an email solution for those people that want role their own. It is responsible for interacting with the other email servers in the world. So Postfix is used to either send mail to, or receive mail from, other servers. Dovecot is the other half of that solution. While postfix interacts with other email servers, Dovecot interacts with your email client (Thunderbird, etc.). So Dovecot is the intermediary between your email client and Postfix.

Installing Postfix

On Redhat systems (Redhat, Fedora, Centos) you install Postfix using:

sudo yum install postfix

Virtual Mailboxes

Virtual mailboxes allow us to unlink the users of the email system from the users on the underlying operating system. This means that there can be mailboxes associated with users that do not have Linux accounts, and those users that do have Linux accounts can have multiple email accounts.

The first thing we need to do when supporting virtual mailboxes is to create a Linux user that will actually own all the virtual mailboxes. In some sense this will be a virtual user in that it will be able to own files and directories but will not be able to log in. We will call this virtual user ‘vmail’ and create it with:

useradd -m -r -s /sbin/nologin vmail

This creates the user and gives it a home directory: ~vmail. The virtual mailboxes will be placed in this directory.

The next thing is to identify all of your virtual mail domains. The domain is the part of the email address after the ‘@’, so the domain for happy@gilmore.nm is gilmore.nm. This tutorial assumes that there is only a small number of virtual mail domains and virtual mail boxes, and those will be configured by hand. It is also possible to connect Postfix to various databases like LDAP if there a large number of constantly changing users and domains, but that situation is not considered here.

Configuring Postfix

The configuration files for Postfix are usually found in /etc/postfix. The two most important files are main.cf and master.cf.

Basic Configuration

The file main.cf will be completely replaced. It should start with the basic configuration:

mydomain = nurdletech.com
myhostname = mail2.$mydomain
myorigin = $mydomain
mydestination = $myhostname, localhost, localhost.$mydomain, localhost.localdomain
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
inet_interfaces = all
mailbox_size_limit = 0
home_mailbox = mail/

# Prevent bad guys from querying for valid email addresses
disable_vrfy_command = yes

myorigin defines the domain used for outgoing messages and mydestination gives the non-virtual domains for which mail will be accepted (mydestination should not contain any of your virtual mail domains).

Notice that $mydomain is not contained in mydestination. That is intensional. It will instead be placed in the list of virtual mail domains (described later).

Postfix will reject any mail that it receives that is not destined for a domain or machine that is not listed in mydestination (or in the virtual mail domains defined later) unless it comes from a machine on the local network. In this way, mail from the local network can be sent to anyone out onto the open internet and mail from anyone on the open internet can be delivered to a known user, but Postfix will not act as an open relay.

The local network is defined by mynetworks. In this case the local network is confined to the server itself.

Finally, mailbox_size_limit = 0 is used to disable limits on the size of the mail that can be received into a mailbox, and home_mailbox is used to define the name of the mailbox used for local users. In this case the mail will be placed in a hidden directory in the users home directory and it will take the form of a directory itself (the trailing slash indicates that the maildir format should be used).

Local Aliases

The next part of main.cf is concerned with defining aliases for local users:

alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases

# Configure list of users/recipients
local_recipient_maps = proxy:unix:passwd.byname $alias_maps

Here we are assuming that an aliases file /etc/aliases exists. In generally you will not have to create this file nor change it. If you do change it, you will need to run:

postalias /etc/aliases

before the changes will take effect.

Virtual Mailboxes

At this point we have a choice to make, and that is how should local delivery be performed. Local delivery is that act of moving a piece of email from Postfix to the users mailbox. Given we are using Postfix and Dovecot, the two obvious choices are Postfix and Dovecot, though they are not the only choices. Using Postfix will be simpler, but using Dovecot has the advantage of being more efficient and allowing some automated filtering to be done.

Using the Postfix LDA

I will show both configurations. First, to configure the Postfix local delivery agent for virtual mailboxes, add the following to main.cf:

virtual_mailbox_domains = nurdletech.com turtleneck.com
virtual_mailbox_maps = hash:/etc/postfix/virtual_mailbox_maps
virtual_alias_maps = hash:/etc/postfix/virtual_alias_maps
virtual_mailbox_base=/home/vmail
virtual_uid_maps = static:997
virtual_gid_maps = static:997
virtual_minimum_uid = 997

This configuration assumes that there are two virtual mail domains: nurdletech.com turtleneck.com. These two domains will be added to the list of domains for which Postfix will accept mail. The actual virtual mailboxes are defined in /etc/postfix/virtual_mailbox_maps. An example of this file is:

dickie@nurdletech.com      nurdletech.com/dickie/mail/
dickie@turtleneck.com      turtleneck.com/dickie/mail/
admin@nurdletech.com       nurdletech.com/admin/mail/

Each line defines a virtual mailbox and consists of two items. The first item is the email address of the virtual mailbox (should be lowercase) and the second item is the physical address on disk. Notice that each of the second entries end with a slash. This indicates that the maildir format should be used rather than the mbox format (maildir places the mail into individual files inside the maildir whereas mbox is one giant file that contains the all of the mail messages). The physical address of the mailbox specified must be consistent with what Dovecot expects, which is set in the userdb section of the Dovecot configuration file. The correspondence is a bit complicated.

Dovecot uses the mail_location setting to specify the location of the mail directory. It will be set to ‘maildir:~/mail’, where ~ is replaced by the value of home from userdb. In home the %d is replaced by the domain name from the virtual mailbox name in and %n is the username. So if your virtual mailbox name is dickie@turtleneck.com then %n is dickie and %d is turtleneck.net. If home is /home/vmail/%d/%n, then Dovecot expects the physical location of the mailbox to be /home/vmail/turtleneck.com/dickie/mail.

In Postfix, the location specified as the second item on the line in virtual_mailbox_maps and that is added to the end virtual_mailbox_base to get the full path. So for dickie@turtleneck.com the mail directory would be /home/vmail/turtleneck.com/dickie/mail/.

These two paths must match (the fact that the trailing slash is missing on one of them is not a problem as long as both are paths to the same directory).

Whenever you modify this file you must run postmap in order to Postfix to be aware of the changes:

postmap /etc/postfix/virtual_mailbox_maps

virtual_alias_maps is a file that contains aliases for the virtual mailboxes. An example file is:

admin@turtleneck.com          admin@nurdletech.com
admin@mail2.turtleneck.com    admin@nurdletech.com
admin@mail2.nurdletech.com    admin@nurdletech.com
dickie@mail2.turtleneck.com   dickie@turtleneck.com
dickie@mail2.nurdletech.com   dickie@nurdletech.com

Each line defines an alias, and the alias contains two items. The first is the address that represents the alias and the second is the destination, which may either be a virtual mailbox or another alias.

The virtual_uid_maps and virtual_gid_maps define the user and group of the owner of the mail files. In this case 997 is the user and group number that correspond to the vmail user (found in /etc/passwd and /etc/group). virtual_minimum_uid must be equal to or smaller than the UID specified in virtual_uid_maps (it is associated with a sanity check that make more sense if you were using more sophisticated set ups).

Using the Dovecot LDA

Alternatively we can use dovecot-lda to deliver the messages for us. This provides better efficiency and will allow us to do email filtering if desired (not described).

To configure Postfix to use the Dovecot LDA, place the following in main.cf:

mailbox_command = /usr/libexec/dovecot/dovecot-lda -f "$SENDER" -a "$RECIPIENT"

virtual_alias_maps = hash:/etc/postfix/virtual_alias_maps
virtual_mailbox_maps = hash:/etc/postfix/virtual_mailbox_maps
virtual_mailbox_domains = nurdletech.com turtleneck.com
virtual_transport=dovecot

# uncomment the following if dovecot-lda seems to hang
#dovecot_destination_concurrency_limit = 1

mailbox_command indicates that dovecot-lda should be used for delivering mail to local (not virtual) mailboxes. The remaining settings control the delivery of mail to the virtual mailboxes.

virtual_mailbox_maps is the same file as given above, but the value given for the second entry is ignored (it must still be present, but the actual value has no effect).

virtual_transport gives the name of the process entry in the master.cf file that is used to deliver mail to the virtual mailboxes. That entry is (this should be added to master.cf):

dovecot   unix -        n       n       -       -       pipe
  flags=DRhu
  user=vmail:vmail
  argv=/usr/libexec/dovecot/dovecot-lda -f ${sender} -d ${recipient}

Transport Layer Security (SSL)

This assumes that you have already created an certificate using OpenSSL. The one I am using is named ssl-140409:

# Configure TLS
tls_random_source=dev:/dev/urandom

# settings that control how email is received when using TLS
smtpd_tls_cert_file=/etc/pki/tls/certs/ssl-140409.crt
smtpd_tls_key_file=/etc/pki/tls/private/ssl-140409.key
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtpd_use_tls=yes
smtpd_tls_security_level=may
smtpd_tls_protocols = !SSLv2, !SSLv3
smtpd_tls_eecdh_grade = strong

# settings that control how email is sent when using TLS
smtp_tls_cert_file=/etc/pki/tls/certs/ssl-140409.crt
smtp_tls_key_file=/etc/pki/tls/private/ssl-140409.key
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
smtp_use_tls=yes

# settings that control authentication
smtpd_sasl_type=dovecot
smtpd_sasl_path=private/auth
smtpd_sasl_security_options=noanonymous

smtpd_sasl_path must correspond to the last part of the auth-client listener path given in the Dovecot configuration file.

Normally, Postfix listens for email on port 25 (smtp). This email may either be sent in plain tex or may be encrypted (with STARTTLS). In addition, two other ports can be observed if desired. Port 587 (submission) is generally used by authorized users to send email. As such, it requires authentication. Port 465 (smpts) is used as an alternative to port 25 by older clients (such as Outlook) for sending unwrapped encrypted mail (without the STARTTLS).

These three ports are configured in master.cf with:

smtp      inet  n       -       n       -       -       smtpd
submission inet n       -       n       -       -       smtpd
  -o smtpd_tls_wrappermode=no
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
smtps     inet  n       -       n       -       -       smtpd
  -o smtpd_tls_wrappermode=yes
  -o smtpd_sasl_auth_enable=yes

You can confirm that TLS is being used for email by examining the headers of a received message. In the Received field, if it says ‘with ESMTPS’ then TLS was used. Remember that for TLS to be used both the source and the destination must support it, and many email servers still do not; Gmail does, so you can use that to send your test message.

Header Checks

The following settings define restrictions that are applied to the email header information to determine whether the email should be permitted or rejected. They are chosen to provide a reasonable level of security:

smtpd_helo_restrictions =
        permit_mynetworks,
        permit_sasl_authenticated,
        reject_non_fqdn_hostname,
        reject_invalid_hostname,
        permit

smtpd_client_restrictions =
        permit_mynetworks,
        permit_sasl_authenticated,
        warn_if_reject reject_unknown_client,
        permit

smtpd_sender_restrictions =
        permit_mynetworks,
        reject_non_fqdn_sender,
        warn_if_reject reject_unknown_sender_domain,
        permit

smtpd_recipient_restrictions =
        permit_mynetworks,
        permit_sasl_authenticated,
        reject_unauth_pipelining,
        reject_non_fqdn_recipient,
        reject_non_fqdn_sender,
        warn_if_reject reject_unknown_recipient_domain,
        warn_if_reject reject_unknown_sender_domain,
        reject_unauth_destination,
        reject_rbl_client zen.spamhaus.org,
        reject_rbl_client dnsbl.njabl.org,
        reject_rbl_client cbl.abuseat.org,
        permit

smtpd_data_restrictions =
        reject_unauth_pipelining,
        permit

Configuring the Firewall

The most common filewall on Linux is iptables, however the more recent versions of Fedora have shifted to using firewalld. These instructions are for firewalld on Fedora.

If you do not have firewalld you can get it on a Redhat system (Redhat, Fedora, CentOs) by running as root:

yum install firewalld

You would then activate it using:

systemctl enable firewalld
systemctl start firewalld

In order for Postfix to operate, we must punch a few holes in the firewall to allow the mail in. In particular we need to open port 25 (smtp), port 587 (submission) and port 465 (smtps). To do so, as root run:

firewall-cmd --permanent --add-service=smtp
firewall-cmd --permanent --add-service=submission
firewall-cmd --permanent --add-service=smtps

If a command fails because its service is ‘invalid’, it means that it is not described in /etc/services. You can overcome this by adding the port instead of the service. For example, if smtps is reported to be invalud, use:

firewall-cmd --permanent --add-port=465/tcp

These commands load these rules into the firewalld persistent configuration, but does not modify the currently active rules. To activate our new rules, run:

firewall-cmd --reload

Now, list the rules to make sure they are both correct and active:

firewall-cmd --list-all

Running Postfix

Before starting Postfix you should make sure that you have run postmap on all appropriate files; in this case on virtual_mailbox_maps and virtual_alias_maps:

postmap virtual_mailbox_maps
postmap virtual_alias_maps

This should also be done whenever these files change.

Start Postfix using:

systemctl start postfix

If Postfix is already running, and you have changed a configuration file, you can get Postfix to reread these files using:

systemctl reload postfix

You can stop Postfix with:

systemctl stop postfix

You can get Postfix status with:

systemctl status postfix

Once Postfix is running, you should verify that it is capable of receiving email and storing it into the appropriate virtual mailbox. Look in /var/log/maillog for messages from Postfix.

Once Postfix is running properly, you can enable it so that it starts automatically when the server starts using:

systemctl enable postfix