diff --git a/machines/gerd.nix b/machines/gerd.nix index d732d35..ed26ee0 100644 --- a/machines/gerd.nix +++ b/machines/gerd.nix @@ -16,9 +16,7 @@ ./gerd/services/hedgedoc.nix ./gerd/services/cyberchef.nix ./gerd/services/nextcloud.nix - - - # ./gerd/services/owncast.nix + ./gerd/services/stalwart.nix ]; networking.hostName = "gerd"; @@ -32,6 +30,7 @@ "safe/svcs/forgejo" = { mountpoint = "/srv/forgejo"; extra.options.quota = "5G"; }; "safe/svcs/hedgedoc" = { mountpoint = "/srv/hedgedoc"; extra.options.quota = "5G"; }; "safe/svcs/nextcloud" = { mountpoint = "/srv/nextcloud"; extra.options.quota = "5G"; }; + "safe/svcs/stalwart" = { mountpoint = "/srv/stalwart"; extra.options.quota = "5G"; }; }; }; diff --git a/machines/gerd/services/stalwart.nix b/machines/gerd/services/stalwart.nix new file mode 100644 index 0000000..78bbf87 --- /dev/null +++ b/machines/gerd/services/stalwart.nix @@ -0,0 +1,143 @@ +{ config, lib, pkgs, ... }: + +let + svc_domain = "mail.${config.mine.shared.settings.domain}"; + svc_domain_smtp = "smtp.${config.mine.shared.settings.domain}"; + svc_domain_imap = "imap.${config.mine.shared.settings.domain}"; + + ports = { + smtp = 25; + submissions = 465; + imaptls = 993; + + http_management = 7377; + }; + + # place data into own zfs dataset + stateDir = config.mine.zfsMounts."rpool/safe/svcs/stalwart"; + + # user/group + stalwart_user = config.users.users.stalwart-mail.name; + stalwart_group = config.users.groups.stalwart-mail.name; + + certLocation = config.security.acme.certs."${svc_domain}".directory; +in { + services.stalwart-mail = { + enable = true; + openFirewall = true; + # package = pkgs.stalwart-mail; + + settings = { + lookup.default.hostname = svc_domain; + + # tracer.stdout.level = "trace"; + store.db.path = "${stateDir}/db"; + + directory.ldap = { + type = "ldap"; + url = config.mine.shared.settings.ldap.url; + base-dn = config.mine.shared.settings.ldap.dc; + tls.enable = false; + + bind = { + dn = config.mine.shared.settings.ldap.bind_dn; + secret = "%{file:${config.age.secrets.lldap-bind-user-pass.path}}%"; + + auth = { + enable = true; + dn = "cn=?,${config.mine.shared.settings.ldap.search_base}"; + }; + }; + + filter = let + _mkFilter = attrs: ph: config.mine.shared.lib.ldap.mkFilter (lconfig: llib: + llib.mkAnd [ + (llib.mkGroup lconfig.groups.member) + (llib.mkOr (lib.forEach attrs (v: llib.mkSearch v ph))) + ] + ); + + attrs = config.mine.shared.settings.ldap.attr // { emailAlias = "mailAlias"; emailList = "mailList"; }; + in { + name = _mkFilter [ attrs.uid ] "?"; + email = _mkFilter [ attrs.email attrs.emailAlias attrs.emailList ] "?"; + verify = _mkFilter [ attrs.email attrs.emailAlias ] "*?*"; + expand = _mkFilter [ attrs.emailList ] "?"; + domains = _mkFilter [ attrs.email attrs.emailAlias ] "*@?"; + }; + + attributes = { + name = "uid"; + class = "objectClass"; + description = "givenName"; + secret = "uid"; + groups = "memberOf"; + email = "mail"; + # email-alias = "mailAlias"; + # quota = "diskQuota"; + }; + + }; + + storage.directory = "ldap"; + + # listeners + server.listener = { + smtp = { bind = [ "[::]:${builtins.toString ports.smtp}"]; protocol = "smtp"; }; + submissions = { bind = [ "[::]:${builtins.toString ports.submissions}"]; protocol = "smtp"; tls.implicit = true; }; + imaptls = { bind = [ "[::]:${builtins.toString ports.imaptls}"]; protocol = "imap"; tls.implicit = true; }; + + management = { bind = [ "127.0.0.1:${builtins.toString ports.http_management}" ]; protocol = "http"; }; + }; + + certificate.domain = { + cert = "%{file:${certLocation + "/cert.pem"}}%"; + private-key = "%{file:${certLocation + "/key.pem"}}%"; + }; + + server.tls = { + certificate = "domain"; + enable = true; + implicit = true; + }; + + # authentication + authentication.fallback-admin = { + user = "admin"; + secret = "%{file:${config.age.secrets.stalwart-admin-fallback-password.owner}}%"; + }; + }; + }; + + # setup so that stalwart can access and write to the directory + systemd.services.stalwart-mail.serviceConfig.ReadWritePaths = [ stateDir ]; + systemd.tmpfiles.rules = [ + "Z ${stateDir} 0700 ${stalwart_user} ${stalwart_group} -" + ]; + + # setup certs + services.nginx.virtualHosts."${svc_domain}" = { + forceSSL = true; + enableACME = true; + serverAliases = [ svc_domain_smtp svc_domain_imap ]; + root = pkgs.writeTextDir "index.html" "Nothing."; + }; + + # need to change group to stalwart-mail for cert + add nginx to stalwart-mail group to do HTTP ACME + users.users.nginx.extraGroups = [ stalwart_group ]; + security.acme.certs."${svc_domain}".group = stalwart_group; + + + # setup secrets for stalwart + # setup access to ldap bind user credential + users.groups.secrets-lldap-bind-user-pass.members = [ stalwart_user ]; + age.secrets.stalwart-admin-fallback-password.owner = stalwart_user; + + mine.shared.settings.mail = { + domain = svc_domain; + domain_smtp = svc_domain_smtp; + domain_imap = svc_domain_imap; + + ports = ports; + }; +} diff --git a/secrets/default.nix b/secrets/default.nix index 54804ed..d8a633c 100644 --- a/secrets/default.nix +++ b/secrets/default.nix @@ -31,6 +31,9 @@ # nextcloud nextcloud-admin-pass.file = ./nextcloud/admin-pass.age; nextcloud-secrets.file = ./nextcloud/secrets.age; + + # stalwart + stalwart-admin-fallback-password.file = ./stalwart/admin-fallback-password.age; }; users.groups.secrets-lldap-bind-user-pass = {}; diff --git a/secrets/secrets.nix b/secrets/secrets.nix index 78c782c..3fe90fd 100644 --- a/secrets/secrets.nix +++ b/secrets/secrets.nix @@ -35,4 +35,7 @@ in # nextcloud "nextcloud/admin-pass.age".publicKeys = defaultAccess; "nextcloud/secrets.age".publicKeys = defaultAccess; + + # mailserver/stalwart + "stalwart/admin-fallback-password.age".publicKeys = defaultAccess; } diff --git a/secrets/stalwart/admin-fallback-password.age b/secrets/stalwart/admin-fallback-password.age new file mode 100644 index 0000000..62f96cd --- /dev/null +++ b/secrets/stalwart/admin-fallback-password.age @@ -0,0 +1,9 @@ +age-encryption.org/v1 +-> ssh-ed25519 QSDXqg KxP3cNdqRorj0JO6SW3FOjcFxgmsspRz/MKnVVviXFM +i9SoYa0sQ0E7S2jo9Js5PJsiHB3lMsVqX5bULg255bw +-> ssh-ed25519 n8n9DQ BrcbGW2zt6E5QjRz+kN/5vl4AreGuOsR+AUcv8sog3M +c+nUCQ9Bifu3bK4R2OgKLbfFFU66/73Oj4y9bMTVJIU +-> ssh-ed25519 BTp6UA RngU7oNTzWJRZG6Qr/t9RiAxEeBelHIzOuSp44b3HVc +DUrunTXLwjLqkuiuzksaqpSwvmKpps/I6Jftv0dD6p8 +--- D75A1a96Q1UKHUsAejeydmjqui9+P3e6fRo3Eeb1I0g +u>_owxu' 7?[5dGTqB]0_(: \ No newline at end of file diff --git a/shared/sources/default.nix b/shared/sources/default.nix index ecc33f1..75f9b9f 100644 --- a/shared/sources/default.nix +++ b/shared/sources/default.nix @@ -13,6 +13,11 @@ in sources // { url = "https://github.com/NixOS/nixpkgs/pull/334590.patch"; sha256 = "sha256-5Uf/jLV0CJFbWyPmkpF4kEVISvoG+fujvTAFIR0a2ek="; }) + # stalwart-mail + (pkgs.fetchpatch { + url = "https://github.com/NixOS/nixpkgs/pull/333507.patch"; + sha256 = "sha256-HAbfKQRnOjdK/rJ5wuePw4hEVQoFz9N0YujxBxROGo0="; + }) ]; }; }