Privacy Tools Guide

Configure Postfix with mandatory TLS encryption using smtp_tls_mandatory_ciphers = high and enforcing certificate verification via smtp_tls_verify_cert_match = hostname. This prevents downgrade attacks and man-in-the-middle interception of email traffic between mail servers. Proper TLS setup requires valid certificates, cipher hardening, and monitoring authentication failures to detect tampering attempts.

Why Mandatory TLS Matters for Email Privacy

SMTP was designed before encryption was a consideration. Without TLS enforcement, mail traverses the internet in plaintext — readable by any network observer. STARTTLS upgrades connections to encrypted ones, but opportunistic TLS is vulnerable to downgrade attacks: an adversary can strip the STARTTLS advertisement from server responses, forcing both parties into cleartext.

Mandatory TLS eliminates this by refusing to deliver or accept mail unless the connection is fully encrypted. For organizations handling sensitive communications — legal, medical, financial, or journalistic — it is a baseline security requirement, not an option. It is also a compliance prerequisite for HIPAA (PHI in transit) and PCI-DSS v4.0 (TLS 1.2+ for cardholder data).

Prerequisites

Before proceeding, ensure you have:

Understanding TLS Modes in Postfix

Postfix supports a hierarchy of TLS security levels, applied independently to outgoing (smtp_) and incoming (smtpd_) connections:

For general internet email, encrypt is the practical mandatory baseline. Use verify or dane for specific high-value partner domains.

Step 1: Generate or Obtain TLS Certificates

For production, obtain a certificate from Let’s Encrypt using Certbot:

sudo apt install certbot
sudo certbot certonly --standalone -d mail.yourdomain.com
# Certificates placed at:
# /etc/letsencrypt/live/mail.yourdomain.com/fullchain.pem
# /etc/letsencrypt/live/mail.yourdomain.com/privkey.pem

Use fullchain.pem (not cert.pem) to include the intermediate CA — remote servers need the full chain to verify your certificate. Set up automatic renewal with a Postfix reload hook:

# /etc/letsencrypt/renewal-hooks/deploy/postfix-reload.sh
#!/bin/bash
systemctl reload postfix
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/postfix-reload.sh

For testing only, a self-signed certificate:

sudo mkdir -p /etc/postfix/tls
sudo openssl req -x509 -nodes -days 365 -newkey rsa:4096 \
  -keyout /etc/postfix/tls/postfix.key \
  -out /etc/postfix/tls/postfix.crt \
  -subj "/CN=mail.yourdomain.com"
sudo chmod 600 /etc/postfix/tls/postfix.key
sudo chmod 644 /etc/postfix/tls/postfix.crt

Step 2: Configure Postfix TLS Parameters

Edit your Postfix main configuration file:

sudo nano /etc/postfix/main.cf

Outgoing Mail (SMTP Client) Configuration

# Mandatory TLS for all outgoing SMTP connections
smtp_tls_security_level = encrypt
smtp_tls_cert_file = /etc/letsencrypt/live/mail.yourdomain.com/fullchain.pem
smtp_tls_key_file = /etc/letsencrypt/live/mail.yourdomain.com/privkey.pem
smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt

# Session cache reduces TLS handshake overhead for repeated connections
smtp_tls_session_cache_database = btree:/var/lib/postfix/smtp_tls_session_cache
smtp_tls_session_cache_timeout = 3600s

# Protocol hardening: exclude obsolete and weak protocols
smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtp_tls_ciphers = high
smtp_tls_mandatory_ciphers = high
smtp_tls_exclude_ciphers = aNULL, eNULL, EXPORT, DES, 3DES, MD5, PSK, RC4

# Log TLS negotiation details for audit and troubleshooting
smtp_tls_loglevel = 1

The smtp_tls_security_level = encrypt setting ensures all outgoing SMTP connections require TLS. If a remote server does not support TLS, the message queues for retry rather than delivering in plaintext. This is the correct behavior for mandatory encryption — failed delivery is preferable to unencrypted delivery.

Incoming Mail (SMTP Server) Configuration

