From 3a00a2e0b0cb47e2634f02ed38baeb7c5b37c5c4 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Sat, 14 Apr 2012 20:48:06 +0200 Subject: LDAP/aliases: arbitrary targets. --- fripost-docs.org | 224 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 147 insertions(+), 77 deletions(-) diff --git a/fripost-docs.org b/fripost-docs.org index f6198ee..73b0982 100644 --- a/fripost-docs.org +++ b/fripost-docs.org @@ -672,23 +672,30 @@ Jamm's (http://jamm.sourceforge.net/howto/html/implementation.html). dc=mail, dc=fripost, dc=org |- ou=mailboxes - | |- mail=user1@fripost.org - | | mail: user1@fripost.org - | | maildir: user1/ - | | mailLocalAddress: user1-alias@fripost.org + | |- uid=user1@fripost.org + | | uid: user1@fripost.org + | | userPassword: xxxxxx + | | maildir: fripost.org/user1/ | | isActive: TRUE + | | |- mailTarget=user1@fripost.org + | | | mailTarget: user1@fripost.org + | | | mailLocalAddress: user1-alias@example.org + | | | isActive: TRUE + | | `- dc=example.org + | | dc: example.org + | | isActive: TRUE + | | `- mailTarget=user1@fripost.org + | | | mailTarget: user1@fripost.org + | | | mailLocalAddress: user1@example.org + | | | isActive: TRUE + | | | + | | `- mailTarget=user1-alias@fripost.org | | - | `- mail=user2@fripost.org + | `- uid=user2@fripost.org | |- ou=domains - | |- dc=fripost.org - | | dc: fripost.org - | | isActive: TRUE - | | - | `- dc=example.org - | dc: example.org - | owner: mail=user1@fripost.org, ou=mailboxes, dc=mail, dc=fripost, dc=org - | mailLocalAddress: user1@example.org + | `- dc=fripost.org + | dc: fripost.org | isActive: TRUE | |- ou=managers @@ -707,28 +714,38 @@ Jamm's (http://jamm.sourceforge.net/howto/html/implementation.html). dn: cn=mail.fripost.org,cn=schema,cn=config objectClass: olcSchemaConfig cn: mail.fripost.org - olcAttributeTypes: ( 1.3.6.1.4.1.12461.1.1.1 NAME 'maildir' + olcAttributeTypes: ( 1.3.6.1.4.1.7914.1.2.1.1 NAME 'maildir' DESC 'The path to the maildir.' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) - olcAttributeTypes: ( 1.3.6.1.4.1.12461.1.1.3 NAME 'quota' + olcAttributeTypes: ( 1.3.6.1.4.1.7914.1.2.1.2 NAME 'quota' DESC 'The quota on a mailbox e.g., "50MB".' - EQUALITY caseExactIA5Match - SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) - olcAttributetypes: ( 1.3.6.1.4.1.12461.1.1.4 NAME 'isActive' + EQUALITY caseExactMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 SINGLE-VALUE ) + olcAttributetypes: ( 1.3.6.1.4.1.7914.1.2.1.3 NAME 'isActive' DESC 'Is the leaf active?' EQUALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE ) + olcAttributeTypes: ( 1.3.6.1.4.1.7914.1.2.1.4 NAME 'mailTarget' + DESC 'The target of e-mail virtual aliases.' + EQUALITY caseExactIA5Match + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) olcObjectclasses: ( 1.3.6.1.4.1.12461.1.2.1 NAME 'virtualDomain' SUP top STRUCTURAL - DESC 'Virtual Domains' + DESC 'Virtual Domains.' MUST ( dc $ isActive ) - MAY ( owner $ mailLocalAddress $ description ) ) - olcObjectclasses: ( 1.3.6.1.4.1.12461.1.2.2 NAME 'virtualMailbox' + MAY ( description ) ) + olcObjectclasses: ( 1.3.6.1.4.1.12461.1.2.2 NAME 'virtualAliases' SUP top STRUCTURAL - DESC 'Virtual Mailboxes' - MUST ( mail $ userPassword $ maildir $ isActive ) - MAY ( mailLocalAddress $ gn $ sn $ quota ) ) + DESC 'Virtual Aliases.' + MUST ( mailTarget $ isActive ) + MAY ( mailLocalAddress ) ) + olcObjectclasses: ( 1.3.6.1.4.1.12461.1.2.3 NAME 'virtualMailbox' + SUP top STRUCTURAL + DESC 'Virtual Mailboxes.' + MUST ( uid $ userPassword $ maildir $ isActive ) + MAY ( gn $ sn $ quota ) ) + Note: For the meaning of the sequences of digits above, grep the output of `ldapsearch -Y EXTERNAL -H ldapi:/// -b "cn=config"' @@ -788,10 +805,13 @@ be looking for e.g., the `mail' attribute. olcDbIndex: dc eq,sub - add: olcDbIndex - olcDbIndex: mail eq,sub + olcDbIndex: uid eq,sub - add: olcDbIndex olcDbIndex: mailLocalAddress eq + - + add: olcDbIndex + olcDbIndex: isActive eq ldapmodify -QY EXTERNAL -H ldapi:/// -f /etc/ldap/local/mail.fripost.org-index.ldif @@ -802,8 +822,9 @@ ldapmodify -QY EXTERNAL -H ldapi:/// -f /etc/ldap/local/mail.fripost.org-index.l olcDbIndex: ou eq olcDbIndex: objectClass pres,eq olcDbIndex: dc eq,sub - olcDbIndex: mail eq,sub + olcDbIndex: uid eq,sub olcDbIndex: mailLocalAddress eq + olcDbIndex: isActive eq ***** Restrict the access @@ -830,23 +851,24 @@ first. by anonymous auth - add: olcAccess - olcAccess: {1}to dn.children="ou=mailboxes,dc=mail,dc=fripost,dc=org" attrs=gn,sn + olcAccess: {1}to dn.one="ou=mailboxes,dc=mail,dc=fripost,dc=org" attrs=children,gn,sn by self write by dn.one="ou=managers,dc=mail,dc=fripost,dc=org" write + by * break - add: olcAccess - olcAccess: {2}to dn.children="ou=domains,dc=mail,dc=fripost,dc=org" attrs=mailLocalAddress - by dnattr=owner write + olcAccess: {2}to dn.regex="(.+,)?dc=[^,]+,(uid=[^,]+,ou=mailboxes,dc=mail,dc=fripost,dc=org)$" + by dn.exact,expand="$2" write by dn.one="ou=managers,dc=mail,dc=fripost,dc=org" write by * break - add: olcAccess - olcAccess: {3}to dn.children="ou=domains,dc=mail,dc=fripost,dc=org" + olcAccess: {3}to dn.children="ou=domains,dc=mail,dc=fripost,dc=org" attrs=entry,dc by dn="cn=SMTP,ou=services,dc=mail,dc=fripost,dc=org" read by * break - add: olcAccess - olcAccess: {4}to dn.children="ou=mailboxes,dc=mail,dc=fripost,dc=org" + olcAccess: {4}to dn.children="ou=mailboxes,dc=mail,dc=fripost,dc=org" attrs=entry,dc,uid,maildir,mailLocalAddress,mailTarget by dn="cn=SMTP,ou=services,dc=mail,dc=fripost,dc=org" read by * break - @@ -862,17 +884,17 @@ ldapmodify -QY EXTERNAL -H ldapi:/// -f /etc/ldap/local/mail.fripost.org-acl.ldi :: ldapsearch -LLLQY EXTERNAL -H ldapi:/// -b cn=config "(olcDatabase={1}hdb)" [...] olcAccess: {0}to dn.children="dc=mail,dc=fripost,dc=org" attrs=userPassword by self write by dn.one="ou=managers,dc=mail,dc=fripost,dc=org" write by anonymous auth - olcAccess: {1}to dn.children="ou=mailboxes,dc=mail,dc=fripost,dc=org" attrs=gn,sn by self write by dn.one="ou=managers,dc=mail,dc=fripost,dc=org" write - olcAccess: {2}to dn.children="ou=domains,dc=mail,dc=fripost,dc=org" attrs=mailLocalAddress by dnattr=owner write by dn.one="ou=managers,dc=mail,dc=fripost,dc=org" write - olcAccess: {3}to dn.children="ou=domains,dc=mail,dc=fripost,dc=org" by dn="cn=SMTP,ou=services,dc=mail,dc=fripost,dc=org" read by * break - olcAccess: {4}to dn.children="ou=mailboxes,dc=mail,dc=fripost,dc=org" by dn="cn=SMTP,ou=services,dc=mail,dc=fripost,dc=org" read by * break + olcAccess: {1}to dn.one="ou=mailboxes,dc=mail,dc=fripost,dc=org" attrs=children,gn,sn by self write by dn.one="ou=managers,dc=mail,dc=fripost,dc=org" write + olcAccess: {2}to dn.regex="(.+,)?dc=[^,]+,(uid=[^,]+,ou=mailboxes,dc=mail,dc=fripost,dc=org)$" by dn.exact,expand="$2" write by dn.one="ou=managers,dc=mail,dc=fripost,dc=org" write by * break + olcAccess: {3}to dn.children="ou=domains,dc=mail,dc=fripost,dc=org" attrs=dc,entry by dn="cn=SMTP,ou=services,dc=mail,dc=fripost,dc=org" read by * break + olcAccess: {4}to dn.children="ou=mailboxes,dc=mail,dc=fripost,dc=org" attrs=entry,dc,uid,maildir,mailLocalAddress,mailTarget by dn="cn=SMTP,ou=services,dc=mail,dc=fripost,dc=org" read by * break olcAccess: {5}to dn.subtree="dc=mail,dc=fripost,dc=org" by dn.one="ou=managers,dc=mail,dc=fripost,dc=org" write by * search olcAccess: {6}to attrs=userPassword,shadowLastChange by self write by anonymous auth by dn="cn=admin,dc=fripost,dc=org" write by * none olcAccess: {7}to dn.base="" by * read olcAccess: {8}to * by self write by dn="cn=admin,dc=fripost,dc=org" write by * read [...] -Note: Users are here able to manage their aliases themselves. Before inserting, we should +Note: Users are here allowed to manage their aliases themselves. Before inserting, we should ensure that aliases are fully qualified with the domain they own! Otherwise it'd be easy to steal aliases and probably even spy on other users... @@ -925,33 +947,54 @@ To delete a leaf or a sub-tree: objectClass: organizationalRole userPassword: {SSHA}xxxxxxx - dn: mail=user@fripost.org,ou=mailboxes,dc=mail,dc=fripost,dc=org - mail: user@fripost.org + dn: uid=user@fripost.org,ou=mailboxes,dc=mail,dc=fripost,dc=org + uid: user@fripost.org objectClass: top - objectClass: inetLocalMailRecipient objectClass: virtualMailbox gn: First Name sn: Last Name userPassword: {SSHA}xxxxxxx maildir: fripost.org/user/ isActive: TRUE - mailLocalAddress: user-alias@fripost.org - dn: dc=fripost.org,ou=domains,dc=mail,dc=fripost,dc=org + dn: mailTarget=user@fripost.org,uid=user@fripost.org,ou=mailboxes,dc=mail,dc=fripost,dc=org + mailTarget: user@fripost.org + objectClass: top + objectClass: inetLocalMailRecipient + objectClass: virtualAliases + mailLocalAddress: user-alias@fripost.org + isActive: TRUE + + dn: dc=example.org,uid=user@fripost.org,ou=mailboxes,dc=mail,dc=fripost,dc=org + dc: example.org objectClass: top objectClass: virtualDomain - dc: fripost.org isActive: TRUE - - dn: dc=example.org,ou=domains,dc=mail,dc=fripost,dc=org + + dn: mailTarget=user@fripost.org,dc=example.org,uid=user@fripost.org,ou=mailboxes,dc=mail,dc=fripost,dc=org + mailTarget: user@fripost.org objectClass: top objectClass: inetLocalMailRecipient - objectClass: virtualDomain - dc: example.org + objectClass: virtualAliases isActive: TRUE - owner: mail=user@fripost.org,ou=mailboxes,dc=mail,dc=fripost,dc=org mailLocalAddress: user@example.org mailLocalAddress: user-alias@example.org + + dn: uid=user2@fripost.org,ou=mailboxes,dc=mail,dc=fripost,dc=org + uid: user2@fripost.org + objectClass: top + objectClass: virtualMailbox + gn: First Name + sn: Last Name + userPassword: {SSHA}xxxxxx + maildir: fripost.org/user2/ + isActive: FALSE + + dn: dc=fripost.org,ou=domains,dc=mail,dc=fripost,dc=org + dc: fripost.org + objectClass: top + objectClass: virtualDomain + isActive: TRUE ldapadd -cxWD cn=admin,dc=fripost,dc=org -f /tmp/populate.ldif @@ -962,11 +1005,11 @@ e.g., `slappasswd -h "{SSHA}"'. **** Check the SASL binds (authentication) -ldapwhoami -xD "mail=user@fripost.org,ou=mailboxes,dc=mail,dc=fripost,dc=org" -W +ldapwhoami -xD "uid=user@fripost.org,ou=mailboxes,dc=mail,dc=fripost,dc=org" -W should return the whole dn: -"mail=user@fripost.org,ou=mailboxes,dc=mail,dc=fripost,dc=org" +"uid=user@fripost.org,ou=mailboxes,dc=mail,dc=fripost,dc=org" **** Check the ACL @@ -983,11 +1026,19 @@ and ***** Anonymous user -`ldapsearch -xLLL -b "ou=mailboxes,dc=mail,dc=fripost,dc=org"' should not return anything. +`ldapsearch -xLLL -b "ou=mailboxes,dc=mail,dc=fripost,dc=org"' should exit with status 0, but not return +anything. + +***** Services + +ldapsearch -xLLL -D "cn=SMTP,ou=services,dc=mail,dc=fripost,dc=org" -b 'ou=mailboxes,dc=mail,dc=fripost,dc=org' -W + +should only print what Postifx needs to retrieve that is, the domain names, the maildirs +and the e-mail addresses. ***** Self -ldapsearch -xLLL -D "mail=user@fripost.org,ou=mailboxes,dc=mail,dc=fripost,dc=org" -b 'ou=mailboxes,dc=mail,dc=fripost,dc=org' -W +ldapsearch -xLLL -D "uid=user@fripost.org,ou=mailboxes,dc=mail,dc=fripost,dc=org" -b 'ou=mailboxes,dc=mail,dc=fripost,dc=org' -W should return all the information for this very user, but not e.g., the password of the other users. @@ -996,17 +1047,17 @@ The user should be able to change his/her password, and aliases in his/her own d :: /tmp/usermod.ldif - dn: mail=user@fripost.org,ou=mailboxes,dc=mail,dc=fripost,dc=org + dn: uid=user@fripost.org,ou=mailboxes,dc=mail,dc=fripost,dc=org changetype: modify replace: userPassword userPassword: hop - dn: dc=example.org,ou=domains,dc=mail,dc=fripost,dc=org + dn: mailTarget=user@fripost.org,dc=example.org,uid=user@fripost.org,ou=mailboxes,dc=mail,dc=fripost,dc=org changetype: modify add: mailLocalAddress mailLocalAddress: user-alias2@example.org -ldapmodify -D "mail=user@fripost.org,ou=mailboxes,dc=mail,dc=fripost,dc=org" -W -f /tmp/usermod.ldif +ldapmodify -D "uid=user@fripost.org,ou=mailboxes,dc=mail,dc=fripost,dc=org" -W -f /tmp/usermod.ldif [Note: Still that should be wrapped up in a script, and there is no need to write on disk since the data is read from the standard input.] @@ -1017,16 +1068,36 @@ from having "mailLocalAddress: admin@fripost.org" for example! We now ensure that the leaf has been updated: - :: slapcat -s "mail=user@fripost.org,ou=mailboxes,dc=mail,dc=fripost,dc=org" + :: slapcat -s "uid=user@fripost.org,ou=mailboxes,dc=mail,dc=fripost,dc=org" [...] userPassword:: aG9w entryCSN: 20120404215647.957317Z#000000#000#000000 - modifiersName: mail=user@fripost.org,ou=mailboxes,dc=mail,dc=fripost,dc=org + modifiersName: uid=user@fripost.org,ou=mailboxes,dc=mail,dc=fripost,dc=org modifyTimestamp: 20120404215647Z -Doing the same thing with eg, trying to change the `maildir', `ldapmodify' -refuses with `Insufficient access (50)'. +Also, he/she's allowed to add new virtual domains: + + :: /tmp/newdomain.ldif + + dn: dc=example2.org,uid=user@fripost.org,ou=mailboxes,dc=mail,dc=fripost,dc=org + objectClass: top + objectClass: virtualDomain + dc: example2.org + isActive: TRUE + + dn: mailTarget=user-alias@fripost.org,dc=example2.org,uid=user@fripost.org,ou=mailboxes,dc=mail,dc=fripost,dc=org + mailTarget: user-alias@fripost.org + objectClass: top + objectClass: inetLocalMailRecipient + objectClass: virtualAliases + isActive: TRUE + mailLocalAddress: user@example2.org + +ldapadd -D "uid=user@fripost.org,ou=mailboxes,dc=mail,dc=fripost,dc=org" -W -f /tmp/newdomain.ldif + +On other modifications, for instance of `maildir', `ldapmodify' +should refuse with `Insufficient access (50)'. *** Configuring the main IMAP server **** Install packages @@ -1097,8 +1168,8 @@ sudo aptitude install dovecot-imapd :: /etc/postfix/main.cf virtual_mailbox_domains = ldap:$config_directory/ldap_virtual_mailbox_domains.cf - virtual_mailbox_maps = ldap:$config_directory/ldap_virtual_mailbox_maps.cf virtual_alias_maps = ldap:$config_directory/ldap_virtual_alias_maps.cf + virtual_mailbox_maps = ldap:$config_directory/ldap_virtual_mailbox_maps.cf [...] @@ -1113,7 +1184,7 @@ http://www.tehinterweb.co.uk/roundcube/#pisieverules server_host = ldap://localhost/ version = 3 - search_base = ou=domains,dc=mail,dc=fripost,dc=org + search_base = dc=mail,dc=fripost,dc=org bind = yes bind_dn = cn=SMTP,ou=services,dc=mail,dc=fripost,dc=org bind_pw = xxxxxx @@ -1122,10 +1193,11 @@ http://www.tehinterweb.co.uk/roundcube/#pisieverules Test it: postmap -q fripost.org ldap:/etc/postfix/ldap_virtual_domains_maps.cf || echo 'failed!' + postmap -q example.org ldap:/etc/postfix/ldap_virtual_domains_maps.cf || echo 'failed!' postmap -q fake.org ldap:/etc/postfix/ldap_virtual_domains_maps.cf || echo 'failed!' -:: /etc/postfix/ldap_virtual_mailbox_maps.cf +:: /etc/postfix/ldap_virtual_alias_maps.cf server_host = ldap://localhost/ version = 3 @@ -1133,29 +1205,27 @@ Test it: bind = yes bind_dn = cn=SMTP,ou=services,dc=mail,dc=fripost,dc=org bind_pw = xxxxxx - query_filter = (&(ObjectClass=virtualMailbox)(mail=%s)(isActive=TRUE)) - result_attribute = maildir + query_filter = (&(ObjectClass=virtualAliases)(mailLocalAddress=%s)(isActive=TRUE)) + result_attribute = mailTarget Test it: - postmap -q user@fripost.org ldap:/etc/postfix/ldap_virtual_mailbox_maps.cf + postmap -q user-alias@fripost.org ldap:/etc/postfix/ldap_virtual_alias_maps.cf + postmap -q user@example.org ldap:/etc/postfix/ldap_virtual_alias_maps.cf -:: /etc/postfix/ldap_virtual_alias_maps.cf +:: /etc/postfix/ldap_virtual_mailbox_maps.cf server_host = ldap://localhost/ version = 3 - search_base = dc=mail,dc=fripost,dc=org + search_base = ou=mailboxes,dc=mail,dc=fripost,dc=org bind = yes bind_dn = cn=SMTP,ou=services,dc=mail,dc=fripost,dc=org bind_pw = xxxxxx - query_filter = (&(|(ObjectClass=virtualMailbox)(ObjectClass=virtualDomain))(mailLocalAddress=%s)(isActive=TRUE)) - result_attribute = mail - special_result_attribute = owner + query_filter = (&(ObjectClass=virtualMailbox)(uid=%s)(isActive=TRUE)) + result_attribute = maildir Test it: - postmap -q user-alias@fripost.org ldap:/etc/postfix/ldap_virtual_alias_maps.cf - postmap -q user@example.org ldap:/etc/postfix/ldap_virtual_alias_maps.cf - + postmap -q user@fripost.org ldap:/etc/postfix/ldap_virtual_mailbox_maps.cf **** Test delivery @@ -1256,14 +1326,14 @@ Copy this file in /etc/dovecot, and chmod 600 it. Uncomment the following lines: hosts = localhost # Or wherever is our LDAP server ldap_version = 3 auth_bind = yes - auth_bind_userdn = mail=%u,ou=mailboxes,dc=mail,dc=fripost,dc=org + auth_bind_userdn = uid=%u,ou=mailboxes,dc=mail,dc=fripost,dc=org base = ou=mailboxes,dc=mail,dc=fripost,dc=org deref = never scope = subtree user_attrs = maildir=home=/home/mail/virtual/%$ - user_filter = (&(objectClass=virtualMailbox)(mail=%u)(dc=%d)(isActive=TRUE)) - pass_attrs = mail=user - pass_filter = (&(objectClass=virtualMailbox)(mail=%u)(dc=%d)(isActive=TRUE)) + user_filter = (&(objectClass=virtualMailbox)(uid=%u)(isActive=TRUE)) + pass_attrs = uid=user + pass_filter = (&(objectClass=virtualMailbox)(uid=%u)(isActive=TRUE)) (And the TLS-related lines in case we are not using a tunnel.) The "base" is the root of our tree structure, in our case dn="ou=mailboxes,dc=mail,dc=fripost,dc=org". @@ -1337,7 +1407,7 @@ test our installation.) ldap_auth_method: bind ldap_search_base: ou=mailboxes,dc=mail,dc=fripost,dc=org ldap_scope: sub - ldap_filter: (&(objectClass=virtualMailbox)(mail=%u)(dc=%d)(isActive=TRUE)) + ldap_filter: (&(objectClass=virtualMailbox)(uid=%u)(isActive=TRUE)) After restarting saslauthd (`/etc/init.d/saslauthd restart'), we can test the authentication: `testsaslauthd -u user@fripost.org -p password'. (The password -- cgit v1.2.3