Postfix is a Mail Transport Agent (MTA) responsible for the transfer of e-mails between mail servers using the SMTP protocol. Postfix is now the default MTA on CentOS 7. Here, as with most other critical network services, its default configuration allows outgoing but does not accept incoming network connections from any host other than the local one. This makes sense if all you need is a local Linux user mailing system and for sending out mails to other external mail servers from localhost too. But if you want to run your own centralized mail server for your own private network and domain, this is quite restrictive. So the purpose of this process is to set up Postfix as a domain-wide mail service to allow e-mails sent from any host in your network and if the recipient is a valid e-mail address within your local domain, deliver them to the correct mailbox on the mail server.
To Start With: What Do You Need?
To complete this process, you will require a working installation of the CentOS 7 operating system with root privileges, a console-based text editor of your choice, and a connection to the Internet to download additional software packages. You need to set up your local network properly and make sure that all the computers that want to send mails through your single-domain mailserver are in the same network and can ping this server. Also, setting your system time correctly is very important for any mail server. Apply the Synchronizing the system clock with NTP and the chrony suite process, Configuring the System before beginning your configuration. Finally, you need to set a Fully Qualified Domain Name (FQDN) for your mail server. Refer to the Setting your hostname and resolving the network process, Configuring the System. It is expected that your server will be using a static IP address and that it maintains one or more system user accounts. It is also assumed that you are working through all the process by process in the order in which they appear.
The Process
Postfix is already installed by default on all CentOS 7 flavors and it should be in a running state. In our example, we want to build a central mail server for our network 192.168.1.0/24 with the local domain name called centos7.home.
- First login as root and test if Postfix is already working locally and can send local mails to your system users. Type the following command to send a mail to a Linux user specified by <username>:
echo "This is a testmail" | sendmail <username>
- On CentOS 7, Postfix is also already configured to send out mails to external e-mail addresses (but from localhost only) without any changes to the configuration file. For example, you could use right out-of-the-box:
echo "This is a testmail" | sendmail contact@example.com
Note
If you don’t have a trusted domain and certificate behind your Postfix server, in times of massive spam e-mails most external e-mail servers will reject or put such e-mails directly into the spam folders. - To see if the local mail message has been delivered successfully, show the latest mail log (Press Ctrl+C to exit the log):
tail -f /var/log/maillog
- Next, check if a FQDN for our server is available. This is mandatory, and if not set properly, refer to the process, Configuring the System to set one (in our example, this will output the name mailserver.centos7.home):
hostname --fqdn
- Now create a backup copy of the main Postfix configuration file before opening this file:
cp /etc/postfix/main.cf /etc/postfix/main.cf.BAK && vi /etc/ postfix/main.cf
- First of all, we will want Postfix to listen on all network interfaces instead of only the local one. Activate or uncomment the following line (which means remove the # sign at the beginning of the line) that starts with inet_interfaces to read:
inet_interfaces = all
- Now, some lines below, you will find the line that reads inet_interfaces = localhost. Deactivate it or comment it out by putting a # sign at the start of the line:
# inet_interfaces = localhost
- Next, we need to set the local domain-name of the mail server. For example, if our mailserver’s FQDN is mailserver.centos7.home and this mailserver is responsible for delivering mail for the whole private centos7.home domain, the domain name will be (it’s best to put it below the line that reads #mydomain = domain.tld):
mydomain = centos7.home
- With the intention that this server may become a domain-wide mail server, you should now update the following line that starts with mydestination to read as follows (for example, in the mydestination section, comment out the first mydestination line and uncomment the second line):
mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain
- Next, we need to specify the pathname of a mailbox file relative to a user’s home directory. To do this, scroll down and locate the line that begins with home_mailbox and uncomment the following option (remove the # sign at the line’s beginning):
home_mailbox = Maildir/
- Save and close the file. Now we want to open the correct Postfix server ports in the firewall to allow the incoming SMTP connections to the server:
firewall-cmd --permanent --add-service=smtp && firewall-cmd --reload
- Next, restart the Postfix service as follows:
systemctl restart postfix
- Afterwards, login to a different computer in the same network and install Swiss Army Knife SMTP (swaks) to test out our Postfix server connection remotely. On CentOS, type the following (it needs the EPEL repository to be installed in advance):
yum install swaks
- Now, to test if you can connect to our new Postfix server using the standard SMTP mail port 25, with our Postfix server running on the IP address 192.168.1.100, we are sending a mail remotely to a Linux system user john which has a system user account on our Postfix server:
swaks --server 192.168.1.100 --to john@centos7.home
- Swaks creates output which should give us a hint if the mail transport has been successful. For example (the output has been truncated):
-> This is a test mailing
<-250 2.0.0 Ok: queued as D18EE52B38
-> QUIT
<- 221 2.0.0 Bye - You can also test that the last command has been successful by logging in as user john on the Postfix server, then checking and reading your local mailbox’s inbox, which should contain a file with the test mail sent from the swaks tool (the filename will be different on your computer), as follows:
ls ~/Maildir/new less ~/Maildir/new/14941584.Vfd02I1M246414.mailserver.centos7.home
How Does It Work?
As we have seen, Postfix is installed and running on every CentOS 7 system by default and in its basic configuration the mail server is listening on the localhost address for incoming mails so you can already send out local mails between your server’s local Linux system users without the need to contact an external MTA. It is already running because your system is already using it for a number of local services, such as the crond daemon or for sending out warnings about security breaches (for example, running a sudo command as a non-sudo user).
Before we can explain how this process works, we need to review some more basics about the Postfix MTA system in general. The Postfix MTA service can receive incoming e-mails from mail clients or other remote MTA servers using the SMTP protocol. If an incoming e-mail is destinated for the MTA server’s configured final destination domain (for example, a mail sent with the recipient address john@centos7.home is incoming to the centos7.home configured Postfix MTA server), it will deliver the mail to a local mailbox installed on the server (either in the filesystem or in a database system such as MariaDB). If the incoming mail is not destinated for this server, it will be relayed (forwarded) to another MTA.
Remember that this is all a Postfix server is capable of doing and nothing more: receiving incoming SMTP connections from mail clients or other MTAs, delivering mail to local mailboxes on the server, and forwarding mail to other MTAs using SMTP. Contrary to common belief, Postfix cannot transfer the mails from its local mailboxes to the end users. Here we need another type of MTA called delivery agent, which uses different mail protocols, such as IMAP or POP3.
In this process, we configured our Postfix server so that the other computers and servers in the same network could also send mails to our Postfix server, which is blocked by default (by default only the server itself can send mails). If an incoming e-mail, sent from another computer in our network, has the same domain name in the recipient’s e-mail address as our Postfix server has its FQDN in, then it gets delivered to the appropriate local mailbox defined by the recipient’s part of the e-mail; all external e-mail addresses get relayed to an external MTA.
So what did we learn from this experience?
We began our journey by testing if we could send out local mails to system users. Here we logged in as our root user and sent a mail to a valid local system user using the sendmail program, which is included in the Postfix package. For every mail you send using sendmail, you should be able to see some new lines appearing in the /var/log/maillog file, which contains status information and other important logging text for the mail. If you sent a message from root to the user john and the FQDN of your server is centos7.home, new output lines appended to the log file should contain amongst other things a from=<root@centos7.home>,a to=<john@centos7.home> and if delivered successfully a status=sent information. If no such logging information shows up, check the status of the Postfix service.
Afterwards, we displayed the FQDN for our server. It is very important to set this up correctly because this information will be used to authenticate the Postfix server when connecting to other MTAs or mail clients. MTAs check the FQDN which has been announced by their partner and some even refuse to connect if it is not provided or if it differs from the real DNS domain name of the server. After our initial test, we then started editing the main Postfix configuration file after we made a backup copy of it first. As said before, by default only the users sitting on the same server the Postfix service is running on can send mails between them as the server defaults to listening on the loopback device only. So first we enabled Postfix to listen to all the available network interfaces instead, using the inet_interfaces = all parameter. This ensured that all our clients in our network could connect to this server. Next, we set the domain name using the mydomain parameter we wanted to have for Postfix. In order for Postfix to work in our network, the domain name defined here in this variable must be the exact same value as the domain name for our server’s network. Afterwards, we changed the mydestination parameter by choosing the line which adds the $mydomain parameter to the list of allowed domains. This will define all domains our Postfix mail server considers as the final destination. If a Postfix mail server is configured as the final destination for a domain, it will deliver the messages to the local mailboxes of the recipient users, which can be found in /var/spool/mail/<username> (we will change this location in the next step) instead of forwarding the mails to the other MTAs (as we added $mydomain to the list of final destinations in our example, we will deliver all mails sent to the centos7.home domain).
Here, you also need to remember that, by default, Postfix trusts all the other computers (SMTP clients) in the same IP subnetwork as the Postfix server is in to send mails to external e-mail addresses (relay mails to external MTAs) through our centralized server, which could be too relaxed for your network policy. Since e-mail spam is an ongoing problem on the Internet and we don’t want to allow any user to abuse our mail server from sending spam (which an open relay mail server does; it this takes anything from any client and sends it to any mail server), we can further increase security by setting mynetworks_style = host, which only trusts and allows the localhost to send mails to external MTAs. Another way to reduce the spam risk might be to use the mynetworks parameter where you can specify which network or IP address is allowed to connect to our mail server and send e-mails through it; for example, mynetworks = 127.0.0.0/8, 192.168.1.0/24. To learn more about all the available Postfix settings, refer to the Postfix configuration parameter manual using the command man 5 postconf. Afterwards, we changed where the local mail should be stored. By default, all the incoming mails go to a centralized mailbox space located at /var/spool/mail/<username>. In order for local users to receive their mail in their own home directory, we used the Maildir parameter for the home_mailbox option, which changes this system to deliver all the mails to /home/<username>/Maildir/ instead. Afterwards, we opened the standard SMTP protocol port in firewalld using the SMPT service, which Postfix uses for communication with the other MTAs or mail clients sending incoming mails through.
Postfix is already configured to start at boot, but to complete this part of the process we restarted the Postfix service for it to accept the new configuration settings. At this stage, the process of configuring Postfix was complete, but to test remote access we needed to log into another computer in the same network. Here we installed a small command line-based mail client called swaks, which can be used to test local or remote SMTP server connections. We ran our test by sending a mail to our remote Postfix mail server and supplied a recipient user and the IP address of our SMTP server. Having done this, you should have received a test message and as a result, you should be happy to know that everything is working correctly. However, if you did happen to encounter any errors, you should refer to the mail server log file located at /var/log/maillog.
There's more…
In this section of the process, we will change your e-mail sender address, encrypt SMTP connections, and configure your BIND DNS server to include our new mailserver’s information.
Changing an e-mail’s appearing domain name
If an MTA sends out an e-mail, Postfix automatically appends the hostname of the sender’s e-mail address by default, if not provided explicitly otherwise, which is a great feature to track down which computer in your network sent the e-mail locally (otherwise it would be hard to find the origin of a mail if you got multiple computers sending out mails by a user called root). Often when sending messages to a remote MTA, you don’t want to have your local hostname appear in the e-mail.
Here it is better to have only the domain name alone. In order to change this, go to the Postfix MTA you want to send mails from, open the Postfix configuration file /etc/postfix/main.cf, and enable this feature by uncommenting (removing the # sign at the beginning of the line) the following line to determine the origin (restart the Postfix service afterwards):myorigin = $mydomain
Using TLS-(SSL) encryption for SMTP communication
Even if you are running your own Postfix server in a small or private environment, you should always be aware that normal SMTP traffic will be sent in clear text over the Internet, making it possible that anyone could sniff the communication. TLS will allow us to set up an encrypted SMTP connection between the server and the mail client, meaning that the complete communication will be made enciphered and impossible to be read by a third-party. In order to do this, if you have not already bought an official SSL certificate or generated some self-signed certificates for your domain, start by creating one here (read the Generating self-signed certificates process, Providing Security to learn more). First login as root on your server and go to the standard certificate location: /etc/pki/tls/certs. Next, create a TLS/SSL keypair consisting of the certificate and its embedded public key as well as the private key (enter your Postfix’s FQDN as the Common name, for example, mailserver.centos7.home) to do this type make postfixserver.pem. Afterwards, open the main Postfix configuration file /etc/postfix/main.cf with your favorite text editor and put in the following lines at the end of the file:smtpd_tls_cert_file = /etc/pki/tls/certs/postfix-server.pem
smtpd_tls_key_file = $smtpd_tls_cert_file
smtpd_tls_security_level = may
smtp_tls_security_level = may
smtp_tls_loglevel = 1
smtpd_tls_loglevel = 1
Then save and close this file. Note that setting smtpd_tls_security_level to may will activate TLS encryption if available in the mail client program, otherwise it will use an unencrypted connection. You should only set this value to encrypt (which will enforce SSL/TLS encryption in any case) if you are absolutely sure that all your senders to your mail server are supporting this feature. If any sender (external MTA or mail client) does not support this feature, the connection will be refused. This means that e-mails from such sources will not be delivered into your local mailboxes. We also specified TLS encryption for outgoing SMTP connections from our Postfix server to other MTAs where possible using smtp_tls_security_level = may. By setting both the Postfix’s client and server mode TLS log level to 1 we get more verbose output so we can check if the TLS connections are working. Some very old mail clients use an ancient port 465 for encrypting SMTP over SSL/TLS instead of the standard SMTP port 25.
In order to activate this feature, open /etc/postfix/master.cf and search, then uncomment (remove # at the start of each line) the following lines, so they read:smtps inet n - n - - smtpd
-o syslog_name=postfix/smtps
-o smtpd_tls_wrappermode=yes
Save and close the file, and then restart Postfix. Next, we need to open the SMTPS port in the firewall to allow incoming connections to our server. Since no SMTPS firewalld rule is available in CentOS 7, we will create our own service file first using the sed utility:sed 's/25/465/g' /usr/lib/firewalld/services/smtp.xml | sed 's/Mail
(SMTP)/Mail (SMTP) over SSL/g' > /etc/firewalld/services/smtps.xml
firewall-cmd --reload
firewall-cmd --permanent --add-service=smtps; firewall-cmd --reload
You should now be able to test if an SMTPS connection can be made by using our swaks SMTP command line tool with the -tls parameter from a remote computer to our Postfix server running on IP 192.168.1.100, for example swaks --server 192.168.1.100 --to john@centos7.home -tls. This command line will test if the SMTP server supports TLS encryption (STARTTLS) and exit with an error message if it is not available for any reason. A working output would look as follows (truncated to only show you the most important lines): -> STARTTLS
<-220 2.0.0 Ready to start TLS
=== TLS started with cipher TLSv1.2:ECDHE-RSA-AES128-GCM-SHA256:128
~> This is a test mailing
<~ 250 2.0.0 Ok: queued as E36F652B38
You can then also recheck your TLS setup by going to the main mail log file on your Postfix server and watching for the following line corresponding to your swaks test mail from the last step (your output will be different):Anonymous TLS connection established from unknown[192.168.1.22]: TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)
Configure BIND to use your new mailserver
After our domain-wide Postfix server has been installed and configured, we should now announce this new mail service in our domain using a DNS server. refer to the process, Working with FTP for details on how to set up and configure a BIND server, and especially read the section about the Mail eXchanger (MX) record if you haven’t already. Then add a new MX entry to your BIND forward and corresponding reverse zone file. In your forward zone file, add the following lines for our Postfix server with the IP 192.168.1.100:IN MX 10 mailhost.centos7.home.
mailhost IN A 192.168.1.100
In your reverse zone file, you could add the following lines instead:
100 IN PTR mailhost.centos7.local.