Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LDAP Support #1

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 23 additions & 8 deletions defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -51,15 +57,24 @@ mailserver_dovecot_sasl_mech: "gssapi"
# In case of kerberos can be left empty
mailserver_dovecot_sasl_realm: ""

# BaseDN. Every user in this ou gets an account with user@postfix_domain
mailserver_ldap_basedn: "ou=users,dc=test,dc=example,dc=com"
mailserver_postfix_sasl_bind: True
mailserver_postfix_sasl_mech: "gssapi"
mailserver_postfix_keytab_location: /etc/postfix/kerberos.keytab

# 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
Expand Down
10 changes: 10 additions & 0 deletions files/postfix/postfix-krbhelper.service
Original file line number Diff line number Diff line change
@@ -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
8 changes: 8 additions & 0 deletions files/postfix/postfix-krbhelper.timer
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[Unit]
Description=Refresh Postfix's kerberos cache regularly

[Timer]
OnUnitActiveSec=2h

[Install]
WantedBy=timers.target
121 changes: 118 additions & 3 deletions tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,17 +103,132 @@
with_items:
- main.cf
- master.cf
- dynamicmaps.cf
- cleanup-headers
- ldap-map.cf
notify:
- 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"
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-host-senders.cf
- ldap-vdomains.cf
- ldap-transport.cf
- ldap-external-send.cf
- ldap-external-receive.cf
notify:
- restart postfix
tags: postfix

- name: Upload postfix startup chroot helper script
become: True
when: mailserver_postfix_sasl_bind or mailserver_config_method == "ldap"
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 or mailserver_config_method == "ldap"
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 or mailserver_config_method == "ldap"
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:
Expand Down
5 changes: 3 additions & 2 deletions templates/dovecot/conf.d/auth-ldap.conf.ext.j2
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
24 changes: 20 additions & 4 deletions templates/dovecot/dovecot-ldap.conf.ext.j2
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand All @@ -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 =
Expand Down Expand Up @@ -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):
Expand Down
22 changes: 22 additions & 0 deletions templates/postfix/chroot-helper.sh.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/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_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
{% endif %}
11 changes: 11 additions & 0 deletions templates/postfix/dropin.conf.j2
Original file line number Diff line number Diff line change
@@ -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
18 changes: 18 additions & 0 deletions templates/postfix/krb-helper.sh.j2
Original file line number Diff line number Diff line change
@@ -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
10 changes: 10 additions & 0 deletions templates/postfix/ldap-aliases.cf.j2
Original file line number Diff line number Diff line change
@@ -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
19 changes: 19 additions & 0 deletions templates/postfix/ldap-conn.j2
Original file line number Diff line number Diff line change
@@ -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 %}
10 changes: 10 additions & 0 deletions templates/postfix/ldap-external-receive.cf.j2
Original file line number Diff line number Diff line change
@@ -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
10 changes: 10 additions & 0 deletions templates/postfix/ldap-external-send.cf.j2
Original file line number Diff line number Diff line change
@@ -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
11 changes: 11 additions & 0 deletions templates/postfix/ldap-host-senders.cf.j2
Original file line number Diff line number Diff line change
@@ -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
Loading