From 69f21501fd8eb305bf73bfba7a3cf3d6ba59d08f Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Sun, 10 May 2020 16:55:16 +0200 Subject: [PATCH 1/6] Prepare postfix SASL bind support --- defaults/main.yml | 4 ++ files/postfix/postfix-krbhelper.service | 10 +++++ files/postfix/postfix-krbhelper.timer | 8 ++++ tasks/main.yml | 57 +++++++++++++++++++++++++ templates/postfix/chroot-helper.sh.j2 | 15 +++++++ templates/postfix/dropin.conf.j2 | 11 +++++ templates/postfix/krb-helper.sh.j2 | 18 ++++++++ templates/postfix/main.cf.j2 | 1 + 8 files changed, 124 insertions(+) create mode 100644 files/postfix/postfix-krbhelper.service create mode 100644 files/postfix/postfix-krbhelper.timer create mode 100644 templates/postfix/chroot-helper.sh.j2 create mode 100644 templates/postfix/dropin.conf.j2 create mode 100644 templates/postfix/krb-helper.sh.j2 diff --git a/defaults/main.yml b/defaults/main.yml index 14c1364..2afca09 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -51,6 +51,10 @@ mailserver_dovecot_sasl_mech: "gssapi" # In case of kerberos can be left empty mailserver_dovecot_sasl_realm: "" +mailserver_postfix_sasl_bind: True +mailserver_postfix_sasl_mech: "gssapi" +mailserver_postfix_keytab_location: /etc/postfix/kerberos.keytab + # BaseDN. Every user in this ou gets an account with user@postfix_domain mailserver_ldap_basedn: "ou=users,dc=test,dc=example,dc=com" diff --git a/files/postfix/postfix-krbhelper.service b/files/postfix/postfix-krbhelper.service new file mode 100644 index 0000000..0a14101 --- /dev/null +++ b/files/postfix/postfix-krbhelper.service @@ -0,0 +1,10 @@ +[Unit] +After=local-fs.target network.target +Description=Refresh Postfix's Kerberos cache + +[Service] +Type=oneshot +WorkingDirectory=/etc/postfix +ExecStart=/etc/postfix/krb-helper.sh +User=root +Group=root diff --git a/files/postfix/postfix-krbhelper.timer b/files/postfix/postfix-krbhelper.timer new file mode 100644 index 0000000..d9080af --- /dev/null +++ b/files/postfix/postfix-krbhelper.timer @@ -0,0 +1,8 @@ +[Unit] +Description=Refresh Postfix's kerberos cache regularly + +[Timer] +OnUnitActiveSec=2h + +[Install] +WantedBy=timers.target diff --git a/tasks/main.yml b/tasks/main.yml index 7d27b52..f230988 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -114,6 +114,63 @@ - restart postfix tags: postfix + - name: Upload postfix startup chroot helper script + become: True + when: mailserver_postfix_sasl_bind + template: + src: postfix/chroot-helper.sh.j2 + dest: /etc/postfix/chroot-helper.sh + owner: root + group: root + mode: 0750 + notify: restart postfix + + - name: Create postfix startup dropin directory + become: True + when: mailserver_postfix_sasl_bind + file: + path: /etc/systemd/system/postfix@.service.d + state: directory + owner: root + group: root + mode: 0644 + + - name: Upload postfix startup dropin + become: True + when: mailserver_postfix_sasl_bind + template: + src: postfix/dropin.conf.j2 + dest: /etc/systemd/system/postfix@.service.d/ansible.conf + owner: root + group: root + mode: 0644 + notify: restart postfix + + - name: Upload kerberos helper for SASL/GSSAPI binds + become: True + when: mailserver_postfix_sasl_bind + template: + src: postfix/krb-helper.sh.j2 + dest: /etc/postfix/krb-helper.sh + owner: root + group: root + mode: 0744 + notify: restart postfix + + - name: Upload kereros helper service/timer + become: True + when: mailserver_postfix_sasl_bind + copy: + src: "{{ item[0] }}" + dest: "{{ item[1] }}" + owner: root + group: root + mode: 0644 + loop: + - ['postfix/postfix-krbhelper.service', '/etc/systemd/system/postfix-krbhelper.service'] + - ['postfix/postfix-krbhelper.timer', '/etc/systemd/system/postfix-krbhelper.timer'] + notify: restart postfix + - name: Configure allowed proxy map become: True template: diff --git a/templates/postfix/chroot-helper.sh.j2 b/templates/postfix/chroot-helper.sh.j2 new file mode 100644 index 0000000..1e8ed02 --- /dev/null +++ b/templates/postfix/chroot-helper.sh.j2 @@ -0,0 +1,15 @@ +#!/bin/bash +############################################################################ +########### Managed by ansible (role: mailserver), do not edit! ############ +############################################################################ + +# Add some files to postfix's chroot +CHROOT=/var/spool/postfix +if [ -d "${CHROOT}" ] ; then + mkdir -p "${CHROOT}" +fi + +{% if mailserver_postfix_sasl_bind %} +mkdir -p "${CHROOT}/run/postfix" || /bin/true +mount --bind /run/postfix "${CHROOT}/run/postfix" || /bin/true +{% endif %} diff --git a/templates/postfix/dropin.conf.j2 b/templates/postfix/dropin.conf.j2 new file mode 100644 index 0000000..cb20a21 --- /dev/null +++ b/templates/postfix/dropin.conf.j2 @@ -0,0 +1,11 @@ +{% if mailserver_postfix_sasl_bind %} +[Unit] +Requires=postfix-krbhelper.service +Requires=postfix-krbhelper.timer +After=postfix-krbhelper.service + +{% endif %} +[Service] +ExecStartPre= +ExecStartPre=/usr/lib/postfix/configure-instance.sh %i +ExecStartPre=/etc/postfix/chroot-helper.sh diff --git a/templates/postfix/krb-helper.sh.j2 b/templates/postfix/krb-helper.sh.j2 new file mode 100644 index 0000000..7cf58a3 --- /dev/null +++ b/templates/postfix/krb-helper.sh.j2 @@ -0,0 +1,18 @@ +#!/bin/bash +############################################################################ +########### Managed by ansible (role: mailserver), do not edit! ############ +############################################################################ + +set -e + +# Postfix is unfortunately unable to fetch kerberos tickets itsself. +# Therefore, we invoke this script pre-startup and then every few hours to +# ensure that the cache always contains a valid kerberos ticket. + +if [ ! -d "/run/postfix" ] ; then + mkdir -p "/run/postfix" +fi + +kinit -k -t {{ mailserver_postfix_keytab_location }} -c FILE:/run/postfix/krb5_ccache smtp/{{ mailserver_hostname }} + +chown -R postfix /run/postfix diff --git a/templates/postfix/main.cf.j2 b/templates/postfix/main.cf.j2 index c85ae85..add3a68 100644 --- a/templates/postfix/main.cf.j2 +++ b/templates/postfix/main.cf.j2 @@ -243,6 +243,7 @@ local_recipient_maps = proxy:ldap:/etc/postfix/ldap-map.cf # This will be required for mailman and is useful for custom sieve scripts recipient_delimiter = + +import_environment = KRB5CCNAME=FILE:/run/postfix/krb5_ccache # Disabled features that are in the main config file per default # relay_recipient_maps = hash:/etc/postfix/relay_recipients From 23b688400c7b40c050984c68ddba6cfe2a384987 Mon Sep 17 00:00:00 2001 From: Maximilian Falkenstein Date: Sun, 22 Jul 2018 19:43:41 +0200 Subject: [PATCH 2/6] Postfix/Dovecot: Add initial support for SQL-less LDAP --- defaults/main.yml | 27 ++++++++---- tasks/main.yml | 44 ++++++++++++++++--- .../dovecot/conf.d/auth-ldap.conf.ext.j2 | 5 ++- templates/dovecot/dovecot-ldap.conf.ext.j2 | 24 ++++++++-- templates/postfix/chroot-helper.sh.j2 | 7 +++ templates/postfix/ldap-aliases.cf.j2 | 10 +++++ templates/postfix/ldap-conn.j2 | 19 ++++++++ templates/postfix/ldap-map.cf.j2 | 16 +------ templates/postfix/ldap-senders.cf.j2 | 10 +++++ templates/postfix/ldap-transport.cf.j2 | 9 ++++ templates/postfix/ldap-vdomains.cf.j2 | 9 ++++ templates/postfix/main.cf.j2 | 8 ++-- 12 files changed, 150 insertions(+), 38 deletions(-) create mode 100644 templates/postfix/ldap-aliases.cf.j2 create mode 100644 templates/postfix/ldap-conn.j2 create mode 100644 templates/postfix/ldap-senders.cf.j2 create mode 100644 templates/postfix/ldap-transport.cf.j2 create mode 100644 templates/postfix/ldap-vdomains.cf.j2 diff --git a/defaults/main.yml b/defaults/main.yml index 2afca09..9118a6b 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -35,9 +35,15 @@ mailserver_ssl_client_key: "/etc/postfix/client.key" mailserver_dovecot_ssl_cert: "{{ mailserver_ssl_cert }}" mailserver_dovecot_ssl_key: "{{ mailserver_ssl_key }}" +# Where are aliases/vdomains/routes stored? Default is mysql, but ldap is also possible +mailserver_config_method: "mysql" + mailserver_ldap_servers: ["127.0.0.1"] # Enforce TLS for LDAP connections mailserver_ldap_tls: False +# If using encryption ... SSL on port 636 or STARTTLS on 389 +mailserver_ldap_dovecot_ssl: False +# Define mailserver_ldap_cert_file if you have a private CA # To list LDAP groups and get the mail of each user, we hopefully need to authenticate. # Specify a user with sufficient privileges here @@ -55,15 +61,20 @@ mailserver_postfix_sasl_bind: True mailserver_postfix_sasl_mech: "gssapi" mailserver_postfix_keytab_location: /etc/postfix/kerberos.keytab -# BaseDN. Every user in this ou gets an account with user@postfix_domain -mailserver_ldap_basedn: "ou=users,dc=test,dc=example,dc=com" +# BaseDN where accounts reside +mailserver_ldap_basedn: "cn=accounts,dc=test,dc=example,dc=com" + +# BaseDN where the configuration entries are stored (e.g postfixConfiguration) +mailserver_ldap_config_basedn: "cn=mailserver,cn=etc,dc=test,dc=example,dc=com" + +# Dovecot LDAP filters +mailserver_ldap_passfilter: "(uid=%n)" +mailserver_ldap_userfilter: "(&(objectClass=mailboxEntity)(uid=%n))" +mailserver_ldap_iteratefilter: "(objectClass=mailboxEntity)" -# LDAP filters -mailserver_ldap_passfilter: "(cn=%n)" -mailserver_ldap_userfilter: "(&(objectClass=posixAccount)(cn=%n))" -mailserver_ldap_usermailfilter: "(&(objectClass=posixAccount)(mail=%n))" -mailserver_ldap_iteratefilter: "(objectClass=posixAccount)" -mailserver_ldap_usermailattr: "mail" +# Postfix LDAP filters +mailserver_ldap_usermailfilter: "(&(objectClass=mailReceiverEntity)(primaryMail=%s))" +mailserver_ldap_usermailattr: "primaryMail" # Enable GSSAPI authentication for users. mailserver_use_gssapi: False diff --git a/tasks/main.yml b/tasks/main.yml index f230988..d91a41f 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -103,20 +103,52 @@ with_items: - main.cf - master.cf + - dynamicmaps.cf + - cleanup-headers + - ldap-map.cf + notify: + - restart postfix + tags: postfix + + - name: Configure postfix (MySQL config) + become: True + when: mailserver_config_method == "mysql" + template: + src: 'postfix/{{ item }}.j2' + dest: '/etc/postfix/{{ item }}' + owner: root + group: root + mode: 0644 + with_items: - mysql-aliases.cf - mysql-senders.cf - mysql-vdomains.cf - mysql-transport.cf - - dynamicmaps.cf - - cleanup-headers - - ldap-map.cf + notify: + - restart postfix + tags: postfix + + - name: Configure postfix (LDAP config) + become: True + when: mailserver_config_method == "ldap" + template: + src: 'postfix/{{ item }}.j2' + dest: '/etc/postfix/{{ item }}' + owner: root + group: root + mode: 0644 + with_items: + - ldap-aliases.cf + - ldap-senders.cf + - ldap-vdomains.cf + - ldap-transport.cf notify: - restart postfix tags: postfix - name: Upload postfix startup chroot helper script become: True - when: mailserver_postfix_sasl_bind + when: mailserver_postfix_sasl_bind or mailserver_config_method == "ldap" template: src: postfix/chroot-helper.sh.j2 dest: /etc/postfix/chroot-helper.sh @@ -127,7 +159,7 @@ - name: Create postfix startup dropin directory become: True - when: mailserver_postfix_sasl_bind + when: mailserver_postfix_sasl_bind or mailserver_config_method == "ldap" file: path: /etc/systemd/system/postfix@.service.d state: directory @@ -137,7 +169,7 @@ - name: Upload postfix startup dropin become: True - when: mailserver_postfix_sasl_bind + when: mailserver_postfix_sasl_bind or mailserver_config_method == "ldap" template: src: postfix/dropin.conf.j2 dest: /etc/systemd/system/postfix@.service.d/ansible.conf diff --git a/templates/dovecot/conf.d/auth-ldap.conf.ext.j2 b/templates/dovecot/conf.d/auth-ldap.conf.ext.j2 index 4f3bf54..e2e8009 100644 --- a/templates/dovecot/conf.d/auth-ldap.conf.ext.j2 +++ b/templates/dovecot/conf.d/auth-ldap.conf.ext.j2 @@ -38,14 +38,15 @@ passdb { # Use SQL to override settings for users. This will accept all users, but non- # existing users will fail the passdb lookup so that's not an issue +{% if mailserver_config_method == "mysql" %} userdb { driver = sql args = /etc/dovecot/dovecot-sql.conf.ext result_success = continue } +{% endif %} -# Second userdb. We should never reach this during normal auth, but it is -# important for a complete output of doveadm user '*' +# Second userdb, the actual ldap userdb { driver = ldap args = /etc/dovecot/dovecot-ldap.conf.ext diff --git a/templates/dovecot/dovecot-ldap.conf.ext.j2 b/templates/dovecot/dovecot-ldap.conf.ext.j2 index 684c190..76d08f1 100644 --- a/templates/dovecot/dovecot-ldap.conf.ext.j2 +++ b/templates/dovecot/dovecot-ldap.conf.ext.j2 @@ -21,11 +21,19 @@ # by * none # Space separated list of LDAP hosts to use. host:port is allowed too. +{% if not mailserver_ldap_dovecot_ssl %} hosts = "{{ mailserver_ldap_servers|join(' ') }}" +{% else %} +#hosts = +{% endif %} # LDAP URIs to use. You can use this instead of hosts list. Note that this # setting isn't supported by all LDAP libraries. -#uris = +{% if mailserver_ldap_dovecot_ssl %} +uris = "ldaps://{{ mailserver_ldap_servers|join(' ldaps://') }}" +{% else %} +#uris = +{% endif %} # Distinguished Name - the username used to login to the LDAP server. # Leave it commented out to bind anonymously (useful with auth_bind=yes). @@ -46,11 +54,16 @@ sasl_realm = "{{ mailserver_dovecot_sasl_realm|upper }}" #sasl_authz_id = # Use TLS to connect to the LDAP server. -{% if mailserver_ldap_tls %} +{% if mailserver_ldap_tls or mailserver_ldap_dovecot_ssl %} +{% if mailserver_ldap_tls and not mailserver_ldap_dovecot_ssl %} tls = yes +{% endif %} # TLS options, currently supported only with OpenLDAP: -#tls_ca_cert_file = +{% if mailserver_ldap_cert_file is defined %} +tls_ca_cert_file = "{{ mailserver_ldap_cert_file }}" +{% else %} tls_ca_cert_dir = "{{ mailserver_tls_base }}" +{% endif %} #tls_cipher_suite = # TLS cert/key is used only if LDAP server requires a client certificate. #tls_cert_file = @@ -114,7 +127,10 @@ scope = subtree # # There are also other special fields which can be returned, see # http://wiki2.dovecot.org/UserDatabase/ExtraFields -user_attrs = +user_attrs = =quota_rule=%{ldap:mailboxQuota}, \ + =uid=vmail, \ + =gid=vmail, \ + =home={{ mailserver_mailbox_dir }}/%{ldap:uid} # Filter for user lookup. Some variables can be used (see # http://wiki2.dovecot.org/Variables for full list): diff --git a/templates/postfix/chroot-helper.sh.j2 b/templates/postfix/chroot-helper.sh.j2 index 1e8ed02..96039b8 100644 --- a/templates/postfix/chroot-helper.sh.j2 +++ b/templates/postfix/chroot-helper.sh.j2 @@ -9,6 +9,13 @@ if [ -d "${CHROOT}" ] ; then mkdir -p "${CHROOT}" fi +{% if mailserver_ldap_cert_file is defined %} +export CAFILEPATH="$(dirname {{ mailserver_ldap_cert_file }})" +echo "CA file is in $CAFILEPATH" +mkdir -p "${CHROOT}${CAFILEPATH}" || /bin/true +cp "{{ mailserver_ldap_cert_file }}" "${CHROOT}/{{ mailserver_ldap_cert_file }}" || /bin/true +{% endif %} + {% if mailserver_postfix_sasl_bind %} mkdir -p "${CHROOT}/run/postfix" || /bin/true mount --bind /run/postfix "${CHROOT}/run/postfix" || /bin/true diff --git a/templates/postfix/ldap-aliases.cf.j2 b/templates/postfix/ldap-aliases.cf.j2 new file mode 100644 index 0000000..a70902a --- /dev/null +++ b/templates/postfix/ldap-aliases.cf.j2 @@ -0,0 +1,10 @@ +############################################################################ +########### Managed by ansible (role: mailserver), do not edit! ############ +############################################################################ + +{% include './templates/postfix/ldap-conn.j2' %} + +search_base = {{ mailserver_ldap_basedn }} +query_filter = alias=%s +special_result_attribute = member +result_attribute = primaryMail diff --git a/templates/postfix/ldap-conn.j2 b/templates/postfix/ldap-conn.j2 new file mode 100644 index 0000000..910b1ea --- /dev/null +++ b/templates/postfix/ldap-conn.j2 @@ -0,0 +1,19 @@ +version = 3 +{% if mailserver_ldap_tls %} +tls_require_cert = yes +server_host = ldaps://{{ mailserver_ldap_servers|join(' ldaps://') }} +{% if mailserver_ldap_cert_file is defined %} +tls_ca_cert_file = {{ mailserver_ldap_cert_file }} +{% endif %} +{% else %} +server_host = {{ mailserver_ldap_servers|join(' ') }} +{% endif %} + +{% if mailserver_postfix_sasl_bind %} +bind = sasl +sasl_mechs = gssapi +{% else %} +bind = yes +bind_dn = {{ mailserver_ldap_binddn }} +bind_pw = {{ mailserver_ldap_bindpwd }} +{% endif %} diff --git a/templates/postfix/ldap-map.cf.j2 b/templates/postfix/ldap-map.cf.j2 index 1e96cfe..b294151 100644 --- a/templates/postfix/ldap-map.cf.j2 +++ b/templates/postfix/ldap-map.cf.j2 @@ -2,20 +2,8 @@ ########### Managed by ansible (role: mailserver), do not edit! ############ ############################################################################ -{% if mailserver_ldap_tls %} -server_host = -{% for item in mailserver_ldap_servers %} - ldaps://{{ item }} -{% endfor %} -{% else %} -server_host = -{% for item in mailserver_ldap_servers %} - ldap://{{ item }} -{% endfor %} -{% endif %} +{% include './templates/postfix/ldap-conn.j2' %} + search_base = {{ mailserver_ldap_basedn }} -version = 3 -bind_dn = {{ mailserver_ldap_binddn }} -bind_pw = {{ mailserver_ldap_bindpwd }} query_filter = {{ mailserver_ldap_usermailfilter }} result_attribute = {{ mailserver_ldap_usermailattr }} diff --git a/templates/postfix/ldap-senders.cf.j2 b/templates/postfix/ldap-senders.cf.j2 new file mode 100644 index 0000000..50130a5 --- /dev/null +++ b/templates/postfix/ldap-senders.cf.j2 @@ -0,0 +1,10 @@ +############################################################################ +########### Managed by ansible (role: mailserver), do not edit! ############ +############################################################################ + +{% include './templates/postfix/ldap-conn.j2' %} + +search_base = {{ mailserver_ldap_basedn }} +query_filter = (&(objectClass=mailSenderEntity)(|(primaryMail=%s)(sendAlias=%s))) +special_result_attribute = member +result_attribute = uid diff --git a/templates/postfix/ldap-transport.cf.j2 b/templates/postfix/ldap-transport.cf.j2 new file mode 100644 index 0000000..29828e4 --- /dev/null +++ b/templates/postfix/ldap-transport.cf.j2 @@ -0,0 +1,9 @@ +############################################################################ +########### Managed by ansible (role: mailserver), do not edit! ############ +############################################################################ + +{% include './templates/postfix/ldap-conn.j2' %} + +search_base = {{ mailserver_ldap_basedn }} +query_filter = (uid=%s) +result_attribute = mailboxTransport diff --git a/templates/postfix/ldap-vdomains.cf.j2 b/templates/postfix/ldap-vdomains.cf.j2 new file mode 100644 index 0000000..8536f71 --- /dev/null +++ b/templates/postfix/ldap-vdomains.cf.j2 @@ -0,0 +1,9 @@ +############################################################################ +########### Managed by ansible (role: mailserver), do not edit! ############ +############################################################################ + +{% include './templates/postfix/ldap-conn.j2' %} + +search_base = {{ mailserver_ldap_config_basedn }} +query_filter = (&(objectClass=postfixConfiguration)(virtualDomain=%s)) +result_attribute = virtualDomain diff --git a/templates/postfix/main.cf.j2 b/templates/postfix/main.cf.j2 index add3a68..14e9005 100644 --- a/templates/postfix/main.cf.j2 +++ b/templates/postfix/main.cf.j2 @@ -43,7 +43,7 @@ mydestination = {{ mailserver_local_destinations|join(', ') }} # Which domains are 'virtual' domains, that is domains that this machine is additionally responsible # for. See http://www.postfix.org/VIRTUAL_README.html#virtual_alias as for how this is supposed to # work. -virtual_alias_domains = mysql:/etc/postfix/mysql-vdomains.cf +virtual_alias_domains = {{ mailserver_config_method }}:/etc/postfix/{{ mailserver_config_method }}-vdomains.cf # Only relay mail for localhost mynetworks_style = host @@ -53,7 +53,7 @@ in_flow_delay = 1s # The difference between mailbox_transport_maps and transport_maps is # that transport_maps will also apply when sending mails! -mailbox_transport_maps = mysql:/etc/postfix/mysql-transport.cf +mailbox_transport_maps = {{ mailserver_config_method }}:/etc/postfix/{{ mailserver_config_method }}-transport.cf # Path to system SSL certificate store smtp_tls_CApath = {{ mailserver_tls_base }} @@ -130,7 +130,7 @@ smtpd_sasl_authenticated_header = yes # Check the client's sender adress to match the adresses it may actually # send mail for -smtpd_sender_login_maps = mysql:/etc/postfix/mysql-senders.cf +smtpd_sender_login_maps = {{ mailserver_config_method }}:/etc/postfix/{{ mailserver_config_method }}-senders.cf message_size_limit = {{ mailserver_message_size_limit }} # Limit mailbox size to 500 MB. This is not actually used, but Postfix has @@ -221,7 +221,7 @@ header_checks = pcre:/etc/postfix/cleanup-headers # Alias maps # See http://serverfault.com/a/644393/371842 as to why we need virtual_ -virtual_alias_maps = mysql:/etc/postfix/mysql-aliases.cf +virtual_alias_maps = {{ mailserver_config_method }}:/etc/postfix/{{ mailserver_config_method }}-aliases.cf # Do not load normal aliases... alias_maps = From c267f2cbca0514ce0a5c1301b2e3cbaa5d3760d2 Mon Sep 17 00:00:00 2001 From: Maximilian Falkenstein Date: Mon, 23 Jul 2018 21:18:44 +0200 Subject: [PATCH 3/6] Postfix: Implement LDAP's 'canSendExternally' setting --- tasks/main.yml | 24 ++++++++++++++++++++++ templates/postfix/ldap-external-send.cf.j2 | 10 +++++++++ templates/postfix/main.cf.j2 | 11 ++++++++-- 3 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 templates/postfix/ldap-external-send.cf.j2 diff --git a/tasks/main.yml b/tasks/main.yml index d91a41f..5d44ed6 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -110,6 +110,29 @@ - restart postfix tags: postfix + - name: Configure postfix (store local domain) + become: True + register: dommap + copy: + content: "{{ mailserver_domain }} OK" + dest: /etc/postfix/local_domains + owner: root + group: root + mode: 0644 + notify: + - restart postfix + tags: postfix + + - name: Configure postfix (generate local domain map) + become: True + when: dommap.changed + shell: postmap /etc/postfix/local_domains + args: + chdir: /etc/postfix + notify: + - restart postfix + tags: postfix + - name: Configure postfix (MySQL config) become: True when: mailserver_config_method == "mysql" @@ -142,6 +165,7 @@ - ldap-senders.cf - ldap-vdomains.cf - ldap-transport.cf + - ldap-external-send.cf notify: - restart postfix tags: postfix diff --git a/templates/postfix/ldap-external-send.cf.j2 b/templates/postfix/ldap-external-send.cf.j2 new file mode 100644 index 0000000..2548098 --- /dev/null +++ b/templates/postfix/ldap-external-send.cf.j2 @@ -0,0 +1,10 @@ +############################################################################ +########### Managed by ansible (role: mailserver), do not edit! ############ +############################################################################ + +{% include './templates/postfix/ldap-conn.j2' %} + +search_base = {{ mailserver_ldap_basedn }} +query_filter = (&(|(primaryMail=%s)(sendAlias=%s))(canSendExternally=FALSE)) +result_attribute = canSendExternally +result_format = local_only diff --git a/templates/postfix/main.cf.j2 b/templates/postfix/main.cf.j2 index 14e9005..8d7f939 100644 --- a/templates/postfix/main.cf.j2 +++ b/templates/postfix/main.cf.j2 @@ -194,8 +194,10 @@ smtpd_sender_restrictions = reject_sender_login_mismatch, # Either you're authenticated OR you are from 127.0.0.1 OR you satisfy a boatload of constraints # We need to find out in prod if this is too restrictive # Also note that the same thing ist in master.cf without sasl restrictions -smtpd_recipient_restrictions = reject_sender_login_mismatch, permit_sasl_authenticated, - permit_mynetworks, reject_invalid_hostname, reject_non_fqdn_hostname, +smtpd_recipient_restrictions = reject_sender_login_mismatch, +{% if mailserver_config_method == "ldap" %} check_sender_access ldap:/etc/postfix/ldap-external-send.cf, +{% endif %} + permit_sasl_authenticated, permit_mynetworks, reject_invalid_hostname, reject_non_fqdn_hostname, reject_non_fqdn_recipient, reject_non_fqdn_sender, reject_unknown_sender_domain, reject_unknown_recipient_domain, reject_unauth_pipelining, reject_unauth_destination, reject_multi_recipient_bounce, reject_non_fqdn_helo_hostname, reject_invalid_helo_hostname, permit @@ -243,6 +245,11 @@ local_recipient_maps = proxy:ldap:/etc/postfix/ldap-map.cf # This will be required for mailman and is useful for custom sieve scripts recipient_delimiter = + +# Create a restriction class "local_only" which can be used to limit +# outgoing mail of some users to this domain only +smtpd_restriction_classes = local_only +local_only = check_recipient_access hash:/etc/postfix/local_domains, reject + import_environment = KRB5CCNAME=FILE:/run/postfix/krb5_ccache # Disabled features that are in the main config file per default From 3bae4b92abd8d0a83af7153cd799521c970a9b07 Mon Sep 17 00:00:00 2001 From: Maximilian Falkenstein Date: Sat, 4 Aug 2018 14:37:31 +0200 Subject: [PATCH 4/6] Postfix: Support canReceiveExternally flag --- tasks/main.yml | 1 + templates/postfix/ldap-external-receive.cf.j2 | 10 ++++++++++ templates/postfix/main.cf.j2 | 3 ++- templates/postfix/master.cf.j2 | 2 ++ 4 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 templates/postfix/ldap-external-receive.cf.j2 diff --git a/tasks/main.yml b/tasks/main.yml index 5d44ed6..c16a6e7 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -166,6 +166,7 @@ - ldap-vdomains.cf - ldap-transport.cf - ldap-external-send.cf + - ldap-external-receive.cf notify: - restart postfix tags: postfix diff --git a/templates/postfix/ldap-external-receive.cf.j2 b/templates/postfix/ldap-external-receive.cf.j2 new file mode 100644 index 0000000..afa0a67 --- /dev/null +++ b/templates/postfix/ldap-external-receive.cf.j2 @@ -0,0 +1,10 @@ +############################################################################ +########### Managed by ansible (role: mailserver), do not edit! ############ +############################################################################ + +{% include './templates/postfix/ldap-conn.j2' %} + +search_base = {{ mailserver_ldap_basedn }} +query_filter = (&(|(primaryMail=%s)(alias=%s))(canReceiveExternally=FALSE)) +result_attribute = canReceiveExternally +result_format = local_only_sender diff --git a/templates/postfix/main.cf.j2 b/templates/postfix/main.cf.j2 index 8d7f939..b3bac26 100644 --- a/templates/postfix/main.cf.j2 +++ b/templates/postfix/main.cf.j2 @@ -247,8 +247,9 @@ recipient_delimiter = + # Create a restriction class "local_only" which can be used to limit # outgoing mail of some users to this domain only -smtpd_restriction_classes = local_only +smtpd_restriction_classes = local_only, local_only_sender local_only = check_recipient_access hash:/etc/postfix/local_domains, reject +local_only_sender = check_sender_access hash:/etc/postfix/local_domains, reject import_environment = KRB5CCNAME=FILE:/run/postfix/krb5_ccache diff --git a/templates/postfix/master.cf.j2 b/templates/postfix/master.cf.j2 index c2a0bb9..634b8b4 100644 --- a/templates/postfix/master.cf.j2 +++ b/templates/postfix/master.cf.j2 @@ -17,8 +17,10 @@ smtp inet n - y - - smtpd -o smtpd_sasl_auth_enable=no {% if not mailserver_behind_proxy %} -o smtpd_recipient_restrictions=permit_mynetworks,reject_invalid_hostname,reject_non_fqdn_hostname,reject_non_fqdn_recipient,reject_non_fqdn_sender,reject_unknown_sender_domain,reject_unknown_recipient_domain,reject_unauth_pipelining,reject_unauth_destination,reject_multi_recipient_bounce,reject_non_fqdn_helo_hostname,reject_invalid_helo_hostname,permit + -o smtpd_sender_restrictions=permit_mynetworks,{% if mailserver_config_method == "ldap" %}check_recipient_access ldap:/etc/postfix/ldap-external-receive.cf,{% endif %}reject_unauth_destination,reject_non_fqdn_sender,reject_non_fqdn_recipient,reject_unknown_recipient_domain,reject_unauth_pipelining,permit {% else %} -o smtpd_recipient_restrictions=permit_mynetworks,reject_unknown_recipient_domain,reject_unauth_pipelining,reject_unauth_destination,reject_multi_recipient_bounce,permit + -o smtpd_sender_restrictions=permit_mynetworks,{% if mailserver_config_method == "ldap" %}check_recipient_access ldap:/etc/postfix/ldap-external-receive.cf,{% endif %}reject_unauth_destination,reject_non_fqdn_sender,reject_non_fqdn_recipient,reject_unknown_recipient_domain,reject_unauth_pipelining,permit {% endif %} -o header_checks= {% if ansible_local['mailserver_have_antispam']|default(False) %} From c7cb80e01e8053c5bb5b874d68e61e50740736a3 Mon Sep 17 00:00:00 2001 From: Maximilian Falkenstein Date: Sat, 4 Aug 2018 16:01:13 +0200 Subject: [PATCH 5/6] Postfix: Cleanup port 25 option overrides --- templates/postfix/main.cf.j2 | 27 +++++++++++++++++++++++---- templates/postfix/master.cf.j2 | 9 ++------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/templates/postfix/main.cf.j2 b/templates/postfix/main.cf.j2 index b3bac26..15c3f32 100644 --- a/templates/postfix/main.cf.j2 +++ b/templates/postfix/main.cf.j2 @@ -165,13 +165,22 @@ delay_warning_time=3h maximal_queue_lifetime=2d bounce_queue_lifetime=1d +# submission sender restrictions +smtpd_sender_restrictions = reject_sender_login_mismatch, + permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination, + reject_non_fqdn_sender, reject_non_fqdn_recipient, reject_unknown_recipient_domain, + reject_unauth_pipelining, + reject + {% if mailserver_behind_proxy %} # We're behind a forwaring proxy that does antispam. Mails therefore do not get delivered to us # from their original sender, therefore, we can't do DNS checks! # smtpd sender restrictions -smtpd_sender_restrictions = reject_sender_login_mismatch, - permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination, +smtpd_sender_restrictions_25 = permit_mynetworks, +{% if mailserver_config_method == "ldap" %} check_recipient_access ldap:/etc/postfix/ldap-external-receive.cf, +{% endif %} + reject_unauth_destination, reject_non_fqdn_sender, reject_non_fqdn_recipient, reject_unknown_recipient_domain, reject_unauth_pipelining, check_client_access hash:/etc/postfix/allowed_proxies, reject @@ -179,14 +188,19 @@ smtpd_sender_restrictions = reject_sender_login_mismatch, # smtp destination restrictions # Either you're authenticated OR you are from 127.0.0.1 OR you satisfy a boatload of constraints # Also note that the same thing ist in master.cf without sasl restrictions +smtpd_recipient_restrictions_25 = permit_mynetworks, reject_unknown_recipient_domain, reject_unauth_pipelining, + reject_unauth_destination, reject_multi_recipient_bounce, permit + smtpd_recipient_restrictions = reject_sender_login_mismatch, permit_sasl_authenticated, permit_mynetworks, reject_unknown_recipient_domain, reject_unauth_pipelining, reject_unauth_destination, reject_multi_recipient_bounce, check_client_access hash:/etc/postfix/allowed_proxies, reject {% else %} # smtpd sender restrictions -smtpd_sender_restrictions = reject_sender_login_mismatch, - permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination, +smtpd_sender_restrictions_25 = permit_mynetworks, +{% if mailserver_config_method == "ldap" %} check_recipient_access ldap:/etc/postfix/ldap-external-receive.cf, +{% endif %} + reject_unauth_destination, reject_non_fqdn_sender, reject_non_fqdn_recipient, reject_unknown_recipient_domain, reject_unauth_pipelining, permit @@ -194,6 +208,11 @@ smtpd_sender_restrictions = reject_sender_login_mismatch, # Either you're authenticated OR you are from 127.0.0.1 OR you satisfy a boatload of constraints # We need to find out in prod if this is too restrictive # Also note that the same thing ist in master.cf without sasl restrictions +smtpd_recipient_restrictions_25 = permit_mynetworks, reject_invalid_hostname, reject_non_fqdn_hostname, + reject_non_fqdn_recipient, reject_non_fqdn_sender, reject_unknown_sender_domain, + reject_unknown_recipient_domain, reject_unauth_pipelining, reject_unauth_destination, + reject_multi_recipient_bounce, reject_non_fqdn_helo_hostname, reject_invalid_helo_hostname, permit + smtpd_recipient_restrictions = reject_sender_login_mismatch, {% if mailserver_config_method == "ldap" %} check_sender_access ldap:/etc/postfix/ldap-external-send.cf, {% endif %} diff --git a/templates/postfix/master.cf.j2 b/templates/postfix/master.cf.j2 index 634b8b4..4ddb72c 100644 --- a/templates/postfix/master.cf.j2 +++ b/templates/postfix/master.cf.j2 @@ -15,13 +15,8 @@ # We list smtp inet n - y - - smtpd -o smtpd_sasl_auth_enable=no -{% if not mailserver_behind_proxy %} - -o smtpd_recipient_restrictions=permit_mynetworks,reject_invalid_hostname,reject_non_fqdn_hostname,reject_non_fqdn_recipient,reject_non_fqdn_sender,reject_unknown_sender_domain,reject_unknown_recipient_domain,reject_unauth_pipelining,reject_unauth_destination,reject_multi_recipient_bounce,reject_non_fqdn_helo_hostname,reject_invalid_helo_hostname,permit - -o smtpd_sender_restrictions=permit_mynetworks,{% if mailserver_config_method == "ldap" %}check_recipient_access ldap:/etc/postfix/ldap-external-receive.cf,{% endif %}reject_unauth_destination,reject_non_fqdn_sender,reject_non_fqdn_recipient,reject_unknown_recipient_domain,reject_unauth_pipelining,permit -{% else %} - -o smtpd_recipient_restrictions=permit_mynetworks,reject_unknown_recipient_domain,reject_unauth_pipelining,reject_unauth_destination,reject_multi_recipient_bounce,permit - -o smtpd_sender_restrictions=permit_mynetworks,{% if mailserver_config_method == "ldap" %}check_recipient_access ldap:/etc/postfix/ldap-external-receive.cf,{% endif %}reject_unauth_destination,reject_non_fqdn_sender,reject_non_fqdn_recipient,reject_unknown_recipient_domain,reject_unauth_pipelining,permit -{% endif %} + -o smtpd_recipient_restrictions=$smtpd_recipient_restrictions_25 + -o smtpd_sender_restrictions=$smtpd_sender_restrictions_25 -o header_checks= {% if ansible_local['mailserver_have_antispam']|default(False) %} -o smtpd_proxy_filter=127.0.0.1:10026 From 3df97a727780b761b797eb0a1cb2d28ce2ec3eb1 Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Sun, 17 May 2020 03:20:35 +0200 Subject: [PATCH 6/6] Postfix: Implement sender check for host accounts --- tasks/main.yml | 1 + templates/postfix/ldap-host-senders.cf.j2 | 11 +++++++++++ templates/postfix/main.cf.j2 | 4 ++++ 3 files changed, 16 insertions(+) create mode 100644 templates/postfix/ldap-host-senders.cf.j2 diff --git a/tasks/main.yml b/tasks/main.yml index c16a6e7..d444f60 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -163,6 +163,7 @@ with_items: - ldap-aliases.cf - ldap-senders.cf + - ldap-host-senders.cf - ldap-vdomains.cf - ldap-transport.cf - ldap-external-send.cf diff --git a/templates/postfix/ldap-host-senders.cf.j2 b/templates/postfix/ldap-host-senders.cf.j2 new file mode 100644 index 0000000..e6691ea --- /dev/null +++ b/templates/postfix/ldap-host-senders.cf.j2 @@ -0,0 +1,11 @@ +############################################################################ +########### Managed by ansible (role: mailserver), do not edit! ############ +############################################################################ + +{% include './templates/postfix/ldap-conn.j2' %} + +search_base = cn=computers,{{ mailserver_ldap_basedn }} +query_filter = (&(objectClass=mailSenderEntity)(|(primaryMail=%s)(sendAlias=%s))) +special_result_attribute = member +result_attribute = cn +result_format = host/%s diff --git a/templates/postfix/main.cf.j2 b/templates/postfix/main.cf.j2 index 15c3f32..e9e55f7 100644 --- a/templates/postfix/main.cf.j2 +++ b/templates/postfix/main.cf.j2 @@ -130,7 +130,11 @@ smtpd_sasl_authenticated_header = yes # Check the client's sender adress to match the adresses it may actually # send mail for +{% if mailserver_config_method == "ldap" %} +smtpd_sender_login_maps = ldap:/etc/postfix/ldap-senders.cf ldap:/etc/postfix/ldap-host-senders.cf +{% else %} smtpd_sender_login_maps = {{ mailserver_config_method }}:/etc/postfix/{{ mailserver_config_method }}-senders.cf +{% endif %} message_size_limit = {{ mailserver_message_size_limit }} # Limit mailbox size to 500 MB. This is not actually used, but Postfix has