diff --git a/README.md b/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..1257657d9e1fd0e7989f748b7dad1737afc3f004
--- /dev/null
+++ b/README.md
@@ -0,0 +1,106 @@
+Postfix
+=========
+
+This role installs Postfix and allows basic configuration.
+
+Requirements
+------------
+
+This role requires Ansible 2.4 or higher.
+
+Role Variables
+--------------
+
+| Variable | Default | Purpose |
+|----------|---------|---------|
+| postfix__recommended_packages | `[]` | Additional packages to install. These packages will have default configuration. |
+| postfix__mailname | `{{ ansible_fqdn }}` | The name of the mail system. |
+| postfix__tables | empty | Dictionaries used to build lookup tables. [Details below.](#postfix__tables) |
+| postfix__main_cf | `{}` | Used to modify or add lines in the main.cf file. [Details below.](#postfix__main_cf) |
+
+### `postfix__tables`
+
+This dictionary contains nested dictionaries that are used to build the lookup
+tables with the corresponding name. So `postfix__tables.transport` is used to
+build the transport lookup table, `postfix__tables.sasl_passwd` is used to build
+the SASL password map table, etc. Within each dictionary the 'key' is the lookup
+pattern and the 'value' is the returned value.
+
+For example, this `postfix__tables.transport` dictionary:
+
+```yaml
+postfix__tables:
+ transport:
+ 'internal.domain.tld': ':'
+ '*': 'discard:'
+```
+
+would result in the following transport table:
+
+```
+internal.domain.tld :
+* discard:
+```
+
+This role currently only supports the transport lookup table and the SASL lookup
+table. More information on the transport table format can be found
+[here][transport-docs] and more information on the SASL passwords lookup table
+format can be found [here.][sasl-passwd-docs]
+
+### `postfix__main_cf`
+
+This dictionary is used to add or modify lines in the main.cf file. Each key
+corresponds to a parameter in main.cf, and the value is what the parameter
+should be set to. If the parameter already exists in the file, then that line
+will be replaced. Otherwise, a new line will be added at the end of the file.
+
+This dictionary is merged with the internal `postfix__main_cf_default`
+dictionary which defines some reasonable defaults, such as enabling
+opportunistic TLS for the SMTP client. All keys in `postfix__main_cf_default`
+can be overridden in `postfix__main_cf`.
+
+Example Playbooks
+----------------
+
+This example configures Postfix to accept mail on the loopback interface and
+relay it to Mailgun's SMTP servers. It also uses SASL + TLS to authenticate with
+Mailgun.
+
+```yaml
+- hosts: servers
+ tasks:
+ - include_role:
+ name: postfix
+ vars:
+ postfix__main_cf:
+ inet_interfaces: loopback-only
+ relayhost: '[smtp.mailgun.org]:587'
+ smtp_sasl_auth_enable: 'yes'
+ smtp_tls_security_level: encrypt
+ smtp_sasl_tls_security_options: noanonymous
+ postfix__tables:
+ sasl_passwd:
+ '[smtp.mailgun.org]:587': 'USERNAME:PASSWORD'
+```
+
+Another common configuration when doing development is to filter all mail so
+that only mail sent to your internal domain is actually sent. All other mail
+will be dropped silently to prevent accidentally sending emails when developing
+against real data. You can do that using transport maps
+
+```yaml
+- hosts: servers
+ tasks:
+ - include_role:
+ name: postfix
+ vars:
+ postfix__tables:
+ transport:
+ 'internal.domain.tld': ':'
+ '*': 'discard:'
+```
+
+
+
+[transport-docs]: http://www.postfix.org/transport.5.html
+[sasl-passwd-docs]: http://www.postfix.org/SASL_README.html#client_sasl_sender
diff --git a/defaults/main.yml b/defaults/main.yml
index bc24882e0a964078cf450bd5b35d50bfe1a4dc54..bab552218f48cff784de2044ca903370e2c2e32e 100644
--- a/defaults/main.yml
+++ b/defaults/main.yml
@@ -1,71 +1,43 @@
---
+# Additional packages to install. These packages won't be configured.
+postfix__recommended_packages: []
+
# The name of this mail system, set in '/etc/mailname'
postfix__mailname: '{{ ansible_fqdn }}'
-
-
-# Default variables for the main.cf template. These are always included.
-postfix__myorigin:
-postfix__smtpd_banner: '$myhostname ESMTP $mail_name ({{ ansible_distribution }})'
-postfix__biff: no
-postfix__append_dot_mydomain: no
-postfix__generate_delayed_mail_warnings: no
-postfix__delay_warning_time: 4h
-postfix__readme_directory: no
-postfix__smtpd_tls_cert_file: /etc/ssl/certs/ssl-cert-snakeoil.pem
-postfix__smtpd_tls_key_file: /etc/ssl/private/ssl-cert-snakeoil.key
-postfix__smtpd_use_tls: yes
-postfix__smtpd_tls_session_cache_database: 'btree:${data_directory}/smtpd_scache'
-postfix__smtp_tls_session_cache_database: 'btree:${data_directory}/smtp_scache'
-postfix__smtpd_relay_restrictions: permit_mynetworks permit_sasl_authenticated defer_unauth_destination
-postfix__myhostname: '{{ ansible_hostname | d() }}'
-postfix__alias_maps: 'hash:/etc/aliases'
-postfix__alias_database: 'hash:/etc/aliases'
-postfix__mydestination: '$myhostname, localhost.localdomain, localhost'
-postfix__relayhost:
-postfix__mynetworks: '127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128'
-postfix__mailbox_size_limit: 0
-postfix__recipient_delimiter: '+'
-postfix__inet_interfaces: all
-postfix__inet_protocols: all
-
-
-
-# Transport map
-# <pattern> is an email address, domain name, or * to lookup the mail recipient
-# <result> specificies how and where to deliver mail and has the format
-# <transport>:<nexthop>. Both <transport> and <nexthop> are optional,
-# but the delimiting ':' is required.
-#
-# EXAMPLES:
-#
-# This configuration will pass mail for the domain 'internal.domain.com' without
-# modifying it, while discard all mail addressed to other recipient domains.
+# These dictionaries build the lookup tables with the corresponding name. So
+# postfix__tables.transport is used to build the transport lookup table,
+# postfix__tables.sasl_passwd is used to build the SASL password map table, etc.
+# Within each dictionary the 'key' is the lookup pattern and the 'value' is the
+# returned value.
#
-# postfix__transport_map:
-# - { pattern: 'internal.domain.com', result: ':' }
-# - { pattern: '*', result: 'discard:' }
+# For example, this postfix__tables.transport example:
#
+# postfix__tables:
+# transport:
+# 'codingallnight.com': ':'
+# '*': 'discard:'
#
-# This configuration will discard mail sent to localhost and will relay all
-# other mail through Mailgun.
+# would result in the following transport table:
#
-# postfix__transport_map:
-# - { pattern: 'localhost', result: 'discard:' }
-# - { pattern: 'localhost.localdomain', result: 'discard:' }
-# - { pattern: '*', result: 'relay:[smtp.mailgun.org]:587' }
+# codingallnight.com :
+# * discard:
#
-#
-# Valid <transport> and <nexthop> values are described in the postfix transport
-# documentation. http://www.postfix.org/transport.5.html
-postfix__transport_map: []
-
+postfix__tables:
+ sasl_passwd: {}
+ transport: {}
+# This dictionary is used to add or modify lines in the main.cf file. Each key
+# corresponds to a parameter in main.cf, and the value is what the parameter
+# should be set to. If the parameter already exists in the file, then that line
+# will be replaced. Otherwise, a new line will be added at the end of the file.
+postfix__main_cf: {}
+# This dictionary holds the default configuration for main.cf and all of its
+# keys can overridden in the postfix__main_cf dictionary.
+postfix__main_cf_default:
+ smtp_tls_security_level: may
+ smtp_sasl_password_maps: 'hash:/etc/postfix/sasl_passwd'
+ transport_maps: 'hash:/etc/postfix/transport'
-# SASL Password Maps
-postfix__smtp_sasl_password_map: []
-postfix__smtp_sasl_auth_enable: yes
-postfix__smtp_sasl_security_options: noanonymous
-postfix__smtp_sasl_tls_security_options: '{{ postfix__smtp_sasl_security_options }}'
...
# vi: set ts=2 sts=2 sw=2 et ft=yaml:
diff --git a/tasks/install-postfix_debian.yml b/tasks/install-postfix.debian.yml
similarity index 83%
rename from tasks/install-postfix_debian.yml
rename to tasks/install-postfix.debian.yml
index 446670bf991ffd93c6f0c81fad9b4bb5027cd00b..582b8b7a26f566678ba1d7e7bcf20f5a167bad56 100644
--- a/tasks/install-postfix_debian.yml
+++ b/tasks/install-postfix.debian.yml
@@ -13,9 +13,6 @@
name: '{{ item }}'
state: present
cache_valid_time: 3600
- with_items:
- - postfix
- - mailutils
- - make
+ with_items: '{{["postfix", "make"] + postfix__recommended_packages }}'
...
# vi: set ts=2 sts=2 sw=2 et ft=yaml:
diff --git a/tasks/install-postfix.fedora.yml b/tasks/install-postfix.fedora.yml
new file mode 100644
index 0000000000000000000000000000000000000000..5a4b301918dee476c801b555799ccbafcb6d6a65
--- /dev/null
+++ b/tasks/install-postfix.fedora.yml
@@ -0,0 +1,8 @@
+---
+- name: Install Postfix and related packages
+ dnf:
+ name: '{{ item }}'
+ state: present
+ with_items: '{{["postfix", "make"] + postfix__recommended_packages }}'
+...
+# vi: set ts=2 sts=2 sw=2 et ft=yaml:
diff --git a/tasks/install-postfix.redhat.yml b/tasks/install-postfix.redhat.yml
new file mode 100644
index 0000000000000000000000000000000000000000..580fcb30bdc213a0095bb185c409cff37d1c6242
--- /dev/null
+++ b/tasks/install-postfix.redhat.yml
@@ -0,0 +1,9 @@
+---
+- name: Install Postfix and related packages
+ yum:
+ name: '{{ item }}'
+ state: present
+ update_cache: yes
+ with_items: '{{["postfix", "make"] + postfix__recommended_packages }}'
+...
+# vi: set ts=2 sts=2 sw=2 et ft=yaml:
diff --git a/tasks/main.yml b/tasks/main.yml
index 11d98561884a4539610f12d3726cb76a19b8a213..af289dbbc900c64973fb1d0fad022d40aadb02ed 100644
--- a/tasks/main.yml
+++ b/tasks/main.yml
@@ -1,22 +1,25 @@
---
-- include_tasks: '{{ task_file }}'
+- name: Include OS-specific variables
+ include_vars: '{{ var_file }}'
loop_control:
- loop_var: task_file
+ loop_var: var_file
with_first_found:
- - tasks/install-postfix_{{ ansible_distribution | lower }}-{{ ansible_distribution_version | lower }}.yml
- - tasks/install-postfix_{{ ansible_distribution | lower }}-{{ ansible_distribution_release | lower }}.yml
- - tasks/install-postfix_{{ ansible_distribution | lower }}-{{ ansible_distribution_major_version | lower }}.yml
- - tasks/install-postfix_{{ ansible_distribution | lower }}.yml
- - tasks/install-postfix_{{ ansible_os_family | lower }}.yml
+ - vars/{{ ansible_distribution | lower }}-{{ ansible_distribution_version | lower }}.yml
+ - vars/{{ ansible_distribution | lower }}-{{ ansible_distribution_release | lower }}.yml
+ - vars/{{ ansible_distribution | lower }}-{{ ansible_distribution_major_version | lower }}.yml
+ - vars/{{ ansible_distribution | lower }}.yml
+ - vars/{{ ansible_os_family | lower }}.yml
-- name: Generate Postfix 'main.cf' configuration
- template:
- src: templates/main.cf.j2
- dest: /etc/postfix/main.cf
- owner: root
- group: root
- mode: 0644
- notify: ['reload postfix']
+- name: Include OS-specific tasks
+ include_tasks: '{{ task_file }}'
+ loop_control:
+ loop_var: task_file
+ with_first_found:
+ - tasks/install-postfix.{{ ansible_distribution | lower }}-{{ ansible_distribution_version | lower }}.yml
+ - tasks/install-postfix.{{ ansible_distribution | lower }}-{{ ansible_distribution_release | lower }}.yml
+ - tasks/install-postfix.{{ ansible_distribution | lower }}-{{ ansible_distribution_major_version | lower }}.yml
+ - tasks/install-postfix.{{ ansible_distribution | lower }}.yml
+ - tasks/install-postfix.{{ ansible_os_family | lower }}.yml
- name: Place the Postfix makefile
template:
@@ -26,24 +29,50 @@
group: root
mode: 0644
-- name: Generate Postfix sasl_passwd map
+- name: Create the SASL password lookup table
template:
- src: templates/sasl_passwd.in.j2
+ src: lookup_table.j2
dest: /etc/postfix/sasl_passwd.in
owner: root
group: root
mode: 0600
- when: postfix__smtp_sasl_password_map
+ vars:
+ table: '{{ postfix__tables.sasl_passwd }}'
notify: ['make postfix sasl_passwd.db']
-- name: Generate Postfix transport map
+- name: Create the transport lookup table
template:
- src: templates/transport.in.j2
+ src: lookup_table.j2
dest: /etc/postfix/transport.in
owner: root
group: root
mode: 0644
- when: postfix__transport_map
+ vars:
+ table: '{{ postfix__tables.transport }}'
notify: ['make postfix transport.db']
+
+- name: Mark the 'main.cf' file as being managed by Ansible
+ lineinfile:
+ path: /etc/postfix/main.cf
+ insertbefore: BOF
+ state: present
+ line: "# This file is managed by Ansible, changes will be overwritten\n"
+ regexp: '^# This file is managed by Ansible'
+
+- name: Merge the main_cf dictionaries
+ set_fact:
+ __postfix__main_cf_merged: '{{ postfix__main_cf_default | combine(postfix__main_cf, recursive=True) }}'
+
+#- debug:
+# var: __postfix__main_cf_merged
+
+- name: Configure the Postfix 'main.cf' file
+ lineinfile:
+ path: /etc/postfix/main.cf
+ line: '{{ item.key }} = {{ item.value }}'
+ regexp: '^\s*{{ item.key }}\s*='
+ state: present
+ with_dict: '{{ __postfix__main_cf_merged }}'
+ notify: ['reload postfix']
...
# vi: set ts=2 sts=2 sw=2 et ft=yaml:
diff --git a/templates/lookup_table.j2 b/templates/lookup_table.j2
new file mode 100644
index 0000000000000000000000000000000000000000..43d3c79bed5d2fc0d9cc3cb6b47deaf90d2823c2
--- /dev/null
+++ b/templates/lookup_table.j2
@@ -0,0 +1,5 @@
+# {{ ansible_managed }}
+
+{% for key, value in table.items() %}
+{{ key }} {{ value }}
+{% endfor %}
diff --git a/templates/sasl_passwd.in.j2 b/templates/sasl_passwd.in.j2
deleted file mode 100644
index eb2cb968464c6e09a33297c6fafabe61d48914c5..0000000000000000000000000000000000000000
--- a/templates/sasl_passwd.in.j2
+++ /dev/null
@@ -1,5 +0,0 @@
-# {{ ansible_managed }}
-
-{% for item in postfix__smtp_sasl_password_map %}
-{{ item.lookup }} {{ item.credentials }}
-{% endfor %}
diff --git a/templates/transport.in.j2 b/templates/transport.in.j2
deleted file mode 100644
index 715d8a2fcaa0aaa01b7464bf195e36c9c345baa7..0000000000000000000000000000000000000000
--- a/templates/transport.in.j2
+++ /dev/null
@@ -1,5 +0,0 @@
-# {{ ansible_managed }}
-
-{% for item in postfix__transport_map %}
-{{ item.pattern }} {{ item.result }}
-{% endfor %}
diff --git a/vars/debian.yml b/vars/debian.yml
new file mode 100644
index 0000000000000000000000000000000000000000..5589c1cfce337b9aa6f7d11b79d750349f5e66d0
--- /dev/null
+++ b/vars/debian.yml
@@ -0,0 +1,8 @@
+---
+# This file overrides variables set in defaults/main.yml.
+# Full documentation of the variables is available in the file.
+
+postfix__recommended_packages:
+ - mailutils
+...
+# vi: set ts=2 sts=2 sw=2 et ft=yaml:
diff --git a/vars/redhat.yml b/vars/redhat.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e64316c11f357dafaa2a624dc21710d02611e278
--- /dev/null
+++ b/vars/redhat.yml
@@ -0,0 +1,9 @@
+---
+# This file overrides variables set in defaults/main.yml.
+# Full documentation of the variables is available in the file.
+
+postfix__recommended_packages:
+ - cyrus-sasl-plain # Most third-party mail servers support PLAIN auth
+ - mailx
+...
+# vi: set ts=2 sts=2 sw=2 et ft=yaml: