diff --git a/default.nix b/default.nix index cc4f7a7..575cc6c 100644 --- a/default.nix +++ b/default.nix @@ -136,7 +136,7 @@ in description = "Message size limit enforced by Postfix."; }; - loginAccounts = mkOption { + accounts = mkOption { type = types.attrsOf ( types.submodule ( { name, ... }: @@ -170,7 +170,7 @@ in storing hashed secrets in the world-readable Nix store. Passing the hash through - {option}`mailserver.loginAccounts..hashedPasswordFile` + {option}`mailserver.accounts..hashedPasswordFile` allows relying on filesystem discretionary access control as another security boundary. ::: @@ -231,7 +231,7 @@ in example = [ ''/^tom\..*@domain\.com$/'' ]; default = [ ]; description = '' - Same as {option}`mailserver.loginAccounts..aliases` but + Same as {option}`mailserver.accounts..aliases` but using PCRE (Perl compatible regex). ''; }; @@ -248,7 +248,7 @@ in :::{warning} Does not allow sending from all addresses of these domains. - Use {option}`mailserver.loginAccounts..aliases` if that + Use {option}`mailserver.accounts..aliases` if that is required. ::: ''; @@ -295,7 +295,7 @@ in Emails sent to send-only accounts will be rejected with the reason configured in - {option}`mailserver.loginAccounts..sendOnlyRejectMessage`. + {option}`mailserver.accounts..sendOnlyRejectMessage`. ''; }; @@ -305,7 +305,7 @@ in description = '' The message returned to the sender for a send-only account. - See {option}`mailserver.loginAccounts..sendOnly`. + See {option}`mailserver.accounts..sendOnly`. ''; }; }; @@ -684,13 +684,13 @@ in aliases = mkOption { type = let - loginAccount = mkOptionType { + account = mkOptionType { name = "Login Account"; - check = account: builtins.elem account (builtins.attrNames cfg.loginAccounts); + check = account: builtins.elem account (builtins.attrNames cfg.accounts); }; in with types; - attrsOf (either loginAccount (nonEmptyListOf loginAccount)); + attrsOf (either account (nonEmptyListOf account)); example = { "postmaster@example.com" = "user1@example.com"; "abuse@example.com" = "user1@example.com"; @@ -1714,5 +1714,6 @@ in [ "mailserver" "ldap" "attributes" "mail" ] ) (mkRenamedOptionModule [ "mailserver" "extraVirtualAliases" ] [ "mailserver" "aliases" ]) + (mkRenamedOptionModule [ "mailserver" "loginAccounts" ] [ "mailserver" "accounts" ]) ]; } diff --git a/docs/faq.rst b/docs/faq.rst index ef306de..674ee0a 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -13,7 +13,7 @@ domain ``example.com`` and send mails with any address of this domain: .. code:: nix - mailserver.loginAccounts = { + mailserver.accounts = { "user@example.com" = { aliases = [ "@example.com" ]; }; diff --git a/docs/radicale.nix b/docs/radicale.nix index e740fb7..6ccecc7 100644 --- a/docs/radicale.nix +++ b/docs/radicale.nix @@ -12,7 +12,7 @@ let mapAttrsToList ; - mailAccounts = config.mailserver.loginAccounts; + mailAccounts = config.mailserver.accounts; htpasswd = pkgs.writeText "radicale.users" ( concatStrings (flip mapAttrsToList mailAccounts (mail: user: "${mail}+:${user.hashedPassword}\n")) ); diff --git a/docs/radicale.rst b/docs/radicale.rst index 244a41e..f594b8e 100644 --- a/docs/radicale.rst +++ b/docs/radicale.rst @@ -10,7 +10,7 @@ Limitations Radicale since the 3.x release (introduced in NixOS 20.09) does not support 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 `htpasswd_encryption`_ methods. Such hashes can for example be created using diff --git a/docs/release-notes.rst b/docs/release-notes.rst index 9ee6ccc..e0c2bd8 100644 --- a/docs/release-notes.rst +++ b/docs/release-notes.rst @@ -21,7 +21,7 @@ NixOS 26.05 is an alternative to hashed passwords that integrates well with workflows established by `agenix`_/`sops-nix`_ that instead rely on encryption. This option prevents files from leaking in to the Nix store. - See :option:`mailserver.loginAccounts..passwordFile`. + See :option:`mailserver.accounts..passwordFile`. - LDAP setups require a migration of Dovecot home directories to `UUID based home directories`_. The exact UUID attribute can be customized through :option:`mailserver.ldap.attributes.uuid`. diff --git a/docs/setup-example.nix b/docs/setup-example.nix index aba25ed..66a8242 100644 --- a/docs/setup-example.nix +++ b/docs/setup-example.nix @@ -38,7 +38,7 @@ # A list of all login accounts. To create the password hashes, use # nix-shell -p mkpasswd --run 'mkpasswd -s' - loginAccounts = { + accounts = { "user1@example.com" = { # Reads the password hash from a file on the server hashedPasswordFile = "/a/file/containing/a/hashed/password"; diff --git a/mail-server/common.nix b/mail-server/common.nix index 3ca5c57..95f317b 100644 --- a/mail-server/common.nix +++ b/mail-server/common.nix @@ -51,12 +51,12 @@ rec { builtins.toString (mkHashFile name value.hashedPassword) else value.passwordFile - ) cfg.loginAccounts; + ) cfg.accounts; # Collect accounts with plain text passwords that require hashing - accountsWithPlaintextPasswordFiles = lib.filter ( - name: cfg.loginAccounts.${name}.passwordFile != null - ) (builtins.attrNames cfg.loginAccounts); + accountsWithPlaintextPasswordFiles = lib.filter (name: cfg.accounts.${name}.passwordFile != null) ( + builtins.attrNames cfg.accounts + ); # Appends the LDAP bind password to files to avoid writing this # password into the Nix store. diff --git a/mail-server/dovecot.nix b/mail-server/dovecot.nix index 9c89c4e..83c4859 100644 --- a/mail-server/dovecot.nix +++ b/mail-server/dovecot.nix @@ -115,7 +115,7 @@ let umask 077 for f in ${ - builtins.toString (lib.mapAttrsToList (name: _: passwordFiles."${name}") cfg.loginAccounts) + builtins.toString (lib.mapAttrsToList (name: _: passwordFiles."${name}") cfg.accounts) }; do if [ ! -f "$f" ]; then 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)"}::::::" else "${name}:${"$(head -n 1 ${passwordFiles."${name}"})"}::::::" - ) cfg.loginAccounts + ) cfg.accounts )} EOF @@ -141,7 +141,7 @@ let name: value: "${name}:::::::" + lib.optionalString (value.quota != null) "userdb_quota_rule=*:storage=${value.quota}" - ) cfg.loginAccounts + ) cfg.accounts )} EOF ''; diff --git a/mail-server/postfix.nix b/mail-server/postfix.nix index 70ae68e..52dc45a 100644 --- a/mail-server/postfix.nix +++ b/mail-server/postfix.nix @@ -51,7 +51,7 @@ let to = name; in map (from: { "${from}" = to; }) (value.aliases ++ lib.singleton name) - ) cfg.loginAccounts + ) cfg.accounts ) ); regex_valiases_postfix = mergeLookupTables ( @@ -62,7 +62,7 @@ let to = name; in map (from: { "${from}" = to; }) value.aliasesRegexp - ) cfg.loginAccounts + ) cfg.accounts ) ); @@ -75,7 +75,7 @@ let to = name; in map (from: { "@${from}" = to; }) value.catchAll - ) cfg.loginAccounts + ) cfg.accounts ) ); @@ -127,7 +127,7 @@ let # denied_recipients_postfix :: [ String ] 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" ( lib.concatStringsSep "\n" denied_recipients_postfix diff --git a/mail-server/users.nix b/mail-server/users.nix index 9863066..4c0f620 100644 --- a/mail-server/users.nix +++ b/mail-server/users.nix @@ -86,7 +86,7 @@ let rm "${sieveDirectory}/${name}/default.svbin" fi '' - ) (map (user: { inherit (user) name sieveScript; }) (lib.attrValues loginAccounts))} + ) (map (user: { inherit (user) name sieveScript; }) (lib.attrValues accounts))} ''; in { @@ -102,14 +102,14 @@ in ] ) == 1; message = "Login account ${acct.name} must provide exactly one of password file, hashed password, or hashed password file"; - }) (lib.attrValues loginAccounts); + }) (lib.attrValues accounts); # warn for accounts that specify both password and file warnings = 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.attrValues loginAccounts + lib.attrValues accounts ) ); diff --git a/scripts/generate-options.py b/scripts/generate-options.py index c195504..f6ea83c 100644 --- a/scripts/generate-options.py +++ b/scripts/generate-options.py @@ -25,7 +25,7 @@ f = open(sys.argv[1]) options = json.load(f) groups = [ - "mailserver.loginAccounts", + "mailserver.accounts", "mailserver.x509", "mailserver.dkim", "mailserver.srs", diff --git a/tests/clamav.nix b/tests/clamav.nix index 900dd32..2600416 100644 --- a/tests/clamav.nix +++ b/tests/clamav.nix @@ -77,7 +77,7 @@ ]; virusScanning = true; - loginAccounts = { + accounts = { "user1@example.com" = { hashedPassword = "$6$/z4n8AQl6K$kiOkBTWlZfBd7PvF5GsJ8PmPgdZsFGN1jPGZufxxr60PoR0oUsrvzm2oQiflyz5ir9fFJ.d/zKm/NgLXNUsNX/"; aliases = [ "postmaster@example.com" ]; diff --git a/tests/external.nix b/tests/external.nix index a149144..e47013c 100644 --- a/tests/external.nix +++ b/tests/external.nix @@ -66,7 +66,7 @@ }; dmarcReporting.enable = true; - loginAccounts = { + accounts = { "user1@example.com" = { hashedPassword = "$6$/z4n8AQl6K$kiOkBTWlZfBd7PvF5GsJ8PmPgdZsFGN1jPGZufxxr60PoR0oUsrvzm2oQiflyz5ir9fFJ.d/zKm/NgLXNUsNX/"; aliases = [ "postmaster@example.com" ]; diff --git a/tests/internal.nix b/tests/internal.nix index a3cbfb8..15ada06 100644 --- a/tests/internal.nix +++ b/tests/internal.nix @@ -87,7 +87,7 @@ in ]; localDnsResolver = false; - loginAccounts = { + accounts = { "user1@example.com" = { hashedPasswordFile = hashedPasswordFile; }; diff --git a/tests/ldap.nix b/tests/ldap.nix index 2999bd8..fd9711f 100644 --- a/tests/ldap.nix +++ b/tests/ldap.nix @@ -133,7 +133,7 @@ in "frank@example.com" = "mallory@example.com"; }; - loginAccounts = { + accounts = { # Colliding local account takes precedence over LDAP account with # same address. "carol@example.com" = { diff --git a/tests/multiple.nix b/tests/multiple.nix index 3f9b5db..d627edd 100644 --- a/tests/multiple.nix +++ b/tests/multiple.nix @@ -35,7 +35,7 @@ let fqdn = "mail.${domain}"; domains = [ domain ]; localDnsResolver = false; - loginAccounts = { + accounts = { "user@${domain}" = { hashedPasswordFile = hashPassword "password"; };