# Mandatory TLS for all incoming SMTP connections
smtpd_tls_security_level = encrypt
smtpd_tls_cert_file = /etc/letsencrypt/live/mail.yourdomain.com/fullchain.pem
smtpd_tls_key_file = /etc/letsencrypt/live/mail.yourdomain.com/privkey.pem
smtpd_tls_CAfile = /etc/ssl/certs/ca-certificates.crt

# Session cache
smtpd_tls_session_cache_database = btree:/var/lib/postfix/smtpd_tls_session_cache
smtpd_tls_session_cache_timeout = 3600s

# Protocol and cipher hardening
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtpd_tls_ciphers = high
smtpd_tls_mandatory_ciphers = high
smtpd_tls_exclude_ciphers = aNULL, eNULL, EXPORT, DES, 3DES, MD5, PSK, RC4

# Only accept AUTH credentials over encrypted connections
smtpd_tls_auth_only = yes

# Enable TLS logging
smtpd_tls_loglevel = 1

# Advertise available protocols explicitly
smtpd_tls_received_header = yes

The smtpd_tls_auth_only = yes parameter is critical: it prevents mail clients from transmitting passwords over unencrypted connections. Without it, AUTH commands succeed on cleartext connections, making credential theft trivial on any network path between the client and server.

Step 3: Configure the Submission Port

Mail User Agents (email clients like Thunderbird, Apple Mail, or Outlook) should connect on port 587 with STARTTLS or port 465 with implicit TLS. Configure master.cf to enforce encryption on both:

sudo nano /etc/postfix/master.cf
# Port 587: Submission with mandatory STARTTLS
submission inet n - y - - smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_tls_auth_only=yes
  -o smtpd_reject_unlisted_recipient=no
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
  -o milter_macro_daemon_name=ORIGINATING

# Port 465: Implicit TLS (SMTPS) — preferred for modern clients
smtps inet n - y - - smtpd
  -o syslog_name=postfix/smtps
  -o smtpd_tls_wrappermode=yes
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_tls_auth_only=yes
  -o smtpd_reject_unlisted_recipient=no
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
  -o milter_macro_daemon_name=ORIGINATING

Port 465 with smtpd_tls_wrappermode=yes uses implicit TLS — the handshake begins immediately on connect, with no plaintext negotiation phase to intercept. Prefer port 465 over 587 for new client configurations.

Step 4: Configure Granular TLS Policies per Destination

Create a TLS policy map for domain-specific requirements:

sudo nano /etc/postfix/tls_policy
# High-security partners: verify certificate against CA
partner-bank.com        verify match=hostname
law-firm.com            verify match=hostname

# Domains with known TLS issues: require encryption but skip cert verify
legacy-partner.org      encrypt

# Global default: mandatory TLS, no cert verification
*                       encrypt protocols=!SSLv2:!SSLv3:!TLSv1:!TLSv1.1

Compile and reference the map:

sudo postmap /etc/postfix/tls_policy

Add to main.cf:

smtp_tls_policy_maps = hash:/etc/postfix/tls_policy

Step 5: Enable DANE and MTA-STS

DANE (DNS-Based Authentication of Named Entities) uses DNSSEC-signed TLSA records to authenticate remote mail servers without trusting commercial CAs:

# In main.cf
smtp_dns_support_level = dnssec
smtp_tls_security_level = dane

Generate and publish your TLSA record:

openssl x509 -in /etc/letsencrypt/live/mail.yourdomain.com/fullchain.pem \
  -noout -pubkey | openssl pkey -pubin -outform DER | \
  openssl dgst -sha256 -binary | xxd -p -c 64
# Add to DNS: _25._tcp.mail.yourdomain.com. IN TLSA 3 1 1 <hash>

MTA-STS complements DANE for domains without DNSSEC. Create a policy at https://mta-sts.yourdomain.com/.well-known/mta-sts.txt:

version: STSv1
mode: enforce
mx: mail.yourdomain.com
max_age: 604800

Add the corresponding DNS TXT record:

_mta-sts.yourdomain.com TXT "v=STSv1; id=20260316T000000"

Update the id value whenever your policy changes.

