diff --git a/nix/modules/nixos/infrastructure/hetzner-instance/default.nix b/nix/modules/nixos/infrastructure/hetzner-instance/default.nix index cc60dec..eb984c9 100644 --- a/nix/modules/nixos/infrastructure/hetzner-instance/default.nix +++ b/nix/modules/nixos/infrastructure/hetzner-instance/default.nix @@ -35,7 +35,63 @@ let ]; description = "ping"; }; - firewallRules = firewallTcpRules ++ firewallUdpRules ++ firewallIcmpRules ++ cfg.extraFirewallRules; + firewallRules = + firewallTcpRules + ++ firewallUdpRules + ++ firewallIcmpRules + ++ cfg.extraFirewallRules + ++ [ + { + direction = "out"; + protocol = "tcp"; + port = 80; + destination_ips = [ + "0.0.0.0/0" + "::/0" + ]; + description = "http"; + } + { + direction = "out"; + protocol = "tcp"; + port = 443; + destination_ips = [ + "0.0.0.0/0" + "::/0" + ]; + description = "http"; + } + { + direction = "out"; + protocol = "udp"; + port = 443; + destination_ips = [ + "0.0.0.0/0" + "::/0" + ]; + description = "quic"; + } + { + direction = "out"; + protocol = "udp"; + port = 53; + destination_ips = [ + "0.0.0.0/0" + "::/0" + ]; + description = "dns"; + } + { + direction = "out"; + protocol = "tcp"; + port = 53; + destination_ips = [ + "0.0.0.0/0" + "::/0" + ]; + description = "dns"; + } + ]; firewallEnable = config.networking.firewall.enable; in { diff --git a/nix/modules/nixos/infrastructure/mailserver/default.nix b/nix/modules/nixos/infrastructure/mailserver/default.nix new file mode 100644 index 0000000..ec83cab --- /dev/null +++ b/nix/modules/nixos/infrastructure/mailserver/default.nix @@ -0,0 +1,98 @@ +{ + config, + lib, + inputs, + ... +}: +let + cfg = config.khscodes.infrastructure.mailserver; + fqdn = config.khscodes.networking.fqdn; +in +{ + 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 = [ ]; + }; + }; + imports = [ + ./dkim.nix + inputs.simple-nixos-mailserver.nixosModules.mailserver + ]; + config = lib.mkIf cfg.enable { + # TODO: Include a similiar rule for openstack + khscodes.infrastructure.hetzner-instance.extraFirewallRules = [ + { + direction = "out"; + protocol = "tcp"; + port = 25; + destination_ips = [ + "0.0.0.0/0" + "::/0" + ]; + description = "smtp"; + } + ]; + khscodes.infrastructure.provisioning.pre.modules = [ + ( + { ... }: + { + khscodes.cloudflare.dns.mxRecords = ( + lib.lists.map (domain: { + fqdn = domain; + priority = 10; + content = fqdn; + ttl = 600; + }) cfg.domains + ); + } + ) + ]; + mailserver = { + enable = true; + certificateScheme = "acme"; + fqdn = config.khscodes.networking.fqdn; + domains = cfg.domains; + }; + services.prometheus.exporters.postfix = { + enable = true; + }; + khscodes.infrastructure.vault-prometheus-sender.exporters.enabled = [ "postfix" ]; + services.fail2ban.jails = { + postfix-sasl = { + settings = { + filter = "postfix[mode=auth]"; + port = "smtp,submission,imap,imaps,pop3,pop3s"; + findtime = 600; + maxretry = 5; + }; + }; + postfix = { + settings = { + enabled = true; + mode = "aggressive"; + findtime = 600; + bantime = "1d"; + maxretry = 3; + }; + }; + dovecot = { + settings = { + enabled = true; + mode = "aggressive"; + findtime = 600; + bantime = "1d"; + maxretry = 3; + }; + }; + roundcube-atuh = { + settings = { + enabled = true; + findtime = 600; + maxretry = 7; + }; + }; + }; + }; +} diff --git a/nix/modules/nixos/infrastructure/mailserver/dkim.nix b/nix/modules/nixos/infrastructure/mailserver/dkim.nix new file mode 100644 index 0000000..5f27112 --- /dev/null +++ b/nix/modules/nixos/infrastructure/mailserver/dkim.nix @@ -0,0 +1,198 @@ +{ + pkgs, + lib, + config, + ... +}: +let + cfg = config.khscodes.infrastructure.mailserver; + publicKeyBegin = ''"-----BEGIN PUBLIC KEY-----\n"''; + publicKeyEnd = ''"-----END PUBLIC KEY-----\n"''; + rsaKeyPath = domain: "/run/secret/dkim/${domain}.snm_rsa.key"; + ed25519KeyPath = domain: "/run/secret/dkim/${domain}.snm_ed25519.key"; + domainKeyPaths = lib.lists.flatten ( + lib.lists.map (domain: [ + (rsaKeyPath domain) + (ed25519KeyPath domain) + ]) cfg.domains + ); + # Currently (2025-07-25) I canot get rspamd to sign with ed25519 key, + # it appears it attempts to parse it as an RSA key + # { + # path: "${ed25519KeyPath domain}"; + # selector: "snm_ed25519"; + # } + dkimSigningForDomain = domain: '' + ${domain} { + selectors [ + { + path: "${rsaKeyPath domain}"; + selector: "snm_rsa"; + } + ] + } + ''; + dkimPublicKey = + tls_key: + ''''${ 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) { + mailserver = { + dkimSigning = false; + }; + services.rspamd.locals."dkim_signing.conf" = lib.mkForce { + text = '' + enabled = true; + domain { + ${lib.strings.concatStringsSep "\n " (lib.lists.map dkimSigningForDomain cfg.domains)} + } + ''; + }; + systemd.services.rspamd = { + unitConfig = { + ConditionPathExists = domainKeyPaths; + }; + }; + systemd.services.postfix = { + unitConfig = { + ConditionPathExists = domainKeyPaths; + }; + }; + khscodes.infrastructure.vault-server-approle.policy = { + "${cfg.dkim.vault.mount}/data/${cfg.dkim.vault.prefixPath}/*" = { + capabilities = [ "read" ]; + }; + }; + khscodes.infrastructure.provisioning.pre.modules = [ + ( + { config, ... }: + { + terraform.required_providers.tls = { + source = "hashicorp/tls"; + version = "4.1.0"; + }; + provider.tls = { }; + + khscodes.cloudflare.enable = true; + resource.tls_private_key = + (lib.listToAttrs ( + lib.lists.map (domain: { + name = "${lib.khscodes.sanitize-terraform-name domain}_dkim_rsa"; + value = { + algorithm = "RSA"; + rsa_bits = 2048; + }; + }) cfg.domains + )) + // (lib.listToAttrs ( + lib.lists.map (domain: { + name = "${lib.khscodes.sanitize-terraform-name domain}_dkim_ed25519"; + value = { + algorithm = "ED25519"; + }; + }) cfg.domains + )); + + khscodes.cloudflare.dns.txtRecords = + (lib.lists.map (domain: { + fqdn = "snm_rsa._domainkey.${domain}"; + content = ''"''${ join("\" \"", regexall(".{1,255}", "v=DKIM1;k=rsa;p=${dkimPublicKey "tls_private_key.${lib.khscodes.sanitize-terraform-name domain}_dkim_rsa"}" )) }"''; + ttl = 600; + }) cfg.domains) + ++ (lib.lists.map (domain: { + fqdn = "snm_ed25519._domainkey.${domain}"; + content = ''"''${ join("\" \"", regexall(".{1,255}", "v=DKIM1;k=ed25519;p=${dkimPublicKey "tls_private_key.${lib.khscodes.sanitize-terraform-name domain}_dkim_ed25519"}" )) }"''; + ttl = 600; + }) cfg.domains) + ++ (lib.lists.map (domain: { + fqdn = domain; + # TODO: Use something here that doesn't require knowing the IP, such that we don't tie this down to hcloud instances. + content = ''"v=spf1 ip4:${config.khscodes.hcloud.output.server.compute.ipv4_address} ip6:${config.khscodes.hcloud.output.server.compute.ipv6_address} -all"''; + ttl = 600; + }) cfg.domains) + ++ (lib.lists.map (domain: { + fqdn = "_dmarc.${domain}"; + content = ''"v=DMARC1; p=reject; adkim=s; aspf=s;"''; + ttl = 600; + }) cfg.domains); + + resource.vault_kv_secret_v2 = + (lib.listToAttrs ( + 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"; + data_json = '' + { "dkim_private_key": ''${ jsonencode(resource.tls_private_key.${lib.khscodes.sanitize-terraform-name domain}_dkim_rsa.private_key_pem) } } + ''; + }; + }) cfg.domains + )) + // (lib.listToAttrs ( + 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"; + data_json = '' + { "dkim_private_key": ''${ jsonencode(resource.tls_private_key.${lib.khscodes.sanitize-terraform-name domain}_dkim_ed25519.private_key_pem) } } + ''; + }; + }) cfg.domains + )); + } + ) + ]; + + khscodes.services.vault-agent.templates = lib.lists.flatten ( + lib.lists.map (domain: [ + { + contents = '' + {{- with secret "${cfg.dkim.vault.mount}/data/${cfg.dkim.vault.prefixPath}/${domain}/rsa" -}} + {{ .Data.data.dkim_private_key }} + {{- end -}} + ''; + destination = rsaKeyPath domain; + perms = "0600"; + owner = "rspamd"; + group = "rspamd"; + restartUnits = [ + "rspamd.service" + "postfix.service" + ]; + } + { + contents = '' + {{- with secret "${cfg.dkim.vault.mount}/data/${cfg.dkim.vault.prefixPath}/${domain}/ed25519" -}} + {{ .Data.data.dkim_private_key }} + {{- end -}} + ''; + destination = ed25519KeyPath domain; + perms = "0600"; + owner = "rspamd"; + group = "rspamd"; + restartUnits = [ + "rspamd.service" + "postfix.service" + ]; + } + ]) cfg.domains + ); + }; +} diff --git a/nix/modules/nixos/services/nginx/default.nix b/nix/modules/nixos/services/nginx/default.nix index 99eabdb..916ece9 100644 --- a/nix/modules/nixos/services/nginx/default.nix +++ b/nix/modules/nixos/services/nginx/default.nix @@ -222,6 +222,14 @@ in }; groups.${config.services.prometheus.exporters.nginxlog.user} = { }; }; + systemd.services.nginx = { + serviceConfig = { + ExecStartPre = [ + ''${pkgs.uutils-coreutils-noprefix}/bin/touch /var/log/nginx/access.fail2ban.log'' + ''${pkgs.uutils-coreutils-noprefix}/bin/chmod 064 /var/log/nginx/access.fail2ban.log'' + ]; + }; + }; systemd.services.fail2ban = { # fail2ban fails starting if the nginx log files don't exist after = [ "nginx.service" ]; @@ -297,7 +305,7 @@ in } add_header Strict-Transport-Security $hsts_header; - add_header X-Frame-Options DENY; + add_header X-Frame-Options SAMEORIGIN; add_header X-Content-Type-Options nosniff; map $http_referer $httpReferer { diff --git a/nix/modules/terranix/cloudflare/default.nix b/nix/modules/terranix/cloudflare/default.nix index 625b115..ff44ef7 100644 --- a/nix/modules/terranix/cloudflare/default.nix +++ b/nix/modules/terranix/cloudflare/default.nix @@ -16,11 +16,9 @@ let fqdn: let split = lib.strings.splitString "." fqdn; + top = lib.lists.takeEnd 2 split; in - if lib.lists.length split < 3 then - fqdn - else - lib.strings.removePrefix "${builtins.head split}." fqdn; + lib.strings.concatStringsSep "." top; dnsARecordModule = lib.khscodes.mkSubmodule { description = "Module for defining dns A/AAAA record"; options = { diff --git a/nix/systems/aarch64-linux/kas.codes/mailserver/default.nix b/nix/systems/aarch64-linux/kas.codes/mailserver/default.nix index 9b4d5d8..4298c40 100644 --- a/nix/systems/aarch64-linux/kas.codes/mailserver/default.nix +++ b/nix/systems/aarch64-linux/kas.codes/mailserver/default.nix @@ -1,76 +1,7 @@ -{ inputs, ... }: +{ ... }: { imports = [ - ./dkim.nix ./forgejo-user.nix - inputs.simple-nixos-mailserver.nixosModules.mailserver - ]; - khscodes.infrastructure.vault-server-approle.policy = { - "forgejo/data/mailserver/*" = { - capabilities = [ "read" ]; - }; - }; - khscodes.infrastructure.hetzner-instance.extraFirewallRules = [ - { - direction = "out"; - protocol = "tcp"; - port = 25; - destination_ips = [ - "0.0.0.0/0" - "::/0" - ]; - description = "smtp"; - } - { - direction = "out"; - protocol = "tcp"; - port = 80; - destination_ips = [ - "0.0.0.0/0" - "::/0" - ]; - description = "http"; - } - { - direction = "out"; - protocol = "tcp"; - port = 443; - destination_ips = [ - "0.0.0.0/0" - "::/0" - ]; - description = "http"; - } - { - direction = "out"; - protocol = "udp"; - port = 443; - destination_ips = [ - "0.0.0.0/0" - "::/0" - ]; - description = "quic"; - } - { - direction = "out"; - protocol = "udp"; - port = 53; - destination_ips = [ - "0.0.0.0/0" - "::/0" - ]; - description = "dns"; - } - { - direction = "out"; - protocol = "tcp"; - port = 53; - destination_ips = [ - "0.0.0.0/0" - "::/0" - ]; - description = "dns"; - } ]; khscodes.infrastructure.provisioning.pre.modules = [ { @@ -87,48 +18,22 @@ }; } ]; - services.prometheus.exporters.postfix = { + khscodes.infrastructure.mailserver = { enable = true; - }; - khscodes.infrastructure.vault-prometheus-sender.exporters.enabled = [ "postfix" ]; - services.fail2ban.jails = { - postfix-sasl = { - settings = { - filter = "postfix[mode=auth]"; - port = "smtp,submission,imap,imaps,pop3,pop3s"; - findtime = 600; - maxretry = 5; - }; - }; - postfix = { - settings = { - enabled = true; - findtime = 600; - maxretry = 3; - }; - }; - dovecot = { - settings = { - enabled = true; - findtime = 600; - maxretry = 3; + domains = [ "kas.codes" ]; + dkim = { + vault = { + mount = "forgejo"; + prefixPath = "mailserver/dkim"; }; }; }; mailserver = { - enable = true; - fqdn = "kas.codes"; - domains = [ "kas.codes" ]; loginAccounts = { "forgejo@kas.codes" = { hashedPasswordFile = "/var/lib/vault-agent/mailserver/users/forgejo.passwd.hash"; sendOnly = true; }; }; - certificateScheme = "acme"; - dkimKeyDirectory = "/var/lib/vault-agent/mailserver/dkim/"; - dkimSelector = "snm_rsa"; - # Not sure we need to set this at all. - dkimKeyBits = 2048; }; } diff --git a/nix/systems/aarch64-linux/kas.codes/mailserver/dkim.nix b/nix/systems/aarch64-linux/kas.codes/mailserver/dkim.nix deleted file mode 100644 index 32fdf7f..0000000 --- a/nix/systems/aarch64-linux/kas.codes/mailserver/dkim.nix +++ /dev/null @@ -1,131 +0,0 @@ -{ pkgs, lib, ... }: -let - publicKeyBegin = ''"-----BEGIN PUBLIC KEY-----\n"''; - publicKeyEnd = ''"-----END PUBLIC KEY-----\n"''; - dkimPublicKey = - tls_key: - ''''${ replace(trimprefix(trimsuffix(${tls_key}.public_key_pem, ${publicKeyEnd}), ${publicKeyBegin}), "\n", "") }''; -in -{ - khscodes.services.vault-agent.templates = [ - { - contents = '' - {{- with secret "forgejo/data/mailserver/dkim/rsa" -}} - {{ .Data.data.dkim_private_key }} - {{- end -}} - ''; - destination = "/var/lib/vault-agent/mailserver/dkim/kas.codes.snm_rsa.key"; - perms = "0600"; - owner = "rspamd"; - group = "rspamd"; - exec = lib.getExe ( - pkgs.writeShellApplication { - name = "kas.codes.snm_rsa-remove-txt"; - runtimeInputs = [ pkgs.uutils-coreutils-noprefix ]; - text = '' - rm -f /var/lib/vault-agent/mailserver/dkim/kas.codes.snm_rsa.txt - ''; - } - ); - restartUnits = [ - "rspamd.service" - "postfix.service" - ]; - } - { - contents = '' - {{- with secret "forgejo/data/mailserver/dkim/ed25519" -}} - {{ .Data.data.dkim_private_key }} - {{- end -}} - ''; - destination = "/var/lib/vault-agent/mailserver/dkim/kas.codes.snm_ed25519.key"; - perms = "0600"; - owner = "rspamd"; - group = "rspamd"; - exec = lib.getExe ( - pkgs.writeShellApplication { - name = "kas.codes.snm_rsa-remove-txt"; - runtimeInputs = [ pkgs.uutils-coreutils-noprefix ]; - text = '' - rm -f /var/lib/vault-agent/mailserver/dkim/kas.codes.snm_ed25519.txt - ''; - } - ); - restartUnits = [ - "rspamd.service" - "postfix.service" - ]; - } - ]; - khscodes.infrastructure.provisioning.pre.modules = [ - ( - { config, ... }: - { - terraform.required_providers.tls = { - source = "hashicorp/tls"; - version = "4.1.0"; - }; - provider.tls = { }; - - resource.tls_private_key.dkim_rsa = { - algorithm = "RSA"; - rsa_bits = 2048; - }; - resource.tls_private_key.dkim_ed25519 = { - algorithm = "ED25519"; - }; - - resource.cloudflare_record.dkim_rsa = { - name = "snm_rsa._domainkey"; - zone_id = "\${ data.cloudflare_zone.kas_codes.id }"; - type = "TXT"; - content = ''"''${ join("\" \"", regexall(".{1,255}", "v=DKIM1;k=rsa;p=${dkimPublicKey "tls_private_key.dkim_rsa"}" )) }"''; - comment = "app=kas.codes"; - ttl = 600; - }; - - resource.cloudflare_record.dkim_ed25519 = { - name = "snm_ed25519._domainkey"; - zone_id = "\${ data.cloudflare_zone.kas_codes.id }"; - type = "TXT"; - content = ''"''${ join("\" \"", regexall(".{1,255}", "v=DKIM1;k=ed25519;p=${dkimPublicKey "tls_private_key.dkim_ed25519"}" )) }"''; - comment = "app=kas.codes"; - ttl = 600; - }; - - resource.cloudflare_record.spf = { - name = "@"; - zone_id = "\${ data.cloudflare_zone.kas_codes.id }"; - type = "TXT"; - content = ''"v=spf1 ip4:${config.khscodes.hcloud.output.server.compute.ipv4_address} ip6:${config.khscodes.hcloud.output.server.compute.ipv6_address} -all"''; - comment = "app=kas.codes"; - ttl = 600; - }; - resource.cloudflare_record.dmarc = { - name = "_dmarc"; - zone_id = "\${ data.cloudflare_zone.kas_codes.id }"; - type = "TXT"; - content = ''"v=DMARC1; p=reject; adkim=s; aspf=s;"''; - comment = "app=kas.codes"; - ttl = 600; - }; - - resource.vault_kv_secret_v2.dkim_rsa_secret = { - mount = config.khscodes.vault.output.mount.forgejo.path; - name = "mailserver/dkim/rsa"; - data_json = '' - { "dkim_private_key": ''${ jsonencode(resource.tls_private_key.dkim_rsa.private_key_pem) } } - ''; - }; - - resource.vault_kv_secret_v2.dkim_ed25519_secret = { - mount = config.khscodes.vault.output.mount.forgejo.path; - name = "mailserver/dkim/ed25519"; - data_json = '' - { "dkim_private_key": ''${ jsonencode(resource.tls_private_key.dkim_ed25519.private_key_pem) } } - ''; - }; - } - ) - ]; -} diff --git a/nix/systems/aarch64-linux/kas.codes/mailserver/forgejo-user.nix b/nix/systems/aarch64-linux/kas.codes/mailserver/forgejo-user.nix index 52edc66..c29ce51 100644 --- a/nix/systems/aarch64-linux/kas.codes/mailserver/forgejo-user.nix +++ b/nix/systems/aarch64-linux/kas.codes/mailserver/forgejo-user.nix @@ -1,4 +1,9 @@ { + khscodes.infrastructure.vault-server-approle.policy = { + "forgejo/data/mailserver/users/*" = { + capabilities = [ "read" ]; + }; + }; khscodes.services.vault-agent.templates = [ { contents = '' diff --git a/nix/systems/aarch64-linux/mail.kaareskovgaard.net/default.nix b/nix/systems/aarch64-linux/mail.kaareskovgaard.net/default.nix index ad5f185..eece7df 100644 --- a/nix/systems/aarch64-linux/mail.kaareskovgaard.net/default.nix +++ b/nix/systems/aarch64-linux/mail.kaareskovgaard.net/default.nix @@ -1,15 +1,63 @@ { inputs, + config, ... }: { imports = [ "${inputs.self}/nix/profiles/nixos/hetzner-server.nix" + ./testuser.nix ]; - khscodes.infrastructure.hetzner-instance = { + khscodes.infrastructure.provisioning.pre.modules = [ + { + khscodes.vault = { + enable = true; + mount."mail.kaareskovgaard.net" = { + path = "mail.kaareskovgaard.net"; + type = "kv"; + options = { + version = "2"; + }; + description = "Secrets used for mail.kaareskovgaard.net"; + }; + }; + } + ]; + khscodes.infrastructure = { + hetzner-instance = { + enable = true; + mapRdns = true; + server_type = "cax11"; + }; + mailserver = { + enable = true; + domains = [ + "agerlin-skovgaard.dk" + "agerlinskovgaard.dk" + ]; + dkim = { + vault = { + mount = "mail.kaareskovgaard.net"; + prefixPath = "dkim"; + }; + }; + }; + }; + services.roundcube = { enable = true; - mapRdns = true; - server_type = "cax11"; + hostName = "mail.kaareskovgaard.net"; + configureNginx = true; + extraConfig = '' + # starttls needed for authentication, so the fqdn required to match + # the certificate + $config['smtp_host'] = "tls://${config.mailserver.fqdn}"; + $config['smtp_user'] = "%u"; + $config['smtp_pass'] = "%p"; + ''; + }; + khscodes.services.nginx = { + enable = true; + virtualHosts."mail.kaareskovgaard.net" = { }; }; khscodes.networking.fqdn = "mail.kaareskovgaard.net"; system.stateVersion = "25.05"; diff --git a/nix/systems/aarch64-linux/mail.kaareskovgaard.net/testuser.nix b/nix/systems/aarch64-linux/mail.kaareskovgaard.net/testuser.nix new file mode 100644 index 0000000..daa53e7 --- /dev/null +++ b/nix/systems/aarch64-linux/mail.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/security.kaareskovgaard.net/kanidm.nix b/nix/systems/aarch64-linux/security.kaareskovgaard.net/kanidm.nix index 2453b77..e70ec15 100644 --- a/nix/systems/aarch64-linux/security.kaareskovgaard.net/kanidm.nix +++ b/nix/systems/aarch64-linux/security.kaareskovgaard.net/kanidm.nix @@ -69,7 +69,11 @@ in acceptInvalidCerts = true; persons.khs = { present = true; - mailAddresses = [ "kaare@kaareskovgaard.net" ]; + mailAddresses = [ + "kaare@kaareskovgaard.net" + "kaare@agerlin-skovgaard.dk" + "kaare@agerlinskovgaard.dk" + ]; legalName = "Kaare Skovgaard"; displayName = "khs"; };