diff --git a/nix/modules/nixos/services/vault-agent/default.nix b/nix/modules/nixos/services/vault-agent/default.nix index 4dfdc40..9f87199 100644 --- a/nix/modules/nixos/services/vault-agent/default.nix +++ b/nix/modules/nixos/services/vault-agent/default.nix @@ -35,9 +35,9 @@ let runtimeInputs = [ pkgs.systemd ]; text = '' chown ${lib.escapeShellArg template.owner}:${lib.escapeShellArg template.group} ${lib.escapeShellArg template.destination} + ${template.exec} ${restartUnits template.restartUnits} ${reloadOrRestartUnits template.reloadOrRestartUnits} - ${template.exec} ''; meta = { mainProgram = "restart-command"; diff --git a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/default.nix b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/default.nix index e0255ad..7f21f0f 100644 --- a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/default.nix +++ b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/default.nix @@ -34,9 +34,13 @@ ]; }; "mx.kaareskovgaard.net" = { + postmaster = "kaare+postmaster@agerlin-skovgaard.dk"; + abuse = "kaare+abuse@agerlin-skovgaard.dk"; domains = [ "agerlin-skovgaard.dk" "agerlinskovgaard.dk" + "k.agerlin-skovgaard.dk" + "k.agerlinskovgaard.dk" ]; accounts = import ./users.nix; }; 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 0478f30..eb7c1a2 100644 --- a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/accounts.nix +++ b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/accounts.nix @@ -7,22 +7,39 @@ let cfg = config.khscodes."mx.kaareskovgaard.net"; passDbFile = "/run/secret/dovecot/passwd"; + isCatchAllAddress = addr: lib.strings.hasPrefix "@" addr; bogusPasswdFile = pkgs.writeTextFile { name = "bogus-passwd"; text = "$6$1234"; }; + userDbAccountOpts = + name: account: + lib.strings.concatStringsSep " " ( + (lib.lists.optional (account.quota != null) "userdb_quota_rule=*:storage=${account.quota}") + ++ [ "userdb_user=${name}" ] + ); + userDbLine = + addr: name: account: + "${addr}:::::::${userDbAccountOpts name account}"; userDbFile = pkgs.writeTextFile { name = "userdb"; - text = ''''; + text = lib.strings.concatStringsSep "\n" ( + lib.lists.flatten ( + lib.mapAttrsToList ( + name: account: + [ + (userDbLine name name account) + ] + ++ (lib.lists.map (addr: userDbLine addr name account) ( + # Don't include catch all addresses, they become aliases in postfix + lib.lists.filter (addr: !(isCatchAllAddress addr)) account.addresses + )) + ) cfg.accounts + ) + ); }; - accountPrimaryEmail = - name: account: if account.isLdapAccount then lib.lists.head account.aliases else name; - accountAlternativeEmails = - name: account: - if account.isLdapAccount then - lib.lists.ifilter0 (idx: _: idx > 0) account.aliases - else - account.aliases; + firstNonCatchAllAddress = + account: lib.lists.head (lib.lists.filter (addr: !(isCatchAllAddress addr)) account.addresses); accountOption = lib.khscodes.mkSubmodule { description = "mail account"; options = { @@ -32,7 +49,7 @@ let description = "Username"; }; - aliases = lib.mkOption { + addresses = lib.mkOption { type = lib.types.listOf lib.types.str; example = [ "abuse@example.com" @@ -40,31 +57,9 @@ let ]; default = [ ]; description = '' - A list of aliases of this login account. - Note: Use list entries like "@example.com" to create a catchAll - that allows sending from all email addresses in these domain. - ''; - }; - - aliasesRegexp = lib.mkOption { - type = lib.types.listOf lib.types.str; - example = [ ''/^tom\..*@domain\.com$/'' ]; - default = [ ]; - description = '' - Same as {option}`mailserver.aliases` but using PCRE (Perl compatible regex). - ''; - }; - - catchAll = lib.mkOption { - type = lib.types.listOf (lib.types.enum config.mailserver.domains); - example = [ - "example.com" - "example2.com" - ]; - default = [ ]; - description = '' - For which domains should this account act as a catch all? - Note: Does not allow sending from all addresses of these domains. + A list of addresses for the account. + Note: Use list entires like "@example.com" to create a catchAll + that allows sending from an email address in these domains. ''; }; @@ -145,6 +140,31 @@ let ) # Just make sure the file is not empty + "\n"; + + catchAllAliases = lib.strings.concatStringsSep "\n" ( + lib.lists.flatten ( + lib.attrsets.mapAttrsToList ( + name: account: + let + regularEmailAddr = firstNonCatchAllAddress account; + catchAlls = lib.lists.filter isCatchAllAddress account.addresses; + in + lib.lists.map (addr: "${addr} ${regularEmailAddr}") catchAlls + ) cfg.accounts + ) + ); + postmasterAliases = lib.strings.concatStringsSep "\n" ( + lib.lists.map (domain: "postmaster@${domain} ${cfg.postmaster}") cfg.domains + ); + + abuseAliases = lib.strings.concatStringsSep "\n" ( + lib.lists.map (domain: "abuse@${domain} ${cfg.abuse}") cfg.domains + ); + extraVirtualAliases = lib.strings.concatStringsSep "\n" [ + catchAllAliases + postmasterAliases + abuseAliases + ]; in { options.khscodes."mx.kaareskovgaard.net".accounts = lib.mkOption { @@ -156,14 +176,12 @@ in loginAccounts = lib.attrsets.mapAttrs (name: value: { inherit (value) name - aliasesRegexp - catchAll quota sieveScript sendOnly sendOnlyRejectMessage - aliases ; + aliases = value.addresses; hashedPasswordFile = bogusPasswdFile; }) cfg.accounts; extraVirtualAliases = { }; @@ -228,9 +246,18 @@ in # with our own. preStart = lib.mkAfter '' cp ${passDbFile} /run/dovecot2/passwd - # cp ${userDbFile} /run/dovecot2/userdb + cp ${userDbFile} /run/dovecot2/userdb ''; }; + # The stuff simple nixos-mailserver sets up here creates aliases to the accounts listed, + # but I use usernames for the accounts, which ends up creating forwards to stuff like khs@mx.kaareskovgaard.net, which causes issues. + # Instead I just want the entries in virtual_mailbox_maps for all regular addresses, which will leave the addresses alone (ie. not rewrite them) + # and send them off via lmtp to dovecot2. Dovecot2 has a fixed userdb which accepts all regular email addresses for every login and maps it to that + # users mailbox. The only thing not handled this way is catchAll addresses (and regexes, which I do not support at the moment, and hopefully won't need). + services.postfix = { + virtual = lib.mkForce extraVirtualAliases; + config.virtual_alias_maps = lib.mkForce [ "hash:/etc/postfix/virtual" ]; + }; # This prevents local usernames without domain names to get rewritten. # services.postfix.submissionOptions = submissionOptions; # services.postfix.submissionsOptions = submissionOptions; 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 deleted file mode 100644 index 3e3712b..0000000 --- a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/accounts/mailbox_map.nix +++ /dev/null @@ -1,140 +0,0 @@ -{ - 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/default.nix b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/default.nix index 789e0a6..cbb60be 100644 --- a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/default.nix +++ b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/default.nix @@ -14,6 +14,12 @@ in type = lib.types.listOf lib.types.str; default = [ ]; }; + postmaster = lib.mkOption { + type = lib.types.str; + }; + abuse = lib.mkOption { + type = lib.types.str; + }; }; imports = [ inputs.simple-nixos-mailserver.nixosModules.mailserver diff --git a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/dkim.nix b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/dkim.nix index 81e0de5..4ed1876 100644 --- a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/dkim.nix +++ b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/dkim.nix @@ -192,6 +192,8 @@ in ''; restartUnits = [ "rspamd.service" + # See note on why there's a condition set on postfix for this to make sense. + "postfix.service" ]; } ]) cfg.domains @@ -222,12 +224,18 @@ in # without this postfix won't forward the mails to rspamd to be signed. non_smtpd_milters = [ "unix:/run/rspamd/rspamd-milter.sock" ]; }; - systemd.services.rspamd = { - unitConfig = { - ConditionPathExists = domainKeyPaths; - }; - serviceConfig = { - ReadOnlyPaths = domainKeyPaths; + systemd.services = { + # Postfix technically does not need these files to exist. But without + # configuring this here, it will attempt to start (ie. getting restarted) many times during startup + # and eventually hit a limit on the number of times. So ensuring it cannot try to start, should prevent this. + postfix.unitConfig.ConditionPathExists = domainKeyPaths; + rspamd = { + unitConfig = { + ConditionPathExists = domainKeyPaths; + }; + serviceConfig = { + ReadOnlyPaths = domainKeyPaths; + }; }; }; }; diff --git a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/openid-connect.nix b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/openid-connect.nix index 3f5014d..4142a1b 100644 --- a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/openid-connect.nix +++ b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/openid-connect.nix @@ -16,7 +16,6 @@ in {{- with secret "kanidm/data/apps/dovecot" -}} scope = email openid profile username_attribute = username - debug = yes introspection_url = https://dovecot:{{ .Data.data.basic_secret }}@login.kaareskovgaard.net/oauth2/token/introspect introspection_mode = post {{- end -}} diff --git a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/users.nix b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/users.nix index b7920d8..44e0b70 100644 --- a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/users.nix +++ b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/users.nix @@ -2,9 +2,11 @@ "khs" = { isLdapAccount = true; name = "Kaare Agerlin Skovgaard"; - aliases = [ + addresses = [ "kaare@agerlin-skovgaard.dk" "kaare@agerlinskovgaard.dk" + "@k.agerlinskovgaard.dk" + "@k.agerlin-skovgaard.dk" ]; quota = "10G"; }; diff --git a/rust/program/ed25519-helper/src/main.rs b/rust/program/ed25519-helper/src/main.rs index c856367..a694a7a 100644 --- a/rust/program/ed25519-helper/src/main.rs +++ b/rust/program/ed25519-helper/src/main.rs @@ -42,6 +42,6 @@ fn pem_private_key_to_sodium_private_key(p: PemPrivateKeyToSodiumPrivateKey) -> let libsodium_seed = &result[16..48]; let keypair = libsodium_rs::crypto_sign::KeyPair::from_seed(libsodium_seed)?; let mut stdout = std::io::stdout(); - stdout.write(keypair.secret_key.as_bytes())?; + stdout.write_all(keypair.secret_key.as_bytes())?; Ok(()) }