diff --git a/README.md b/README.md index e8367d4..16ad813 100644 --- a/README.md +++ b/README.md @@ -70,8 +70,6 @@ SNM branch corresponding to your NixOS version. * [ ] [Mobileconfig](https://support.apple.com/guide/profile-manager/distribute-profiles-manually-pmdbd71ebc9/mac) * Improve the Forwarding Experience * [ ] Support [ARC](https://en.wikipedia.org/wiki/Authenticated_Received_Chain) signing with [Rspamd](https://rspamd.com/doc/modules/arc.html) -* User management - * [ ] Allow local and LDAP user to coexist * OpenID Connect * Depends on relevant clients adding support, e.g. [Thunderbird](https://bugzilla.mozilla.org/show_bug.cgi?id=1602166) diff --git a/docs/ldap.rst b/docs/ldap.rst index 4750e31..d65ccfd 100644 --- a/docs/ldap.rst +++ b/docs/ldap.rst @@ -40,15 +40,25 @@ follow best practices to simplify maintenance. Limitations ~~~~~~~~~~~ -We have various assertions in place, that prevent using LDAP together with -other features. Most of them are not technical limitations per se, but instead -lack configuration or validation. +Design choices +^^^^^^^^^^^^^^ -- Local users (:option:`mailserver.loginAccounts`) and aliases - (:option:`mailserver.extraVirtualAliases`) are not currently allowed with - :option:`mailserver.ldap.enable` enabled -- Aliases based on LDAP attributes are currently not implemented -- Quotas based on LDAP attributes are currently not implemented +These are intentional choices in how the mail server operates that affect the +LDAP integration. + +- For mail address routing local accounts always take priority over LDAP accounts. + +Planned +^^^^^^^ + +These are features we are interested in but require implementation, +documentation and tests. + +- Aliases based on LDAP attributes +- Quotas based on LDAP attributes + +Avoided +^^^^^^^ The following features will likely never be implemented, since they would complicate the setup significantly. @@ -58,7 +68,9 @@ complicate the setup significantly. - Use of ``homeDirectory``, ``uid``, ``gid`` LDAP attributes (we are committed to a virtual setup with one vmail user/uid/gid and UUID based home directories) - +- Declarative aliases through :option:`mailserver.extraVirtualAliases`. These + are limited to local accounts, because Postfix enforces sender ownership based + on login identity and does not consult virtual aliases for authorization. Enabling LDAP support ~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/release-notes.rst b/docs/release-notes.rst index e3122d4..9ee6ccc 100644 --- a/docs/release-notes.rst +++ b/docs/release-notes.rst @@ -30,6 +30,8 @@ NixOS 26.05 rather than their email address, which is more convenient and consistent with typical LDAP practices. The exact attribute can be customized through :option:`mailserver.ldap.attributes.username`. +- Local and LDAP accounts can now co-exist. For overlapping names and addresses + the local account will always win. - The following integrations are deprecated and will be removed before the next release: diff --git a/mail-server/assertions.nix b/mail-server/assertions.nix index 8a346ad..a3c2428 100644 --- a/mail-server/assertions.nix +++ b/mail-server/assertions.nix @@ -98,16 +98,6 @@ in ) config.mailserver.dkim.domains ) ) - ++ lib.optionals config.mailserver.ldap.enable [ - { - assertion = config.mailserver.loginAccounts == { }; - message = "When the LDAP support is enable (mailserver.ldap.enable = true), it is not possible to define mailserver.loginAccounts"; - } - { - assertion = config.mailserver.extraVirtualAliases == { }; - message = "When the LDAP support is enable (mailserver.ldap.enable = true), it is not possible to define mailserver.extraVirtualAliases"; - } - ] ++ lib.optionals (config.mailserver.ldap.enable && config.mailserver.mailDirectory != "/var/vmail") [ diff --git a/tests/ldap.nix b/tests/ldap.nix index a67362b..7cde1d0 100644 --- a/tests/ldap.nix +++ b/tests/ldap.nix @@ -1,7 +1,25 @@ +{ + pkgs, + ... +}: let + hashPassword = + password: + pkgs.runCommand "password-${password}-hashed" + { + buildInputs = [ pkgs.mkpasswd ]; + inherit password; + } + '' + mkpasswd -s <<<"$password" > $out + ''; + bindPassword = "unsafegibberish"; alicePassword = "testalice"; bobPassword = "testbob"; + carolPassword = "testcarol"; + frankPassword = "testfrank"; + malloryPassword = "testmallory"; in { name = "ldap"; @@ -83,6 +101,22 @@ in mail: bob@example.com homeDirectory: /home/bob userPassword: ${bobPassword} + + dn: cn=carol,ou=users,dc=example + entryUUID: 41240499-27e2-4fa2-be4f-4113a77661b1 + objectClass: inetOrgPerson + uid: carol + sn: Baz + mail: carol@example.com + userPassword: ${carolPassword} + + dn: cn=frank,ou=users,dc=example + entryUUID: ca16f594-f6b2-418f-87d3-0d02d746461f + objectClass: inetOrgPerson + uid: frank + sn: Moo + mail: frank@example.com + userPassword: ${frankPassword} ''; }; @@ -93,6 +127,24 @@ in localDnsResolver = false; indexDir = "/var/lib/dovecot/indices"; + extraVirtualAliases = { + # Steal frank@example.com from LDAP user frank + "frank@example.com" = "mallory@example.com"; + }; + + loginAccounts = { + # Colliding local account takes precedence over LDAP account with + # same address. + "carol@example.com" = { + hashedPasswordFile = hashPassword carolPassword; + }; + # Another account used as a virtual alias target to steal + # frank@example.com from the LDAP user frank + "mallory@example.com" = { + hashedPasswordFile = hashPassword malloryPassword; + }; + }; + ldap = { enable = true; uris = [ @@ -237,5 +289,37 @@ in ])) machine.succeed("journalctl -u postfix | grep -q 'Sender address rejected: not owned by user bob'") + with subtest("Local addresses take priority over those learnt from LDAP"): + # carol@example.com is routed to the local user account + machine.succeed(" ".join([ + "mail-check send-and-read", + "--smtp-port 465", + "--smtp-ssl", + "--smtp-host localhost", + "--smtp-username alice", # LDAP user + "--imap-host localhost", + "--imap-username carol@example.com", # Local user + "--from-addr alice@example.com", + "--to-addr carol@example.com", + "--src-password-file <(echo '${alicePassword}')", + "--dst-password-file <(echo '${carolPassword}')", + "--ignore-dkim-spf" + ])) + + # frank@example.com gets routed to mallory@example.com due to a virtual alias + machine.succeed(" ".join([ + "mail-check send-and-read", + "--smtp-port 465", + "--smtp-ssl", + "--smtp-host localhost", + "--smtp-username alice", # LDAP user + "--imap-host localhost", + "--imap-username mallory@example.com", # Local user + "--from-addr alice@example.com", + "--to-addr frank@example.com", + "--src-password-file <(echo '${alicePassword}')", + "--dst-password-file <(echo '${malloryPassword}')", + "--ignore-dkim-spf" + ])) ''; }