diff options
| author | Jakub Stachurski <jakub@wilkuu.xyz> | 2026-02-11 15:35:44 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-02-11 15:35:44 +0100 |
| commit | 04707c728441000d64d3d750916354310ff2d2ab (patch) | |
| tree | a6855656f35bc4c351e215827b58a83b4f2a99ec /services/email.nix | |
| parent | 2f65e7f40e97f6ccb3d164169698033ce1692a76 (diff) | |
Omega-Relay host replacement for Ubuntu VM on Feox
* Add omega-relay prototype host
* Add omega-relay prototype host
* Inital commit for working omega-relay host.
This commit includes:
- Mysql module from umbriel
- Disko configuration for the Ferox VM
- Freshrss module
- Stalwart module
- Vaultwarden module
- Wakapi module
- Uptime Kuma module
- Support for using mysql socket
- Server user that does not depend on full home-manager preset.
- ACME for wilkuu.xyz domains, including all the services.
* Nix fmt
* Fixes in secrets and services.
Mostly fixes connection to mysql and the unix-socket auth for it.
* Little fixes and update
* Format and do fixes
* Update secrets and keys for omega-relay
* Apply changes from messing around and making things work
Diffstat (limited to 'services/email.nix')
| -rw-r--r-- | services/email.nix | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/services/email.nix b/services/email.nix new file mode 100644 index 0000000..906b44d --- /dev/null +++ b/services/email.nix @@ -0,0 +1,174 @@ +{ config, lib, ... }: +let + cfg = config.wilkuu.services.stalwart; + hostname = config.networking.hostName; +in +{ + options.wilkuu.services.stalwart = with lib; { + domain = mkOption { + type = types.str; + default = "mail.${hostname}.local"; + example = "mail.wilkuu.xyz"; + description = "Domain for http connections."; + }; + wellKnownDomains = mkOption { + type = types.listOf types.str; + default = [ "${hostname}.local" ]; + example = [ "wilkuu.xyz" ]; + description = "Domain for well-known items"; + }; + doACME = mkEnableOption "Enable ACME for stalwart here"; + enable = mkEnableOption "Enable the email service"; + dataDir = mkOption { + type = types.path; + description = "Storage localtion for Stalwart user data"; + default = "/srv/data/stalwart"; + example = "/srv/data/stalwart"; + }; + }; + + config = lib.mkIf cfg.enable ( + let + sopsPath = ../secrets/${hostname}/stalwart.yaml; + secrets = [ "user_admin_password" ]; + toSops = (sname: "stalwart/${sname}"); + toCredfilePath = (name: config.sops.secrets.${toSops name}.path); + toStalwartCred = name: "%{file:/run/credentials/${config.systemd.services.stalwart.name}/${name}}%"; + + basicListener = proto: port: tls: { + bind = [ "[::]:${toString port}" ]; + protocol = proto; + tls.implicit = tls; + }; + + proxyWellKnown = + names: + let + uris = map (n: "/.well-known/${n}") names; + in + (lib.genAttrs uris (uri: { + proxyPass = "http://localhost:3080${uri}"; + recommendedProxySettings = true; + })); + + makeHttpRedirect = target: https: { + return = "302 ${if https then "https" else "http"}://${target}"; + }; + + in + { + networking.hosts = { + "127.0.0.1" = [ cfg.domain ]; + }; + + # Need this bc otherwise sops will complain for some reason + users = { + groups.stalwart = { }; + users.stalwart = { + isSystemUser = true; + group = "stalwart"; + }; + }; + + # TODO: Move this into a util function or option; + sops.secrets = ( + lib.genAttrs (map toSops secrets) (_name: { + sopsFile = sopsPath; + mode = "0440"; + owner = "stalwart-mail"; + }) + ); + + services.nginx.virtualHosts = + (lib.genAttrs cfg.wellKnownDomains ( + (_wdomain: { + locations = + (proxyWellKnown [ + "jmap" + "mta-sts.txt" + "mail-v1.xml" + "autoconfig/mail" + ]) + // (lib.genAttrs [ "/.well_known/caldav" "/.well_known/webdav" ] ( + uri: (makeHttpRedirect "${cfg.domain}${uri}") cfg.doACME + )); + }) + )) // + { ${cfg.domain} = { + addSSL = cfg.doACME; + enableACME = cfg.doACME; + serverName = "${cfg.domain}"; + locations."/" = { + proxyPass = "http://localhost:3080"; + recommendedProxySettings = true; + }; + };}; + + services.stalwart = { + enable = true; + dataDir = cfg.dataDir; + openFirewall = false; + credentials = (lib.genAttrs secrets toCredfilePath) // (let + acme_dir = config.security.acme.certs.${cfg.domain}.directory; + cert_path = file: "${acme_dir}/${file}"; + in (if cfg.doACME then { + "tls_cert.pem" = cert_path "cert.pem"; + "tls_key.pem" = cert_path "key.pem"; + } else {})); + + settings = { + server.listener = { + smtp = basicListener "smtp" 25 false; + submission = basicListener "smtp" 465 true; + imaptls = basicListener "imap" 993 true; + imap = basicListener "imap" 143 true; + # webdav = basicListener "http" 3080 false; + # jmap = basicListener "http" 3080 false; + http = basicListener "http" 3080 false; + }; + + store.rocksdb = { + type = "rocksdb"; + path = cfg.dataDir; + compression = "lz4"; + }; + + directory.internal = { + type = "internal"; + store = "rocksdb"; + }; + + storage = { + data = "rocksdb"; + fts = "rocksdb"; + blob = "rocksdb"; + lookup = "rocksdb"; + directory = "internal"; + }; + + authentication.fallback-admin = { + user = "admin"; + secret = toStalwartCred "user_admin_password"; + }; + + http = { + use-x-forwarded = true; + url = "protocol + \"://${cfg.domain}\""; + }; + + session.connect = { + hostname = "config_get('server.hostname')"; + }; + + server.hostname = "${cfg.domain}"; + + certificate."nix_${cfg.domain}" = lib.mkIf cfg.doACME { + cert = toStalwartCred "tls_cert.pem"; + private-key = toStalwartCred "tls_key.pem"; + default = true; + }; + }; + }; + } + ); +} |
