summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuilhem Moulin <guilhem@fripost.org>2013-12-02 03:42:57 +0100
committerGuilhem Moulin <guilhem@fripost.org>2015-06-07 02:51:05 +0200
commit5a7bec1a590e20e263d41eaf414cfe9b5ba48a75 (patch)
tree9c3ffabaed59ab3a0a5d324b5f2d74a200f4f4a2
parent7275b307b8e26e60392e600a3de0671d0aa49043 (diff)
LDAP Sync Replication.
-rw-r--r--common.yml2
-rw-r--r--group_vars/all.yml2
-rw-r--r--lib/openldap1
-rw-r--r--roles/common-LDAP/tasks/main.yml2
-rw-r--r--roles/common-LDAP/templates/etc/default/slapd.j22
-rw-r--r--roles/common-LDAP/templates/etc/ldap/database.ldif.j234
6 files changed, 39 insertions, 4 deletions
diff --git a/common.yml b/common.yml
index cea8611..ab8ab1d 100644
--- a/common.yml
+++ b/common.yml
@@ -3,25 +3,25 @@
# applied playbook by playbook and not globally for the whole inventory;
# therefore if two playbooks are given the role 'common', the tasks
# defined in 'common' would be run twice.
# The quickfix to ensure that plays are role-disjoint is to create a
# separate play for each role. Of course the downside is that we loose
# (most of) the advantage of roles...
- name: Common tasks
hosts: all
roles:
- common
- name: Common SQL tasks
hosts: MDA:webmail:backup
gather_facts: False
tags: mysql,sql
roles:
- common-SQL
- name: Common LDAP tasks
- hosts: MDA:MSA:lists:LDAP-producer:MX
+ hosts: MDA:MSA:lists:LDAP-provider:MX
gather_facts: False
tags: slapd,ldap
roles:
- common-LDAP
diff --git a/group_vars/all.yml b/group_vars/all.yml
index fb7feb8..b9e025f 100644
--- a/group_vars/all.yml
+++ b/group_vars/all.yml
@@ -1,12 +1,14 @@
---
postfix_instance:
# The keys are the group names associated with a Postfix role, and the
# values are the name and group (optional) of the instance dedicated
# to that role.
IMAP: { name: mda }
MX: { name: mx, group: mta }
MTA-out: { name: mta-out,group: mta }
MSA: { name: msa }
MTA_out: { IPv4: outgoing.fripost.org, port: 2525 }
LDA: { IPv4: lda.fripost.org, port: 2526 }
+
+LDAP_provider: host1.libvirt.guilhem.org
diff --git a/lib/openldap b/lib/openldap
index 020017c..a90a386 100644
--- a/lib/openldap
+++ b/lib/openldap
@@ -16,40 +16,41 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import ldap, ldap.sasl
from ldap.filter import filter_format
from ldap.dn import dn2str,explode_dn,str2dn
from ldap.modlist import addModlist
from ldif import LDIFParser
from functools import partial
import re, pwd
# Dirty hack to check equality between the targetted LDIF and that
# currently in the directory. The value of some configuration (olc*)
# attributes is automatically indexed when added; for those we'll add
# explicit indices to what we find in the LDIF.
indexedAttributes = frozenset([
'olcAttributeTypes',
'olcObjectClasses',
'olcAccess',
+ 'olcSyncrepl',
])
# Another hack. Configuration entries sometimes pollutes the DNs with
# indices, thus it's not possible to directly use them as base.
# Instead, we use their parent as a pase, and search for the *unique*
# match with the same ObjectClass and the matching extra attributes.
# ('%s' in the attribute value is replaced with the value of the source
# entry.)
indexedDN = {
'olcSchemaConfig': [('cn', '{*}%s')],
'olcHdbConfig': [('olcDbDirectory', '%s' )],
}
# Allow for flexible ACLs for user using SASL's EXTERNAL mechanism.
# "username=postfix,cn=peercred,cn=external,cn=auth" is replaced by
# "gidNumber=106+uidNumber=102,cn=peercred,cn=external,cn=auth" where
# 102 is postfix's UID and 106 its primary GID.
# (Regular expressions are not allowed.)
sasl_ext_re = re.compile( r"""(?P<start>\sby\s+dn(?:\.exact)?)=
diff --git a/roles/common-LDAP/tasks/main.yml b/roles/common-LDAP/tasks/main.yml
index 270924c..27a0298 100644
--- a/roles/common-LDAP/tasks/main.yml
+++ b/roles/common-LDAP/tasks/main.yml
@@ -63,21 +63,21 @@
openldap: target=/etc/ldap/{{ item }} state=present
with_items:
- schema/fripost.ldif
# TODO load other required schemas *before* loading the database
- fripost/database.ldif
- name: Load LDAP modules
openldap: module={{ item }}.la state=present
with_items:
# TODO only if provider
- syncprov
# TODO only if writable
- constraint
- name: Start slapd
service: name=slapd state=started
when: not (r1.changed or r2.changed)
- meta: flush_handlers
-# TODO: authz constraint syncprov syncrepl
+# TODO: authz constraint syncprov
diff --git a/roles/common-LDAP/templates/etc/default/slapd.j2 b/roles/common-LDAP/templates/etc/default/slapd.j2
index 7eea421..92b3b22 100644
--- a/roles/common-LDAP/templates/etc/default/slapd.j2
+++ b/roles/common-LDAP/templates/etc/default/slapd.j2
@@ -6,41 +6,41 @@ SLAPD_CONF=
# System account to run the slapd server under. If empty the server
# will run as root.
SLAPD_USER="openldap"
# System group to run the slapd server under. If empty the server will
# run in the primary group of its user.
SLAPD_GROUP="openldap"
# Path to the pid file of the slapd server. If not set the init.d script
# will try to figure it out from $SLAPD_CONF (/etc/ldap/slapd.conf by
# default)
SLAPD_PIDFILE=
# slapd normally serves ldap only on all TCP-ports 389. slapd can also
# service requests on TCP-port 636 (ldaps) and requests via unix
# sockets.
SLAPD_SERVICES="ldapi:///"
{% for i in postfix_instance.keys() | intersect(group_names) | list %}
SLAPD_SERVICES="$SLAPD_SERVICES ldapi://%2Fvar%2Fspool%2Fpostfix-{{ postfix_instance[i].name }}%2Fprivate%2Fldapi/"
{% endfor %}
-{% if 'LDAP-producer' in group_names %}
+{% if 'LDAP-provider' in group_names %}
SLAPD_SERVICES="$SLAPD_SERVICES ldap://172.16.0.1:389/"
{% endif %}
# If SLAPD_NO_START is set, the init script will not start or restart
# slapd (but stop will still work). Uncomment this if you are
# starting slapd via some other means or if you don't want slapd normally
# started at boot.
#SLAPD_NO_START=1
# If SLAPD_SENTINEL_FILE is set to path to a file and that file exists,
# the init script will not start or restart slapd (but stop will still
# work). Use this for temporarily disabling startup of slapd (when doing
# maintenance, for example, or through a configuration management system)
# when you don't want to edit a configuration file.
SLAPD_SENTINEL_FILE=/etc/ldap/noslapd
# For Kerberos authentication (via SASL), slapd by default uses the system
# keytab file (/etc/krb5.keytab). To use a different keytab file,
# uncomment this line and change the path.
#export KRB5_KTNAME=/etc/krb5.keytab
diff --git a/roles/common-LDAP/templates/etc/ldap/database.ldif.j2 b/roles/common-LDAP/templates/etc/ldap/database.ldif.j2
index 03691f9..5a8674a 100644
--- a/roles/common-LDAP/templates/etc/ldap/database.ldif.j2
+++ b/roles/common-LDAP/templates/etc/ldap/database.ldif.j2
@@ -34,73 +34,97 @@ olcRootDN: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
########################################################################
########################################################################
# Performance considerations
#
# To reindex an existing database, you have to
# * Stop slapd sudo service slapd stop
# * Reindex su openldap -c "slapindex -b 'o=mailHosting,dc=fripost,dc=org'"
# * Restart slapd sudo service slapd start
#
# References
# - https://wiki.zimbra.com/wiki/OpenLDAP_Performance_Tuning_5.0
# - http://www.openldap.org/doc/admin24/tuning.html
# - http://www.openldap.org/faq/data/cache/42.html
# - http://www.openldap.org/faq/data/cache/136.html
# - http://www.zytrax.com/books/ldap/apa/indeces.html
#
olcDbIndex: objectClass eq
# Let us make Postfix's life easier. TODO: only if MX, lists.f.o, MDA, etc.
olcDbIndex: fripostIsStatusActive,fvd,fvl,fripostLocalAlias eq
olcDbIndex: fripostOptionalMaildrop pres
-# SyncProv/SyncRepl specific indexing. TODO: only if SyncProv/SyncRepl
+# SyncProv/SyncRepl specific indexing.
olcDbIndex: entryCSN,entryUUID eq
#
#
#
# 1. On single- and dual-core systems, change the maximum number of
# threads to 8. (The default, 16, is fine for 4- and 8-core systems.)
#
# dn: cn=config
# changetype: modify
# add: olcThreads
# olcThreads: 8
#
#
# 2. It may be a good idea to modify DB_CONFIG, depending on the output
# of
#
# db_stat -mh /var/lib/ldap/fripost | head -16
#
# (For optimal performance, the Requested pages found in the cache
# should be above 95%, and the dirty/clean pages forced from the cache
# should be 0.)
#
# and
#
# db_stat -ch /var/lib/ldap/fripost | head -16
#
# (For optimal performance, usage should be within 85% of the configured
# values.)
#
#
########################################################################
########################################################################
+# Sync Replication
+# TODO: replace the simple bind by Kerberos/GSSAPI
+#
+# References:
+# - http://www.openldap.org/doc/admin24/replication.html#Syncrepl
+# - http://www.zytrax.com/books/ldap/ch7/#ol-syncrepl-rap
+#
+{% if 'LDAP_provider' not in group_names %}
+olcSyncrepl: rid=000
+ provider=ldap://{{ LDAP_provider }}
+ type=refreshAndPersist
+ retry="5 5 300 +"
+ searchbase="ou=virtual,o=mailHosting,dc=fripost,dc=org"
+ attrs=objectClass,fvd,fvl,fripostMaildrop,fripostOptionalMaildrop,fripostLocalAlias,fripostPostmaster,fripostOwner
+ scope=sub
+ schemachecking=off
+ bindmethod=simple
+ binddn="cn=Postfix,ou=services,o=mailHosting,dc=fripost,dc=org"
+ credentials=postfix
+{% endif %}
+#
+#
+########################################################################
+########################################################################
# Access control
# /!\ WARN: All modification to the ACL should be reflected to the test
# /!\ suite as well!
#
# References:
# - http://www.openldap.org/doc/admin24/access-control.html
# - http://www.openldap.org/faq/data/cache/189.html
# - http://www.openldap.org/faq/data/cache/1140.html
# - http://www.openldap.org/faq/data/cache/1133.html
# - man 5 slapd.access
#
#
########################################################################
# Most common services: Postfix, Amavis, Dovecot
# (Most used ACLs are cheaper when written first.)
#
# Postfix have read access to the attribute it needs when eg, doing
# alias resolution.
olcAccess: to dn.children="ou=virtual,o=mailHosting,dc=fripost,dc=org"
attrs=entry,objectClass,fvd,fvl,fripostMaildrop,fripostOptionalMaildrop,fripostLocalAlias
@@ -116,40 +140,48 @@ olcAccess: to dn.exact="ou=virtual,o=mailHosting,dc=fripost,dc=org"
by users =0 break
#
# Search domain owners / postmasters (used by reserved-alias.pl).
olcAccess: to dn.children="ou=virtual,o=mailHosting,dc=fripost,dc=org"
attrs=entry,objectClass,fvd,fvl,fripostPostmaster,fripostOwner
filter=(&(objectClass=FripostVirtualDomain)(!(objectClass=FripostPendingEntry))(!(fripostIsStatusActive=FALSE)))
by dn.exact="cn=Postfix,ou=services,o=mailHosting,dc=fripost,dc=org" =rsd
by dn.exact="username=postfix,cn=peercred,cn=external,cn=auth" =rsd
by users =0 break
#
# Anonymous can authenticate into the services. (But not read or write the password.)
olcAccess: to dn.one="ou=services,o=mailHosting,dc=fripost,dc=org"
attrs=userPassword
by realanonymous =xd
#
# The following is required for SASL proxy Authorize the web application.
olcAccess: to dn.exact="cn=AdminWebPanel,ou=services,o=mailHosting,dc=fripost,dc=org"
attrs=entry,objectClass,authzTo
by realanonymous =x
#
+# The following is required for Sync Replication.
+{% if 'LDAP-provider' in group_names %}
+olcAccess: to dn.subtree="ou=virtual,o=mailHosting,dc=fripost,dc=org"
+ attrs=entry,objectClass,structuralObjectClass,createTimestamp,creatorsName,entryDN,entryUUID,modifiersName,modifyTimestamp,hasSubordinates,subschemaSubentry
+ by dn.exact="cn=Postfix,ou=services,o=mailHosting,dc=fripost,dc=org" =rsd
+ by users =0 break
+{% endif %}
+#
# 1. The WebPanel itself cannot bind, read or write passwords. This
# guarantees that, if an attacker gains its priviledge, it will *not* be
# able to change user passwords (which would allow him/her to read every
# emails). This is a trick to tackle the absence of 'realgroup'.
# 2. Anonymous users can bind.
# 3. Users can change their password (but not read it).
# 4. The postmaster of a domain can change (replace) his/her users' password (but not read it).
olcAccess: to dn.regex="^fvl=[^,]+,(fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=org)$"
filter=(objectClass=FripostVirtualUser)
attrs=userPassword
by realdn.exact="uid=AdminWebPanel@fripost.org,cn=auth" =0
by realanonymous =xd
by realself =w
by group/FripostVirtualDomain/fripostPostmaster.expand="$1" =w
by dn.onelevel="ou=managers,o=mailHosting,dc=fripost,dc=org" =w
#
# A catch-all, to be sure that noone else have access to the passwords.
olcAccess: to dn.subtree="o=mailHosting,dc=fripost,dc=org"
attrs=userPassword
by * =0