ldap: reorganize and regroup options

Now that we have more experience with how we use the LDAP module options
we can make smarter decisions in how to organize them. We can also
explain much better what these options imply, which results in more
extensive option documentation.
This commit is contained in:
Martin Weinelt
2026-03-12 03:23:51 +01:00
parent 609fd80936
commit a87d01ea79
4 changed files with 86 additions and 53 deletions
+76 -43
View File
@@ -319,7 +319,11 @@ in
] ]
''; '';
description = '' description = ''
URIs where your LDAP server can be reached List of LDAP server URIs. Multiple can be specified.
Use `ldaps://` for implicit TLS or `ldap://` for a plain connection. See
also {option}`mailserver.ldap.startTls` to enable StartTLS on plain
connections.
''; '';
}; };
@@ -327,16 +331,16 @@ in
type = types.bool; type = types.bool;
default = false; default = false;
description = '' description = ''
Whether to enable StartTLS upon connection to the server. Whether to enable StartTLS on ``ldap://`` connections.
''; '';
}; };
tlsCAFile = mkOption { caFile = mkOption {
type = types.path; type = types.path;
default = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"; default = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
defaultText = literalMD "see [source](https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/blob/master/default.nix)"; defaultText = lib.literalExpression "\${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
description = '' description = ''
Certificate trust anchors used to verify the LDAP server certificate. Bundle of CA certificates used to authenticate the LDAP server certificate.
''; '';
}; };
@@ -345,37 +349,44 @@ in
type = types.str; type = types.str;
example = "cn=mail,ou=accounts,dc=example,dc=com"; example = "cn=mail,ou=accounts,dc=example,dc=com";
description = '' description = ''
Distinguished name used by the mail server to do lookups DN used to bind against the LDAP server.
against the LDAP servers.
The server uses this account to lookup and filter accounts.
''; '';
}; };
passwordFile = mkOption { passwordFile = mkOption {
type = types.str; type = types.pathWith { inStore = false; };
example = "/run/my-secret"; example = "/run/my-secret";
description = '' description = ''
A file containing the password required to authenticate against the LDAP servers. File containing the password required to bind against the LDAP server.
''; '';
}; };
}; };
searchBase = mkOption { base = mkOption {
type = types.str; type = types.str;
example = "ou=people,ou=accounts,dc=example,dc=com"; example = "ou=people,ou=accounts,dc=example,dc=com";
description = '' description = ''
Base DN at below which to search for users accounts. Base DN below which user accounts are searched for.
''; '';
}; };
searchScope = mkOption { scope = mkOption {
type = types.enum [ type = types.enum [
"sub"
"base" "base"
"one" "one"
"sub"
]; ];
default = "sub"; default = "sub";
description = '' description = ''
Search scope below which users accounts are looked for. Search scope relative to the {option}`mailserver.ldap.base`.
- base: Only the exact Base DN
- one: Immediate child entries of the Base DN, but not the Base DN itself.
- sub: Base DN and all descendant entries at any depth.
In practice only `one` or `sub` are suitable for multiple LDAP users.
''; '';
}; };
@@ -395,6 +406,17 @@ in
''; '';
}; };
username = mkOption {
type = types.str;
default = "uid";
example = "name";
description = ''
The LDAP attribute referencing the username used to login with.
Typically the `uid` attribute which is part of the `inetOrgPerson` schema.
'';
};
password = mkOption { password = mkOption {
type = types.str; type = types.str;
default = "userPassword"; default = "userPassword";
@@ -405,6 +427,19 @@ in
Typically the `userPassword` attribute which is part of the `inetOrgPerson` schema. Typically the `userPassword` attribute which is part of the `inetOrgPerson` schema.
''; '';
}; };
mail = mkOption {
type = types.str;
default = "mail";
example = "maildrop";
description = ''
The attribute name used for looking up accounts by mail address.
Typically this can be the `mail` attribute from the `inetOrgPerson`
schema, or the `maildrop` attribute from the unofficial Postfix
schema.
'';
};
}; };
dovecot = { dovecot = {
@@ -413,11 +448,12 @@ in
default = "mail=%{user}"; default = "mail=%{user}";
example = "(&(objectClass=inetOrgPerson)(mail=%{user}))"; example = "(&(objectClass=inetOrgPerson)(mail=%{user}))";
description = '' description = ''
Filter for user lookups in Dovecot. LDAP filter used for LMTP delivery from Postfix and post-login
information construction, like the home directory.
See the user_filter reference at See the [user_filter] reference at in the Dovecot manual.
https://doc.dovecot.org/2.3/configuration_manual/authentication/ldap_settings_auth/#user-filter
in the Dovecot manual. [user_filter]: https://doc.dovecot.org/2.3/configuration_manual/authentication/ldap_settings_auth/#user-filter
''; '';
}; };
@@ -426,11 +462,12 @@ in
default = "mail=%{user}"; default = "mail=%{user}";
example = "(&(objectClass=inetOrgPerson)(mail=%{user}))"; example = "(&(objectClass=inetOrgPerson)(mail=%{user}))";
description = '' description = ''
Filter for password lookups in Dovecot. LDAP filter used to restrict which users are eligible to
authenticate against Dovecot.
See the pass_filter reference for See the [pass_filter] reference in the Dovecot manual.
https://doc.dovecot.org/2.3/configuration_manual/authentication/ldap_settings_auth/#pass-filter
in the Dovecot manual. [pass_filter]: https://doc.dovecot.org/2.3/configuration_manual/authentication/ldap_settings_auth/#pass-filter
''; '';
}; };
}; };
@@ -438,29 +475,14 @@ in
postfix = { postfix = {
filter = mkOption { filter = mkOption {
type = types.str; type = types.str;
default = "mail=%s"; default = with cfg.ldap.attributes; "${mail}=%s";
defaultText = lib.literalExpression ''
with config.mailserver.ldap.attributes; "''${mail}=%s";
'';
example = "(&(objectClass=inetOrgPerson)(mail=%s))"; example = "(&(objectClass=inetOrgPerson)(mail=%s))";
description = '' description = ''
LDAP filter used to search for an account by mail, where LDAP filter used to search for an account by mail, where `%s` is a
`%s` is a substitute for the address in substitute for the address in question.
question.
'';
};
uidAttribute = mkOption {
type = types.str;
default = "mail";
example = "uid";
description = ''
The LDAP attribute referencing the account name for a user.
'';
};
mailAttribute = mkOption {
type = types.str;
default = "mail";
description = ''
The LDAP attribute holding mail addresses for a user.
''; '';
}; };
}; };
@@ -1640,5 +1662,16 @@ in
(mkRemovedOptionModule [ "mailserver" "ldap" "dovecot" "passAttrs" ] '' (mkRemovedOptionModule [ "mailserver" "ldap" "dovecot" "passAttrs" ] ''
The pass_attrs field is now used internally. You can customize the `mailserver.ldap.attributes.password` field instead. The pass_attrs field is now used internally. You can customize the `mailserver.ldap.attributes.password` field instead.
'') '')
(mkRenamedOptionModule [ "mailserver" "ldap" "tlsCAFile" ] [ "mailserver" "ldap" "caFile" ])
(mkRenamedOptionModule [ "mailserver" "ldap" "searchBase" ] [ "mailserver" "ldap" "base" ])
(mkRenamedOptionModule [ "mailserver" "ldap" "searchScope" ] [ "mailserver" "ldap" "scope" ])
(mkRenamedOptionModule
[ "mailserver" "ldap" "postfix" "uidAttribute" ]
[ "mailserver" "ldap" "attributes" "username" ]
)
(mkRenamedOptionModule
[ "mailserver" "ldap" "postfix" "mailAttribute" ]
[ "mailserver" "ldap" "attributes" "mail" ]
)
]; ];
} }
+3 -3
View File
@@ -71,12 +71,12 @@ let
tls = yes tls = yes
''} ''}
tls_require_cert = hard tls_require_cert = hard
tls_ca_cert_file = ${cfg.ldap.tlsCAFile} tls_ca_cert_file = ${cfg.ldap.caFile}
dn = ${cfg.ldap.bind.dn} dn = ${cfg.ldap.bind.dn}
sasl_bind = no sasl_bind = no
auth_bind = yes auth_bind = yes
base = ${cfg.ldap.searchBase} base = ${cfg.ldap.base}
scope = ${mkLdapSearchScope cfg.ldap.searchScope} scope = ${mkLdapSearchScope cfg.ldap.scope}
user_attrs = \ user_attrs = \
${ldapUuidAttribute}=${ldapUuidAttribute}, \ ${ldapUuidAttribute}=${ldapUuidAttribute}, \
=home=/var/vmail/ldap/%{ldap:${ldapUuidAttribute}}, \ =home=/var/vmail/ldap/%{ldap:${ldapUuidAttribute}}, \
+5 -5
View File
@@ -209,11 +209,11 @@ let
server_host = ${lib.concatStringsSep " " cfg.ldap.uris} server_host = ${lib.concatStringsSep " " cfg.ldap.uris}
start_tls = ${if cfg.ldap.startTls then "yes" else "no"} start_tls = ${if cfg.ldap.startTls then "yes" else "no"}
version = 3 version = 3
tls_ca_cert_file = ${cfg.ldap.tlsCAFile} tls_ca_cert_file = ${cfg.ldap.caFile}
tls_require_cert = yes tls_require_cert = yes
search_base = ${cfg.ldap.searchBase} search_base = ${cfg.ldap.base}
scope = ${cfg.ldap.searchScope} scope = ${cfg.ldap.scope}
bind = yes bind = yes
bind_dn = ${cfg.ldap.bind.dn} bind_dn = ${cfg.ldap.bind.dn}
@@ -222,7 +222,7 @@ let
ldapSenderLoginMap = pkgs.writeText "ldap-sender-login-map.cf" '' ldapSenderLoginMap = pkgs.writeText "ldap-sender-login-map.cf" ''
${commonLdapConfig} ${commonLdapConfig}
query_filter = ${cfg.ldap.postfix.filter} query_filter = ${cfg.ldap.postfix.filter}
result_attribute = ${cfg.ldap.postfix.mailAttribute} result_attribute = ${cfg.ldap.attributes.mail}
''; '';
ldapSenderLoginMapFile = "/run/postfix/ldap-sender-login-map.cf"; ldapSenderLoginMapFile = "/run/postfix/ldap-sender-login-map.cf";
appendPwdInSenderLoginMap = appendLdapBindPwd { appendPwdInSenderLoginMap = appendLdapBindPwd {
@@ -236,7 +236,7 @@ let
ldapVirtualMailboxMap = pkgs.writeText "ldap-virtual-mailbox-map.cf" '' ldapVirtualMailboxMap = pkgs.writeText "ldap-virtual-mailbox-map.cf" ''
${commonLdapConfig} ${commonLdapConfig}
query_filter = ${cfg.ldap.postfix.filter} query_filter = ${cfg.ldap.postfix.filter}
result_attribute = ${cfg.ldap.postfix.uidAttribute} result_attribute = ${cfg.ldap.attributes.username}
''; '';
ldapVirtualMailboxMapFile = "/run/postfix/ldap-virtual-mailbox-map.cf"; ldapVirtualMailboxMapFile = "/run/postfix/ldap-virtual-mailbox-map.cf";
appendPwdInVirtualMailboxMap = appendLdapBindPwd { appendPwdInVirtualMailboxMap = appendLdapBindPwd {
+2 -2
View File
@@ -110,8 +110,8 @@ in
dn = "cn=mail,dc=example"; dn = "cn=mail,dc=example";
passwordFile = "/etc/bind-password"; passwordFile = "/etc/bind-password";
}; };
searchBase = "ou=users,dc=example"; base = "ou=users,dc=example";
searchScope = "sub"; scope = "sub";
}; };
forwards = { forwards = {