Step 6: Test and Verify Configuration

sudo postfix check
sudo postconf -n | grep -i tls
sudo systemctl reload postfix

Verify TLS Enforcement

Test that your server enforces TLS on incoming connections:

# Check that STARTTLS is advertised
openssl s_client -connect mail.yourdomain.com:587 -starttls smtp 2>&1 | \
  grep -i "Protocol\|Cipher\|Verify"

# Verify TLS 1.3 is negotiated
openssl s_client -connect mail.yourdomain.com:465 2>&1 | grep "Protocol"
# Expected: Protocol : TLSv1.3

Test with swaks for an end-to-end check:

sudo apt install swaks
swaks --to test@yourdomain.com --server mail.yourdomain.com \
  --port 587 --tls --auth PLAIN \
  --auth-user admin@yourdomain.com --auth-password testpass

Monitor logs for TLS handshakes and verify only TLS 1.2/1.3 appears:

grep -oP 'TLSv[\d.]+' /var/log/mail.log | sort | uniq -c | sort -rn

Use CheckTLS.com or SSL Labs to validate from external networks — they identify weak ciphers and missing intermediate certificates.

Troubleshooting Common Issues

Connection failures after enabling mandatory TLS: Some legacy mail servers still operate without TLS. Check logs:

sudo grep "TLS\|refused\|reject" /var/log/mail.log | tail -50

If a specific domain’s mail is failing, add a domain-specific exception in the TLS policy map temporarily while you investigate whether the remote server genuinely lacks TLS support.

Certificate verification failures: Ensure you are using fullchain.pem rather than cert.pem alone. Remote servers need the full chain to verify trust:

# Verify certificate chain is complete
openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt \
  /etc/letsencrypt/live/mail.yourdomain.com/fullchain.pem

Authentication rejected over TLS: Verify SASL is properly configured. Check that Dovecot’s authentication socket is accessible to the postfix user:

sudo postconf smtpd_sasl_type  # Should show: dovecot
sudo ls -la /var/spool/postfix/private/auth

Performance degradation: TLS handshakes add 1-5ms per connection. Session caching (configured above) mitigates this for repeat connections.

Security Considerations

Mandatory TLS protects mail in transit but operates at the transport layer. Messages are decrypted and re-encrypted at each MTA hop — the destination mail server sees plaintext. For end-to-end message confidentiality, senders and recipients must use PGP or S/MIME in addition to TLS. TLS secures the channel; PGP/S/MIME secures the content itself. TLS also does not prevent metadata exposure — the sender address, recipient address, and timing remain visible to any MTA in the delivery path.

Frequently Asked Questions

Will mandatory TLS cause delivery failures to legitimate servers?

Some legacy servers still lack STARTTLS support. With smtp_tls_security_level = encrypt, mail to those servers queues and retries until the retry period expires. Monitor the queue with mailq and logs for repeat failures. Add domain-specific may exceptions only after confirming the domain genuinely cannot support TLS.

Should I use encrypt or verify as my default outgoing policy?

encrypt requires TLS but does not verify the remote certificate. verify adds CA validation, preventing connections to servers with fraudulent certificates. For internet-facing mail, encrypt with verify in the TLS policy map for specific high-value partners is the practical balance between security and delivery reliability.

Does this satisfy HIPAA encryption requirements?

Yes, when properly configured. HIPAA’s Security Rule (45 CFR 164.312(e)(1)) requires encryption of ePHI in transit. TLS 1.2 or higher with strong ciphers meets this requirement. The configuration in this guide — excluding TLS 1.0, 1.1, and weak ciphers — aligns with NIST SP 800-52 Rev. 2. Document your configuration and test results as evidence for compliance audits.

What is the difference between MTA-STS and DANE?

DANE uses DNSSEC-signed TLSA records to publish certificate fingerprints; remote servers verify your TLS without trusting any CA. MTA-STS uses HTTPS and DNS TXT records to publish a cached policy. DANE requires DNSSEC; MTA-STS does not. Both prevent downgrade attacks through different mechanisms — deploy both where possible as they are complementary.

Built by theluckystrike — More at zovo.one