diff --git a/nix/modules/nixos/infrastructure/mailserver/acme.nix b/nix/modules/nixos/infrastructure/mailserver/acme.nix new file mode 100644 index 0000000..1321901 --- /dev/null +++ b/nix/modules/nixos/infrastructure/mailserver/acme.nix @@ -0,0 +1,9 @@ +{ 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/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/dane.nix b/nix/modules/nixos/infrastructure/mailserver/dane.nix similarity index 100% rename from nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/dane.nix rename to nix/modules/nixos/infrastructure/mailserver/dane.nix diff --git a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/default.nix b/nix/modules/nixos/infrastructure/mailserver/default.nix similarity index 89% rename from nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/default.nix rename to nix/modules/nixos/infrastructure/mailserver/default.nix index 789e0a6..c3a3aa9 100644 --- a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/default.nix +++ b/nix/modules/nixos/infrastructure/mailserver/default.nix @@ -5,11 +5,12 @@ ... }: let - cfg = config.khscodes."mx.kaareskovgaard.net"; + cfg = config.khscodes.infrastructure.mailserver; fqdn = config.khscodes.networking.fqdn; in { - options.khscodes."mx.kaareskovgaard.net" = { + options.khscodes.infrastructure.mailserver = { + enable = lib.mkEnableOption "Enables setting up stuff for a mail server"; domains = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; @@ -17,7 +18,6 @@ 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 = { + config = lib.mkIf cfg.enable { + # TODO: Include a similiar rule for openstack khscodes.infrastructure.hetzner-instance.extraFirewallRules = [ { direction = "out"; @@ -80,7 +80,6 @@ in ) ]; mailserver = { - inherit (cfg) domains; enable = true; enableImap = false; enableImapSsl = true; @@ -88,6 +87,7 @@ in enableSubmissionSsl = true; fqdn = config.khscodes.networking.fqdn; useUTF8FolderNames = true; + domains = cfg.domains; certificateScheme = "acme"; }; services.fail2ban.jails = { diff --git a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/dkim.nix b/nix/modules/nixos/infrastructure/mailserver/dkim.nix similarity index 87% rename from nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/dkim.nix rename to nix/modules/nixos/infrastructure/mailserver/dkim.nix index 81e0de5..ef1cfeb 100644 --- a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/dkim.nix +++ b/nix/modules/nixos/infrastructure/mailserver/dkim.nix @@ -5,10 +5,7 @@ ... }: let - cfg = config.khscodes."mx.kaareskovgaard.net"; - mount = "mx.kaareskovgaard.net"; - mountExpr = "\${ vault_mount.${lib.khscodes.sanitize-terraform-name mount}.path }"; - prefixPath = "dkim"; + cfg = config.khscodes.infrastructure.mailserver; opensslStripAsn1 = pkgs.writeTextFile { name = "openssl-strip-asn1"; executable = true; @@ -51,9 +48,23 @@ let ''''${ replace(trimprefix(trimsuffix(${tls_key}.public_key_pem, ${publicKeyEnd}), ${publicKeyBegin}), "\n", "") }''; in { - config = { + 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) { khscodes.infrastructure.vault-server-approle.policy = { - "${mount}/data/${prefixPath}/*" = { + "${cfg.dkim.vault.mount}/data/${cfg.dkim.vault.prefixPath}/*" = { capabilities = [ "read" ]; }; }; @@ -131,8 +142,8 @@ in lib.lists.map (domain: { name = "${lib.khscodes.sanitize-terraform-name domain}_dkim_rsa"; value = { - mount = mountExpr; - name = prefixPath + "/${domain}/rsa"; + mount = cfg.dkim.vault.mountExpr; + name = cfg.dkim.vault.prefixPath + "/${domain}/rsa"; data_json = '' { "dkim_private_key": ''${ jsonencode(resource.tls_private_key.${lib.khscodes.sanitize-terraform-name domain}_dkim_rsa.private_key_pem) } } ''; @@ -143,8 +154,8 @@ in lib.lists.map (domain: { name = "${lib.khscodes.sanitize-terraform-name domain}_dkim_ed25519"; value = { - mount = mountExpr; - name = prefixPath + "/${domain}/ed25519"; + mount = cfg.dkim.vault.mountExpr; + name = cfg.dkim.vault.prefixPath + "/${domain}/ed25519"; data_json = '' { "dkim_private_key": ''${ jsonencode(resource.tls_private_key.${lib.khscodes.sanitize-terraform-name domain}_dkim_ed25519.private_key_pem) } } ''; @@ -158,7 +169,7 @@ in lib.lists.map (domain: [ { contents = '' - {{- with secret "${mount}/data/${prefixPath}/${domain}/rsa" -}} + {{- with secret "${cfg.dkim.vault.mount}/data/${cfg.dkim.vault.prefixPath}/${domain}/rsa" -}} {{ .Data.data.dkim_private_key }} {{- end -}} ''; @@ -174,7 +185,7 @@ in # rspamd does not like reading ed25519 keys with begin/end pairs, as they get parsed as # rsa keys contents = '' - {{- with secret "${mount}/data/${prefixPath}/${domain}/ed25519" -}} + {{- with secret "${cfg.dkim.vault.mount}/data/${cfg.dkim.vault.prefixPath}/${domain}/ed25519" -}} {{ .Data.data.dkim_private_key }} {{- end -}} ''; diff --git a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/dmarc.nix b/nix/modules/nixos/infrastructure/mailserver/dmarc.nix similarity index 82% rename from nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/dmarc.nix rename to nix/modules/nixos/infrastructure/mailserver/dmarc.nix index 998b8e6..bd5a147 100644 --- a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/dmarc.nix +++ b/nix/modules/nixos/infrastructure/mailserver/dmarc.nix @@ -1,9 +1,9 @@ { config, lib, ... }: let - cfg = config.khscodes."mx.kaareskovgaard.net"; + cfg = config.khscodes.infrastructure.mailserver; in { - config = { + config = lib.mkIf cfg.enable { 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/modules/nixos/infrastructure/mailserver/ldap.nix new file mode 100644 index 0000000..8699fbc --- /dev/null +++ b/nix/modules/nixos/infrastructure/mailserver/ldap.nix @@ -0,0 +1,96 @@ +{ lib, config, ... }: +let + cfg = config.khscodes.infrastructure.mailserver; + secretFile = "/run/secret/dovecot/ldap"; +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 { + mailserver.ldap = { + enable = true; + uris = [ "ldaps://login.kaareskovgaard.net" ]; + searchBase = "dc=login,dc=kaareskovgaard,dc=net"; + searchScope = "sub"; + bind = { + dn = "dn=token"; + passwordFile = secretFile; + }; + dovecot = { + # Map LDAP uid to dovecot user, and ldap userPassword to dovecot password + passAttrs = "uid=user"; + passFilter = "(&(class=account)(memberOf=mail_user)(uid=%u))"; + # This filter is used both when receiving mail (thus needing to lookup by mail address, and when authenticating, requriing the lookup by uid.) + # Note that the pass filter only allows looking up by uid, so should still only be able to authenticate using that. + userFilter = "(&(class=account)(memberOf=mail_user)(|(mail=%u)(uid=%u)))"; + userAttrs = "uid=user"; + }; + postfix = { + filter = "(&(class=account)(memberOf=mail_user)(mail=%s))"; + mailAttribute = "uid"; + uidAttribute = "uid"; + }; + }; + systemd.services = { + dovecot2 = { + unitConfig = { + ConditionPathExists = [ secretFile ]; + }; + }; + postfix = { + unitConfig = { + ConditionPathExists = [ secretFile ]; + }; + }; + postfix-setup = { + unitConfig = { + ConditionPathExists = [ secretFile ]; + }; + }; + }; + + khscodes.services.vault-agent.templates = [ + { + contents = '' + {{- with secret "${cfg.ldap.mount}/data/${cfg.ldap.path}" -}} + {{ .Data.data.apiToken }} + {{- end -}} + ''; + destination = secretFile; + owner = "dovecot"; + group = "dovecot"; + restartUnits = [ + "dovecot2.service" + "postfix.service" + ]; + } + ]; + # TODO: Include a similiar rule for openstack + khscodes.infrastructure.hetzner-instance.extraFirewallRules = [ + { + direction = "out"; + protocol = "tcp"; + port = 636; + destination_ips = [ + "0.0.0.0/0" + "::/0" + ]; + description = "ldaps"; + } + ]; + khscodes.infrastructure.vault-server-approle.policy = { + "${cfg.ldap.mount}/data/${cfg.ldap.path}" = { + capabilities = [ "read" ]; + }; + }; + }; +} diff --git a/nix/modules/nixos/infrastructure/mailserver/managesieve.nix b/nix/modules/nixos/infrastructure/mailserver/managesieve.nix new file mode 100644 index 0000000..982cfbd --- /dev/null +++ b/nix/modules/nixos/infrastructure/mailserver/managesieve.nix @@ -0,0 +1,9 @@ +{ 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/mailserver/mta-sts.nix b/nix/modules/nixos/infrastructure/mailserver/mta-sts.nix similarity index 93% rename from nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/mta-sts.nix rename to nix/modules/nixos/infrastructure/mailserver/mta-sts.nix index 58ea779..57a4bb6 100644 --- a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/mta-sts.nix +++ b/nix/modules/nixos/infrastructure/mailserver/mta-sts.nix @@ -6,7 +6,7 @@ }: let fqdn = config.khscodes.networking.fqdn; - cfg = config.khscodes."mx.kaareskovgaard.net"; + cfg = config.khscodes.infrastructure.mailserver; # Increment this if ever changing mta-sts settings. policyVersion = 2; mtaStsWellKnown = pkgs.writeTextFile { @@ -20,7 +20,7 @@ let }; in { - config = { + config = lib.mkIf cfg.enable { khscodes.services.nginx.virtualHosts = ( lib.listToAttrs ( lib.lists.map (domain: { diff --git a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/openid-connect.nix b/nix/modules/nixos/infrastructure/mailserver/openid-connect.nix similarity index 55% rename from nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/openid-connect.nix rename to nix/modules/nixos/infrastructure/mailserver/openid-connect.nix index 3f5014d..120886d 100644 --- a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/openid-connect.nix +++ b/nix/modules/nixos/infrastructure/mailserver/openid-connect.nix @@ -1,15 +1,10 @@ -{ config, ... }: +{ config, lib, ... }: let + cfg = config.khscodes.infrastructure.mailserver; oauthConfigFile = "/run/secret/dovecot/dovecot-oauth2.conf.ext"; in { - config = { - khscodes.infrastructure.kanidm-client-application = { - enable = true; - appName = "dovecot"; - secretOwner = "root"; - perms = "0644"; - }; + config = lib.mkIf cfg.enable { khscodes.services.vault-agent.templates = [ { contents = '' @@ -28,17 +23,6 @@ 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/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/prometheus.nix b/nix/modules/nixos/infrastructure/mailserver/prometheus.nix similarity index 60% rename from nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/prometheus.nix rename to nix/modules/nixos/infrastructure/mailserver/prometheus.nix index b6e2717..3318a54 100644 --- a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/prometheus.nix +++ b/nix/modules/nixos/infrastructure/mailserver/prometheus.nix @@ -1,5 +1,9 @@ +{ config, lib, ... }: +let + cfg = config.khscodes.infrastructure.mailserver; +in { - config = { + config = lib.mkIf cfg.enable { services.prometheus.exporters.postfix = { enable = true; diff --git a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/spf.nix b/nix/modules/nixos/infrastructure/mailserver/spf.nix similarity index 78% rename from nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/spf.nix rename to nix/modules/nixos/infrastructure/mailserver/spf.nix index 4b4b5c8..97b437f 100644 --- a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/spf.nix +++ b/nix/modules/nixos/infrastructure/mailserver/spf.nix @@ -1,9 +1,9 @@ { config, lib, ... }: let - cfg = config.khscodes."mx.kaareskovgaard.net"; + cfg = config.khscodes.infrastructure.mailserver; in { - config = { + config = lib.mkIf cfg.enable { khscodes.infrastructure.provisioning.pre.modules = [ { khscodes.cloudflare.dns.txtRecords = lib.lists.map (domain: { diff --git a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/tls-rpt.nix b/nix/modules/nixos/infrastructure/mailserver/tls-rpt.nix similarity index 80% rename from nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/tls-rpt.nix rename to nix/modules/nixos/infrastructure/mailserver/tls-rpt.nix index 1ffb0a4..d4418a3 100644 --- a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/tls-rpt.nix +++ b/nix/modules/nixos/infrastructure/mailserver/tls-rpt.nix @@ -1,9 +1,9 @@ { config, lib, ... }: let - cfg = config.khscodes."mx.kaareskovgaard.net"; + cfg = config.khscodes.infrastructure.mailserver; in { - config = { + config = lib.mkIf cfg.enable { khscodes.infrastructure.provisioning.pre.modules = [ { khscodes.cloudflare.dns.txtRecords = lib.lists.map (domain: { diff --git a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/default.nix b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/default.nix index e0255ad..5e9e54a 100644 --- a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/default.nix +++ b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/default.nix @@ -1,46 +1,84 @@ { inputs, + config, ... }: { imports = [ "${inputs.self}/nix/profiles/nixos/hetzner-server.nix" - ./mailserver + # ./testuser.nix ]; - 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"; - }; + khscodes.infrastructure.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"; + }; + }; + } + ) + ]; + khscodes.infrastructure = { + kanidm-client-application = { + enable = true; + appName = "dovecot"; + secretOwner = "dovecot2"; + perms = "0644"; }; - "mx.kaareskovgaard.net" = { + hetzner-instance = { + enable = true; + mapRdns = true; + server_type = "cax11"; + }; + mailserver = { + enable = true; domains = [ "agerlin-skovgaard.dk" "agerlinskovgaard.dk" ]; - accounts = import ./users.nix; + dkim = { + vault = { + mount = "mx.kaareskovgaard.net"; + prefixPath = "dkim"; + }; + }; + ldap = { + mount = "kanidm"; + path = "ldap/dovecot"; + }; }; }; + 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 deleted file mode 100644 index cb4c0ad..0000000 --- a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/accounts.nix +++ /dev/null @@ -1,244 +0,0 @@ -{ - 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 deleted file mode 100644 index 8cd30ec..0000000 --- a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/acme.nix +++ /dev/null @@ -1,6 +0,0 @@ -{ config, ... }: -{ - config = { - khscodes.services.nginx.virtualHosts."${config.khscodes.networking.fqdn}" = { }; - }; -} diff --git a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/ldap.nix b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/ldap.nix deleted file mode 100644 index 200476b..0000000 --- a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/ldap.nix +++ /dev/null @@ -1,74 +0,0 @@ -{ - pkgs, - ... -}: -let - ldapMount = "kanidm"; - ldapPath = "ldap/dovecot"; - secretFile = "/run/secret/dovecot/dovecot-ldap.conf.ext"; -in -{ - config = { - services.dovecot2.extraConfig = '' - passdb { - driver = ldap - args = ${secretFile} - } - ''; - systemd.services = { - dovecot2 = { - unitConfig = { - ConditionPathExists = [ secretFile ]; - }; - serviceConfig.ReadOnlyPaths = [ - secretFile - ]; - }; - }; - - khscodes.services.vault-agent.templates = [ - { - contents = '' - {{- with secret "${ldapMount}/data/${ldapPath}" -}} - ldap_version = 3 - uris = ldaps://login.kaareskovgaard.net - - tls_require_cert = hard - tls_ca_cert_file = ${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt - dn = dn=token - dnpass = {{ .Data.data.apiToken }} - auth_bind = yes - base = dc=login,dc=kaareskovgaard,dc=net - scope = subtree - pass_attrs = uid=user - pass_filter = (&(class=account)(memberOf=mail_user)(uid=%u) - {{- end -}} - ''; - destination = secretFile; - owner = "root"; - group = "root"; - restartUnits = [ - "dovecot2.service" - ]; - } - ]; - # TODO: Include a similiar rule for openstack - khscodes.infrastructure.hetzner-instance.extraFirewallRules = [ - { - direction = "out"; - protocol = "tcp"; - port = 636; - destination_ips = [ - "0.0.0.0/0" - "::/0" - ]; - description = "ldaps"; - } - ]; - khscodes.infrastructure.vault-server-approle.policy = { - "${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 deleted file mode 100644 index 7264678..0000000 --- a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/managesieve.nix +++ /dev/null @@ -1,10 +0,0 @@ -{ - config = { - services.dovecot2.protocols = [ "sieve" ]; - services.roundcube.extraConfig = '' - $config['plugins'] = [ - 'managesieve', - ]; - ''; - }; -} diff --git a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/roundcube.nix b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/roundcube.nix deleted file mode 100644 index 8d6ab37..0000000 --- a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/mailserver/roundcube.nix +++ /dev/null @@ -1,18 +0,0 @@ -{ 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/systems/aarch64-linux/mx.kaareskovgaard.net/testuser.nix b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/testuser.nix new file mode 100644 index 0000000..daa53e7 --- /dev/null +++ b/nix/systems/aarch64-linux/mx.kaareskovgaard.net/testuser.nix @@ -0,0 +1,71 @@ +{ + 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 deleted file mode 100644 index b7920d8..0000000 --- a/nix/systems/aarch64-linux/mx.kaareskovgaard.net/users.nix +++ /dev/null @@ -1,11 +0,0 @@ -{ - "khs" = { - isLdapAccount = true; - name = "Kaare Agerlin Skovgaard"; - aliases = [ - "kaare@agerlin-skovgaard.dk" - "kaare@agerlinskovgaard.dk" - ]; - quota = "10G"; - }; -}