Postfix Articles
After coming to understand that apps that offer a username and password doesn't mean they necessarily support client submission on port 587, I needed another way to move apps away from Exchange 2016 on-prem for mail relay to Office365.
The most common choice is to set up an Ubuntu server with Postfix and then have Postfix submit the email with an O365 account. However, that has the major drawback of only allowing one email 'from address' to be used. I have many apps to support and some that are important enough to require their own address.
A better option is to use Postfix and connect to an O365 receive connector (aka Inbound Connector), which allows for server-to-server communication over port 25. The recommended way to secure the connection is with a 3rd party cert, but for testing, you can use an external IP address.
Having limited Linux experience over the years I was a little surprised at how bad certain things were:
Despite that, there were at least workarounds for the above problems. Postfix is lightweight and easy to configure while Linux is stable and reboots in under a minute if maintenance is ever required. This is useful if you only plan to run one server and not load balance.
The config is simple enough, you want to set the smart host so it points to O365 for all received mail:
/etc/postfix/main.cf
===================
relayhost = [domain-com.mail.protection.outlook.com]:25
===================
relayhost = [domain-com.mail.protection.outlook.com]:25
What apps can relay via the server can be restricted by IP, this is important because the connector to O365 is an open relay, with only some filtering in place.
mynetworks = 192.168.1.2
We need the Postfix server to relay to O365 using encryption, so having purchased and installed a 3rd party cert we set the following config:
smtp_tls_security_level = encrypt
smtp_use_tls = yes
smtp_tls_note_starttls_offer=yes
smtp_tls_key_file = /etc/ssl/private.key
smtp_tls_cert_file = /etc/ssl/relay_cert.cer
smtp_tls_CAfile = /etc/ssl/inter_cert.cer
smtp_use_tls = yes
smtp_tls_note_starttls_offer=yes
smtp_tls_key_file = /etc/ssl/private.key
smtp_tls_cert_file = /etc/ssl/relay_cert.cer
smtp_tls_CAfile = /etc/ssl/inter_cert.cer
Next, we restarted the Postfix service and set the O365 receive connector to validate by matching the cert to the domain name.

↑ Connector validates against the cert name, this is the recommended setup.
With that in-pace I sent a test mail, not expecting it to work the first time, but there was a nice surprise.

↑ Email received and sent to O365 using TLS 1.3. The time delay turned out to be NTP not being setup on Ubuntu.
One thing to note is you will probably need to update your SPF record to include the external IP address that the relay server uses. The messages that arrive via the O365 connector are still subject to spam filtering.
The next test was to send to an external recipient, eg relay@domain.com to joe.soap@gmail.com. This worked without issue and the email was authenticated in Gmail, so no spam issues to deal with there. O365 will add its own DKIM mark to the outgoing mail.
At this point, all of the functionality that was in my old Exchange server was now present in the Postfix server. But what if you want to provide a better service? Most apps will relay in clear text on port 25 to the relay server but some support implicit SSL on port 465. To support this we add the following to the main.cf file:
smtpd_tls_key_file = /etc/ssl/private.key
smtpd_tls_cert_file = /etc/ssl/relay_cert.cer
smtpd_tls_CAfile = /etc/ssl/inter_cert.cer
smtpd_use_tls=yes
smtpd_tls_security_level=may
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtpd_tls_cert_file = /etc/ssl/relay_cert.cer
smtpd_tls_CAfile = /etc/ssl/inter_cert.cer
smtpd_use_tls=yes
smtpd_tls_security_level=may
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
I have to admit I tested this with Powershell's send-mailmessage and got an obscure cert error message.
Send-MailMessage -To joe.soap@gmail.com -from relay@domain.com -Subject 'test' -SmtpServer server.domain.local -UseSsl -Port 465
Send-MailMessage : The remote certificate is invalid according to the validation procedure.
Send-MailMessage : The remote certificate is invalid according to the validation procedure.
I eventually worked out that with PowerShell the specified SMTP server has to match the certificate name. So you may need to edit the hosts file to point relay.domain.com to your internal Postfix server. Most apps don't seem to be as fussy as PowerShell.
I also tested with OpenSSL and was able to send a test mail, the only difference in the headers is that we see ESMTPS instead of ESMTP.

