diff --git a/nix/modules/nixos/os/auto-update/default.nix b/nix/modules/nixos/os/auto-update/default.nix index 721b890..765e015 100644 --- a/nix/modules/nixos/os/auto-update/default.nix +++ b/nix/modules/nixos/os/auto-update/default.nix @@ -43,6 +43,7 @@ in system.autoUpgrade = { enable = true; flake = upgradePath; + allowReboot = true; }; systemd.services.nixos-upgrade-prepare-flake = { wantedBy = [ "nixos-upgrade.service" ]; diff --git a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/accounts.nix b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/accounts.nix index cb4c0ad..0478f30 100644 --- a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/accounts.nix +++ b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/accounts.nix @@ -11,6 +11,10 @@ let name = "bogus-passwd"; text = "$6$1234"; }; + userDbFile = pkgs.writeTextFile { + name = "userdb"; + text = ''''; + }; accountPrimaryEmail = name: account: if account.isLdapAccount then lib.lists.head account.aliases else name; accountAlternativeEmails = @@ -141,17 +145,6 @@ let ) # Just make sure the file is not empty + "\n"; - - # I would like to not use emails as usernames, but this is not something postfix is particularly happy with, - # as far as I can see. I *think* I can do this by randomly selecting an address, ie. the first, as the "primary" - # address, and then map all other addresses to that one. Then create a virtual mailbox map that maps that back - # to the username before delivering it to lmtp. All of this just worked when using ldap, but that meant - # not using code configured accounts set up with opentofu as well, and would also make the email delivery - # take a hard dependency on ldap (and not just the login process). - - # Create extra virtual aliases for all accounts (as they are not emails, that maps them to their primary email). - # Then we map those emails back into the account name in the mailbox maps. - extraVirtualAliases = lib.mapAttrs (name: account: accountPrimaryEmail name account) cfg.accounts; in { options.khscodes."mx.kaareskovgaard.net".accounts = lib.mkOption { @@ -160,7 +153,6 @@ in }; config = { mailserver = { - inherit extraVirtualAliases; loginAccounts = lib.attrsets.mapAttrs (name: value: { inherit (value) name @@ -170,10 +162,11 @@ in sieveScript sendOnly sendOnlyRejectMessage + aliases ; - aliases = accountAlternativeEmails name value; hashedPasswordFile = bogusPasswdFile; }) cfg.accounts; + extraVirtualAliases = { }; }; khscodes.infrastructure.vault-server-approle.policy = { "mx.kaareskovgaard.net/data/users/*" = { @@ -235,6 +228,7 @@ in # with our own. preStart = lib.mkAfter '' cp ${passDbFile} /run/dovecot2/passwd + # cp ${userDbFile} /run/dovecot2/userdb ''; }; # This prevents local usernames without domain names to get rewritten. diff --git a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/accounts/mailbox_map.nix b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/accounts/mailbox_map.nix new file mode 100644 index 0000000..3e3712b --- /dev/null +++ b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/accounts/mailbox_map.nix @@ -0,0 +1,140 @@ +{ + lib, + accounts, + accountPrimaryEmail, + accountAlternativeEmails, + extraVirtualAliases ? { }, +}: +let + # I would like to not use emails as usernames, but this is not something postfix is particularly happy with, + # as far as I can see. I *think* I can do this by randomly selecting an address, ie. the first, as the "primary" + # address, and then map all other addresses to that one. Then create a virtual mailbox map that maps that back + # to the username before delivering it to lmtp. All of this just worked when using ldap, but that meant + # not using code configured accounts set up with opentofu as well, and would also make the email delivery + # take a hard dependency on ldap (and not just the login process). + + # Most of these functions are almost the same as what is found inside nixos-simple-mailserver, + # just modified with this in mind. + + # Merge several lookup tables. A lookup table is a attribute set where + # - the key is an address (user@example.com) or a domain (@example.com) + # - the value is a list of addresses + mergeLookupTables = tables: lib.zipAttrsWith (_: v: lib.flatten v) tables; + + # lookupTableToString :: Map String [String] -> String + lookupTableToString = + attrs: + let + valueToString = value: lib.concatStringsSep ", " value; + in + lib.concatStringsSep "\n" ( + lib.mapAttrsToList (name: value: "${name} ${valueToString value}") attrs + ); + # attrsToLookupTable :: Map String (Either String [ String ]) -> Map String [String] + attrsToLookupTable = + aliases: + let + lookupTables = lib.mapAttrsToList (from: to: { "${from}" = to; }) aliases; + in + mergeLookupTables lookupTables; + + # valiases_postfix :: Map String [String] + valiases_postfix = mergeLookupTables ( + lib.flatten ( + lib.mapAttrsToList ( + name: value: + let + to = name; + in + map (from: { "${from}" = to; }) ( + (accountAlternativeEmails name value) ++ lib.singleton (accountPrimaryEmail name value) + ) + ) accounts + ) + ); + # catchAllPostfix :: Map String [String] + catchAllPostfix = mergeLookupTables ( + lib.flatten ( + lib.mapAttrsToList ( + name: value: + let + to = name; + in + map (from: { "@${from}" = to; }) value.catchAll + ) accounts + ) + ); + + # mailserverExtraVirtualAliases :: Map String (Map String) + mailserverExtraVirtualAliases = lib.attrsets.mapAttrs ( + name: value: + let + value = if lib.lists.isList value then value else [ value ]; + to = name; + in + { + name = to; + value = lib.lists.map ( + addr: + assert (lib.attrsets.hasAttr addr accounts); + accountPrimaryEmail name accounts."${addr}" + ) value; + } + ) extraVirtualAliases; + + # extra_valiases_postfix :: Map String [String] + extra_valiases_postfix = attrsToLookupTable extraVirtualAliases; + + # all_valiases_postfix :: Map String [String] + all_valiases_postfix = mergeLookupTables [ + valiases_postfix + extra_valiases_postfix + ]; + + # valiases_file :: Path + valiases_file = + let + content = lookupTableToString (mergeLookupTables [ + all_valiases_postfix + catchAllPostfix + ]); + in + builtins.toFile "valias" content; + + regex_valiases_postfix = mergeLookupTables ( + lib.flatten ( + lib.mapAttrsToList ( + name: value: + let + to = name; + in + map (from: { "${from}" = to; }) value.aliasesRegexp + ) accounts + ) + ); + regex_valiases_file = + let + content = lookupTableToString regex_valiases_postfix; + in + builtins.toFile "regex_valias" content; + + # vaccounts_file :: Path + # see + # https://blog.grimneko.de/2011/12/24/a-bunch-of-tips-for-improving-your-postfix-setup/ + # for details on how this file looks. By using the same file as valiases, + # every alias is owned (uniquely) by its user. + # The user's own address is already in all_valiases_postfix. + vaccounts_file = builtins.toFile "vaccounts" (lookupTableToString all_valiases_postfix); + regex_vaccounts_file = builtins.toFile "regex_vaccounts" ( + lookupTableToString regex_valiases_postfix + ); +in +{ + inherit + valiases_file + regex_valiases_file + vaccounts_file + regex_vaccounts_file + mailserverExtraVirtualAliases + ; +} diff --git a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/ldap.nix b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/ldap.nix index 200476b..c8bc2f6 100644 --- a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/ldap.nix +++ b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/ldap.nix @@ -41,7 +41,7 @@ in base = dc=login,dc=kaareskovgaard,dc=net scope = subtree pass_attrs = uid=user - pass_filter = (&(class=account)(memberOf=mail_user)(uid=%u) + pass_filter = (&(class=account)(memberOf=mail_user)(uid=%u)) {{- end -}} ''; destination = secretFile;