Ansible, Postfix, and Mailgun

by
Annika Backstrom
in Technology, on 20 March 2018. It is tagged #ansible, #smtp, #mailgun, #postfix, and #Linux.

Here's a simple set of Ansible files that allow your hosts to send mail through Mailgun via Postfix. I've incorporated this setup into my common.yml playbook that acts as a baseline configuration for all my hosts (handling timezone setup, hostname normalization, etc.). With this addition, all my hosts can send email, regardless of their network.

This setup aggressively rewrites any outgoing From: addresses. This may not suit your needs, but it's fine for me.

roles/common/tasks/smtp.yml, included from roles/common/tasks/main.yml:

- name: install mail packages
  apt: pkg={{item}}
  with_items:
    - postfix
    - mailutils
    - libsasl2-modules

- name: install postfix config files
  template: src={{item.src}} dest={{item.dest}} owner=root group=root mode=0644
  notify: restart postfix
  with_items:
    - { src: header_check, dest: /etc/postfix/header_check }
    - { src: main.cf, dest: /etc/postfix/main.cf }
    - { src: mailname, dest: /etc/mailname }
    - { src: sender_canonical_maps, dest: /etc/postfix/sender_canonical_maps }

- name: enable postfix service
  service: name=postfix enabled=yes

roles/common/defaults/main.cf, default variables:

---
# 2525 is allowed on Google Cloud Platform
mailgun_port: 2525
mailgun_domain: put.your.hostname.here.example.com
mailgun_from_address: somebody@{{mailgun_domain}}
mailgun_from: "\"{{inventory_hostname_short}}\" <{{mailgun_from_address}}>"

# smtp login credentials
mailgun_login: postmaster@{{mailgun_domain}}
mailgun_api_key: put-your-api-key-here

roles/common/templates/main.cf:

# {{ansible_managed}}

# See /usr/share/postfix/main.cf.dist for a commented, more complete version

# Debian specific:  Specifying a file name will cause the first
# line of that file to be used as the name.  The Debian default
# is /etc/mailname.
myorigin = /etc/mailname

smtpd_banner = $myhostname ESMTP $mail_name (Ubuntu)
biff = no

# appending .domain is the MUA's job.
append_dot_mydomain = no

# Uncomment the next line to generate "delayed mail" warnings
#delay_warning_time = 4h

readme_directory = no

#
# mailgun configuration
# https://documentation.mailgun.com/en/latest/user_manual.html#smtp-relay
#

mydestination = localhost.localdomain, localhost
relayhost = [smtp.mailgun.org]:{{mailgun_port}}
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = static:{{mailgun_login}}:{{mailgun_api_key}}
smtp_sasl_security_options = noanonymous

# TLS support
smtp_tls_security_level = may
smtpd_tls_security_level = may
smtp_tls_note_starttls_offer = yes

smtpd_tls_key_file = /etc/ssl/private/smtpd.key
smtpd_tls_cert_file = /etc/ssl/certs/smtpd.crt
smtpd_tls_CApath = /etc/ssl/certs

sender_canonical_classes = envelope_sender, header_sender
sender_canonical_maps =  regexp:/etc/postfix/sender_canonical_maps
smtp_header_checks = regexp:/etc/postfix/header_check

roles/common/templates/mailname, appended to bare addresses. Probably not needed due to other rewrites, but I include it for completeness. See also: EtcMailName.

{{mailgun_domain}}

roles/common/templates/header_check:

# {{ansible_managed}}
/From:.*/ REPLACE From: {{mailgun_from}}

roles/common/templates/sender_canonical_maps:

# {{ansible_managed}}
/.+/    {{mailgun_from_address}}