Merge branch 'ldap-local-coex' into 'master'
ldap: allow coexistence with local accounts See merge request simple-nixos-mailserver/nixos-mailserver!502
This commit is contained in:
@@ -70,8 +70,6 @@ SNM branch corresponding to your NixOS version.
|
|||||||
* [ ] [Mobileconfig](https://support.apple.com/guide/profile-manager/distribute-profiles-manually-pmdbd71ebc9/mac)
|
* [ ] [Mobileconfig](https://support.apple.com/guide/profile-manager/distribute-profiles-manually-pmdbd71ebc9/mac)
|
||||||
* Improve the Forwarding Experience
|
* Improve the Forwarding Experience
|
||||||
* [ ] Support [ARC](https://en.wikipedia.org/wiki/Authenticated_Received_Chain) signing with [Rspamd](https://rspamd.com/doc/modules/arc.html)
|
* [ ] 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
|
* OpenID Connect
|
||||||
* Depends on relevant clients adding support, e.g. [Thunderbird](https://bugzilla.mozilla.org/show_bug.cgi?id=1602166)
|
* Depends on relevant clients adding support, e.g. [Thunderbird](https://bugzilla.mozilla.org/show_bug.cgi?id=1602166)
|
||||||
|
|
||||||
|
|||||||
+20
-19
@@ -649,7 +649,7 @@ in
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
extraVirtualAliases = mkOption {
|
aliases = mkOption {
|
||||||
type =
|
type =
|
||||||
let
|
let
|
||||||
loginAccount = mkOptionType {
|
loginAccount = mkOptionType {
|
||||||
@@ -660,7 +660,6 @@ in
|
|||||||
with types;
|
with types;
|
||||||
attrsOf (either loginAccount (nonEmptyListOf loginAccount));
|
attrsOf (either loginAccount (nonEmptyListOf loginAccount));
|
||||||
example = {
|
example = {
|
||||||
"info@example.com" = "user1@example.com";
|
|
||||||
"postmaster@example.com" = "user1@example.com";
|
"postmaster@example.com" = "user1@example.com";
|
||||||
"abuse@example.com" = "user1@example.com";
|
"abuse@example.com" = "user1@example.com";
|
||||||
"multi@example.com" = [
|
"multi@example.com" = [
|
||||||
@@ -669,15 +668,14 @@ in
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
description = ''
|
description = ''
|
||||||
Virtual Aliases. A virtual alias `"info@example.com" = "user1@example.com"` means that
|
Aliases are additional mail addresses routed to one or more existing local accounts.
|
||||||
all mail to `info@example.com` is forwarded to `user1@example.com`. Note
|
|
||||||
that it is expected that `postmaster@example.com` and `abuse@example.com` is
|
The target accounts are allowed to use the alias as the sender address.
|
||||||
forwarded to some valid email address. (Alternatively you can create login
|
|
||||||
accounts for `postmaster` and (or) `abuse`). Furthermore, it also allows
|
:::{note}
|
||||||
the user `user1@example.com` to send emails as `info@example.com`.
|
This feature is limited to local accounts and does not support LDAP or
|
||||||
It's also possible to create an alias for multiple accounts. In this
|
other external accounts.
|
||||||
example all mails for `multi@example.com` will be forwarded to both
|
:::
|
||||||
`user1@example.com` and `user2@example.com`.
|
|
||||||
'';
|
'';
|
||||||
default = { };
|
default = { };
|
||||||
};
|
};
|
||||||
@@ -685,16 +683,18 @@ in
|
|||||||
forwards = mkOption {
|
forwards = mkOption {
|
||||||
type = with types; attrsOf (either (listOf str) str);
|
type = with types; attrsOf (either (listOf str) str);
|
||||||
example = {
|
example = {
|
||||||
"user@example.com" = "user@elsewhere.com";
|
"user@example.com" = "user@example.edu";
|
||||||
|
"gamenight@example.com" = [
|
||||||
|
"bob@example.com"
|
||||||
|
"frank@example.org"
|
||||||
|
"wendy@example.net"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
description = ''
|
description = ''
|
||||||
To forward mails to an external address. For instance,
|
Forwards route mail from local addresses to one or more local or external addresses.
|
||||||
the value `{ "user@example.com" = "user@elsewhere.com"; }`
|
|
||||||
means that mails to `user@example.com` are forwarded to
|
Unlike {option}`mailserver.aliases`, the target addresses cannot send
|
||||||
`user@elsewhere.com`. The difference with the
|
mail using the forward address.
|
||||||
{option}`mailserver.extraVirtualAliases` option is that `user@elsewhere.com`
|
|
||||||
can't send mail as `user@example.com`. Also, this option
|
|
||||||
allows to forward mails to external addresses.
|
|
||||||
'';
|
'';
|
||||||
default = { };
|
default = { };
|
||||||
};
|
};
|
||||||
@@ -1681,5 +1681,6 @@ in
|
|||||||
[ "mailserver" "ldap" "postfix" "mailAttribute" ]
|
[ "mailserver" "ldap" "postfix" "mailAttribute" ]
|
||||||
[ "mailserver" "ldap" "attributes" "mail" ]
|
[ "mailserver" "ldap" "attributes" "mail" ]
|
||||||
)
|
)
|
||||||
|
(mkRenamedOptionModule [ "mailserver" "extraVirtualAliases" ] [ "mailserver" "aliases" ])
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-9
@@ -40,15 +40,25 @@ follow best practices to simplify maintenance.
|
|||||||
Limitations
|
Limitations
|
||||||
~~~~~~~~~~~
|
~~~~~~~~~~~
|
||||||
|
|
||||||
We have various assertions in place, that prevent using LDAP together with
|
Design choices
|
||||||
other features. Most of them are not technical limitations per se, but instead
|
^^^^^^^^^^^^^^
|
||||||
lack configuration or validation.
|
|
||||||
|
|
||||||
- Local users (:option:`mailserver.loginAccounts`) and aliases
|
These are intentional choices in how the mail server operates that affect the
|
||||||
(:option:`mailserver.extraVirtualAliases`) are not currently allowed with
|
LDAP integration.
|
||||||
:option:`mailserver.ldap.enable` enabled
|
|
||||||
- Aliases based on LDAP attributes are currently not implemented
|
- For mail address routing local accounts always take priority over LDAP accounts.
|
||||||
- Quotas based on LDAP attributes are currently not implemented
|
|
||||||
|
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
|
The following features will likely never be implemented, since they would
|
||||||
complicate the setup significantly.
|
complicate the setup significantly.
|
||||||
@@ -58,7 +68,9 @@ complicate the setup significantly.
|
|||||||
- Use of ``homeDirectory``, ``uid``, ``gid`` LDAP attributes (we are
|
- Use of ``homeDirectory``, ``uid``, ``gid`` LDAP attributes (we are
|
||||||
committed to a virtual setup with one vmail user/uid/gid and UUID based home
|
committed to a virtual setup with one vmail user/uid/gid and UUID based home
|
||||||
directories)
|
directories)
|
||||||
|
- Declarative aliases through :option:`mailserver.aliases`. 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
|
Enabling LDAP support
|
||||||
~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ NixOS 26.05
|
|||||||
rather than their email address, which is more convenient and consistent
|
rather than their email address, which is more convenient and consistent
|
||||||
with typical LDAP practices. The exact attribute can be customized through
|
with typical LDAP practices. The exact attribute can be customized through
|
||||||
:option:`mailserver.ldap.attributes.username`.
|
: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
|
- The following integrations are deprecated and will be removed before the next
|
||||||
release:
|
release:
|
||||||
|
|
||||||
|
|||||||
@@ -98,16 +98,6 @@ in
|
|||||||
) config.mailserver.dkim.domains
|
) 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")
|
lib.optionals (config.mailserver.ldap.enable && config.mailserver.mailDirectory != "/var/vmail")
|
||||||
[
|
[
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ let
|
|||||||
mergeLookupTables lookupTables;
|
mergeLookupTables lookupTables;
|
||||||
|
|
||||||
# extra_valiases_postfix :: Map String [String]
|
# extra_valiases_postfix :: Map String [String]
|
||||||
extra_valiases_postfix = attrsToLookupTable cfg.extraVirtualAliases;
|
extra_valiases_postfix = attrsToLookupTable cfg.aliases;
|
||||||
|
|
||||||
# forwards :: Map String [String]
|
# forwards :: Map String [String]
|
||||||
forwards = attrsToLookupTable cfg.forwards;
|
forwards = attrsToLookupTable cfg.forwards;
|
||||||
|
|||||||
+2
-2
@@ -85,7 +85,7 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
extraVirtualAliases = {
|
aliases = {
|
||||||
"single-alias@example.com" = "user1@example.com";
|
"single-alias@example.com" = "user1@example.com";
|
||||||
"multi-alias@example.com" = [
|
"multi-alias@example.com" = [
|
||||||
"user1@example.com"
|
"user1@example.com"
|
||||||
@@ -494,7 +494,7 @@
|
|||||||
# if this succeeds, it means that user1 received the mail that was intended for chuck.
|
# if this succeeds, it means that user1 received the mail that was intended for chuck.
|
||||||
client.fail("fetchmail --nosslcertck -v")
|
client.fail("fetchmail --nosslcertck -v")
|
||||||
|
|
||||||
with subtest("extraVirtualAliases"):
|
with subtest("Test sending from alias address (mailserver.aliases)"):
|
||||||
client.execute("rm ~/mail/*")
|
client.execute("rm ~/mail/*")
|
||||||
# send email from single-alias to user1
|
# send email from single-alias to user1
|
||||||
client.succeed(
|
client.succeed(
|
||||||
|
|||||||
@@ -1,7 +1,25 @@
|
|||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
|
hashPassword =
|
||||||
|
password:
|
||||||
|
pkgs.runCommand "password-${password}-hashed"
|
||||||
|
{
|
||||||
|
buildInputs = [ pkgs.mkpasswd ];
|
||||||
|
inherit password;
|
||||||
|
}
|
||||||
|
''
|
||||||
|
mkpasswd -s <<<"$password" > $out
|
||||||
|
'';
|
||||||
|
|
||||||
bindPassword = "unsafegibberish";
|
bindPassword = "unsafegibberish";
|
||||||
alicePassword = "testalice";
|
alicePassword = "testalice";
|
||||||
bobPassword = "testbob";
|
bobPassword = "testbob";
|
||||||
|
carolPassword = "testcarol";
|
||||||
|
frankPassword = "testfrank";
|
||||||
|
malloryPassword = "testmallory";
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
name = "ldap";
|
name = "ldap";
|
||||||
@@ -83,6 +101,22 @@ in
|
|||||||
mail: bob@example.com
|
mail: bob@example.com
|
||||||
homeDirectory: /home/bob
|
homeDirectory: /home/bob
|
||||||
userPassword: ${bobPassword}
|
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;
|
localDnsResolver = false;
|
||||||
indexDir = "/var/lib/dovecot/indices";
|
indexDir = "/var/lib/dovecot/indices";
|
||||||
|
|
||||||
|
aliases = {
|
||||||
|
# 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 = {
|
ldap = {
|
||||||
enable = true;
|
enable = true;
|
||||||
uris = [
|
uris = [
|
||||||
@@ -237,5 +289,37 @@ in
|
|||||||
]))
|
]))
|
||||||
machine.succeed("journalctl -u postfix | grep -q 'Sender address rejected: not owned by user bob'")
|
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"
|
||||||
|
]))
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user