↑ ESMTPS means the email was encrypted from the app server to Postfix.
Some commands that help administer Ubuntu and Postfix:
postfix reload # restart the Postfix service
postfix logrotate # saves off the existing postfix.log and creates a new log
sudo reboot # reboot the server, needed after some config changes
setxkbmap gb # set keyboard to UK, unfortunately doesn't stick after reboot
postfix logrotate # saves off the existing postfix.log and creates a new log
sudo reboot # reboot the server, needed after some config changes
setxkbmap gb # set keyboard to UK, unfortunately doesn't stick after reboot
Further Postfix config explained:
smtp_*** # server to server communication
smtpd_*** # client to server communication
smtp_tls_key_file = /etc/ssl/private.key # your private key
smtp_tls_cert_file = /etc/ssl/relay_cert.cer # your public key
smtp_tls_CAfile = /etc/ssl/inter_cert.cer # your intermediate key, O365 does not seem to require this but it's good practice to include it
smtp_use_tls = yes # opportunistic, use TLS when a remote SMTP server announces STARTTLS support
smtp_tls_security_level = encrypt # enforces TLS to be used when sending mail
smtp_tls_note_starttls_offer=yes # keeps a log of servers using TLS, not really sure if needed
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache # used to cache TLS requests, needed for performance
smtpd_tls_loglevel = 1# shows what TLS version is used by clients, adds line to log
myhostname = relay.domain.com # hostname returned to telnet EHLO
maillog_file = /var/log/postfix.log # where to place the Postfix log file
compatibility_level = 3.6 # backwards compatibility safety guard
alias_maps = hash:/etc/aliases# mapping logons to addresses, only added to remove error in log
smtpd_*** # client to server communication
smtp_tls_key_file = /etc/ssl/private.key # your private key
smtp_tls_cert_file = /etc/ssl/relay_cert.cer # your public key
smtp_tls_CAfile = /etc/ssl/inter_cert.cer # your intermediate key, O365 does not seem to require this but it's good practice to include it
smtp_use_tls = yes # opportunistic, use TLS when a remote SMTP server announces STARTTLS support
smtp_tls_security_level = encrypt # enforces TLS to be used when sending mail
smtp_tls_note_starttls_offer=yes # keeps a log of servers using TLS, not really sure if needed
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache # used to cache TLS requests, needed for performance
smtpd_tls_loglevel = 1# shows what TLS version is used by clients, adds line to log
myhostname = relay.domain.com # hostname returned to telnet EHLO
maillog_file = /var/log/postfix.log # where to place the Postfix log file
compatibility_level = 3.6 # backwards compatibility safety guard
alias_maps = hash:/etc/aliases# mapping logons to addresses, only added to remove error in log
Full config file main.cf:
# General
# =======
compatibility_level = 3.6
maillog_file = /var/log/postfix.log
alias_maps = hash:/etc/aliases
# Server to Server
# ================
smtp_tls_key_file = /etc/ssl/private.key
smtp_tls_cert_file = /etc/ssl/relay_cert.cer
smtp_tls_CAfile = /etc/ssl/inter_cert.cer
smtp_use_tls = yes
smtp_tls_security_level = encrypt
smtp_tls_note_starttls_offer=yes
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
relayhost = [domain-com.mail.protection.outlook.com]:25
myhostname = relay.domain.com
# Client to Server
# ================
smtpd_tls_key_file = /etc/ssl/private.key
smtpd_tls_cert_file = /etc/ssl/relay_cert.cer
smtpd_tls_CAfile = /etc/ssl/inter_cert.cer
smtpd_use_tls=yes
smtpd_tls_security_level=may
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtpd_tls_loglevel = 1
mynetworks = 192.168.1.2 192.168.1.17
# =======
compatibility_level = 3.6
maillog_file = /var/log/postfix.log
alias_maps = hash:/etc/aliases
# Server to Server
# ================
smtp_tls_key_file = /etc/ssl/private.key
smtp_tls_cert_file = /etc/ssl/relay_cert.cer
smtp_tls_CAfile = /etc/ssl/inter_cert.cer
smtp_use_tls = yes
smtp_tls_security_level = encrypt
smtp_tls_note_starttls_offer=yes
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
relayhost = [domain-com.mail.protection.outlook.com]:25
myhostname = relay.domain.com
# Client to Server
# ================
smtpd_tls_key_file = /etc/ssl/private.key
smtpd_tls_cert_file = /etc/ssl/relay_cert.cer
smtpd_tls_CAfile = /etc/ssl/inter_cert.cer
smtpd_use_tls=yes
smtpd_tls_security_level=may
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtpd_tls_loglevel = 1
mynetworks = 192.168.1.2 192.168.1.17
References: https://secopsmonkey.com/better-mail-relaying-postfix-through-office-365.html