diff --git a/action_plugins/postconf.py b/action_plugins/postconf.py index 357e100..5a7b9dd 100755 --- a/action_plugins/postconf.py +++ b/action_plugins/postconf.py @@ -22,6 +22,7 @@ import re from ansible.plugins.action import ActionBase from ansible.utils.vars import merge_hash from ansible.module_utils._text import to_text +from six import string_types class PfWorkflowError(Exception): pass @@ -29,7 +30,7 @@ class PfWorkflowError(Exception): class ActionModule(ActionBase): def getarg(self, key, default=True): - if isinstance(default, basestring): + if isinstance(default, string_types): if key == 'parameter': return self._task.args.get(key, default) return '{}'.format(self._task.args.get(key, default)) @@ -234,7 +235,7 @@ class ActionModule(ActionBase): ''' # value should be string - if not isinstance(value, basestring): + if not isinstance(value, string_types): try: value = ', '.join(value) except TypeError: @@ -341,7 +342,7 @@ class ActionModule(ActionBase): state = self.getarg('state') if parameter: # build (parameter, value) pairs for iteration - if isinstance(parameter, basestring): + if isinstance(parameter, string_types): pv = [ (parameter, self.getarg('value')) ] elif isinstance(parameter, dict): # if dict ignore new_value diff --git a/defaults/main.yml b/defaults/main.yml index 5d554d5..e1cec18 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -1,10 +1,6 @@ --- - -# Postfix config directory (changing this is probably a bad idea) -postfix_config_dir: "/etc/postfix" - -# Where to save access lists -postfix_rules_dir: "{{ postfix_config_dir }}/rules" +# directorio con reglas +postfix_rules_dir: /etc/postfix/rules # accepted email domains postfix_mail_domains: "{{ mail_domains | default(['example.com']) }}" @@ -13,7 +9,7 @@ postfix_mail_domains: "{{ mail_domains | default(['example.com']) }}" postfix_server_domain: "{{ postfix_mail_domains | first }}" # postfix server identification -postfix_server_name: "mail.{{ postfix_server_domain }}" +postfix_server_name: mail.{{ postfix_server_domain }} # domains considered as "local" unix domains (local server users) # ansible_fqdn, localhost.localdomain, and localhost are always added regardless of this value @@ -29,14 +25,14 @@ postfix_domain_config: {} # LDAP ------------------------------------------------------------------------ # Default LDAP connection parameters -postfix_ldap_server: "localhost" -postfix_ldap_port: 389 -postfix_ldap_version: 3 -postfix_ldap_scope: "sub" -postfix_ldap_bind: no -postfix_ldap_bind_dn: '' -postfix_ldap_bind_pw: '' -postfix_ldap_start_tls: no +postfix_ldap_server: "{{ ldap_server | default('localhost') }}" +postfix_ldap_port: "{{ ldap_port | default(389) }}" +postfix_ldap_version: "{{ ldap_version | default(3) }}" +postfix_ldap_scope: "{{ ldap_search_scope | default('sub') }}" +postfix_ldap_bind: "{{ ldap_bind | default(False) }}" +postfix_ldap_bind_dn: "{{ ldap_bind_dn | default('') }}" +postfix_ldap_bind_pw: "{{ ldap_bind_password | default('') }}" +postfix_ldap_start_tls: "{{ ldap_starttls | default(False) }}" postfix_ldap_tls_ca_cert_dir: '' postfix_ldap_tls_ca_cert_file: '' postfix_ldap_use_group_alias: yes diff --git a/files/11-postfix.conf b/files/11-postfix.conf new file mode 100644 index 0000000..40fd6b7 --- /dev/null +++ b/files/11-postfix.conf @@ -0,0 +1,17 @@ +# LMTP delivery service for Postfix +service lmtp { + unix_listener /var/spool/postfix/private/dovecot-lmtp { + mode = 0600 + group = postfix + user = postfix + } +} + +# Authentication service for Postfix +service auth { + unix_listener /var/spool/postfix/private/auth { + mode = 0660 + user = postfix + group = postfix + } +} diff --git a/tasks/lookup_tables.yml b/tasks/lookup_tables.yml new file mode 100644 index 0000000..8aec000 --- /dev/null +++ b/tasks/lookup_tables.yml @@ -0,0 +1,59 @@ +--- +- name: Template LDAP lookup tables + template: + src: ldap_table.cf.j2 + dest: /etc/postfix/{{ domain }}_ldap_{{ item }}.cf" + when: + - postfix_domain_config[domain][item+'_lookup'].provider|default(postfix_lookup_provider) == 'ldap' + loop: + - user + - alias + - group + notify: reload postfix + +- name: Template SQLite lookup tables + template: + src: sqlite_table.cf.j2 + dest: /etc/postfix/{{ domain }}_sqlite_{{ item }}.cf + when: + - postfix_domain_config[domain][item+'_lookup'].provider|default(postfix_lookup_provider) == 'sqlite' + loop: + - user + - alias + notify: reload postfix + +- name: Template file user lookup table + blockinfile: + block: | + {% for item in postfix_domain_config[domain]['users']|default([]) -%} + {% if item is string %}{{ item }} /nomailbox/{{ item }} + {% else %}{{ item.user }} {{ item.mailbox }} + {% endif %}{% endfor %} + dest: "{{ postfix_domain_config[domain].user_lookup.file|default(vmail_home+'/'+domain+'_users') }}" + marker: "# {mark} ANSIBLE-MANAGED USERS" + create: yes + when: + - postfix_domain_config[domain].user_lookup.provider|default(postfix_lookup_provider) == 'file' + notify: postmap hash users + +- name: Template file alias lookup table + blockinfile: + block: | + {% for key in postfix_domain_config[domain]['aliases']|default([]) -%} + {{ key.alias }} {{ key.dest }} + {% endfor %} + dest: "{{ postfix_domain_config[domain].user_lookup.file|default(vmail_home+'/'+domain+'_aliases') }}" + marker: "# {mark} ANSIBLE-MANAGED ALIASES" + create: yes + when: + - postfix_domain_config[domain].alias_lookup.provider|default(postfix_lookup_provider) == 'file' + notify: postmap hash aliases + +- name: Template no-reply aliases file + copy: + content: | + {% for address in postfix_domain_config[domain].noreply_aliases|default(['noreply']) %} + {{ address }}@domain _dev_null + {% endfor %} + dest: "{{ postfix_domain_config[domain].noreply_file|default(vmail_home+'/'+domain+'_noreply') }}" + notify: postmap no reply aliases diff --git a/tasks/main.yml b/tasks/main.yml index c875ad7..0f97c32 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -1,123 +1,118 @@ --- -- name: "Load default config for domains" - set_fact: - dc: "{{ dc|default({})|combine( { item: { - 'user_lookup': { - 'provider': 'file', - 'file': vmail_home +'/'+item+'_users', - 'domain': item, - 'server_host': postfix_ldap_server, - 'server_port': postfix_ldap_port, - 'version': postfix_ldap_version, - 'scope': postfix_ldap_scope, - 'bind': postfix_ldap_bind, - 'bind_dn': postfix_ldap_bind_dn, - 'bind_pw': postfix_ldap_bind_pw, - 'start_tls': postfix_ldap_start_tls, - 'tls_ca_cert_file': postfix_ldap_tls_ca_cert_file, - 'tls_ca_cert_dir': postfix_ldap_tls_ca_cert_dir, - 'search_base': - 'ou=People,'+item.split('.')|map('regex_replace','^','dc=')|join(','), - 'query_filter': '(&(objectClass=inetOrgPerson)(uid=%u))', - 'result_attribute': 'uid', - 'result_format': vmail_home+'/mail/'+item+'/%s/', - 'dbpath': vmail_home+'/'+item+'_users.sqlite', - 'query': postfix_sqlite_user_query - }, - 'users': [], - 'alias_lookup': { - 'provider': 'file', - 'file': vmail_home +'/'+item+'_aliases', - 'domain': item, - 'server_host': postfix_ldap_server, - 'server_port': postfix_ldap_port, - 'version': postfix_ldap_version, - 'scope': postfix_ldap_scope, - 'bind': postfix_ldap_bind, - 'bind_dn': postfix_ldap_bind_dn, - 'bind_pw': postfix_ldap_bind_pw, - 'start_tls': postfix_ldap_start_tls, - 'tls_ca_cert_file': postfix_ldap_tls_ca_cert_file, - 'tls_ca_cert_dir': postfix_ldap_tls_ca_cert_dir, - 'search_base': - 'ou=Alias,'+item.split('.')|map('regex_replace','^','dc=')|join(','), - 'query_filter': '(&(objectClass=nisMailAlias)(cn=%u))', - 'result_attribute': 'rfc822MailMember', - 'result_format': '%s', - 'dbpath': vmail_home+'/'+item+'_aliases.sqlite', - 'query': postfix_sqlite_alias_query - }, - 'aliases': [], - 'use_group_as_alias': postfix_ldap_use_group_alias, - 'group_lookup': { - 'provider': 'ldap', - 'domain': item, - 'server_host': postfix_ldap_server, - 'server_port': postfix_ldap_port, - 'version': postfix_ldap_version, - 'scope': postfix_ldap_scope, - 'bind': postfix_ldap_bind, - 'bind_dn': postfix_ldap_bind_dn, - 'bind_pw': postfix_ldap_bind_pw, - 'start_tls': postfix_ldap_start_tls, - 'tls_ca_cert_file': postfix_ldap_tls_ca_cert_file, - 'tls_ca_cert_dir': postfix_ldap_tls_ca_cert_dir, - 'search_base': - 'ou=Group,'+item.split('.')|map('regex_replace','^','dc=')|join(','), - 'query_filter': '(&(objectClass=posixGroup)(cn=%u))', - 'result_attribute': 'memberUid', - 'result_format': '%s@{{d}}', - }, - 'noreply_aliases': [ 'noreply' ], - 'noreply_file': vmail_home +'/'+item+'_noreply', - } }, recursive=True) }}" - with_items: "{{ postfix_mail_domains|belist }}" +# - name: "Load default config for domains" +# set_fact: +# dc: "{{ dc|default({})|combine( { item: { +# 'user_lookup': { +# 'provider': 'file', +# 'file': vmail_home +'/'+item+'_users', +# 'domain': item, +# 'server_host': postfix_ldap_server, +# 'server_port': postfix_ldap_port, +# 'version': postfix_ldap_version, +# 'scope': postfix_ldap_scope, +# 'bind': postfix_ldap_bind, +# 'bind_dn': postfix_ldap_bind_dn, +# 'bind_pw': postfix_ldap_bind_pw, +# 'start_tls': postfix_ldap_start_tls, +# 'tls_ca_cert_file': postfix_ldap_tls_ca_cert_file, +# 'tls_ca_cert_dir': postfix_ldap_tls_ca_cert_dir, +# 'search_base': +# 'ou=People,'+item.split('.')|map('regex_replace','^','dc=')|join(','), +# 'query_filter': '(&(objectClass=inetOrgPerson)(uid=%u))', +# 'result_attribute': 'uid', +# 'result_format': vmail_home+'/mail/'+item+'/%s/', +# 'dbpath': vmail_home+'/'+item+'_users.sqlite', +# 'query': postfix_sqlite_user_query +# }, +# 'users': [], +# 'alias_lookup': { +# 'provider': 'file', +# 'file': vmail_home +'/'+item+'_aliases', +# 'domain': item, +# 'server_host': postfix_ldap_server, +# 'server_port': postfix_ldap_port, +# 'version': postfix_ldap_version, +# 'scope': postfix_ldap_scope, +# 'bind': postfix_ldap_bind, +# 'bind_dn': postfix_ldap_bind_dn, +# 'bind_pw': postfix_ldap_bind_pw, +# 'start_tls': postfix_ldap_start_tls, +# 'tls_ca_cert_file': postfix_ldap_tls_ca_cert_file, +# 'tls_ca_cert_dir': postfix_ldap_tls_ca_cert_dir, +# 'search_base': +# 'ou=Alias,'+item.split('.')|map('regex_replace','^','dc=')|join(','), +# 'query_filter': '(&(objectClass=nisMailAlias)(cn=%u))', +# 'result_attribute': 'rfc822MailMember', +# 'result_format': '%s', +# 'dbpath': vmail_home+'/'+item+'_aliases.sqlite', +# 'query': postfix_sqlite_alias_query +# }, +# 'aliases': [], +# 'use_group_as_alias': postfix_ldap_use_group_alias, +# 'group_lookup': { +# 'provider': 'ldap', +# 'domain': item, +# 'server_host': postfix_ldap_server, +# 'server_port': postfix_ldap_port, +# 'version': postfix_ldap_version, +# 'scope': postfix_ldap_scope, +# 'bind': postfix_ldap_bind, +# 'bind_dn': postfix_ldap_bind_dn, +# 'bind_pw': postfix_ldap_bind_pw, +# 'start_tls': postfix_ldap_start_tls, +# 'tls_ca_cert_file': postfix_ldap_tls_ca_cert_file, +# 'tls_ca_cert_dir': postfix_ldap_tls_ca_cert_dir, +# 'search_base': +# 'ou=Group,'+item.split('.')|map('regex_replace','^','dc=')|join(','), +# 'query_filter': '(&(objectClass=posixGroup)(cn=%u))', +# 'result_attribute': 'memberUid', +# 'result_format': '%s@{{d}}', +# }, +# 'noreply_aliases': [ 'noreply' ], +# 'noreply_file': vmail_home +'/'+item+'_noreply', +# } }, recursive=True) }}" +# with_items: "{{ postfix_mail_domains }}" -- name: "Override config for domains" - set_fact: - dc: '{{ dc | combine(postfix_domain_config, recursive=True) }}' +# - name: "Override config for domains" +# set_fact: +# dc: '{{ dc | combine(postfix_domain_config, recursive=True) }}' -- apt: name=postfix update_cache=yes -- apt: name=postfix-pcre +- name: Instalar Postfix + apt: + name: + - postfix + - postfix-pcre + - postfix-ldap + - postfix-sqlite + state: present notify: restart postfix -- apt: name=postfix-ldap - when: - # see http://jmespath.org/ - - '"ldap" in dc|json_query("*.[ alias_lookup, user_lookup ][].provider")' - notify: restart postfix -- apt: name=postfix-sqlite - when: - # see http://jmespath.org/ - - '"sqlite" in dc|json_query("*.[ alias_lookup, user_lookup ][].provider")' - notify: restart postfix - -- name: "Template Dovecot delivery/auth service config for Postfix" - template: - src: 11-postfix.conf.j2 +- name: Servicio delivery+auth mediante Dovecot + copy: + src: 11-postfix.conf dest: /etc/dovecot/conf.d/11-postfix.conf notify: restart dovecot -- name: "Configure lookup tables" +- name: Configurar lookup tables include_tasks: lookup_tables.yml - with_items: "{{postfix_mail_domains|belist}}" + loop: "{{ postfix_mail_domains }}" loop_control: - loop_var: "domain" + loop_var: domain -- name: "Configure no-reply local mail alias" +- name: Alias local para usuario no-reply blockinfile: block: | _dev_null: /dev/null marker: "# {mark} ANSIBLE-MANAGED ALIASES" - path: "/etc/aliases" + path: /etc/aliases notify: newaliases -- name: "Create rules directory for access lists" +- name: Directorio de reglas para access lists file: name: "{{ postfix_rules_dir }}" state: directory -- name: "Template client access list" +- name: Template client access list blockinfile: path: "{{ postfix_rules_dir }}/client_access_list" create: yes @@ -127,7 +122,7 @@ {{ entry.regex }} {{ entry.action }} {% endfor %} -- name: "Template helo access list" +- name: Template helo access list blockinfile: path: "{{ postfix_rules_dir }}/helo_access_list" create: yes @@ -138,7 +133,7 @@ {% endfor %} notify: postmap access lists -- name: "Template recipient access list" +- name: Template recipient access list blockinfile: path: "{{ postfix_rules_dir }}/recipient_access_list" create: yes @@ -149,7 +144,7 @@ {% endfor %} notify: postmap access lists -- name: "Template sender access list" +- name: Template sender access list blockinfile: path: "{{ postfix_rules_dir }}/sender_access_list" create: yes @@ -160,61 +155,80 @@ {% endfor %} notify: postmap access lists -- name: "Set main.cf parameters" +- name: Configurar main.cf postconf: parameter: mydestination: >- - {{ postfix_unix_domains | belist | + {{ postfix_unix_domains | union( [ ansible_fqdn, 'localhost.localdomain', 'localhost'] ) | - difference( postfix_mail_domains|belist ) }} - myhostname: "{{ postfix_server_name }}" - mydomain: "{{ postfix_server_domain }}" + difference( postfix_mail_domains ) }} + myhostname: + "{{ postfix_server_name }}" + mydomain: + "{{ postfix_server_domain }}" mynetworks: >- {{ ['127.0.0.0/8', '[::ffff:127.0.0.0]/104', '[::1]/128'] | union( postfix_local_networks ) }} virtual_alias_maps: >- - {% for d in postfix_mail_domains|belist %} - {% set p = dc[d]['alias_lookup']['provider'] %} + {% for d in postfix_mail_domains %} + {% set p = postfix_domain_config[d].alias_lookup.provider|default(postfix_lookup_provider) %} {% if p == "ldap" %} - ldap:{{ postfix_config_dir }}/{{d}}_ldap_alias.cf - {% if dc[d]['use_group_as_alias'] %}, - ldap:{{ postfix_config_dir }}/{{d}}_ldap_group.cf + ldap:/etc/postfix/{{ d }}_ldap_alias.cf + {% if postfix_domain_config[d].alias_lookup.use_group_as_alias|default(postfix_ldap_use_group_alias) %}, + ldap:/etc/postfix/{{ d }}_ldap_group.cf {% endif %} {% elif p == "sqlite" %} - sqlite:{{ postfix_config_dir }}/{{d}}_sqlite_alias.cf + sqlite:/etc/postfix/{{d}}_sqlite_alias.cf {% elif p == "file" %} - hash:{{ vmail_home }}/{{d}}_aliases + hash:{{ vmail_home }}/{{ d }}_aliases {% endif %}{{ '' if loop.last else ',' }}{% endfor %}, - hash:{{ postfix_config_dir }}/noreply_aliases + hash:/etc/postfix/noreply_aliases virtual_mailbox_maps: >- - {% for d in postfix_mail_domains|belist %} - {% set p = dc[d]['user_lookup']['provider'] %} + {% for d in postfix_mail_domains %} + {% set p = postfix_domain_config[d].user_lookup.provider|default(postfix_lookup_provider) %} {% if p == "ldap" %} - ldap:{{ postfix_config_dir }}/{{d}}_ldap_user.cf + ldap:/etc/postfix/{{ d }}_ldap_user.cf {% elif p == "sqlite" %} - sqlite:{{ postfix_config_dir }}/{{d}}_sqlite_user.cf + sqlite:/etc/postfix/{{ d }}_sqlite_user.cf {% elif p == "file" %} - hash:{{ vmail_home }}/{{d}}_users + hash:{{ vmail_home }}/{{ d }}_users {% endif %}{{ '' if loop.last else ',' }}{% endfor %}, - virtual_transport: "lmtp:unix:private/dovecot-lmtp" - virtual_mailbox_domains: "{{ postfix_mail_domains }}" + virtual_transport: + lmtp:unix:private/dovecot-lmtp + virtual_mailbox_domains: + "{{ postfix_mail_domains }}" smtpd_sasl_path: private/auth smtpd_sasl_type: dovecot - smtpd_sasl_auth_enable: "{{ 'yes' if postfix_enable_smtpd_auth else 'no' }}" - smtpd_tls_cert_file: "{{ postfix_tls_certificate }}" - smtpd_tls_key_file: "{{ postfix_tls_private_key }}" - smtp_tls_security_level: "{{postfix_incoming_tls_security}}" - smtpd_tls_security_level: "{{postfix_outgoing_tls_security}}" - smtpd_tls_auth_only: "{{ 'yes' if postfix_allow_insecure_auth else 'no'}}" - smtpd_tls_session_cache_database: "{{ 'btree:${data_directory}/smtpd_scache' if postfix_tls_session_cache else '' }}" - smtpd_client_restrictions: "{{ postfix_client_restrictions }}" - smtpd_data_restrictions: "{{ postfix_data_restrictions }}" - smtpd_helo_restrictions: "{{ postfix_helo_restrictions }}" - smtpd_relay_restrictions: "{{ postfix_relay_restrictions }}" - smtpd_recipient_restrictions: "{{ postfix_recipient_restrictions }}" - message_size_limit: "{{ postfix_message_size_limit }}" - smtpd_helo_required: "{{ 'yes' if postfix_helo_required else 'no' }}" - biff: "{{ 'yes' if postfix_biff else 'no' }}" + smtpd_sasl_auth_enable: + "{{ 'yes' if postfix_enable_smtpd_auth else 'no' }}" + smtpd_tls_cert_file: + "{{ postfix_tls_certificate }}" + smtpd_tls_key_file: + "{{ postfix_tls_private_key }}" + smtp_tls_security_level: + "{{postfix_incoming_tls_security}}" + smtpd_tls_security_level: + "{{postfix_outgoing_tls_security}}" + smtpd_tls_auth_only: + "{{ 'yes' if postfix_allow_insecure_auth else 'no'}}" + smtpd_tls_session_cache_database: + "{{ 'btree:${data_directory}/smtpd_scache' if postfix_tls_session_cache else '' }}" + smtpd_client_restrictions: + "{{ postfix_client_restrictions }}" + smtpd_data_restrictions: + "{{ postfix_data_restrictions }}" + smtpd_helo_restrictions: + "{{ postfix_helo_restrictions }}" + smtpd_relay_restrictions: + "{{ postfix_relay_restrictions }}" + smtpd_recipient_restrictions: + "{{ postfix_recipient_restrictions }}" + message_size_limit: + "{{ postfix_message_size_limit }}" + smtpd_helo_required: + "{{ 'yes' if postfix_helo_required else 'no' }}" + biff: + "{{ 'yes' if postfix_biff else 'no' }}" notify: reload postfix - name: "Enable submission service"