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: