Merge branch 'cleanup' into 'master'
Rename loginAccounts and group storage related settings See merge request simple-nixos-mailserver/nixos-mailserver!501
This commit is contained in:
+151
-83
@@ -25,7 +25,7 @@ let
|
|||||||
inherit (lib)
|
inherit (lib)
|
||||||
literalExpression
|
literalExpression
|
||||||
literalMD
|
literalMD
|
||||||
mkDefault
|
mkChangedOptionModule
|
||||||
mkEnableOption
|
mkEnableOption
|
||||||
mkOption
|
mkOption
|
||||||
mkOptionType
|
mkOptionType
|
||||||
@@ -137,7 +137,7 @@ in
|
|||||||
description = "Message size limit enforced by Postfix.";
|
description = "Message size limit enforced by Postfix.";
|
||||||
};
|
};
|
||||||
|
|
||||||
loginAccounts = mkOption {
|
accounts = mkOption {
|
||||||
type = types.attrsOf (
|
type = types.attrsOf (
|
||||||
types.submodule (
|
types.submodule (
|
||||||
{ name, ... }:
|
{ name, ... }:
|
||||||
@@ -145,8 +145,13 @@ in
|
|||||||
options = {
|
options = {
|
||||||
name = mkOption {
|
name = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
|
default = name;
|
||||||
example = "user1@example.com";
|
example = "user1@example.com";
|
||||||
description = "Username";
|
readOnly = true;
|
||||||
|
internal = true;
|
||||||
|
description = ''
|
||||||
|
The login username for this account.
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
hashedPassword = mkOption {
|
hashedPassword = mkOption {
|
||||||
@@ -154,24 +159,33 @@ in
|
|||||||
default = null;
|
default = null;
|
||||||
example = "$y$j9T$vfGrwkAaXCjCEWtVNMQck1$383uIXQmn2z0hnmVAA8kwFQmjNj78.nYbvWeyNLIaP1";
|
example = "$y$j9T$vfGrwkAaXCjCEWtVNMQck1$383uIXQmn2z0hnmVAA8kwFQmjNj78.nYbvWeyNLIaP1";
|
||||||
description = ''
|
description = ''
|
||||||
The user's hashed password. Use `mkpasswd` as follows
|
The hashed login password for this account.
|
||||||
|
|
||||||
|
Use `mkpasswd` to create password hashes:
|
||||||
```
|
```
|
||||||
nix-shell -p mkpasswd --run 'mkpasswd -s'
|
nix-shell -p mkpasswd --run 'mkpasswd -s'
|
||||||
```
|
```
|
||||||
|
|
||||||
Warning: this is stored in plaintext in the Nix store!
|
:::{note}
|
||||||
Use {option}`mailserver.loginAccounts.<name>.hashedPasswordFile` instead.
|
This is a convenience option, when your threat model allows
|
||||||
|
storing hashed secrets in the world-readable Nix store.
|
||||||
|
|
||||||
|
Passing the hash through
|
||||||
|
{option}`mailserver.accounts.<name>.hashedPasswordFile`
|
||||||
|
allows relying on filesystem discretionary access control as
|
||||||
|
another security boundary.
|
||||||
|
:::
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
hashedPasswordFile = mkOption {
|
hashedPasswordFile = mkOption {
|
||||||
type = with types; nullOr path;
|
type = with types; nullOr path;
|
||||||
default = null;
|
default = null;
|
||||||
example = "/run/keys/user1-passwordhash";
|
example = "/run/keys/user1-pw-hash";
|
||||||
description = ''
|
description = ''
|
||||||
A file containing the user's hashed password. Use `mkpasswd` as follows
|
The hashed login password for this account read from a file.
|
||||||
|
|
||||||
|
Use `mkpasswd to create password hashes:
|
||||||
```
|
```
|
||||||
nix-shell -p mkpasswd --run 'mkpasswd -s'
|
nix-shell -p mkpasswd --run 'mkpasswd -s'
|
||||||
```
|
```
|
||||||
@@ -185,9 +199,13 @@ in
|
|||||||
inStore = false;
|
inStore = false;
|
||||||
});
|
});
|
||||||
default = null;
|
default = null;
|
||||||
example = "/run/keys/user1-password";
|
example = "/run/keys/user1-pw";
|
||||||
description = ''
|
description = ''
|
||||||
A file containing the user's plain text password. The value will be hashed at runtime.
|
The plaintext login password for this account read from a file.
|
||||||
|
|
||||||
|
:::{note}
|
||||||
|
The password is hashed before it is passed on to Dovecot.
|
||||||
|
:::
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -199,9 +217,13 @@ in
|
|||||||
];
|
];
|
||||||
default = [ ];
|
default = [ ];
|
||||||
description = ''
|
description = ''
|
||||||
A list of aliases of this login account.
|
List of additional mail addresses (aliases) that get routed to this account.
|
||||||
Note: Use list entries like "@example.com" to create a catchAll
|
|
||||||
that allows sending from all email addresses in these domain.
|
:::{admonition} Catch-all with sending permissions
|
||||||
|
:class: tip
|
||||||
|
Configure `@example.com` to create a catch-all for this domain
|
||||||
|
that also allows sending from all addresses.
|
||||||
|
:::
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -210,7 +232,7 @@ in
|
|||||||
example = [ ''/^tom\..*@domain\.com$/'' ];
|
example = [ ''/^tom\..*@domain\.com$/'' ];
|
||||||
default = [ ];
|
default = [ ];
|
||||||
description = ''
|
description = ''
|
||||||
Same as {option}`mailserver.loginAccounts.<name>.aliases` but
|
Same as {option}`mailserver.accounts.<name>.aliases` but
|
||||||
using PCRE (Perl compatible regex).
|
using PCRE (Perl compatible regex).
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
@@ -224,7 +246,12 @@ in
|
|||||||
default = [ ];
|
default = [ ];
|
||||||
description = ''
|
description = ''
|
||||||
For which domains should this account act as a catch all?
|
For which domains should this account act as a catch all?
|
||||||
Note: Does not allow sending from all addresses of these domains.
|
|
||||||
|
:::{warning}
|
||||||
|
Does not allow sending from all addresses of these domains.
|
||||||
|
Use {option}`mailserver.accounts.<name>.aliases` if that
|
||||||
|
is required.
|
||||||
|
:::
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -266,9 +293,10 @@ in
|
|||||||
default = false;
|
default = false;
|
||||||
description = ''
|
description = ''
|
||||||
Specifies if the account should be a send-only account.
|
Specifies if the account should be a send-only account.
|
||||||
Emails sent to send-only accounts will be rejected from
|
|
||||||
unauthorized senders with the `sendOnlyRejectMessage`
|
Emails sent to send-only accounts will
|
||||||
stating the reason.
|
be rejected with the reason configured in
|
||||||
|
{option}`mailserver.accounts.<name>.sendOnlyRejectMessage`.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -276,33 +304,38 @@ in
|
|||||||
type = types.str;
|
type = types.str;
|
||||||
default = "This account cannot receive emails.";
|
default = "This account cannot receive emails.";
|
||||||
description = ''
|
description = ''
|
||||||
The message that will be returned to the sender when an email is
|
The message returned to the sender for a send-only account.
|
||||||
sent to a send-only account. Only used if the account is marked
|
|
||||||
as send-only.
|
See {option}`mailserver.accounts.<name>.sendOnly`.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config.name = mkDefault name;
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
example = {
|
example = lib.literalExpression ''
|
||||||
user1 = {
|
{
|
||||||
hashedPassword = "$y$j9T$y6eZ1o.IvVNfdGMAsUEvh1$6K/llP52uw2iDh4iSwtAn54/JYy7FzCcoCHmjmx00H5";
|
user1 = {
|
||||||
};
|
# This password hash leaks into the Nix store
|
||||||
user2 = {
|
hashedPassword = "$y$j9T$y6eZ1o.IvVNfdGMAsUEvh1$6K/llP52uw2iDh4iSwtAn54/JYy7FzCcoCHmjmx00H5";
|
||||||
hashedPassword = "$y$j9T$hZ.ubq0M897Hw.znxnGG9.$14EJBoOwbwKeWt.W4vpnBPEBZC9mYz4fWI9kOCLoZf4";
|
};
|
||||||
};
|
user2 = {
|
||||||
};
|
# Hashed password passed as a file
|
||||||
|
hashedPasswordFile = "/run/keys/user2-pw-hash";
|
||||||
|
};
|
||||||
|
user3 = {
|
||||||
|
# Plaintext password file
|
||||||
|
passwordFile = "/run/keys/user3-pw";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
'';
|
||||||
description = ''
|
description = ''
|
||||||
The login account of the domain. Every account is mapped to a unix user,
|
Attribute set of mail accounts.
|
||||||
e.g. `user1@example.com`. To generate the passwords use `mkpasswd` as
|
|
||||||
follows
|
|
||||||
|
|
||||||
```
|
Each entry defines a mailbox and login credentials, where the attribute
|
||||||
nix-shell -p mkpasswd --run 'mkpasswd -s'
|
name is used as the login username and optionally routed mail address.
|
||||||
```
|
|
||||||
|
Use `mkpasswd` to generate password hashes.
|
||||||
'';
|
'';
|
||||||
default = { };
|
default = { };
|
||||||
};
|
};
|
||||||
@@ -652,13 +685,13 @@ in
|
|||||||
aliases = mkOption {
|
aliases = mkOption {
|
||||||
type =
|
type =
|
||||||
let
|
let
|
||||||
loginAccount = mkOptionType {
|
account = mkOptionType {
|
||||||
name = "Login Account";
|
name = "Login Account";
|
||||||
check = account: builtins.elem account (builtins.attrNames cfg.loginAccounts);
|
check = account: builtins.elem account (builtins.attrNames cfg.accounts);
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
with types;
|
with types;
|
||||||
attrsOf (either loginAccount (nonEmptyListOf loginAccount));
|
attrsOf (either account (nonEmptyListOf account));
|
||||||
example = {
|
example = {
|
||||||
"postmaster@example.com" = "user1@example.com";
|
"postmaster@example.com" = "user1@example.com";
|
||||||
"abuse@example.com" = "user1@example.com";
|
"abuse@example.com" = "user1@example.com";
|
||||||
@@ -751,53 +784,80 @@ in
|
|||||||
default = [ ];
|
default = [ ];
|
||||||
};
|
};
|
||||||
|
|
||||||
vmailUID = mkOption {
|
storage = {
|
||||||
type = types.int;
|
path = mkOption {
|
||||||
default = 5000;
|
type = types.path;
|
||||||
description = ''
|
default = "/var/vmail";
|
||||||
The unix UID of the virtual mail user. Be mindful that if this is
|
description = ''
|
||||||
changed, you will need to manually adjust the permissions of
|
Path on disk where mail home directories are stored.
|
||||||
`mailDirectory`.
|
'';
|
||||||
'';
|
};
|
||||||
};
|
|
||||||
|
|
||||||
vmailUserName = mkOption {
|
directoryLayout = mkOption {
|
||||||
type = types.str;
|
type = types.enum [
|
||||||
default = "virtualMail";
|
"fs"
|
||||||
description = ''
|
"maildir++"
|
||||||
The user name and group name of the user that owns the directory where all
|
];
|
||||||
the mail is stored.
|
default = "maildir++";
|
||||||
'';
|
description = ''
|
||||||
};
|
Sets whether dovecot should organize mail in subdirectories:
|
||||||
|
|
||||||
vmailGroupName = mkOption {
|
- /var/vmail/example.com/user/.folder.subfolder/ (Maildir++ layout)
|
||||||
type = types.str;
|
- /var/vmail/example.com/user/folder/subfolder/ (FS layout)
|
||||||
default = "virtualMail";
|
|
||||||
description = ''
|
|
||||||
The user name and group name of the user that owns the directory where all
|
|
||||||
the mail is stored.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
mailDirectory = mkOption {
|
See <https://doc.dovecot.org/main/core/config/mailbox_formats/maildir.html#directory-layout>
|
||||||
type = types.path;
|
See https://doc.dovecot.org/main/core/config/mailbox_formats/maildir.html#maildir-mailbox-format for details.
|
||||||
default = "/var/vmail";
|
'';
|
||||||
description = ''
|
};
|
||||||
Where to store the mail.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
useFsLayout = mkOption {
|
uid = mkOption {
|
||||||
type = types.bool;
|
type = types.ints.positive;
|
||||||
default = false;
|
default = 5000;
|
||||||
description = ''
|
description = ''
|
||||||
Sets whether dovecot should organize mail in subdirectories:
|
The user id assigned to the vmail user.
|
||||||
|
|
||||||
- /var/vmail/example.com/user/.folder.subfolder/ (default layout)
|
This user owns the mail storage files and directories and is used by
|
||||||
- /var/vmail/example.com/user/folder/subfolder/ (FS layout)
|
services accessing the mail store.
|
||||||
|
|
||||||
See https://doc.dovecot.org/main/core/config/mailbox_formats/maildir.html#maildir-mailbox-format for details.
|
:::{warning}
|
||||||
'';
|
If you change this value you also need to manually adjust the
|
||||||
|
permissions of your :option:`mailserver.storage.path`.
|
||||||
|
:::
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
owner = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "virtualMail";
|
||||||
|
description = ''
|
||||||
|
The name of the user that owns the :option:`mailserver.storage.path`.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
gid = mkOption {
|
||||||
|
type = types.ints.positive;
|
||||||
|
default = 5000;
|
||||||
|
description = ''
|
||||||
|
The group id of the primary group of the vmail user.
|
||||||
|
|
||||||
|
This group owns the mail storage directories. Access can be delegated
|
||||||
|
to other users via group membership.
|
||||||
|
|
||||||
|
:::{warning}
|
||||||
|
If you change this value you also need to manually adjust the
|
||||||
|
permissions of your :option:`mailserver.storage.path`.
|
||||||
|
:::
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
group = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "virtualMail";
|
||||||
|
description = ''
|
||||||
|
The primary group name of the user that owns the
|
||||||
|
:option:`mailserver.storage.path`.
|
||||||
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
useUTF8FolderNames = mkOption {
|
useUTF8FolderNames = mkOption {
|
||||||
@@ -1481,8 +1541,8 @@ in
|
|||||||
|
|
||||||
locations = mkOption {
|
locations = mkOption {
|
||||||
type = types.listOf types.path;
|
type = types.listOf types.path;
|
||||||
default = [ cfg.mailDirectory ];
|
default = [ cfg.storage.path ];
|
||||||
defaultText = literalExpression "[ config.mailserver.mailDirectory ]";
|
defaultText = literalExpression "[ config.mailserver.storage.path ]";
|
||||||
description = "The locations that are to be backed up by borg.";
|
description = "The locations that are to be backed up by borg.";
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1682,5 +1742,13 @@ in
|
|||||||
[ "mailserver" "ldap" "attributes" "mail" ]
|
[ "mailserver" "ldap" "attributes" "mail" ]
|
||||||
)
|
)
|
||||||
(mkRenamedOptionModule [ "mailserver" "extraVirtualAliases" ] [ "mailserver" "aliases" ])
|
(mkRenamedOptionModule [ "mailserver" "extraVirtualAliases" ] [ "mailserver" "aliases" ])
|
||||||
|
(mkRenamedOptionModule [ "mailserver" "loginAccounts" ] [ "mailserver" "accounts" ])
|
||||||
|
(mkRenamedOptionModule [ "mailserver" "vmailUID" ] [ "mailserver" "storage" "uid" ])
|
||||||
|
(mkRenamedOptionModule [ "mailserver" "vmailUserName" ] [ "mailserver" "storage" "owner" ])
|
||||||
|
(mkRenamedOptionModule [ "mailserver" "vmailGroupName" ] [ "mailserver" "storage" "group" ])
|
||||||
|
(mkRenamedOptionModule [ "mailserver" "mailDirectory" ] [ "mailserver" "storage" "path" ])
|
||||||
|
(mkChangedOptionModule [ "mailserver" "useFSLayout" ] [ "mailserver" "storage" "directoryLayout" ] (
|
||||||
|
config: if config.mailserver.useFSLayout then "fs" else "maildir++"
|
||||||
|
))
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,14 +5,13 @@ First off you should have a backup of your ``configuration.nix`` file
|
|||||||
where you have the server config (but that is already in a git
|
where you have the server config (but that is already in a git
|
||||||
repository right?)
|
repository right?)
|
||||||
|
|
||||||
Next you need to backup ``/var/vmail`` or whatever you have specified
|
Next you need to backup ``/var/vmail`` or whatever you have specified for the
|
||||||
for the option ``mailDirectory``. This is where all the mails reside.
|
option :option:`mailserver.storage.path`. This is where all the mails reside.
|
||||||
Good options are a cron job with ``rsync`` or ``scp``. But really
|
Good options are a cron job with ``rsync`` or ``scp``. But really anything
|
||||||
anything works, as it is simply a folder with plenty of files in it. If
|
works, as it is simply a folder with plenty of files in it. If your backup
|
||||||
your backup solution does not preserve the owner of the files don’t
|
solution does not preserve the owner of the files don’t forget to ``chown`` them
|
||||||
forget to ``chown`` them to ``virtualMail:virtualMail`` if you copy them
|
to ``virtualMail:virtualMail`` if you copy them back (or whatever you specified
|
||||||
back (or whatever you specified as ``vmailUserName``, and
|
as :option:`mailserver.storage.owner`, and :option:`mailserver.storage.group`).
|
||||||
``vmailGroupName``).
|
|
||||||
|
|
||||||
If you enabled ``enableManageSieve`` then you also may want to backup
|
If you enabled ``enableManageSieve`` then you also may want to backup
|
||||||
``/var/sieve`` or whatever you have specified as ``sieveDirectory``.
|
``/var/sieve`` or whatever you have specified as ``sieveDirectory``.
|
||||||
|
|||||||
+1
-1
@@ -13,7 +13,7 @@ domain ``example.com`` and send mails with any address of this domain:
|
|||||||
|
|
||||||
.. code:: nix
|
.. code:: nix
|
||||||
|
|
||||||
mailserver.loginAccounts = {
|
mailserver.accounts = {
|
||||||
"user@example.com" = {
|
"user@example.com" = {
|
||||||
aliases = [ "@example.com" ];
|
aliases = [ "@example.com" ];
|
||||||
};
|
};
|
||||||
|
|||||||
+3
-3
@@ -40,18 +40,18 @@ best practices to mailserver management.
|
|||||||
|
|
||||||
systemctl stop dovecot.service
|
systemctl stop dovecot.service
|
||||||
|
|
||||||
3. Create a backup or snapshot of your :option:`mailserver.mailDirectory`, so
|
3. Create a backup or snapshot of your :option:`mailserver.storage.path`, so
|
||||||
you can restore should anything go wrong.
|
you can restore should anything go wrong.
|
||||||
|
|
||||||
4. Run the migration script and pass the required arguments to enable LDAP lookups:
|
4. Run the migration script and pass the required arguments to enable LDAP lookups:
|
||||||
|
|
||||||
The script should be run under the user who owns the :option:`mailserver.mailDirectory`.
|
The script should be run under the user who owns the :option:`mailserver.storage.path`.
|
||||||
If run as root it will automatically switch into the appropriate user by itself.
|
If run as root it will automatically switch into the appropriate user by itself.
|
||||||
|
|
||||||
The script will not modify your data unless called with ``--execute``.
|
The script will not modify your data unless called with ``--execute``.
|
||||||
|
|
||||||
The migration script finds all Dovecot home directories in
|
The migration script finds all Dovecot home directories in
|
||||||
``/var/vmail/ldap/`` (or any other :option:`mailserver.mailDirectory`),
|
``/var/vmail/ldap/`` (or any other :option:`mailserver.storage.path`),
|
||||||
for example that of bob at ``/var/vmail/ldap/bob@example.com``.
|
for example that of bob at ``/var/vmail/ldap/bob@example.com``.
|
||||||
It then takes ``bob@example.com`` and queries the LDAP server for
|
It then takes ``bob@example.com`` and queries the LDAP server for
|
||||||
``mail=bob@example.com`` to retrieve the UUID attribute. Finally
|
``mail=bob@example.com`` to retrieve the UUID attribute. Finally
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@ let
|
|||||||
mapAttrsToList
|
mapAttrsToList
|
||||||
;
|
;
|
||||||
|
|
||||||
mailAccounts = config.mailserver.loginAccounts;
|
mailAccounts = config.mailserver.accounts;
|
||||||
htpasswd = pkgs.writeText "radicale.users" (
|
htpasswd = pkgs.writeText "radicale.users" (
|
||||||
concatStrings (flip mapAttrsToList mailAccounts (mail: user: "${mail}+:${user.hashedPassword}\n"))
|
concatStrings (flip mapAttrsToList mailAccounts (mail: user: "${mail}+:${user.hashedPassword}\n"))
|
||||||
);
|
);
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@ Limitations
|
|||||||
|
|
||||||
Radicale since the 3.x release (introduced in NixOS 20.09) does not support
|
Radicale since the 3.x release (introduced in NixOS 20.09) does not support
|
||||||
traditional crypt() password hashes any longer. To establish access for
|
traditional crypt() password hashes any longer. To establish access for
|
||||||
existing :option:`mailserver.loginAccounts`, the hashing method used
|
existing :option:`mailserver.accounts`, the hashing method used
|
||||||
for ``hashedPassword`` needs to be compatible with one of the available
|
for ``hashedPassword`` needs to be compatible with one of the available
|
||||||
`htpasswd_encryption`_ methods. Such hashes can for example be created using
|
`htpasswd_encryption`_ methods. Such hashes can for example be created using
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ NixOS 26.05
|
|||||||
is an alternative to hashed passwords that integrates well with workflows
|
is an alternative to hashed passwords that integrates well with workflows
|
||||||
established by `agenix`_/`sops-nix`_ that instead rely on encryption. This
|
established by `agenix`_/`sops-nix`_ that instead rely on encryption. This
|
||||||
option prevents files from leaking in to the Nix store.
|
option prevents files from leaking in to the Nix store.
|
||||||
See :option:`mailserver.loginAccounts.<name>.passwordFile`.
|
See :option:`mailserver.accounts.<name>.passwordFile`.
|
||||||
- LDAP setups require a migration of Dovecot home directories to
|
- LDAP setups require a migration of Dovecot home directories to
|
||||||
`UUID based home directories`_. The exact UUID attribute can be customized
|
`UUID based home directories`_. The exact UUID attribute can be customized
|
||||||
through :option:`mailserver.ldap.attributes.uuid`.
|
through :option:`mailserver.ldap.attributes.uuid`.
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
|
|
||||||
# A list of all login accounts. To create the password hashes, use
|
# A list of all login accounts. To create the password hashes, use
|
||||||
# nix-shell -p mkpasswd --run 'mkpasswd -s'
|
# nix-shell -p mkpasswd --run 'mkpasswd -s'
|
||||||
loginAccounts = {
|
accounts = {
|
||||||
"user1@example.com" = {
|
"user1@example.com" = {
|
||||||
# Reads the password hash from a file on the server
|
# Reads the password hash from a file on the server
|
||||||
hashedPasswordFile = "/a/file/containing/a/hashed/password";
|
hashedPasswordFile = "/a/file/containing/a/hashed/password";
|
||||||
|
|||||||
+13
-15
@@ -98,22 +98,20 @@ in
|
|||||||
) config.mailserver.dkim.domains
|
) config.mailserver.dkim.domains
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
++
|
++ lib.optionals (config.mailserver.ldap.enable && config.mailserver.storage.path != "/var/vmail") [
|
||||||
lib.optionals (config.mailserver.ldap.enable && config.mailserver.mailDirectory != "/var/vmail")
|
{
|
||||||
[
|
assertion = config.mailserver.stateVersion != null -> config.mailserver.stateVersion >= 2;
|
||||||
{
|
message = ''
|
||||||
assertion = config.mailserver.stateVersion != null -> config.mailserver.stateVersion >= 2;
|
Issue: The dovecot homedir for LDAP users was previously not respecting `mailserver.storage.path`.
|
||||||
message = ''
|
Remediation:
|
||||||
Issue: The dovecot homedir for LDAP users was previously not respecting `mailserver.mailDirectory`.
|
- Stop the `dovecot.service`
|
||||||
Remediation:
|
- Move `/var/vmail/ldap` below your `mailserver.storage.path`
|
||||||
- Stop the `dovecot.service`
|
- Increase the `stateVersion` to 2.
|
||||||
- Move `/var/vmail/ldap` below your `mailserver.mailDirectory`
|
|
||||||
- Increase the `stateVersion` to 2.
|
|
||||||
|
|
||||||
Check https://nixos-mailserver.readthedocs.io/en/latest/migrations.html#dovecot-ldap-home-directory-migration for more information.
|
Check https://nixos-mailserver.readthedocs.io/en/latest/migrations.html#dovecot-ldap-home-directory-migration for more information.
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
++ [
|
++ [
|
||||||
{
|
{
|
||||||
assertion = config.mailserver.stateVersion != null -> config.mailserver.stateVersion >= 3;
|
assertion = config.mailserver.stateVersion != null -> config.mailserver.stateVersion >= 3;
|
||||||
|
|||||||
@@ -51,12 +51,12 @@ rec {
|
|||||||
builtins.toString (mkHashFile name value.hashedPassword)
|
builtins.toString (mkHashFile name value.hashedPassword)
|
||||||
else
|
else
|
||||||
value.passwordFile
|
value.passwordFile
|
||||||
) cfg.loginAccounts;
|
) cfg.accounts;
|
||||||
|
|
||||||
# Collect accounts with plain text passwords that require hashing
|
# Collect accounts with plain text passwords that require hashing
|
||||||
accountsWithPlaintextPasswordFiles = lib.filter (
|
accountsWithPlaintextPasswordFiles = lib.filter (name: cfg.accounts.${name}.passwordFile != null) (
|
||||||
name: cfg.loginAccounts.${name}.passwordFile != null
|
builtins.attrNames cfg.accounts
|
||||||
) (builtins.attrNames cfg.loginAccounts);
|
);
|
||||||
|
|
||||||
# Appends the LDAP bind password to files to avoid writing this
|
# Appends the LDAP bind password to files to avoid writing this
|
||||||
# password into the Nix store.
|
# password into the Nix store.
|
||||||
|
|||||||
+13
-13
@@ -50,7 +50,7 @@ let
|
|||||||
}) attrs
|
}) attrs
|
||||||
);
|
);
|
||||||
|
|
||||||
maildirLayoutAppendix = lib.optionalString cfg.useFsLayout ":LAYOUT=fs";
|
maildirLayoutAppendix = lib.optionalString (cfg.storage.directoryLayout == "fs") ":LAYOUT=fs";
|
||||||
maildirUTF8FolderNames = lib.optionalString cfg.useUTF8FolderNames ":UTF-8";
|
maildirUTF8FolderNames = lib.optionalString cfg.useUTF8FolderNames ":UTF-8";
|
||||||
|
|
||||||
# https://doc.dovecot.org/2.3/configuration_manual/home_directories_for_virtual_users/#ways-to-set-up-home-directory
|
# https://doc.dovecot.org/2.3/configuration_manual/home_directories_for_virtual_users/#ways-to-set-up-home-directory
|
||||||
@@ -79,7 +79,7 @@ let
|
|||||||
scope = ${mkLdapSearchScope cfg.ldap.scope}
|
scope = ${mkLdapSearchScope cfg.ldap.scope}
|
||||||
user_attrs = \
|
user_attrs = \
|
||||||
${ldapUuidAttribute}=${ldapUuidAttribute}, \
|
${ldapUuidAttribute}=${ldapUuidAttribute}, \
|
||||||
=home=${cfg.mailDirectory}/ldap/%{ldap:${ldapUuidAttribute}}, \
|
=home=${cfg.storage.path}/ldap/%{ldap:${ldapUuidAttribute}}, \
|
||||||
=mail=maildir:~/mail${maildirLayoutAppendix}${maildirUTF8FolderNames}${
|
=mail=maildir:~/mail${maildirLayoutAppendix}${maildirUTF8FolderNames}${
|
||||||
lib.optionalString (cfg.indexDir != null) ":INDEX=${cfg.indexDir}/ldap/%{ldap:${ldapUuidAttribute}}"
|
lib.optionalString (cfg.indexDir != null) ":INDEX=${cfg.indexDir}/ldap/%{ldap:${ldapUuidAttribute}}"
|
||||||
}
|
}
|
||||||
@@ -115,7 +115,7 @@ let
|
|||||||
umask 077
|
umask 077
|
||||||
|
|
||||||
for f in ${
|
for f in ${
|
||||||
builtins.toString (lib.mapAttrsToList (name: _: passwordFiles."${name}") cfg.loginAccounts)
|
builtins.toString (lib.mapAttrsToList (name: _: passwordFiles."${name}") cfg.accounts)
|
||||||
}; do
|
}; do
|
||||||
if [ ! -f "$f" ]; then
|
if [ ! -f "$f" ]; then
|
||||||
echo "Expected password hash file $f does not exist!"
|
echo "Expected password hash file $f does not exist!"
|
||||||
@@ -131,7 +131,7 @@ let
|
|||||||
"${name}:${"$(sed -n '1{p;p;q}' ${passwordFiles."${name}"} | ${lib.getExe' pkgs.dovecot "doveadm"} pw)"}::::::"
|
"${name}:${"$(sed -n '1{p;p;q}' ${passwordFiles."${name}"} | ${lib.getExe' pkgs.dovecot "doveadm"} pw)"}::::::"
|
||||||
else
|
else
|
||||||
"${name}:${"$(head -n 1 ${passwordFiles."${name}"})"}::::::"
|
"${name}:${"$(head -n 1 ${passwordFiles."${name}"})"}::::::"
|
||||||
) cfg.loginAccounts
|
) cfg.accounts
|
||||||
)}
|
)}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@ let
|
|||||||
name: value:
|
name: value:
|
||||||
"${name}:::::::"
|
"${name}:::::::"
|
||||||
+ lib.optionalString (value.quota != null) "userdb_quota_rule=*:storage=${value.quota}"
|
+ lib.optionalString (value.quota != null) "userdb_quota_rule=*:storage=${value.quota}"
|
||||||
) cfg.loginAccounts
|
) cfg.accounts
|
||||||
)}
|
)}
|
||||||
EOF
|
EOF
|
||||||
'';
|
'';
|
||||||
@@ -228,8 +228,8 @@ in
|
|||||||
enablePop3 = cfg.enablePop3 || cfg.enablePop3Ssl;
|
enablePop3 = cfg.enablePop3 || cfg.enablePop3Ssl;
|
||||||
enablePAM = false;
|
enablePAM = false;
|
||||||
enableQuota = true;
|
enableQuota = true;
|
||||||
mailGroup = cfg.vmailGroupName;
|
mailGroup = cfg.storage.group;
|
||||||
mailUser = cfg.vmailUserName;
|
mailUser = cfg.storage.owner;
|
||||||
mailLocation = dovecotMaildir;
|
mailLocation = dovecotMaildir;
|
||||||
sslServerCert = x509CertificateFile;
|
sslServerCert = x509CertificateFile;
|
||||||
sslServerKey = x509PrivateKeyFile;
|
sslServerKey = x509PrivateKeyFile;
|
||||||
@@ -371,7 +371,7 @@ in
|
|||||||
mail_max_userip_connections = ${toString cfg.maxConnectionsPerUser}
|
mail_max_userip_connections = ${toString cfg.maxConnectionsPerUser}
|
||||||
}
|
}
|
||||||
|
|
||||||
mail_access_groups = ${cfg.vmailGroupName}
|
mail_access_groups = ${cfg.storage.group}
|
||||||
|
|
||||||
# https://ssl-config.mozilla.org/#server=dovecot&version=2.3.21&config=intermediate&openssl=3.4.1&guideline=5.7
|
# https://ssl-config.mozilla.org/#server=dovecot&version=2.3.21&config=intermediate&openssl=3.4.1&guideline=5.7
|
||||||
ssl = required
|
ssl = required
|
||||||
@@ -431,9 +431,9 @@ in
|
|||||||
driver = passwd-file
|
driver = passwd-file
|
||||||
args = ${userdbFile}
|
args = ${userdbFile}
|
||||||
default_fields = \
|
default_fields = \
|
||||||
home=${cfg.mailDirectory}/%{domain}/%{username} \
|
home=${cfg.storage.path}/%{domain}/%{username} \
|
||||||
uid=${builtins.toString cfg.vmailUID} \
|
uid=${builtins.toString cfg.storage.uid} \
|
||||||
gid=${builtins.toString cfg.vmailUID}
|
gid=${builtins.toString cfg.storage.uid}
|
||||||
}
|
}
|
||||||
|
|
||||||
${lib.optionalString cfg.ldap.enable ''
|
${lib.optionalString cfg.ldap.enable ''
|
||||||
@@ -446,8 +446,8 @@ in
|
|||||||
driver = ldap
|
driver = ldap
|
||||||
args = ${ldapConfFile}
|
args = ${ldapConfFile}
|
||||||
override_fields = \
|
override_fields = \
|
||||||
uid=${toString cfg.vmailUID} \
|
uid=${toString cfg.storage.uid} \
|
||||||
gid=${toString cfg.vmailUID}
|
gid=${toString cfg.storage.uid}
|
||||||
}
|
}
|
||||||
''}
|
''}
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ let
|
|||||||
to = name;
|
to = name;
|
||||||
in
|
in
|
||||||
map (from: { "${from}" = to; }) (value.aliases ++ lib.singleton name)
|
map (from: { "${from}" = to; }) (value.aliases ++ lib.singleton name)
|
||||||
) cfg.loginAccounts
|
) cfg.accounts
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
regex_valiases_postfix = mergeLookupTables (
|
regex_valiases_postfix = mergeLookupTables (
|
||||||
@@ -62,7 +62,7 @@ let
|
|||||||
to = name;
|
to = name;
|
||||||
in
|
in
|
||||||
map (from: { "${from}" = to; }) value.aliasesRegexp
|
map (from: { "${from}" = to; }) value.aliasesRegexp
|
||||||
) cfg.loginAccounts
|
) cfg.accounts
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ let
|
|||||||
to = name;
|
to = name;
|
||||||
in
|
in
|
||||||
map (from: { "@${from}" = to; }) value.catchAll
|
map (from: { "@${from}" = to; }) value.catchAll
|
||||||
) cfg.loginAccounts
|
) cfg.accounts
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -127,7 +127,7 @@ let
|
|||||||
|
|
||||||
# denied_recipients_postfix :: [ String ]
|
# denied_recipients_postfix :: [ String ]
|
||||||
denied_recipients_postfix = map (acct: "${acct.name} REJECT ${acct.sendOnlyRejectMessage}") (
|
denied_recipients_postfix = map (acct: "${acct.name} REJECT ${acct.sendOnlyRejectMessage}") (
|
||||||
lib.filter (acct: acct.sendOnly) (lib.attrValues cfg.loginAccounts)
|
lib.filter (acct: acct.sendOnly) (lib.attrValues cfg.accounts)
|
||||||
);
|
);
|
||||||
denied_recipients_file = builtins.toFile "denied_recipients" (
|
denied_recipients_file = builtins.toFile "denied_recipients" (
|
||||||
lib.concatStringsSep "\n" denied_recipients_postfix
|
lib.concatStringsSep "\n" denied_recipients_postfix
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ in
|
|||||||
retain hourly ${toString cfg.backup.retain.hourly}
|
retain hourly ${toString cfg.backup.retain.hourly}
|
||||||
retain daily ${toString cfg.backup.retain.daily}
|
retain daily ${toString cfg.backup.retain.daily}
|
||||||
retain weekly ${toString cfg.backup.retain.weekly}
|
retain weekly ${toString cfg.backup.retain.weekly}
|
||||||
backup ${cfg.mailDirectory}/ localhost/
|
backup ${cfg.storage.path}/ localhost/
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ in
|
|||||||
preStart =
|
preStart =
|
||||||
let
|
let
|
||||||
directories = lib.strings.escapeShellArgs (
|
directories = lib.strings.escapeShellArgs (
|
||||||
[ cfg.mailDirectory ] ++ lib.optional (cfg.indexDir != null) cfg.indexDir
|
[ cfg.storage.path ] ++ lib.optional (cfg.indexDir != null) cfg.indexDir
|
||||||
);
|
);
|
||||||
in
|
in
|
||||||
''
|
''
|
||||||
@@ -55,7 +55,7 @@ in
|
|||||||
# Prevent world-readable paths, even temporarily.
|
# Prevent world-readable paths, even temporarily.
|
||||||
umask 007
|
umask 007
|
||||||
mkdir -p ${directories}
|
mkdir -p ${directories}
|
||||||
chgrp "${cfg.vmailGroupName}" ${directories}
|
chgrp "${cfg.storage.group}" ${directories}
|
||||||
chmod 02770 ${directories}
|
chmod 02770 ${directories}
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|||||||
+30
-47
@@ -16,32 +16,13 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
config,
|
config,
|
||||||
options,
|
|
||||||
pkgs,
|
pkgs,
|
||||||
lib,
|
lib,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
|
|
||||||
with (import ./common.nix {
|
|
||||||
inherit
|
|
||||||
config
|
|
||||||
options
|
|
||||||
lib
|
|
||||||
pkgs
|
|
||||||
;
|
|
||||||
});
|
|
||||||
|
|
||||||
with config.mailserver;
|
|
||||||
|
|
||||||
let
|
let
|
||||||
vmail_user = {
|
cfg = config.mailserver;
|
||||||
name = vmailUserName;
|
|
||||||
isSystemUser = true;
|
|
||||||
uid = vmailUID;
|
|
||||||
home = mailDirectory;
|
|
||||||
createHome = true;
|
|
||||||
group = vmailGroupName;
|
|
||||||
};
|
|
||||||
|
|
||||||
virtualMailUsersActivationScript =
|
virtualMailUsersActivationScript =
|
||||||
pkgs.writeScript "activate-virtual-mail-users"
|
pkgs.writeScript "activate-virtual-mail-users"
|
||||||
@@ -55,10 +36,10 @@ let
|
|||||||
umask 007
|
umask 007
|
||||||
|
|
||||||
# Create directory to store user sieve scripts if it doesn't exist
|
# Create directory to store user sieve scripts if it doesn't exist
|
||||||
if (! test -d "${sieveDirectory}"); then
|
if (! test -d "${cfg.sieveDirectory}"); then
|
||||||
mkdir "${sieveDirectory}"
|
mkdir "${cfg.sieveDirectory}"
|
||||||
chown "${vmailUserName}:${vmailGroupName}" "${sieveDirectory}"
|
chown "${cfg.storage.owner}:${cfg.storage.group}" "${cfg.sieveDirectory}"
|
||||||
chmod 770 "${sieveDirectory}"
|
chmod 770 "${cfg.sieveDirectory}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Copy user's sieve script to the correct location (if it exists). If it
|
# Copy user's sieve script to the correct location (if it exists). If it
|
||||||
@@ -67,30 +48,30 @@ let
|
|||||||
{ name, sieveScript }:
|
{ name, sieveScript }:
|
||||||
if lib.isString sieveScript then
|
if lib.isString sieveScript then
|
||||||
''
|
''
|
||||||
if (! test -d "${sieveDirectory}/${name}"); then
|
if (! test -d "${cfg.sieveDirectory}/${name}"); then
|
||||||
mkdir -p "${sieveDirectory}/${name}"
|
mkdir -p "${cfg.sieveDirectory}/${name}"
|
||||||
chown "${vmailUserName}:${vmailGroupName}" "${sieveDirectory}/${name}"
|
chown "${cfg.storage.owner}:${cfg.storage.group}" "${cfg.sieveDirectory}/${name}"
|
||||||
chmod 770 "${sieveDirectory}/${name}"
|
chmod 770 "${cfg.sieveDirectory}/${name}"
|
||||||
fi
|
fi
|
||||||
cat << 'EOF' > "${sieveDirectory}/${name}/default.sieve"
|
cat << 'EOF' > "${cfg.sieveDirectory}/${name}/default.sieve"
|
||||||
${sieveScript}
|
${sieveScript}
|
||||||
EOF
|
EOF
|
||||||
chown "${vmailUserName}:${vmailGroupName}" "${sieveDirectory}/${name}/default.sieve"
|
chown "${cfg.storage.owner}:${cfg.storage.group}" "${cfg.sieveDirectory}/${name}/default.sieve"
|
||||||
''
|
''
|
||||||
else
|
else
|
||||||
''
|
''
|
||||||
if (test -f "${sieveDirectory}/${name}/default.sieve"); then
|
if (test -f "${cfg.sieveDirectory}/${name}/default.sieve"); then
|
||||||
rm "${sieveDirectory}/${name}/default.sieve"
|
rm "${cfg.sieveDirectory}/${name}/default.sieve"
|
||||||
fi
|
fi
|
||||||
if (test -f "${sieveDirectory}/${name}.svbin"); then
|
if (test -f "${cfg.sieveDirectory}/${name}.svbin"); then
|
||||||
rm "${sieveDirectory}/${name}/default.svbin"
|
rm "${cfg.sieveDirectory}/${name}/default.svbin"
|
||||||
fi
|
fi
|
||||||
''
|
''
|
||||||
) (map (user: { inherit (user) name sieveScript; }) (lib.attrValues loginAccounts))}
|
) (map (user: { inherit (user) name sieveScript; }) (lib.attrValues cfg.accounts))}
|
||||||
'';
|
'';
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config = lib.mkIf enable {
|
config = lib.mkIf cfg.enable {
|
||||||
# assert that all accounts provide a password
|
# assert that all accounts provide a password
|
||||||
assertions = map (acct: {
|
assertions = map (acct: {
|
||||||
assertion =
|
assertion =
|
||||||
@@ -102,27 +83,29 @@ in
|
|||||||
]
|
]
|
||||||
) == 1;
|
) == 1;
|
||||||
message = "Login account ${acct.name} must provide exactly one of password file, hashed password, or hashed password file";
|
message = "Login account ${acct.name} must provide exactly one of password file, hashed password, or hashed password file";
|
||||||
}) (lib.attrValues loginAccounts);
|
}) (lib.attrValues cfg.accounts);
|
||||||
|
|
||||||
# warn for accounts that specify both password and file
|
# warn for accounts that specify both password and file
|
||||||
warnings =
|
warnings =
|
||||||
map (acct: "${acct.name} specifies both a password hash and hash file; hash file will be used")
|
map (acct: "${acct.name} specifies both a password hash and hash file; hash file will be used")
|
||||||
(
|
(
|
||||||
lib.filter (acct: (acct.hashedPassword != null && acct.hashedPasswordFile != null)) (
|
lib.filter (acct: (acct.hashedPassword != null && acct.hashedPasswordFile != null)) (
|
||||||
lib.attrValues loginAccounts
|
lib.attrValues cfg.accounts
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
# set the vmail gid to a specific value
|
users.groups.${cfg.storage.group} = {
|
||||||
users.groups = {
|
inherit (cfg.storage) gid;
|
||||||
"${vmailGroupName}" = {
|
|
||||||
gid = vmailUID;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
users.users.${cfg.storage.owner} = lib.mkForce {
|
||||||
# define all users
|
inherit (cfg.storage)
|
||||||
users.users = {
|
group
|
||||||
"${vmail_user.name}" = lib.mkForce vmail_user;
|
uid
|
||||||
|
;
|
||||||
|
name = cfg.storage.owner;
|
||||||
|
isSystemUser = true;
|
||||||
|
home = cfg.storage.path;
|
||||||
|
createHome = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.services.activate-virtual-mail-users = {
|
systemd.services.activate-virtual-mail-users = {
|
||||||
|
|||||||
@@ -25,8 +25,9 @@ f = open(sys.argv[1])
|
|||||||
options = json.load(f)
|
options = json.load(f)
|
||||||
|
|
||||||
groups = [
|
groups = [
|
||||||
"mailserver.loginAccounts",
|
"mailserver.accounts",
|
||||||
"mailserver.x509",
|
"mailserver.x509",
|
||||||
|
"mailserver.storage",
|
||||||
"mailserver.dkim",
|
"mailserver.dkim",
|
||||||
"mailserver.srs",
|
"mailserver.srs",
|
||||||
"mailserver.dmarcReporting",
|
"mailserver.dmarcReporting",
|
||||||
|
|||||||
+1
-1
@@ -77,7 +77,7 @@
|
|||||||
];
|
];
|
||||||
virusScanning = true;
|
virusScanning = true;
|
||||||
|
|
||||||
loginAccounts = {
|
accounts = {
|
||||||
"user1@example.com" = {
|
"user1@example.com" = {
|
||||||
hashedPassword = "$6$/z4n8AQl6K$kiOkBTWlZfBd7PvF5GsJ8PmPgdZsFGN1jPGZufxxr60PoR0oUsrvzm2oQiflyz5ir9fFJ.d/zKm/NgLXNUsNX/";
|
hashedPassword = "$6$/z4n8AQl6K$kiOkBTWlZfBd7PvF5GsJ8PmPgdZsFGN1jPGZufxxr60PoR0oUsrvzm2oQiflyz5ir9fFJ.d/zKm/NgLXNUsNX/";
|
||||||
aliases = [ "postmaster@example.com" ];
|
aliases = [ "postmaster@example.com" ];
|
||||||
|
|||||||
+1
-1
@@ -66,7 +66,7 @@
|
|||||||
};
|
};
|
||||||
dmarcReporting.enable = true;
|
dmarcReporting.enable = true;
|
||||||
|
|
||||||
loginAccounts = {
|
accounts = {
|
||||||
"user1@example.com" = {
|
"user1@example.com" = {
|
||||||
hashedPassword = "$6$/z4n8AQl6K$kiOkBTWlZfBd7PvF5GsJ8PmPgdZsFGN1jPGZufxxr60PoR0oUsrvzm2oQiflyz5ir9fFJ.d/zKm/NgLXNUsNX/";
|
hashedPassword = "$6$/z4n8AQl6K$kiOkBTWlZfBd7PvF5GsJ8PmPgdZsFGN1jPGZufxxr60PoR0oUsrvzm2oQiflyz5ir9fFJ.d/zKm/NgLXNUsNX/";
|
||||||
aliases = [ "postmaster@example.com" ];
|
aliases = [ "postmaster@example.com" ];
|
||||||
|
|||||||
+7
-4
@@ -87,7 +87,7 @@ in
|
|||||||
];
|
];
|
||||||
localDnsResolver = false;
|
localDnsResolver = false;
|
||||||
|
|
||||||
loginAccounts = {
|
accounts = {
|
||||||
"user1@example.com" = {
|
"user1@example.com" = {
|
||||||
hashedPasswordFile = hashedPasswordFile;
|
hashedPasswordFile = hashedPasswordFile;
|
||||||
};
|
};
|
||||||
@@ -109,8 +109,11 @@ in
|
|||||||
"user2@example.com" = "user1@example.com";
|
"user2@example.com" = "user1@example.com";
|
||||||
};
|
};
|
||||||
|
|
||||||
vmailGroupName = "vmail";
|
storage = {
|
||||||
vmailUID = 5000;
|
gid = 5000;
|
||||||
|
group = "vmail";
|
||||||
|
};
|
||||||
|
|
||||||
indexDir = "/var/lib/dovecot/indices";
|
indexDir = "/var/lib/dovecot/indices";
|
||||||
|
|
||||||
enableImap = false;
|
enableImap = false;
|
||||||
@@ -218,7 +221,7 @@ in
|
|||||||
|
|
||||||
with subtest("Check dovecot maildir and index locations"):
|
with subtest("Check dovecot maildir and index locations"):
|
||||||
# If these paths change we need a migration
|
# If these paths change we need a migration
|
||||||
machine.succeed("doveadm user -f home user1@example.com | grep ${nodes.machine.mailserver.mailDirectory}/example.com/user1")
|
machine.succeed("doveadm user -f home user1@example.com | grep ${nodes.machine.mailserver.storage.path}/example.com/user1")
|
||||||
machine.succeed("doveadm user -f mail user1@example.com | grep 'maildir:~/mail:INDEX=${nodes.machine.mailserver.indexDir}/example.com/user1'")
|
machine.succeed("doveadm user -f mail user1@example.com | grep 'maildir:~/mail:INDEX=${nodes.machine.mailserver.indexDir}/example.com/user1'")
|
||||||
|
|
||||||
with subtest("mail to send only accounts is rejected"):
|
with subtest("mail to send only accounts is rejected"):
|
||||||
|
|||||||
+5
-5
@@ -125,7 +125,7 @@ in
|
|||||||
fqdn = "mail.example.com";
|
fqdn = "mail.example.com";
|
||||||
domains = [ "example.com" ];
|
domains = [ "example.com" ];
|
||||||
localDnsResolver = false;
|
localDnsResolver = false;
|
||||||
mailDirectory = "/var/lib/dovecot/vmail";
|
storage.path = "/var/lib/dovecot/vmail";
|
||||||
indexDir = "/var/lib/dovecot/indices";
|
indexDir = "/var/lib/dovecot/indices";
|
||||||
|
|
||||||
aliases = {
|
aliases = {
|
||||||
@@ -133,7 +133,7 @@ in
|
|||||||
"frank@example.com" = "mallory@example.com";
|
"frank@example.com" = "mallory@example.com";
|
||||||
};
|
};
|
||||||
|
|
||||||
loginAccounts = {
|
accounts = {
|
||||||
# Colliding local account takes precedence over LDAP account with
|
# Colliding local account takes precedence over LDAP account with
|
||||||
# same address.
|
# same address.
|
||||||
"carol@example.com" = {
|
"carol@example.com" = {
|
||||||
@@ -214,10 +214,10 @@ in
|
|||||||
machine.succeed("doveadm user -u alice")
|
machine.succeed("doveadm user -u alice")
|
||||||
machine.log(machine.succeed("doveadm user -u bob"))
|
machine.log(machine.succeed("doveadm user -u bob"))
|
||||||
|
|
||||||
machine.succeed("doveadm user -f uid bob@example.com | grep ${toString nodes.machine.mailserver.vmailUID}")
|
machine.succeed("doveadm user -f uid bob@example.com | grep ${toString nodes.machine.mailserver.storage.uid}")
|
||||||
machine.succeed("doveadm user -f gid bob@example.com | grep ${toString nodes.machine.mailserver.vmailUID}")
|
machine.succeed("doveadm user -f gid bob@example.com | grep ${toString nodes.machine.mailserver.storage.uid}")
|
||||||
|
|
||||||
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 home bob@example.com | grep ${nodes.machine.mailserver.storage.path}/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'")
|
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"):
|
with subtest("Files containing secrets are only readable by root"):
|
||||||
|
|||||||
+1
-1
@@ -35,7 +35,7 @@ let
|
|||||||
fqdn = "mail.${domain}";
|
fqdn = "mail.${domain}";
|
||||||
domains = [ domain ];
|
domains = [ domain ];
|
||||||
localDnsResolver = false;
|
localDnsResolver = false;
|
||||||
loginAccounts = {
|
accounts = {
|
||||||
"user@${domain}" = {
|
"user@${domain}" = {
|
||||||
hashedPasswordFile = hashPassword "password";
|
hashedPasswordFile = hashPassword "password";
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user