From 57bfae2d7eb74663f5211d2fa0cc0f861f64c6a0 Mon Sep 17 00:00:00 2001 From: emilylange Date: Sun, 24 May 2026 01:30:59 +0200 Subject: [PATCH 1/2] dovecot: fix non-default `cfg.ldap.attributes.password` The option got recently introduced, but never properly wired. --- mail-server/dovecot.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mail-server/dovecot.nix b/mail-server/dovecot.nix index deb7bca..7c4f439 100644 --- a/mail-server/dovecot.nix +++ b/mail-server/dovecot.nix @@ -438,7 +438,7 @@ in driver = "ldap"; filter = cfg.ldap.dovecot.passFilter; fields = { - password = "%{ldap:userPassword}"; + password = "%{ldap:${cfg.ldap.attributes.password}}"; }; ldap_connection_group = "ldap-passdb-conn"; }; From eea473ea124beea73d800f6a4f36900d781e8bcc Mon Sep 17 00:00:00 2001 From: emilylange Date: Sun, 24 May 2026 01:41:13 +0200 Subject: [PATCH 2/2] dovecot: reintroduce LDAP bind auth for passdb LDAP bind auth used to be enabled by default (and not configurable) before the dovecot 2.4 migration. I changed the default option value to match the old Dovecot 2.3 behavior. The use of authentication bind is required for LDAP servers that simply do not have such LDAP attribute like Kanidm, or in cases where the password scheme used is not supported by Dovecot. --- default.nix | 14 ++++++++++--- mail-server/dovecot.nix | 3 ++- tests/ldap.nix | 46 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 58 insertions(+), 5 deletions(-) diff --git a/default.nix b/default.nix index 9be324e..995de30 100644 --- a/default.nix +++ b/default.nix @@ -483,13 +483,21 @@ in }; password = mkOption { - type = types.str; - default = "userPassword"; - example = "unix_password"; + type = types.nullOr types.str; + default = null; + example = "userPassword"; description = '' The LDAP attribute referencing the account password used to login with. + The account passwords stored in LDAP must be hashed with a supported + [Password Scheme] in order for Dovecot to understand them. + Typically the `userPassword` attribute which is part of the `inetOrgPerson` schema. + + If `null`, [Authentication Binds] will be used instead. + + [Password Scheme]: https://doc.dovecot.org/2.4.4/core/config/auth/schemes.html + [Authentication Binds]: https://doc.dovecot.org/2.4.4/core/config/auth/databases/ldap.html#authentication-binds ''; }; diff --git a/mail-server/dovecot.nix b/mail-server/dovecot.nix index 7c4f439..dc51c61 100644 --- a/mail-server/dovecot.nix +++ b/mail-server/dovecot.nix @@ -437,8 +437,9 @@ in "passdb ldap" = { driver = "ldap"; filter = cfg.ldap.dovecot.passFilter; + bind = cfg.ldap.attributes.password == null; fields = { - password = "%{ldap:${cfg.ldap.attributes.password}}"; + password = mkIf (cfg.ldap.attributes.password != null) "%{ldap:${cfg.ldap.attributes.password}}"; }; ldap_connection_group = "ldap-passdb-conn"; }; diff --git a/tests/ldap.nix b/tests/ldap.nix index 7a771ad..4425b53 100644 --- a/tests/ldap.nix +++ b/tests/ldap.nix @@ -25,7 +25,7 @@ in nodes = { machine = - { pkgs, ... }: + { pkgs, lib, ... }: { imports = [ ../default.nix @@ -166,12 +166,51 @@ in }; base = "ou=users,dc=example"; scope = "sub"; + attributes = { + # disable auth bind + password = "userPassword"; + }; }; forwards = { "bob_fw@example.com" = "bob@example.com"; }; }; + + specialisation.auth_bind = { + inheritParentConfig = true; + configuration = { + mailserver = { + ldap = { + attributes = { + # enable auth bind + password = lib.mkForce null; + }; + }; + }; + + services.openldap.settings.children = { + "olcDatabase={1}mdb" = { + attrs = { + olcAccess = [ + # disallow access to userPassword + '' + to * attrs=userPassword + by anonymous auth + by * none + '' + + # default policy (same as if we would specify none as all) + '' + to * + by * read + '' + ]; + }; + }; + }; + }; + }; }; }; testScript = @@ -331,5 +370,10 @@ in "--dst-password-file <(echo '${malloryPassword}')", "--ignore-dkim-spf" ])) + + with subtest("LDAP Authentication Binds"): + machine.succeed("/run/booted-system/specialisation/auth_bind/bin/switch-to-configuration test") + machine.wait_for_unit("openldap.service") + machine.succeed("doveadm auth test alice '${alicePassword}'") ''; }