diff options
Diffstat (limited to 'roles/common-LDAP')
-rwxr-xr-x | roles/common-LDAP/files/usr/local/sbin/slapcat-all.sh | 33 | ||||
-rw-r--r-- | roles/common-LDAP/tasks/main.yml | 58 | ||||
-rw-r--r-- | roles/common-LDAP/templates/etc/default/slapd.j2 | 4 | ||||
-rw-r--r-- | roles/common-LDAP/templates/etc/ldap/database.ldif.j2 | 163 |
4 files changed, 171 insertions, 87 deletions
diff --git a/roles/common-LDAP/files/usr/local/sbin/slapcat-all.sh b/roles/common-LDAP/files/usr/local/sbin/slapcat-all.sh index cd5abd9..db128c9 100755 --- a/roles/common-LDAP/files/usr/local/sbin/slapcat-all.sh +++ b/roles/common-LDAP/files/usr/local/sbin/slapcat-all.sh @@ -1,20 +1,31 @@ #!/bin/sh # Usage: slapcat-all.sh DIR -# Save all LDAP databases in DIR: DIR/0.ldif, DIR/1.ldif, ... +# Save all LDAP databases in DIR: DIR/SUFFIX0.ldif, DIR/SUFFIX1.ldif, ... set -ue -PATH=/usr/sbin:/sbin:/usr/bin:/bin +PATH="/usr/bin:/bin" +export PATH -target="$1" +TARGET="$1" umask 0077 -prefix=slapcat- -slapcat -n0 -l"$target/${prefix}0.ldif" -n=$(grep -Ec '^dn:\s+olcDatabase={[1-9][0-9]*}' "$target/${prefix}0.ldif") +ldapsearch() { + command ldapsearch -H "ldapi://" -QY EXTERNAL "$@" +} -while [ $n -gt 0 ]; do - # the Monitor backend can't be slapcat(8)'ed - grep -qE "^dn:\s+olcDatabase=\{$n\}monitor,cn=config$" "$target/${prefix}0.ldif" || slapcat -n$n -l"$target/${prefix}$n.ldif" - n=$(( $n - 1 )) -done +backup_database() { + local base="$1" + ldapsearch -b "$base" \+ \* >"$TARGET/$base.ldif" +} + +backup_database "cn=config" + +SUFFIXES="$TARGET/slapd-suffixes" +ldapsearch -LLL -oldif-wrap="no" -b "cn=config" "(&(objectClass=olcDatabaseConfig)(objectClass=olcMdbConfig))" "olcSuffix" >"$SUFFIXES" +sed -n -i "s/^olcSuffix:\\s*//p" "$SUFFIXES" + +while IFS= read -r b; do + [ "${b%,dc=fripost-test,dc=org}" = "$b" ] || continue + backup_database "$b" +done <"$SUFFIXES" diff --git a/roles/common-LDAP/tasks/main.yml b/roles/common-LDAP/tasks/main.yml index aff0e58..e17bc3a 100644 --- a/roles/common-LDAP/tasks/main.yml +++ b/roles/common-LDAP/tasks/main.yml @@ -1,112 +1,120 @@ # XXX If #742056 gets fixed, we should preseed slapd to use peercreds as # RootDN once the fix enters stable. - name: Install OpenLDAP - apt: pkg={{ item }} - with_items: + apt: pkg={{ packages }} + vars: + packages: - slapd - ldap-utils - ldapvi - db-util - - python-ldap + - python3-ldap # for the 'slapd2' munin plugin - libnet-ldap-perl - libauthen-sasl-perl - name: Configure slapd template: src=etc/default/slapd.j2 dest=/etc/default/slapd owner=root group=root mode=0644 register: r1 notify: - Restart slapd - name: Create directory /etc/ldap/ssl file: path=/etc/ldap/ssl state=directory owner=root group=root mode=0755 tags: - genkey -# XXX: It's ugly to list all roles here, and to prunes them with a -# conditional... - name: Generate a private key and a X.509 certificate for slapd - # XXX: GnuTLS (libgnutls26 2.12.20-8+deb7u2, found in Wheezy) doesn't - # support ECDSA; and slapd doesn't seem to support DHE (!?) so - # we're stuck with "plain RSA" Key-Exchange. Also, there is a bug with - # SHA-512. command: genkeypair.sh x509 --pubkey=/etc/ldap/ssl/{{ item.name }}.pem --privkey=/etc/ldap/ssl/{{ item.name }}.key --ou=LDAP {{ item.ou }} --cn={{ item.name }} - --usage=digitalSignature,keyEncipherment,keyCertSign - -t rsa -b 4096 -h sha256 - --chown="root:openldap" --chmod=0640 + --usage=digitalSignature,keyEncipherment + -t ed25519 + --owner=root --group=openldap --mode=0640 register: r2 changed_when: r2.rc == 0 failed_when: r2.rc > 1 with_items: - - { group: 'LDAP-provider', name: ldap.fripost.org, ou: } + - { group: 'LDAP_provider', name: ldap.fripost.org, ou: } - { group: 'MX', name: mx, ou: --ou=SyncRepl } - { group: 'lists', name: lists, ou: --ou=SyncRepl } when: "item.group in group_names" + notify: + - Restart slapd + tags: + - genkey + +- name: Fetch the SyncProv's X.509 certificate + # Ensure we don't fetch private data + become: False + fetch_cmd: cmd="openssl x509" + stdin=/etc/ldap/ssl/ldap.fripost.org.pem + dest=certs/ldap/ldap.fripost.org.pem + when: "'LDAP_provider' in group_names" tags: - genkey - name: Fetch slapd's X.509 certificate # Ensure we don't fetch private data - sudo: False - fetch: src=/etc/ldap/ssl/{{ item.name }}.pem - dest=certs/ldap/ - fail_on_missing=yes - flat=yes + become: False + fetch_cmd: cmd="openssl x509" + stdin=/etc/ldap/ssl/{{ item.name }}.pem + dest=certs/ldap/syncrepl/{{ item.name }}@{{ inventory_hostname_short }}.pem with_items: - - { group: 'LDAP-provider', name: ldap.fripost.org } - { group: 'MX', name: mx } - { group: 'lists', name: lists } when: "item.group in group_names" tags: - genkey - name: Copy the SyncProv's server certificate copy: src=certs/ldap/ldap.fripost.org.pem dest=/etc/ldap/ssl/ldap.fripost.org.pem owner=root group=root mode=0644 - when: "'LDAP-provider' not in group_names" + when: "'LDAP_provider' not in group_names" tags: - genkey - name: Copy the SyncRepls's client certificates - assemble: src=certs/ldap remote_src=no - dest=/etc/ldap/ssl/clients.pem + assemble: src=certs/ldap/syncrepl remote_src=no + dest=/etc/ldap/ssl/syncrepl.pem owner=root group=root mode=0644 - when: "'LDAP-provider' in group_names" + when: "'LDAP_provider' in group_names" tags: - genkey + register: r3 + notify: + - Restart slapd - name: Start slapd service: name=slapd state=started - when: not (r1.changed or r2.changed) + when: not (r1.changed or r2.changed or r3.changed) - meta: flush_handlers - name: Copy fripost & amavis' schema copy: src=etc/ldap/schema/{{ item }} dest=/etc/ldap/schema/{{ item }} owner=root group=root mode=0644 # It'd certainly be nicer if we didn't have to deploy amavis' schema # everywhere, but we need the 'objectClass' in our replicates, hence # they need to be aware of the 'amavisAccount' class. with_items: - fripost.ldif - amavis.schema tags: - amavis - name: Load amavis' schema openldap: target=/etc/ldap/schema/amavis.schema format=slapd.conf name=amavis @@ -116,36 +124,36 @@ - name: Load the back_monitor overlay openldap: module=back_monitor # We assume a clean (=stock) cn=config - name: Configure the LDAP database openldap: target=etc/ldap/database.ldif.j2 local=template # On read-only replicates, you might have to temporarily switch back to # read-write, delete the SyncRepl, and delete the DN manually: # sudo ldapdelete -Y EXTERNAL -H ldapi:// cn=admin,dc=fripost,dc=org - name: Remove cn=admin,dc=fripost,dc=org openldap: name="cn=admin,dc=fripost,dc=org" delete=entry - name: Remove the rootDN under the 'config' database openldap: name="olcDatabase={0}config,cn=config" delete=olcRootDN,olcRootPW - name: Copy /usr/local/sbin/slapcat-all.sh copy: src=usr/local/sbin/slapcat-all.sh dest=/usr/local/sbin/slapcat-all.sh - owner=root group=root + owner=root group=staff mode=0755 - name: Install 'slapd2' Munin plugin # we don't install 'slapd_' because it doesn't support SASL binds and # ours is more parcimonious with LDAP connections file: src=/usr/local/share/munin/plugins/slapd2 dest=/etc/munin/plugins/slapd2 owner=root group=root state=link force=yes tags: - munin - munin-node notify: - Restart munin-node diff --git a/roles/common-LDAP/templates/etc/default/slapd.j2 b/roles/common-LDAP/templates/etc/default/slapd.j2 index 80c1be1..dd3f87e 100644 --- a/roles/common-LDAP/templates/etc/default/slapd.j2 +++ b/roles/common-LDAP/templates/etc/default/slapd.j2 @@ -3,44 +3,44 @@ # /etc/ldap/slapd.conf). 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 group_names | intersect(['MX','lists']) | sort %} +{% for i in group_names | intersect(['MX','lists','MSA']) | sort %} SLAPD_SERVICES="$SLAPD_SERVICES ldapi://%2Fvar%2Fspool%2Fpostfix-{{ postfix_instance[i].name }}%2Fprivate%2Fldapi/" {% endfor %} -{% if 'LDAP-provider' in group_names %} +{% if 'LDAP_provider' in group_names %} SLAPD_SERVICES="$SLAPD_SERVICES ldaps:///" {% 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 8310818..a0ac705 100644 --- a/roles/common-LDAP/templates/etc/ldap/database.ldif.j2 +++ b/roles/common-LDAP/templates/etc/ldap/database.ldif.j2 @@ -1,157 +1,159 @@ # Fripost's LDAP database definition # Copyright (c) 2013-2014 Guilhem Moulin <guilhem@fripost.org> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. dn: cn=config objectClass: olcGlobal cn: config -olcArgsFile: /var/run/slapd/slapd.args -olcPidFile: /var/run/slapd/slapd.pid +olcArgsFile: /run/slapd/slapd.args +olcPidFile: /run/slapd/slapd.pid olcLogLevel: none olcToolThreads: 1 {% if ansible_processor_vcpus > 4 %} olcThreads: {{ 2 * ansible_processor_vcpus }} {% else %} olcThreads: 8 {% endif %} -{% if 'LDAP-provider' in group_names %} +{% if 'LDAP_provider' in group_names %} olcTLSCertificateFile: /etc/ldap/ssl/ldap.fripost.org.pem olcTLSCertificateKeyFile: /etc/ldap/ssl/ldap.fripost.org.key # If we are being offered a client cert, it has to be trusted (in which # case we map the X.509 subject to a DN in our namespace), or we # terminate the connection. Not providing a certificate is fine for # TLS-protected simple binds, though. olcTLSVerifyClient: try -olcTLSCACertificateFile: /etc/ldap/ssl/clients.pem +olcTLSCACertificateFile: /etc/ldap/ssl/syncrepl.pem olcAuthzRegexp: "^(cn=[^,]+,ou=syncRepl),ou=LDAP,ou=SSLcerts,o=Fripost$" - "$1,dc=fripost,dc=org" + "dn.exact:$1,dc=fripost,dc=org" olcSaslSecProps: minssf=128,noanonymous,noplain,nodict olcTLSCipherSuite: PFS:%LATEST_RECORD_VERSION:!CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:!VERS-SSL3.0:!VERS-TLS1.0:!VERS-TLS1.1 +olcTLSDHParamFile: /etc/ssl/dhparams.pem {% endif %} olcLocalSSF: 128 # /!\ This is not portable! But we only use glibc's crypt(3), which # supports (salted, streched) SHA512 olcPasswordHash: {CRYPT} olcPasswordCryptSaltFormat: $6$%s dn: olcDatabase=monitor,cn=config objectClass: olcDatabaseConfig objectClass: olcMonitorConfig olcRootDN: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth olcAccess: to dn.subtree="cn=monitor" by dn.exact="username=munin,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" read by * =0 dn: olcDatabase=mdb,cn=config objectClass: olcDatabaseConfig objectClass: olcMdbConfig olcDbDirectory: /var/lib/ldap olcSuffix: dc=fripost,dc=org -{% if 'LDAP-provider' not in group_names and 'MX' in group_names %} +{% if 'LDAP_provider' not in group_names and 'MX' in group_names %} olcReadOnly: TRUE {% endif %} -{% if 'LDAP-provider' in group_names %} +{% if 'LDAP_provider' in group_names %} olcLastMod: TRUE olcDbCheckpoint: 512 15 {% else %} olcLastMod: FALSE {% endif %} # The root user has all rights on the whole database (when SASL-binding # on a UNIX socket). olcRootDN: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth # Ensure that all DIT access is made according to the LDAPv3 protocol, # and must use 1/ authentication, and 2/ SASL or TLS. (Local clients # should use ldapi:// and SASL/EXERNAL, while remote clients should use # TLS.) olcRequires: none LDAPv3 authc strong olcSecurity: simple_bind=128 ssf=128 update_ssf=128 # # ######################################################################## # Performance considerations # # To reindex an existing database, you have to # * Stop slapd sudo service slapd stop # * Reindex sudo -u openldap slapindex -b 'dc=fripost,dc=org' # * Restart slapd sudo service slapd start # olcDbIndex: objectClass eq # Let us make Postfix's life easier. -{% if 'LDAP-provider' in group_names %} +{% if 'LDAP_provider' in group_names %} olcDbIndex: fvd,fvl eq,sub olcDbIndex: fripostIsStatusActive eq {% elif 'MX' in group_names or 'MDA' in group_names %} olcDbIndex: fripostIsStatusActive,fvd,fvl eq {% endif %} -{% if 'LDAP-provider' in group_names %} +{% if 'LDAP_provider' in group_names %} olcDbIndex: fripostOptionalMaildrop,fripostMaildrop eq,sub olcDbIndex: fripostCanAddDomain,fripostCanAddAlias,fripostCanAddList,fripostOwner,fripostPostmaster,fripostListManager eq {% elif 'MX' in group_names %} olcDbIndex: fripostOptionalMaildrop pres {% endif %} -{% if 'LDAP-provider' in group_names %} +{% if 'LDAP_provider' in group_names %} +olcDbIndex: member,cn eq {% endif %} -{% if ('LDAP-provider' not in group_names and 'MX' in group_names) or - ('LDAP-provider' in group_names and groups.MX | difference([inventory_hostname])) %} +{% if ('LDAP_provider' not in group_names and 'MX' in group_names) or + ('LDAP_provider' in group_names and groups.MX | difference([inventory_hostname])) %} # SyncProv/SyncRepl specific indexing. olcDbIndex: entryCSN,entryUUID eq {% endif%} # # # 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 # # ######################################################################## # Sync Replication # # References: # - http://www.openldap.org/doc/admin24/replication.html#Syncrepl # - http://www.zytrax.com/books/ldap/ch7/#ol-syncrepl-rap # -{% if 'LDAP-provider' in group_names %} +{% if 'LDAP_provider' in group_names %} olcLimits: dn.onelevel="ou=syncRepl,dc=fripost,dc=org" time.soft=unlimited time.hard=unlimited size.soft=unlimited size.hard=unlimited {% endif %} -{% if 'MX' in group_names and 'LDAP-provider' not in group_names %} +{% if 'MX' in group_names and 'LDAP_provider' not in group_names %} # Test it: # LDAPSASL_MECH=external LDAPTLS_CACERT=/etc/ldap/ssl/ldap.fripost.org.pem LDAPTLS_CERT=/etc/ldap/ssl/mx.pem LDAPTLS_KEY=/etc/ldap/ssl/mx.key sudo -u openldap ldapwhoami -H ldaps://ldap.fripost.org/ # LDAPSASL_MECH=external LDAPTLS_CACERT=/etc/ldap/ssl/ldap.fripost.org.pem LDAPTLS_CERT=/etc/ldap/ssl/mx.pem LDAPTLS_KEY=/etc/ldap/ssl/mx.key sudo -u openldap ldapsearch -H ldaps://ldap.fripost.org/ -b ou=virtual,dc=fripost,dc=org olcSyncrepl: rid=000 provider=ldaps://ldap.fripost.org type=refreshAndPersist retry="10 30 300 +" searchbase="ou=virtual,dc=fripost,dc=org" attrs=objectClass,fvd,fvl,fripostIsStatusActive,fripostMaildrop,fripostOptionalMaildrop,fripostPostmaster,fripostOwner,fripostUseContentFilter,fripostListManager scope=sub sizelimit=unlimited schemachecking=off bindmethod=sasl saslmech=external tls_cert=/etc/ldap/ssl/mx.pem tls_key=/etc/ldap/ssl/mx.key tls_cacert=/etc/ldap/ssl/ldap.fripost.org.pem tls_reqcert=hard {% endif %} # @@ -196,300 +198,363 @@ olcAddContentAcl: TRUE # local ldapi:// socket (when using auth_binds, Dovecot delegates # authentication to the LDAP server). # * Authenticated users are allowed to change (ie replace) their # password through TLS-protected connections, but read access is not # granted. # * Domain postmasters are allowed to change (ie replace) their users' # password through TLS-protected connections, but read access is not # granted. # * The same goes for general admins. # * The same goes for local admins. olcAccess: to dn.regex="^fvl=[^,]+,(fvd=[^,]+,ou=virtual,dc=fripost,dc=org)$" filter=(objectClass=FripostVirtualUser) attrs=userPassword by realanonymous tls_ssf=128 =xd by realanonymous sockurl.regex="^ldapi://" =xd by realself tls_ssf=128 =w by group/FripostVirtualDomain/fripostPostmaster.expand="$1" tls_ssf=128 =w by dn.onelevel="ou=admins,dc=fripost,dc=org" tls_ssf=128 =w by group.exact="cn=admin,ou=groups,dc=fripost,dc=org" =w # -# TODO: are there other services which need to be able to simple bind? +# * Services can authenticate +{% if 'LDAP_provider' in group_names -%} +olcAccess: to dn.onelevel="ou=services,dc=fripost,dc=org" + filter=(objectClass=simpleSecurityObject) + attrs=userPassword + by realanonymous tls_ssf=128 =xd +{% endif -%} # # * Catch-all: no one else may access the passwords (including for # simple bind). olcAccess: to dn.subtree="dc=fripost,dc=org" attrs=userPassword by * =0 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Base # # * Only SyncRepl replicates may access operational attributes in the # subtree, when using a TLS-protected connection. -{% if 'LDAP-provider' in group_names -%} +{% if 'LDAP_provider' in group_names -%} olcAccess: to dn.subtree="ou=virtual,dc=fripost,dc=org" - attrs=entryDN,entryCSN,entryUUID,structuralObjectClass,hasSubordinates,subschemaSubentry + attrs=entryCSN,structuralObjectClass,hasSubordinates,subschemaSubentry by dn.onelevel="ou=syncRepl,dc=fripost,dc=org" tls_ssf=128 =rsd by * =0 # # * They may also read entries (ie, the attributes they have access to # as per the ACL below) in that subtree, when using a TLS-protected # connection. Listing entries (their DN) is required to replicate # deletions properly. olcAccess: to dn.subtree="ou=virtual,dc=fripost,dc=org" attrs=entry,objectClass by dn.onelevel="ou=syncRepl,dc=fripost,dc=org" tls_ssf=128 =rsd by group.exact="cn=admin,ou=groups,dc=fripost,dc=org" =wrsd by users =0 break olcAccess: to dn.children="ou=virtual,dc=fripost,dc=org" by group.exact="cn=admin,ou=groups,dc=fripost,dc=org" =wrsd by users =0 break {% endif -%} # # * Postfix may use the base as a searchBase on the MX:es, when # connecting a local ldapi:// socket from the 'private' directory in # one of the non-default instance's chroot. -# * So may Dovecot on the MDA (needed for the iterate filter), when -# SASL-binding using the EXTERNAL mechanism and connecting to a local -# ldapi:// socket. +# * So may _dovecot-auth-proxy on the MDA (needed for the iterate +# logic), when SASL-binding using the EXTERNAL mechanism and +# connecting to a local ldapi:// socket. +# * So may Nextcloud on the LDAP provider olcAccess: to dn.exact="ou=virtual,dc=fripost,dc=org" attrs=entry,objectClass filter=(objectClass=FripostVirtual) {% if 'MDA' in group_names -%} - by dn.exact="username=dovecot,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =sd + by dn.exact="username=_dovecot-auth-proxy,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =sd {% endif -%} {% if 'MX' in group_names -%} by dn.exact="username=postfix,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://%2Fvar%2Fspool%2Fpostfix-[-[:alnum:]]+%2Fprivate%2F" =sd {% endif -%} + {% if 'MSA' in group_names -%} + by dn.exact="username=_postfix-sender-login,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =sd + {% endif -%} + {% if 'LDAP_provider' in group_names -%} + by dn.exact="cn=nextcloud,ou=services,dc=fripost,dc=org" tls_ssf=128 =sd + {% endif -%} by users =0 break # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Domain entries # # * The SyncRepl replicates have read access to the entry itself, when # using a TLS-protected connection. # * So has Postfix, when connecting a local ldapi:// socket from the # 'private' directory in one of the non-default instance's chroot. -# * So has Dovecot on the MDA (for the iterate filter), when -# SASL-binding using the EXTERNAL mechanism and connecting to a local -# ldapi:// socket. -# * Amavis may use the entry as searchBase (required to look for the -# per-user preferences) but doesn't have read access to the entry. # * The 'nobody' UNIX user has read access on the MX:es, when using # SASL-binding using the EXTERNAL mechanism and connecting to a local # ldapi:// socket. This is required for the 'reserved-alias.pl' # script. +# * Amavis may use the entry as searchBase (required to look for the +# per-user preferences) but doesn't have read access to the entry. +# * So has _dovecot-auth-proxy on the MDA (for the iterate logic), when +# SASL-binding using the EXTERNAL mechanism and connecting to a local +# ldapi:// socket. +# * So has _postfix-sender-login on the submission service to verify +# envelope sender ownership olcAccess: to dn.regex="^fvd=[^,]+,ou=virtual,dc=fripost,dc=org$" attrs=entry,objectClass,fvd filter=(&(objectClass=FripostVirtualDomain)(!(objectClass=FripostPendingEntry))) - {% if 'LDAP-provider' in group_names -%} - {% if groups.MX | difference([inventory_hostname]) -%} + {% if 'LDAP_provider' in group_names and groups.MX | difference([inventory_hostname]) -%} by dn.exact="cn=mX,ou=syncRepl,dc=fripost,dc=org" tls_ssf=128 =rsd {% endif -%} - {% endif -%} + {% if 'MX' in group_names -%} by dn.exact="username=postfix,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://%2Fvar%2Fspool%2Fpostfix-[-[:alnum:]]+%2Fprivate%2F" =rsd + by dn.exact="username=nobody,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =rsd + {% endif -%} {% if 'MDA' in group_names -%} - by dn.exact="username=dovecot,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =rsd by dn.exact="username=amavis,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =sd {% endif -%} - {% if 'MX' in group_names -%} - by dn.exact="username=nobody,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =rsd + {% if 'IMAP' in group_names -%} + by dn.exact="username=_dovecot-auth-proxy,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =rsd + {% endif -%} + {% if 'MSA' in group_names -%} + by dn.exact="username=_postfix-sender-login,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =rsd {% endif -%} by users =0 break # # * The SyncRepl MX replicates can check whether a virtual domain is # active, and read the destination address for catch-alls, when using # a TLS-protected connection. # * So can Postfix on the MX:es, when connecting a local ldapi:// socket # from the 'private' directory in one of the non-default instance's # chroot. -{% if 'MX' in group_names or ('LDAP-provider' in group_names and groups.MX | difference([inventory_hostname])) %} +{% if 'MX' in group_names or ('LDAP_provider' in group_names and groups.MX | difference([inventory_hostname])) %} olcAccess: to dn.regex="^fvd=[^,]+,ou=virtual,dc=fripost,dc=org$" attrs=fripostIsStatusActive,fripostOptionalMaildrop filter=(&(objectClass=FripostVirtualDomain)(!(objectClass=FripostPendingEntry))) - {% if 'LDAP-provider' in group_names and groups.MX | difference([inventory_hostname]) -%} + {% if 'LDAP_provider' in group_names and groups.MX | difference([inventory_hostname]) -%} by dn.exact="cn=mX,ou=syncRepl,dc=fripost,dc=org" tls_ssf=128 =rsd {% endif -%} {% if 'MX' in group_names -%} by dn.exact="username=postfix,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://%2Fvar%2Fspool%2Fpostfix-[-[:alnum:]]+%2Fprivate%2F" =rsd {% endif -%} by users =0 break {% endif %} # # * The 'nobody' UNIX user can list the domain owners and postmasters on # the MX:es, when SASL-binding using the EXTERNAL mechanism and # connecting to a local ldapi:// socket. This is required for the # 'reserved-alias.pl' script. olcAccess: to dn.regex="^fvd=[^,]+,ou=virtual,dc=fripost,dc=org$" attrs=fripostOwner,fripostPostmaster filter=(&(objectClass=FripostVirtualDomain)(!(objectClass=FripostPendingEntry))) - {% if 'LDAP-provider' in group_names and groups.MX | difference([inventory_hostname]) -%} + {% if 'LDAP_provider' in group_names and groups.MX | difference([inventory_hostname]) -%} by dn.exact="cn=mX,ou=syncRepl,dc=fripost,dc=org" tls_ssf=128 =rsd {% endif -%} {% if 'MX' in group_names %} by dn.exact="username=nobody,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =rsd {% endif -%} by users =0 break # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Alias domain entries # # * The SyncRepl MX replicates have read access to the entry itself and # the destination domain it aliases to, when using a TLS-protected # connection. # * So has Postfix on the MX:es, when connecting a local ldapi:// socket # from the 'private' directory in one of the non-default instance's # chroot. -{% if 'MX' in group_names or ('LDAP-provider' in group_names and groups.MX | difference([inventory_hostname])) %} +{% if 'MX' in group_names or ('LDAP_provider' in group_names and groups.MX | difference([inventory_hostname])) %} olcAccess: to dn.regex="^fvd=[^,]+,ou=virtual,dc=fripost,dc=org$" attrs=entry,fripostMaildrop filter=(&(objectClass=FripostVirtualAliasDomain)(!(objectClass=FripostPendingEntry))) - {% if 'LDAP-provider' in group_names and groups.MX | difference([inventory_hostname]) -%} + {% if 'LDAP_provider' in group_names and groups.MX | difference([inventory_hostname]) -%} by dn.exact="cn=mX,ou=syncRepl,dc=fripost,dc=org" tls_ssf=128 =rsd {% endif -%} {% if 'MX' in group_names -%} by dn.exact="username=postfix,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://%2Fvar%2Fspool%2Fpostfix-[-[:alnum:]]+%2Fprivate%2F" =rsd {% endif -%} by users =0 break {% endif %} # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # User entries # # * The SyncRepl replicates have read access to the entry itself, when # using a TLS-protected connection. # * So has Postfix, when connecting a local ldapi:// socket from the # 'private' directory in one of the non-default instance's chroot. -# * So has Dovecot on the MDA (for the iterate filter), when +# * So has _dovecot-auth-proxy on the MDA (for the iterate logic), when # SASL-binding using the EXTERNAL mechanism and connecting to a local # ldapi:// socket. # * So has Amavis on the MDA, when SASL-binding using the EXTERNAL # mechanism and connecting to a local ldapi:// socket. olcAccess: to dn.regex="^fvl=[^,]+,fvd=[^,]+,ou=virtual,dc=fripost,dc=org$" attrs=entry,objectClass,fvl filter=(objectClass=FripostVirtualUser) - {% if 'LDAP-provider' in group_names and groups.MX | difference([inventory_hostname]) -%} + {% if 'LDAP_provider' in group_names and groups.MX | difference([inventory_hostname]) -%} by dn.exact="cn=mX,ou=syncRepl,dc=fripost,dc=org" tls_ssf=128 =rsd {% endif -%} + {% if 'MX' in group_names -%} by dn.exact="username=postfix,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://%2Fvar%2Fspool%2Fpostfix-[-[:alnum:]]+%2Fprivate%2F" =rsd + {% endif -%} {% if 'MDA' in group_names -%} - by dn.exact="username=dovecot,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =rsd by dn.exact="username=amavis,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =rsd {% endif -%} + {% if 'IMAP' in group_names -%} + by dn.exact="username=_dovecot-auth-proxy,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =rsd + {% endif -%} + {% if 'MSA' in group_names -%} + by dn.exact="username=_postfix-sender-login,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =rsd + {% endif -%} by users =0 break # # * The SyncRepl MX replicates can check whether a virtual user is # active, when using a TLS-protected connection. # * So can Postfix on the MX:es, when connecting a local ldapi:// socket # from the 'private' directory in one of the non-default instance's # chroot. -{% if 'MX' in group_names or ('LDAP-provider' in group_names and groups.MX | difference([inventory_hostname])) %} +{% if 'MX' in group_names or ('LDAP_provider' in group_names and groups.MX | difference([inventory_hostname])) %} olcAccess: to dn.regex="^fvl=[^,]+,fvd=[^,]+,ou=virtual,dc=fripost,dc=org$" attrs=fripostIsStatusActive,fripostUseContentFilter filter=(objectClass=FripostVirtualUser) - {% if 'LDAP-provider' in group_names and groups.MX | difference([inventory_hostname]) -%} + {% if 'LDAP_provider' in group_names and groups.MX | difference([inventory_hostname]) -%} by dn.exact="cn=mX,ou=syncRepl,dc=fripost,dc=org" tls_ssf=128 =rsd {% endif -%} {% if 'MX' in group_names -%} by dn.exact="username=postfix,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://%2Fvar%2Fspool%2Fpostfix-[-[:alnum:]]+%2Fprivate%2F" =rsd {% endif -%} by users =0 break {% endif %} {% if 'MDA' in group_names %} # # * Amavis can look for per-user configuration options, when # SASL-binding using the EXTERNAL mechanism and connecting to a local # ldapi:// socket. # TODO: only allow it to read the configuration options users are allowed # to set and modify. olcAccess: to dn.regex="^fvl=[^,]+,fvd=[^,]+,ou=virtual,dc=fripost,dc=org$" attrs=@AmavisAccount filter=(&(objectClass=FripostVirtualUser)(objectClass=AmavisAccount)(fripostIsStatusActive=TRUE)(fripostUseContentFilter=TRUE)) by dn.exact="username=amavis,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =rsd by users =0 break # # * Dovecot can look for user quotas, when SASL-binding using the # EXTERNAL mechanism and connecting to a local ldapi:// socket. olcAccess: to dn.regex="^fvl=[^,]+,fvd=[^,]+,ou=virtual,dc=fripost,dc=org$" attrs=fripostUserQuota filter=(objectClass=FripostVirtualUser) by dn.exact="username=dovecot,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =rsd by users =0 break {% endif %} # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Alias entries # # * The SyncRepl MX replicates can read the entry itelf, whether it # is active, and the address(es) it aliases to, when using a # TLS-protected connection. # * So can Postfix on the MX:es, when connecting a local ldapi:// socket # from the 'private' directory in one of the non-default instance's # chroot. -{% if 'MX' in group_names or ('LDAP-provider' in group_names and groups.MX | difference([inventory_hostname])) %} +{% if 'MX' in group_names or ('LDAP_provider' in group_names and groups.MX | difference([inventory_hostname])) %} olcAccess: to dn.regex="^fvl=[^,]+,fvd=[^,]+,ou=virtual,dc=fripost,dc=org$" attrs=entry,objectClass,fvl,fripostMaildrop,fripostIsStatusActive filter=(objectClass=FripostVirtualAlias) - {% if 'LDAP-provider' in group_names and groups.MX | difference([inventory_hostname]) -%} + {% if 'LDAP_provider' in group_names and groups.MX | difference([inventory_hostname]) -%} by dn.exact="cn=mX,ou=syncRepl,dc=fripost,dc=org" tls_ssf=128 =rsd {% endif -%} {% if 'MX' in group_names -%} by dn.exact="username=postfix,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://%2Fvar%2Fspool%2Fpostfix-[-[:alnum:]]+%2Fprivate%2F" =rsd {% endif -%} by users =0 break {% endif %} # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # List entries # # * The SyncRepl replicates can read the entry itelf and the list manager, when # using a TLS-protected connection. # * So can Postfix on the MX:es, when connecting a local ldapi:// socket # from the 'private' directory in one of the non-default instance's chroot. -{% if 'MX' in group_names or ('LDAP-provider' in group_names and groups.MX | difference([inventory_hostname])) %} +{% if 'MX' in group_names or ('LDAP_provider' in group_names and groups.MX | difference([inventory_hostname])) %} olcAccess: to dn.regex="^fvl=[^,]+,fvd=[^,]+,ou=virtual,dc=fripost,dc=org$" attrs=entry,objectClass,fvl,fripostListManager filter=(&(objectClass=FripostVirtualList)(!(objectClass=FripostPendingEntry))) - {% if 'LDAP-provider' in group_names and groups.MX | difference([inventory_hostname]) -%} + {% if 'LDAP_provider' in group_names and groups.MX | difference([inventory_hostname]) -%} by dn.exact="cn=mX,ou=syncRepl,dc=fripost,dc=org" tls_ssf=128 =rsd {% endif -%} {% if 'MX' in group_names -%} by dn.exact="username=postfix,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://%2Fvar%2Fspool%2Fpostfix-[-[:alnum:]]+%2Fprivate%2F" =rsd {% endif -%} by users =0 break {% endif %} # # * The SyncRepl MX replicates can check whether a virtual list is # active when using a TLS-protected connection. # * So can Postfix on the MX:es, when connecting a local ldapi:// socket # from the 'private' directory in one of the non-default instance's # chroot. -{% if 'MX' in group_names or ('LDAP-provider' in group_names and groups.MX | difference([inventory_hostname])) %} +{% if 'MX' in group_names or ('LDAP_provider' in group_names and groups.MX | difference([inventory_hostname])) %} olcAccess: to dn.regex="^fvl=[^,]+,fvd=[^,]+,ou=virtual,dc=fripost,dc=org$" attrs=fripostIsStatusActive filter=(&(objectClass=FripostVirtualList)(!(objectClass=FripostPendingEntry))) - {% if 'LDAP-provider' in group_names and groups.MX | difference([inventory_hostname]) -%} + {% if 'LDAP_provider' in group_names and groups.MX | difference([inventory_hostname]) -%} by dn.exact="cn=mX,ou=syncRepl,dc=fripost,dc=org" tls_ssf=128 =rsd {% endif -%} {% if 'MX' in group_names -%} by dn.exact="username=postfix,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://%2Fvar%2Fspool%2Fpostfix-[-[:alnum:]]+%2Fprivate%2F" =rsd {% endif -%} by users =0 break {% endif %} -{% if 'LDAP-provider' in group_names %} +# +# * The MSA's _postfix-sender-login user can read entry ownership to +# dermine the SASL login name(s) owning a given sender address +{% if 'MSA' in group_names %} +olcAccess: to dn.regex="^fvd=[^,]+,ou=virtual,dc=fripost,dc=org$" + attrs=fripostOwner,fripostPostmaster + filter=(|(objectClass=FripostVirtualAliasDomain)(objectClass=FripostVirtualDomain)) + by dn.exact="username=_postfix-sender-login,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =rsd + by users =0 break +olcAccess: to dn.regex="^fvl=[^,]+,fvd=[^,]+,ou=virtual,dc=fripost,dc=org$" + attrs=entry,objectClass,fvl,fripostOwner + filter=(|(objectClass=FripostVirtualAlias)(objectClass=FripostVirtualList)(objectClass=FripostVirtualUser)) + by dn.exact="username=_postfix-sender-login,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =rsd + by users =0 break +{% endif %} +{% if 'LDAP_provider' in group_names %} # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# Export Fripost members to Nextcloud +olcAccess: to dn.exact="fvd=fripost.org,ou=virtual,dc=fripost,dc=org" + attrs=entry,objectClass,fvd + filter=(&(objectClass=FripostVirtualDomain)(!(objectClass=FripostPendingEntry))) + by dn.exact="cn=nextcloud,ou=services,dc=fripost,dc=org" tls_ssf=128 =rsd + by users =0 break +olcAccess: to dn.regex="^fvl=[^,]+,fvd=fripost.org,ou=virtual,dc=fripost,dc=org$" + attrs=entry,entryDN,entryUUID,objectClass,fvl,fripostIsStatusActive + filter=(&(objectClass=FripostVirtualUser)(!(objectClass=FripostPendingEntry))(fripostIsStatusActive=TRUE)) + by dn.exact="cn=nextcloud,ou=services,dc=fripost,dc=org" tls_ssf=128 =rsd + by users =0 break +olcAccess: to dn.exact="ou=groups,dc=fripost,dc=org" + attrs=entry,objectClass + by dn.exact="cn=nextcloud,ou=services,dc=fripost,dc=org" tls_ssf=128 =rsd + by users =0 break +olcAccess: to dn.exact="cn=medlemmar,ou=groups,dc=fripost,dc=org" + by dn.exact="cn=nextcloud,ou=services,dc=fripost,dc=org" tls_ssf=128 =rsd + by users =0 break +olcAccess: to dn.exact="cn=styrelse,ou=groups,dc=fripost,dc=org" + by dn.exact="cn=nextcloud,ou=services,dc=fripost,dc=org" tls_ssf=128 =rsd + by users =0 break +# # TODO: allow users to edit their entry, etc # {% endif %} # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Catch-all # # * Catch all the breaks above. # * Deny any access to everyone else. olcAccess: to dn.subtree="dc=fripost,dc=org" by dn.children="ou=virtual,dc=fripost,dc=org" +0 by * =0 # vim: set filetype=ldif : |