diff --git a/nix/modules/nixos/infrastructure/mailserver/accounts.nix b/nix/modules/nixos/infrastructure/mailserver/accounts.nix deleted file mode 100644 index 5a948b6..0000000 --- a/nix/modules/nixos/infrastructure/mailserver/accounts.nix +++ /dev/null @@ -1,128 +0,0 @@ -{ - config, - lib, - pkgs, - ... -}: -let - cfg = config.khscodes.infrastructure.mailserver; - accountOption = lib.khscodes.mkSubmodule { - description = "mail account"; - options = { - name = lib.mkOption { - type = lib.types.str; - example = "user1@example.com"; - description = "Username"; - }; - - aliases = lib.mkOption { - type = lib.types.listOf lib.types.str; - example = [ - "abuse@example.com" - "postmaster@example.com" - ]; - 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.typs.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. - ''; - }; - - quota = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = null; - example = "2G"; - description = '' - Per user quota rules. Accepted sizes are `xx k/M/G/T` with the - obvious meaning. Leave blank for the standard quota `100G`. - ''; - }; - - sieveScript = lib.mkOption { - type = lib.types.nullOr lib.types.lines; - default = null; - example = '' - require ["fileinto", "mailbox"]; - - if address :is "from" "gitlab@mg.gitlab.com" { - fileinto :create "GitLab"; - stop; - } - - # This must be the last rule, it will check if list-id is set, and - # file the message into the Lists folder for further investigation - elsif header :matches "list-id" "" { - fileinto :create "Lists"; - stop; - } - ''; - description = '' - Per-user sieve script. - ''; - }; - - sendOnly = lib.types.mkOption { - type = lib.types.bool; - default = false; - description = '' - 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` - stating the reason. - ''; - }; - - sendOnlyRejectMessage = lib.mkOption { - type = lib.types.str; - default = "This account cannot receive emails."; - description = '' - The message that will be returned to the sender when an email is - sent to a send-only account. Only used if the account is marked - as send-only. - ''; - }; - - isLdapAccount = lib.mkOption { - type = lib.types.bool; - default = false; - description = "Marks the account as an LDAP account. Non LDAP accounts gets passwords provisioned, such that other services can use them"; - }; - }; - }; - - systemAccounts = lib.attrsets.foldlAttrs ( - acc: name: value: - if value.isLdapAccount then acc else acc ++ [ name ] - ) [ ] cfg.accounts; -in -{ - options.khscodes.infrastructure.mailserver.accounts = lib.mkOption { - type = lib.types.attrsOf accountOption; - default = { }; - }; - config = lib.mkIf cfg.enable { - }; -} diff --git a/nix/modules/nixos/infrastructure/mailserver/acme.nix b/nix/modules/nixos/infrastructure/mailserver/acme.nix deleted file mode 100644 index 1321901..0000000 --- a/nix/modules/nixos/infrastructure/mailserver/acme.nix +++ /dev/null @@ -1,9 +0,0 @@ -{ lib, config, ... }: -let - cfg = config.khscodes.infrastructure.mailserver; -in -{ - config = lib.mkIf cfg.enable { - khscodes.services.nginx.virtualHosts."${config.khscodes.networking.fqdn}" = { }; - }; -} diff --git a/nix/modules/nixos/infrastructure/mailserver/managesieve.nix b/nix/modules/nixos/infrastructure/mailserver/managesieve.nix deleted file mode 100644 index 982cfbd..0000000 --- a/nix/modules/nixos/infrastructure/mailserver/managesieve.nix +++ /dev/null @@ -1,9 +0,0 @@ -{ config, lib, ... }: -let - cfg = config.khscodes.infrastructure.mailserver; -in -{ - config = lib.mkIf cfg.enable { - services.dovecot2.protocols = [ "sieve" ]; - }; -} diff --git a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/default.nix b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/default.nix index 5e9e54a..e0255ad 100644 --- a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/default.nix +++ b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/default.nix @@ -1,84 +1,46 @@ { inputs, - config, ... }: { imports = [ "${inputs.self}/nix/profiles/nixos/hetzner-server.nix" - # ./testuser.nix + ./mailserver ]; - khscodes.infrastructure.provisioning.pre.modules = [ - ( - { ... }: - { - khscodes.vault = { - enable = true; - mount."mx.kaareskovgaard.net" = { - path = "mx.kaareskovgaard.net"; - type = "kv"; - options = { - version = "2"; + khscodes = { + infrastructure = { + hetzner-instance = { + enable = true; + mapRdns = true; + server_type = "cax11"; + }; + provisioning.pre.modules = [ + ( + { ... }: + { + khscodes.vault = { + enable = true; + mount."mx.kaareskovgaard.net" = { + path = "mx.kaareskovgaard.net"; + type = "kv"; + options = { + version = "2"; + }; + description = "Secrets used for mx.kaareskovgaard.net"; + }; }; - description = "Secrets used for mx.kaareskovgaard.net"; - }; - }; - } - ) - ]; - khscodes.infrastructure = { - kanidm-client-application = { - enable = true; - appName = "dovecot"; - secretOwner = "dovecot2"; - perms = "0644"; + } + ) + ]; }; - hetzner-instance = { - enable = true; - mapRdns = true; - server_type = "cax11"; - }; - mailserver = { - enable = true; + "mx.kaareskovgaard.net" = { domains = [ "agerlin-skovgaard.dk" "agerlinskovgaard.dk" ]; - dkim = { - vault = { - mount = "mx.kaareskovgaard.net"; - prefixPath = "dkim"; - }; - }; - ldap = { - mount = "kanidm"; - path = "ldap/dovecot"; - }; + accounts = import ./users.nix; }; }; - services.roundcube = { - enable = true; - hostName = "mail.kaareskovgaard.net"; - configureNginx = true; - extraConfig = '' - # starttls needed for authentication, so the fqdn required to match - # the certificate - $config['smtp_host'] = "ssl://${config.khscodes.networking.fqdn}"; - $config['imap_host'] = "ssl://${config.khscodes.networking.fqdn}"; - $config['oauth_provider'] = 'generic'; - $config['oauth_provider_name'] = 'Kanidm'; - $config['oauth_client_id'] = 'dovecot'; - $config['oauth_client_secret'] = file_get_contents("${config.khscodes.infrastructure.kanidm-client-application.secretFile}"); - $config['oauth_auth_uri'] = 'https://login.kaareskovgaard.net/ui/oauth2'; - $config['oauth_token_uri'] = 'https://login.kaareskovgaard.net/oauth2/token'; - $config['oauth_identity_uri'] = 'https://login.kaareskovgaard.net/oauth2/openid/dovecot/userinfo'; - $config['oauth_identity_fields'] = ['preferred_username']; - $config['oauth_scope'] = 'email openid profile'; - $config['plugins'] = [ - 'managesieve', - ]; - ''; - }; khscodes.services.nginx = { enable = true; virtualHosts."mail.kaareskovgaard.net" = { }; diff --git a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/accounts.nix b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/accounts.nix new file mode 100644 index 0000000..cb4c0ad --- /dev/null +++ b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/accounts.nix @@ -0,0 +1,244 @@ +{ + config, + lib, + pkgs, + ... +}: +let + cfg = config.khscodes."mx.kaareskovgaard.net"; + passDbFile = "/run/secret/dovecot/passwd"; + bogusPasswdFile = pkgs.writeTextFile { + name = "bogus-passwd"; + text = "$6$1234"; + }; + 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; + accountOption = lib.khscodes.mkSubmodule { + description = "mail account"; + options = { + name = lib.mkOption { + type = lib.types.str; + example = "user1@example.com"; + description = "Username"; + }; + + aliases = lib.mkOption { + type = lib.types.listOf lib.types.str; + example = [ + "abuse@example.com" + "postmaster@example.com" + ]; + 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. + ''; + }; + + quota = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + example = "2G"; + description = '' + Per user quota rules. Accepted sizes are `xx k/M/G/T` with the + obvious meaning. Leave blank for the standard quota `100G`. + ''; + }; + + sieveScript = lib.mkOption { + type = lib.types.nullOr lib.types.lines; + default = null; + example = '' + require ["fileinto", "mailbox"]; + + if address :is "from" "gitlab@mg.gitlab.com" { + fileinto :create "GitLab"; + stop; + } + + # This must be the last rule, it will check if list-id is set, and + # file the message into the Lists folder for further investigation + elsif header :matches "list-id" "" { + fileinto :create "Lists"; + stop; + } + ''; + description = '' + Per-user sieve script. + ''; + }; + + sendOnly = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + 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` + stating the reason. + ''; + }; + + sendOnlyRejectMessage = lib.mkOption { + type = lib.types.str; + default = "This account cannot receive emails."; + description = '' + The message that will be returned to the sender when an email is + sent to a send-only account. Only used if the account is marked + as send-only. + ''; + }; + + isLdapAccount = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Marks the account as an LDAP account. Non LDAP accounts gets passwords provisioned, such that other services can use them"; + }; + }; + }; + + systemAccounts = lib.attrsets.foldlAttrs ( + acc: name: value: + if value.isLdapAccount then acc else acc ++ [ name ] + ) [ ] cfg.accounts; + + systemAccountsPassDbTemplateContents = + lib.concatStringsSep "\n" ( + lib.lists.map (account: '' + {{- with secret "mx.kaareskovgaard.net/data/users/${account}" -}} + "${account}:{{ .Data.data.hashed_password }}::::::" + {{- end -}} + '') systemAccounts + ) + # 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 { + type = lib.types.attrsOf accountOption; + default = { }; + }; + config = { + mailserver = { + inherit extraVirtualAliases; + loginAccounts = lib.attrsets.mapAttrs (name: value: { + inherit (value) + name + aliasesRegexp + catchAll + quota + sieveScript + sendOnly + sendOnlyRejectMessage + ; + aliases = accountAlternativeEmails name value; + hashedPasswordFile = bogusPasswdFile; + }) cfg.accounts; + }; + khscodes.infrastructure.vault-server-approle.policy = { + "mx.kaareskovgaard.net/data/users/*" = { + capabilities = [ "read" ]; + }; + }; + khscodes.infrastructure.provisioning.pre.modules = [ + { + terraform.required_providers.random = { + source = "hashicorp/random"; + version = "3.7.2"; + }; + provider.random = { }; + } + ] + ++ (lib.lists.map ( + account: + let + tfName = lib.khscodes.sanitize-terraform-name account; + in + { + resource.random_password."${tfName}" = { + length = 48; + numeric = true; + lower = true; + upper = true; + special = false; + }; + + resource.vault_kv_secret_v2."${tfName}" = { + mount = config.khscodes.vault.output.mount."mx.kaareskovgaard.net".path; + name = "users/${account}"; + data_json = '' + { + "hashed_password": ''${ jsonencode(resource.random_password.${tfName}.bcrypt_hash) }, + "password": ''${ jsonencode(resource.random_password.${tfName}.result) } + } + ''; + }; + } + ) systemAccounts); + khscodes.services.vault-agent.templates = [ + { + contents = systemAccountsPassDbTemplateContents; + destination = passDbFile; + perms = "0600"; + owner = "dovecot2"; + group = "dovecot2"; + restartUnits = [ + "dovecot2.service" + ]; + } + ]; + systemd.services.dovecot2 = { + unitConfig.ConditionPathExists = [ passDbFile ]; + serviceConfig.ReadOnlyPaths = [ passDbFile ]; + # simple-nixos-mailserver creates its own passwd file, + # but the passwords in that file are all bogus, so replace them + # with our own. + preStart = lib.mkAfter '' + cp ${passDbFile} /run/dovecot2/passwd + ''; + }; + # 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/acme.nix b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/acme.nix new file mode 100644 index 0000000..8cd30ec --- /dev/null +++ b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/acme.nix @@ -0,0 +1,6 @@ +{ config, ... }: +{ + config = { + khscodes.services.nginx.virtualHosts."${config.khscodes.networking.fqdn}" = { }; + }; +} diff --git a/nix/modules/nixos/infrastructure/mailserver/dane.nix b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/dane.nix similarity index 100% rename from nix/modules/nixos/infrastructure/mailserver/dane.nix rename to nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/dane.nix diff --git a/nix/modules/nixos/infrastructure/mailserver/default.nix b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/default.nix similarity index 89% rename from nix/modules/nixos/infrastructure/mailserver/default.nix rename to nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/default.nix index c3a3aa9..789e0a6 100644 --- a/nix/modules/nixos/infrastructure/mailserver/default.nix +++ b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/default.nix @@ -5,12 +5,11 @@ ... }: let - cfg = config.khscodes.infrastructure.mailserver; + cfg = config.khscodes."mx.kaareskovgaard.net"; fqdn = config.khscodes.networking.fqdn; in { - options.khscodes.infrastructure.mailserver = { - enable = lib.mkEnableOption "Enables setting up stuff for a mail server"; + options.khscodes."mx.kaareskovgaard.net" = { domains = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; @@ -18,6 +17,7 @@ in }; imports = [ inputs.simple-nixos-mailserver.nixosModules.mailserver + ./accounts.nix ./acme.nix ./dmarc.nix ./dane.nix @@ -29,9 +29,9 @@ in ./prometheus.nix ./openid-connect.nix ./ldap.nix + ./roundcube.nix ]; - config = lib.mkIf cfg.enable { - # TODO: Include a similiar rule for openstack + config = { khscodes.infrastructure.hetzner-instance.extraFirewallRules = [ { direction = "out"; @@ -80,6 +80,7 @@ in ) ]; mailserver = { + inherit (cfg) domains; enable = true; enableImap = false; enableImapSsl = true; @@ -87,7 +88,6 @@ in enableSubmissionSsl = true; fqdn = config.khscodes.networking.fqdn; useUTF8FolderNames = true; - domains = cfg.domains; certificateScheme = "acme"; }; services.fail2ban.jails = { diff --git a/nix/modules/nixos/infrastructure/mailserver/dkim.nix b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/dkim.nix similarity index 87% rename from nix/modules/nixos/infrastructure/mailserver/dkim.nix rename to nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/dkim.nix index ef1cfeb..81e0de5 100644 --- a/nix/modules/nixos/infrastructure/mailserver/dkim.nix +++ b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/dkim.nix @@ -5,7 +5,10 @@ ... }: let - cfg = config.khscodes.infrastructure.mailserver; + cfg = config.khscodes."mx.kaareskovgaard.net"; + mount = "mx.kaareskovgaard.net"; + mountExpr = "\${ vault_mount.${lib.khscodes.sanitize-terraform-name mount}.path }"; + prefixPath = "dkim"; opensslStripAsn1 = pkgs.writeTextFile { name = "openssl-strip-asn1"; executable = true; @@ -48,23 +51,9 @@ let ''''${ replace(trimprefix(trimsuffix(${tls_key}.public_key_pem, ${publicKeyEnd}), ${publicKeyBegin}), "\n", "") }''; in { - options.khscodes.infrastructure.mailserver.dkim = { - vault = { - mount = lib.mkOption { - type = lib.types.str; - }; - mountExpr = lib.mkOption { - type = lib.types.str; - default = "\${ vault_mount.${lib.khscodes.sanitize-terraform-name cfg.dkim.vault.mount}.path }"; - }; - prefixPath = lib.mkOption { - type = lib.types.str; - }; - }; - }; - config = lib.mkIf (cfg.enable) { + config = { khscodes.infrastructure.vault-server-approle.policy = { - "${cfg.dkim.vault.mount}/data/${cfg.dkim.vault.prefixPath}/*" = { + "${mount}/data/${prefixPath}/*" = { capabilities = [ "read" ]; }; }; @@ -142,8 +131,8 @@ in lib.lists.map (domain: { name = "${lib.khscodes.sanitize-terraform-name domain}_dkim_rsa"; value = { - mount = cfg.dkim.vault.mountExpr; - name = cfg.dkim.vault.prefixPath + "/${domain}/rsa"; + mount = mountExpr; + name = prefixPath + "/${domain}/rsa"; data_json = '' { "dkim_private_key": ''${ jsonencode(resource.tls_private_key.${lib.khscodes.sanitize-terraform-name domain}_dkim_rsa.private_key_pem) } } ''; @@ -154,8 +143,8 @@ in lib.lists.map (domain: { name = "${lib.khscodes.sanitize-terraform-name domain}_dkim_ed25519"; value = { - mount = cfg.dkim.vault.mountExpr; - name = cfg.dkim.vault.prefixPath + "/${domain}/ed25519"; + mount = mountExpr; + name = prefixPath + "/${domain}/ed25519"; data_json = '' { "dkim_private_key": ''${ jsonencode(resource.tls_private_key.${lib.khscodes.sanitize-terraform-name domain}_dkim_ed25519.private_key_pem) } } ''; @@ -169,7 +158,7 @@ in lib.lists.map (domain: [ { contents = '' - {{- with secret "${cfg.dkim.vault.mount}/data/${cfg.dkim.vault.prefixPath}/${domain}/rsa" -}} + {{- with secret "${mount}/data/${prefixPath}/${domain}/rsa" -}} {{ .Data.data.dkim_private_key }} {{- end -}} ''; @@ -185,7 +174,7 @@ in # rspamd does not like reading ed25519 keys with begin/end pairs, as they get parsed as # rsa keys contents = '' - {{- with secret "${cfg.dkim.vault.mount}/data/${cfg.dkim.vault.prefixPath}/${domain}/ed25519" -}} + {{- with secret "${mount}/data/${prefixPath}/${domain}/ed25519" -}} {{ .Data.data.dkim_private_key }} {{- end -}} ''; diff --git a/nix/modules/nixos/infrastructure/mailserver/dmarc.nix b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/dmarc.nix similarity index 82% rename from nix/modules/nixos/infrastructure/mailserver/dmarc.nix rename to nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/dmarc.nix index bd5a147..998b8e6 100644 --- a/nix/modules/nixos/infrastructure/mailserver/dmarc.nix +++ b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/dmarc.nix @@ -1,9 +1,9 @@ { config, lib, ... }: let - cfg = config.khscodes.infrastructure.mailserver; + cfg = config.khscodes."mx.kaareskovgaard.net"; in { - config = lib.mkIf cfg.enable { + config = { khscodes.infrastructure.provisioning.pre.modules = [ { khscodes.cloudflare.dns.txtRecords = lib.lists.map (domain: { diff --git a/nix/modules/nixos/infrastructure/mailserver/ldap.nix b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/ldap.nix similarity index 78% rename from nix/modules/nixos/infrastructure/mailserver/ldap.nix rename to nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/ldap.nix index 2b0ae43..200476b 100644 --- a/nix/modules/nixos/infrastructure/mailserver/ldap.nix +++ b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/ldap.nix @@ -1,26 +1,14 @@ { - lib, - config, pkgs, ... }: let - cfg = config.khscodes.infrastructure.mailserver; + ldapMount = "kanidm"; + ldapPath = "ldap/dovecot"; secretFile = "/run/secret/dovecot/dovecot-ldap.conf.ext"; in { - options.khscodes.infrastructure.mailserver = { - ldap = { - mount = lib.mkOption { - type = lib.types.str; - }; - path = lib.mkOption { - type = lib.types.str; - }; - }; - }; - - config = lib.mkIf cfg.enable { + config = { services.dovecot2.extraConfig = '' passdb { driver = ldap @@ -41,7 +29,7 @@ in khscodes.services.vault-agent.templates = [ { contents = '' - {{- with secret "${cfg.ldap.mount}/data/${cfg.ldap.path}" -}} + {{- with secret "${ldapMount}/data/${ldapPath}" -}} ldap_version = 3 uris = ldaps://login.kaareskovgaard.net @@ -78,7 +66,7 @@ in } ]; khscodes.infrastructure.vault-server-approle.policy = { - "${cfg.ldap.mount}/data/${cfg.ldap.path}" = { + "${ldapMount}/data/${ldapPath}" = { capabilities = [ "read" ]; }; }; diff --git a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/managesieve.nix b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/managesieve.nix new file mode 100644 index 0000000..7264678 --- /dev/null +++ b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/managesieve.nix @@ -0,0 +1,10 @@ +{ + config = { + services.dovecot2.protocols = [ "sieve" ]; + services.roundcube.extraConfig = '' + $config['plugins'] = [ + 'managesieve', + ]; + ''; + }; +} diff --git a/nix/modules/nixos/infrastructure/mailserver/mta-sts.nix b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/mta-sts.nix similarity index 93% rename from nix/modules/nixos/infrastructure/mailserver/mta-sts.nix rename to nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/mta-sts.nix index 57a4bb6..58ea779 100644 --- a/nix/modules/nixos/infrastructure/mailserver/mta-sts.nix +++ b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/mta-sts.nix @@ -6,7 +6,7 @@ }: let fqdn = config.khscodes.networking.fqdn; - cfg = config.khscodes.infrastructure.mailserver; + cfg = config.khscodes."mx.kaareskovgaard.net"; # Increment this if ever changing mta-sts settings. policyVersion = 2; mtaStsWellKnown = pkgs.writeTextFile { @@ -20,7 +20,7 @@ let }; in { - config = lib.mkIf cfg.enable { + config = { khscodes.services.nginx.virtualHosts = ( lib.listToAttrs ( lib.lists.map (domain: { diff --git a/nix/modules/nixos/infrastructure/mailserver/openid-connect.nix b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/openid-connect.nix similarity index 55% rename from nix/modules/nixos/infrastructure/mailserver/openid-connect.nix rename to nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/openid-connect.nix index 120886d..3f5014d 100644 --- a/nix/modules/nixos/infrastructure/mailserver/openid-connect.nix +++ b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/openid-connect.nix @@ -1,10 +1,15 @@ -{ config, lib, ... }: +{ config, ... }: let - cfg = config.khscodes.infrastructure.mailserver; oauthConfigFile = "/run/secret/dovecot/dovecot-oauth2.conf.ext"; in { - config = lib.mkIf cfg.enable { + config = { + khscodes.infrastructure.kanidm-client-application = { + enable = true; + appName = "dovecot"; + secretOwner = "root"; + perms = "0644"; + }; khscodes.services.vault-agent.templates = [ { contents = '' @@ -23,6 +28,17 @@ in restartUnits = [ "dovecot2.service" ]; } ]; + services.roundcube.extraConfig = '' + $config['oauth_provider'] = 'generic'; + $config['oauth_provider_name'] = 'Kanidm'; + $config['oauth_client_id'] = 'dovecot'; + $config['oauth_client_secret'] = file_get_contents("${config.khscodes.infrastructure.kanidm-client-application.secretFile}"); + $config['oauth_auth_uri'] = 'https://login.kaareskovgaard.net/ui/oauth2'; + $config['oauth_token_uri'] = 'https://login.kaareskovgaard.net/oauth2/token'; + $config['oauth_identity_uri'] = 'https://login.kaareskovgaard.net/oauth2/openid/dovecot/userinfo'; + $config['oauth_identity_fields'] = ['preferred_username']; + $config['oauth_scope'] = 'email openid profile'; + ''; services.dovecot2.extraConfig = '' auth_mechanisms = $auth_mechanisms oauthbearer xoauth2 diff --git a/nix/modules/nixos/infrastructure/mailserver/prometheus.nix b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/prometheus.nix similarity index 60% rename from nix/modules/nixos/infrastructure/mailserver/prometheus.nix rename to nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/prometheus.nix index 3318a54..b6e2717 100644 --- a/nix/modules/nixos/infrastructure/mailserver/prometheus.nix +++ b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/prometheus.nix @@ -1,9 +1,5 @@ -{ config, lib, ... }: -let - cfg = config.khscodes.infrastructure.mailserver; -in { - config = lib.mkIf cfg.enable { + config = { services.prometheus.exporters.postfix = { enable = true; diff --git a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/roundcube.nix b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/roundcube.nix new file mode 100644 index 0000000..8d6ab37 --- /dev/null +++ b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/roundcube.nix @@ -0,0 +1,18 @@ +{ config, ... }: +{ + services.roundcube = { + enable = true; + hostName = "mail.kaareskovgaard.net"; + configureNginx = true; + extraConfig = '' + # starttls needed for authentication, so the fqdn required to match + # the certificate + $config['smtp_host'] = "ssl://${config.khscodes.networking.fqdn}"; + $config['imap_host'] = "ssl://${config.khscodes.networking.fqdn}"; + ''; + }; + khscodes.services.nginx = { + enable = true; + virtualHosts."mail.kaareskovgaard.net" = { }; + }; +} diff --git a/nix/modules/nixos/infrastructure/mailserver/spf.nix b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/spf.nix similarity index 78% rename from nix/modules/nixos/infrastructure/mailserver/spf.nix rename to nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/spf.nix index 97b437f..4b4b5c8 100644 --- a/nix/modules/nixos/infrastructure/mailserver/spf.nix +++ b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/spf.nix @@ -1,9 +1,9 @@ { config, lib, ... }: let - cfg = config.khscodes.infrastructure.mailserver; + cfg = config.khscodes."mx.kaareskovgaard.net"; in { - config = lib.mkIf cfg.enable { + config = { khscodes.infrastructure.provisioning.pre.modules = [ { khscodes.cloudflare.dns.txtRecords = lib.lists.map (domain: { diff --git a/nix/modules/nixos/infrastructure/mailserver/tls-rpt.nix b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/tls-rpt.nix similarity index 80% rename from nix/modules/nixos/infrastructure/mailserver/tls-rpt.nix rename to nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/tls-rpt.nix index d4418a3..1ffb0a4 100644 --- a/nix/modules/nixos/infrastructure/mailserver/tls-rpt.nix +++ b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/tls-rpt.nix @@ -1,9 +1,9 @@ { config, lib, ... }: let - cfg = config.khscodes.infrastructure.mailserver; + cfg = config.khscodes."mx.kaareskovgaard.net"; in { - config = lib.mkIf cfg.enable { + config = { khscodes.infrastructure.provisioning.pre.modules = [ { khscodes.cloudflare.dns.txtRecords = lib.lists.map (domain: { diff --git a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/testuser.nix b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/testuser.nix deleted file mode 100644 index daa53e7..0000000 --- a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/testuser.nix +++ /dev/null @@ -1,71 +0,0 @@ -{ - khscodes.infrastructure.vault-server-approle.policy = { - "mail.kaareskovgaard.net/data/users/*" = { - capabilities = [ "read" ]; - }; - }; - khscodes.services.vault-agent.templates = [ - { - contents = '' - {{- with secret "mail.kaareskovgaard.net/data/users/test" -}} - {{ .Data.data.hashed_password }} - {{- end -}} - ''; - destination = "/run/secret/mailserver/users/test.passwd.hash"; - perms = "0600"; - owner = "rspamd"; - group = "rspamd"; - restartUnits = [ - "rspamd.service" - "postfix.service" - ]; - } - ]; - systemd.services.postfix = { - unitConfig = { - ConditionPathExists = [ "/run/secret/mailserver/users/test.passwd.hash" ]; - }; - }; - - systemd.services.rspamd = { - unitConfig = { - ConditionPathExists = [ "/run/secret/mailserver/users/test.passwd.hash" ]; - }; - }; - khscodes.infrastructure.provisioning.pre.modules = [ - ( - { config, ... }: - { - terraform.required_providers.random = { - source = "hashicorp/random"; - version = "3.7.2"; - }; - provider.random = { }; - - resource.random_password.test = { - length = 48; - numeric = true; - lower = true; - upper = true; - special = false; - }; - - resource.vault_kv_secret_v2.test = { - mount = config.khscodes.vault.output.mount."mail.kaareskovgaard.net".path; - name = "users/test"; - data_json = '' - { - "hashed_password": ''${ jsonencode(resource.random_password.test.bcrypt_hash) }, - "password": ''${ jsonencode(resource.random_password.test.result) } - } - ''; - }; - } - ) - ]; - mailserver.loginAccounts = { - "test@agerlin-skovgaard.dk" = { - hashedPasswordFile = "/run/secret/mailserver/users/test.passwd.hash"; - }; - }; -} diff --git a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/users.nix b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/users.nix new file mode 100644 index 0000000..b7920d8 --- /dev/null +++ b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/users.nix @@ -0,0 +1,11 @@ +{ + "khs" = { + isLdapAccount = true; + name = "Kaare Agerlin Skovgaard"; + aliases = [ + "kaare@agerlin-skovgaard.dk" + "kaare@agerlinskovgaard.dk" + ]; + quota = "10G"; + }; +}