DB-유

Setting Up Postfix for Outgoing Mail Only

· linux, postfix, email

My servers never receive mail, but they send plenty: cron output, monitoring alerts, password resets, backup reports. I once left a "temporary" full mail server running on a web box and never want to do that again. What you want is a send-only setup (a null client, or satellite system): Postfix on localhost, accepting mail from local processes and relaying it through an authenticated smarthost.

I'll do it on Ubuntu 18.04 with the relay option, since most cloud providers block or throttle outbound port 25 and a relay through a real provider keeps you out of spam folders. It works on Debian 9 too, and CentOS 7 uses the same main.cf settings with different package commands. You need SMTP credentials for a provider accepting submission on 587 with STARTTLS, and a hostname with a valid A record (some receivers reject a HELO name that doesn't resolve).

Install

sudo apt update
sudo apt install -y postfix mailutils libsasl2-modules

The installer asks for a configuration type. Choose Satellite system if you will relay, or Internet Site if you will deliver directly, and enter the server's fully qualified hostname as the mail name. Either choice can be corrected afterwards in main.cf.

Lock it to localhost

Edit /etc/postfix/main.cf and make sure these settings are present. postconf -e edits the file safely and is easier than hunting for the lines:

sudo postconf -e 'inet_interfaces = loopback-only'
sudo postconf -e 'inet_protocols = ipv4'
sudo postconf -e 'mydestination ='
sudo postconf -e 'myhostname = app01.example.com'
sudo postconf -e 'myorigin = $myhostname'
sudo postconf -e 'mynetworks = 127.0.0.0/8'
sudo postconf -e 'local_transport = error:local delivery is disabled'

inet_interfaces = loopback-only is the important one: Postfix binds to 127.0.0.1 only, so no outside host can connect to port 25. An empty mydestination tells Postfix it is not the final destination for any domain, and the local_transport line makes attempts at local delivery bounce with a clear error instead of piling up in /var/mail.

Relay host and credentials

sudo postconf -e 'relayhost = [smtp.mailprovider.example]:587'
sudo postconf -e 'smtp_sasl_auth_enable = yes'
sudo postconf -e 'smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd'
sudo postconf -e 'smtp_sasl_security_options = noanonymous'
sudo postconf -e 'smtp_tls_security_level = encrypt'
sudo postconf -e 'smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt'

The square brackets around the relay hostname disable MX lookup, which is what you want for a submission server. smtp_tls_security_level = encrypt refuses to send if STARTTLS is unavailable.

Create the credentials file and build its hash map:

sudo nano /etc/postfix/sasl_passwd
[smtp.mailprovider.example]:587 username:password
sudo postmap /etc/postfix/sasl_passwd
sudo chmod 600 /etc/postfix/sasl_passwd /etc/postfix/sasl_passwd.db

Sender addresses and root's mail

Local processes send mail as root@app01.example.com or www-data@app01.example.com, which many relays reject unless the domain is verified. A canonical map rewrites the envelope and header sender to an address the provider accepts:

sudo postconf -e 'sender_canonical_maps = regexp:/etc/postfix/sender_canonical'
sudo postconf -e 'sender_canonical_classes = envelope_sender, header_sender'
sudo nano /etc/postfix/sender_canonical
/.+/    noreply@example.com

Also make sure root's mail goes somewhere a human reads by editing /etc/aliases:

root: admin@example.com
sudo newaliases
sudo systemctl restart postfix

Send a test

echo "Test from $(hostname)" | mail -s "Postfix relay test" you@example.com
sudo tail -f /var/log/mail.log

A successful relay logs a line ending in status=sent (250 2.0.0 OK ...) with relay=smtp.mailprovider.example[...]:587. Then confirm nothing is listening externally:

sudo ss -tlnp | grep :25

The only entry should be 127.0.0.1:25. From another host, nc -zv app01.example.com 25 must fail.

When mail doesn't leave

  • SASL authentication failed; server smtp.mailprovider.example said: 535 5.7.8 Authentication failed. Wrong credentials, or the sasl_passwd key does not match relayhost exactly (brackets and port included). Rerun postmap after every edit; Postfix reads the .db file, not the text.
  • warning: SASL authentication failure: No worthy mechs found. The libsasl2-modules package is missing, so Postfix has no PLAIN or LOGIN mechanism. Install it and restart Postfix.
  • status=deferred (connect to smtp.mailprovider.example[203.0.113.25]:587: Connection timed out). Outbound 587 is blocked by a firewall or security group. nc -zv smtp.mailprovider.example 587 from the server confirms it.
  • Certificate verification failed for smtp.mailprovider.example. smtp_tls_CAfile is wrong or the ca-certificates package is outdated. apt install --reinstall ca-certificates and confirm the path exists.

Mail sits in /var/spool/postfix while deferred and retries for five days by default; mailq lists what is waiting and postqueue -f retries immediately after you fix a configuration problem. I check mailq first whenever an alert goes missing.