diff --git a/default.nix b/default.nix index ad000ff..3472d57 100644 --- a/default.nix +++ b/default.nix @@ -379,19 +379,24 @@ in ''; }; - dovecot = { - userAttrs = mkOption { - type = types.nullOr types.str; - default = null; + attributes = { + uuid = mkOption { + type = types.str; + default = "entryUUID"; + example = "uuid"; description = '' - LDAP attributes to be retrieved during userdb lookups. + The long-term stable LDAP attribute to reference accounts across + username changes. Used to determine a stable Dovecot home and + mail directory location. - See the users_attrs reference at - https://doc.dovecot.org/2.3/configuration_manual/authentication/ldap_settings_auth/#user-attrs - in the Dovecot manual. + Typically the `entryUUID` attribute as defined by [RFC4530]. + + [RFC4530]: https://www.rfc-editor.org/rfc/rfc4530.html ''; }; + }; + dovecot = { userFilter = mkOption { type = types.str; default = "mail=%{user}"; @@ -1630,5 +1635,8 @@ in [ "mailserver" "dkimKeyBits" ] [ "mailserver" "dkim" "defaults" "keyLength" ] ) + (mkRemovedOptionModule [ "mailserver" "ldap" "dovecot" "userAttrs" ] '' + The user_attrs field is now used internally to map the home and mail directories. + '') ]; } diff --git a/mail-server/dovecot.nix b/mail-server/dovecot.nix index 7831b0f..fda742a 100644 --- a/mail-server/dovecot.nix +++ b/mail-server/dovecot.nix @@ -61,6 +61,7 @@ let postfixCfg = config.services.postfix; + ldapUuidAttribute = cfg.ldap.attributes.uuid; ldapConfig = pkgs.writeTextFile { name = "dovecot-ldap.conf.ext.template"; text = '' @@ -76,9 +77,12 @@ let auth_bind = yes base = ${cfg.ldap.searchBase} scope = ${mkLdapSearchScope cfg.ldap.searchScope} - ${lib.optionalString (cfg.ldap.dovecot.userAttrs != null) '' - user_attrs = ${cfg.ldap.dovecot.userAttrs} - ''} + user_attrs = \ + ${ldapUuidAttribute}=${ldapUuidAttribute}, \ + =home=/var/vmail/ldap/%{ldap:${ldapUuidAttribute}}, \ + =mail=maildir:~/mail${maildirLayoutAppendix}${maildirUTF8FolderNames}${ + lib.optionalString (cfg.indexDir != null) ":INDEX=${cfg.indexDir}/ldap/%{ldap:${ldapUuidAttribute}}" + } user_filter = ${cfg.ldap.dovecot.userFilter} ${lib.optionalString (cfg.ldap.dovecot.passAttrs != "") '' pass_attrs = ${cfg.ldap.dovecot.passAttrs} @@ -444,13 +448,8 @@ in driver = ldap args = ${ldapConfFile} default_fields = \ - home=${cfg.mailDirectory}/ldap/%{user} \ uid=${toString cfg.vmailUID} \ - gid=${toString cfg.vmailUID} \ - mail=maildir:~/mail${maildirLayoutAppendix}${maildirUTF8FolderNames}${ - lib.optionalString (cfg.indexDir != null) ":INDEX=${cfg.indexDir}/ldap/%{user}" - } - + gid=${toString cfg.vmailUID} } ''} diff --git a/tests/ldap.nix b/tests/ldap.nix index b875715..d2e89fa 100644 --- a/tests/ldap.nix +++ b/tests/ldap.nix @@ -72,6 +72,7 @@ in ou: users dn: cn=alice,ou=users,dc=example + entryUUID: c52f777b-a6e8-4507-80f9-c4de47e8520d objectClass: inetOrgPerson cn: alice sn: Foo @@ -79,10 +80,16 @@ in userPassword: ${alicePassword} dn: cn=bob,ou=users,dc=example + entryUUID: f3b4e8ea-087f-42cc-95f0-cbfd99386092 objectClass: inetOrgPerson + objectClass: posixAccount cn: bob + uid: bob + uidNumber: 9999 + gidNumber: 9999 sn: Bar mail: bob@example.com + homeDirectory: /home/bob userPassword: ${bobPassword} ''; }; @@ -164,6 +171,12 @@ in machine.succeed("doveadm user -u alice@example.com") machine.succeed("doveadm user -u bob@example.com") + machine.succeed("doveadm user -f uid bob@example.com | grep ${toString nodes.machine.mailserver.vmailUID}") + machine.succeed("doveadm user -f gid bob@example.com | grep ${toString nodes.machine.mailserver.vmailUID}") + + machine.succeed("doveadm user -f home bob@example.com | grep ${nodes.machine.mailserver.mailDirectory}/ldap/f3b4e8ea-087f-42cc-95f0-cbfd99386092") + machine.succeed("doveadm user -f mail bob@example.com | grep 'maildir:~/mail:INDEX=${nodes.machine.mailserver.indexDir}/ldap/f3b4e8ea-087f-42cc-95f0-cbfd99386092'") + with subtest("Files containing secrets are only readable by root"): machine.succeed("ls -l /run/postfix/*.cf | grep -e '-rw------- 1 root root'") machine.succeed("ls -l /run/dovecot2/dovecot-ldap.conf.ext | grep -e '-rw------- 1 root root'") @@ -234,9 +247,5 @@ in ])) machine.succeed("journalctl -u postfix | grep -q 'Sender address rejected: not owned by user bob@example.com'") - with subtest("Check dovecot mail and index locations"): - # If these paths change we need a migration - machine.succeed("doveadm user -f home bob@example.com | grep ${nodes.machine.mailserver.mailDirectory}/ldap/bob@example.com") - machine.succeed("doveadm user -f mail bob@example.com | grep 'maildir:~/mail:INDEX=${nodes.machine.mailserver.indexDir}/ldap/bob@example.com'") ''; }