Postfix
This role installs and configures Postfix as an SMTP relay for sending system emails through an external mail server.
Postfix Role
Overview
This role installs and configures Postfix as an SMTP relay for sending system emails through an external mail server. It sets up SASL authentication, TLS encryption, header rewriting for proper sender display names, and configures the main Postfix configuration file. The role is designed for systems that need to send notification emails (like Centreon alerts) via a relay host such as Gmail, Office 365, or a dedicated SMTP server.
Purpose
- SMTP Relay: Send system emails through external mail server
- Centreon Notifications: Enable alert emails from monitoring system
- SASL Authentication: Authenticate to relay with username/password
- TLS Encryption: Secure email transmission
- Header Rewriting: Set proper display name for sender
- System Email: Enable mail command for scripts and services
- Automated Configuration: Deploy consistent mail config across hosts
Requirements
- Ansible 2.9 or higher
- Target system: Debian/Ubuntu or FreeBSD
- Root or sudo privileges
- Internet connectivity (to install packages and send mail)
- SMTP relay credentials (stored in Ansible Vault)
- Relay host accessible from target system
What is an SMTP Relay?
SMTP relay forwards email from local system to external mail server:
Without relay:
System tries to send directly to recipient's mail server
↓
Often blocked by ISP, firewalls, or spam filters
↓
Email fails to deliver
With relay:
System → Postfix → SMTP Relay (Gmail/Office365) → Recipient
↓
Authenticated, properly configured sender
↓
Email delivered successfully
Common relay providers:
- Gmail: smtp.gmail.com:587
- Office 365: smtp.office365.com:587
- SendGrid: smtp.sendgrid.net:587
- Custom SMTP server
Role Variables
Required Variables
| Variable | Required | Description |
|---|---|---|
postfix_smtp_host | Yes | SMTP relay hostname |
postfix_smtp_port | Yes | SMTP relay port |
postfix_sender_email | Yes | Sender email address |
postfix_sender_password | Yes | SMTP authentication password (in vault) |
postfix_myhostname | Yes | System hostname for mail |
Optional Variables
| Variable | Default | Description |
|---|---|---|
postfix_sender_display_name | Centreon Homelab | Display name in From field |
postfix_smtp_use_tls | yes | Enable TLS encryption |
postfix_smtp_sasl_auth_enable | yes | Enable SASL authentication |
postfix_smtp_sasl_security_options | noanonymous | SASL security options |
postfix_smtp_sasl_tls_security_options | noanonymous | SASL TLS security options |
postfix_config_path | /etc/postfix | Postfix config directory |
postfix_password_maps_file | sasl_passwd | SASL password filename |
postfix_header_checks_file | smtp_header_checks | Header checks filename |
Variable Details
postfix_smtp_host
SMTP relay server hostname.
Examples:
postfix_smtp_host: smtp.gmail.com # Gmail
postfix_smtp_host: smtp.office365.com # Office 365
postfix_smtp_host: mail.example.com # Custom server
postfix_smtp_port
SMTP relay server port.
Common ports:
587: STARTTLS (recommended)465: SMTPS (SSL/TLS wrapper)25: Plain SMTP (usually blocked)
Recommended: 587 with TLS
postfix_sender_email
Email address used for authentication and as sender.
Examples:
postfix_sender_email: notifications@example.com
postfix_sender_email: admin@example.com
Note: Must match relay authentication account.
postfix_sender_password
Password for SMTP authentication.
IMPORTANT: Store in Ansible Vault
# In vault.yml
vault_postfix_sender_password: your_secure_password
# In playbook
postfix_sender_password: "{{ vault_postfix_sender_password }}"
Gmail: Use app password, not account password
postfix_sender_display_name
Human-readable name shown in email client.
Default: Centreon Homelab
Examples:
postfix_sender_display_name: "Server Alerts"
postfix_sender_display_name: "Homelab Monitoring"
postfix_sender_display_name: "IT Notifications"
Result: Email shows as:
From: "Homelab Monitoring" <notifications@example.com>
postfix_myhostname
Hostname used in SMTP HELO/EHLO and headers.
Examples:
postfix_myhostname: centreon.homelab.local
postfix_myhostname: "{{ ansible_fqdn }}"
postfix_myhostname: "{{ inventory_hostname }}"
Best practice: Use FQDN (fully qualified domain name)
Dependencies
No Ansible role dependencies, but requires:
- Internet connectivity for package installation
- SMTP relay account credentials
- Network access to relay host (port 587/465)
Often used with:
- deploy_centreon: Centreon monitoring system
- rsyslog_configuration: Log forwarding
- System services: Cron, monitoring tools
Example Playbook
Basic Gmail Relay
---
- name: Configure Postfix SMTP Relay
hosts: centreon
become: true
vars:
postfix_smtp_host: smtp.gmail.com
postfix_smtp_port: 587
postfix_sender_email: admin@example.com
postfix_sender_password: "{{ vault_gmail_app_password }}"
postfix_myhostname: centreon.homelab.local
roles:
- postfix
Office 365 Relay
---
- name: Configure Postfix with Office 365
hosts: all
become: true
vars:
postfix_smtp_host: smtp.office365.com
postfix_smtp_port: 587
postfix_sender_email: admin@example.com
postfix_sender_password: "{{ vault_o365_password }}"
postfix_myhostname: "{{ ansible_fqdn }}"
postfix_sender_display_name: "Company Alerts"
roles:
- postfix
Custom SMTP Server
---
- name: Configure Postfix with Custom Relay
hosts: servers
become: true
vars:
postfix_smtp_host: mail.example.com
postfix_smtp_port: 587
postfix_sender_email: noreply@example.com
postfix_sender_password: "{{ vault_smtp_password }}"
postfix_myhostname: "{{ inventory_hostname }}.example.com"
roles:
- postfix
What This Role Does
-
Install packages:
postfix: SMTP server/relaypostfix-pcre: Perl regex support (header rewriting)s-nail: Mail command utilitycyrus-sasl-plain: SASL PLAIN authentication
-
Configure SASL password:
- Creates
sasl_passwdwith relay credentials - Format:
[relay:port] user:password - Sets permissions:
0600(root only) - Runs
postmapto create hash database
- Creates
-
Configure header checks:
- Creates
smtp_header_checksfor From rewriting - Rewrites centreon-engine emails with display name
- Uses PCRE regex matching
- Creates
-
Configure main.cf:
- Sets relay host and port
- Enables TLS and SASL authentication
- References password maps and header checks
- Sets system hostname
-
Restart Postfix:
- Restarts service to apply changes
- Enables Postfix on boot
Installed Packages
postfix
Main SMTP server and relay.
What it does:
- Accepts local mail from system
- Relays to external SMTP server
- Handles queuing and retries
postfix-pcre
Perl Compatible Regular Expression support.
What it does:
- Enables regex in header_checks
- Allows complex pattern matching
- Used for From header rewriting
s-nail
Mail command utility (formerly mailx).
What it does:
- Provides
mailcommand for scripts - Send email from command line
- Used by cron, monitoring, scripts
Example:
echo "Test message" | mail -s "Test Subject" user@example.com
cyrus-sasl-plain
SASL PLAIN authentication mechanism.
What it does:
- Enables username/password authentication
- Required for SMTP relay authentication
- Secure when used with TLS
Configuration Files
sasl_passwd
Location: /etc/postfix/sasl_passwd
Content:
[smtp.gmail.com]:587 admin@example.com:app_password_here
Permissions: 0600 (root only)
Hash database: Created by postmap sasl_passwd
- Generates:
sasl_passwd.db - Used by Postfix for authentication
smtp_header_checks
Location: /etc/postfix/smtp_header_checks
Content:
# Rewrite From header for centreon-engine emails
/^From:.*centreon-engine/ REPLACE From: "Centreon Homelab" <admin@example.com>
How it works:
- Match emails with
centreon-enginein From header - Replace entire From line with display name and email
- Other emails pass through unchanged
Why needed: Centreon sends emails as centreon-engine@localhost, which looks unprofessional and may trigger spam filters.
main.cf
Location: /etc/postfix/main.cf
Key settings:
myhostname = centreon.homelab.local
relayhost = [smtp.gmail.com]:587
smtp_use_tls = yes
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_sasl_tls_security_options = noanonymous
smtp_header_checks = pcre:/etc/postfix/smtp_header_checks
SASL Authentication
SASL (Simple Authentication and Security Layer) authenticates to relay server.
Authentication Flow
1. Client: EHLO
2. Server: 250-STARTTLS, 250-AUTH PLAIN LOGIN
3. Client: STARTTLS
4. [TLS negotiation]
5. Client: AUTH PLAIN [base64(user:pass)]
6. Server: 235 Authentication successful
7. Client: MAIL FROM, RCPT TO, DATA
SASL Options
noanonymous: Don’t allow anonymous authentication
- Requires username/password
- More secure
Other options:
noplaintext: Require encryptionnodictionary: Prevent dictionary attacksnoactive: Prevent active attacks
TLS Encryption
TLS (Transport Layer Security) encrypts SMTP connection.
STARTTLS vs SMTPS
STARTTLS (Port 587):
1. Connect on plain port
2. Send STARTTLS command
3. Upgrade to encrypted connection
4. Continue with SMTP
SMTPS (Port 465):
1. Connect with immediate TLS
2. Entire connection encrypted
3. No upgrade negotiation
Recommended: STARTTLS on port 587
Testing the Configuration
Test Postfix Config
# Check Postfix configuration syntax
postfix check
# Should output nothing if config is valid
Send Test Email
# Using mail command
echo "Test message body" | mail -s "Test Subject" recipient@example.com
# Using sendmail
echo -e "Subject: Test\n\nTest message" | sendmail recipient@example.com
Check Mail Queue
# View mail queue
mailq
# Should show either empty queue or queued messages
View Mail Logs
# Debian/Ubuntu
tail -f /var/log/mail.log
# FreeBSD
tail -f /var/log/maillog
# Look for:
# - "status=sent" (successful)
# - "authentication failed" (auth problem)
# - "connection timed out" (network problem)
Test SMTP Connection
# Test connection to relay
telnet smtp.gmail.com 587
# Should see:
# 220 smtp.gmail.com ESMTP
# Type: EHLO test
# Should see: 250-STARTTLS
Security Considerations
- Vault Credentials: Store passwords in Ansible Vault
- File Permissions: sasl_passwd is 0600 (root only)
- TLS Required: Always use TLS for authentication
- App Passwords: Use app passwords for Gmail (not account password)
- Relay Authentication: SASL prevents open relay abuse
- Header Rewriting: Verify sender address authorized by relay
- Network Security: Ensure firewall allows outbound 587/465
- Log Monitoring: Watch for authentication failures
Gmail-Specific Configuration
Enable App Passwords
-
Enable 2FA on Google account
-
Generate app password:
- Google Account → Security
- 2-Step Verification → App passwords
- Select “Mail” and device
- Copy 16-character password
-
Use app password in Ansible Vault:
vault_postfix_sender_password: "abcd efgh ijkl mnop"
Gmail Rate Limits
Limits:
- 500 emails per day (free accounts)
- 2,000 emails per day (Google Workspace)
- 100 recipients per message
For homelab: Limits are typically sufficient
Office 365 Configuration
Enable SMTP Authentication
- Admin Center → Users → Active users
- Select user → Mail → Manage email apps
- Enable Authenticated SMTP
Conditional Access
May need to:
- Allow legacy authentication for SMTP
- Create app password (if MFA enabled)
- Add sender to SPF/DKIM records (for best deliverability)
Troubleshooting
Authentication failed
Log shows:
status=deferred (SASL authentication failed)
Causes:
- Wrong username/password
- App password not enabled (Gmail)
- SMTP auth disabled (Office 365)
Solutions:
- Verify credentials in vault
- Enable app password (Gmail)
- Enable SMTP auth (Office 365)
- Test credentials manually:
# Test authentication
swaks --to test@example.com \
--from admin@example.com \
--server smtp.gmail.com:587 \
--auth LOGIN \
--auth-user admin@example.com \
--auth-password "your_password" \
--tls
Connection timeout
Log shows:
connect to smtp.gmail.com[...]:587: Connection timed out
Causes:
- Firewall blocking outbound SMTP
- ISP blocking port 587
- Network connectivity issue
Solutions:
# Test connectivity
telnet smtp.gmail.com 587
nc -zv smtp.gmail.com 587
# Check firewall rules
iptables -L OUTPUT
TLS handshake failed
Log shows:
TLS library problem
Causes:
- Outdated CA certificates
- TLS version mismatch
Solutions:
# Update CA certificates
apt update && apt install ca-certificates # Debian/Ubuntu
pkg install ca_root_nss # FreeBSD
Emails in queue
Check queue:
mailq
Flush queue (retry delivery):
postqueue -f
Delete stuck messages:
# Delete all in queue
postsuper -d ALL
# Delete specific message
postsuper -d <queue-id>
Wrong sender name
Issue: Email shows wrong From name
Check header checks:
cat /etc/postfix/smtp_header_checks
Test regex:
# Create test file
echo "From: centreon-engine@localhost" > /tmp/test
# Test header_checks
postmap -q "From: centreon-engine@localhost" pcre:/etc/postfix/smtp_header_checks
Reload Postfix:
postfix reload
Tags
This role does not define any tags. Use playbook-level tags if needed:
- hosts: all
roles:
- postfix
tags:
- postfix
- mail
- smtp
- notifications
Notes
- Role runs on target system (not localhost)
become: truerequired for package installation and config- Postfix service enabled and started automatically
- SASL password file hashed with
postmap - Header checks use PCRE regex (requires postfix-pcre)
- Configuration applied with Postfix restart
- Compatible with Debian/Ubuntu and FreeBSD
Best Practices
- Use Vault: Always store passwords in Ansible Vault
- App Passwords: Use app passwords for Gmail (not main password)
- TLS Required: Always enable TLS for authentication security
- Test First: Test relay credentials manually before deploying
- Monitor Logs: Watch mail logs after deployment
- Queue Monitoring: Set up alerts for stuck mail queue
- Rate Limits: Be aware of relay provider limits
- SPF/DKIM: Configure DNS records for better deliverability
- Backup Config: Store Postfix config in version control
- Regular Updates: Keep Postfix and SASL libraries updated
Related Roles
This role is often used with:
- deploy_centreon: Centreon monitoring (needs email alerts)
- rsyslog_configuration: System logging
- System services: Any service sending notifications
License
MIT
Author
Created for homelab infrastructure management.