From 28ae135d00a637e57459bccd8aebba2b451a8b7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20B=C3=B6hme?= Date: Wed, 14 May 2025 16:55:53 +0200 Subject: [PATCH] feat: add mail server --- flake.lock | 73 +++++++++++++++++++++++- flake.nix | 3 + machines/moritz-server/configuration.nix | 1 + machines/moritz-server/mail-server.nix | 73 ++++++++++++++++++++++++ 4 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 machines/moritz-server/mail-server.nix diff --git a/flake.lock b/flake.lock index 26058b1..702ded0 100644 --- a/flake.lock +++ b/flake.lock @@ -33,6 +33,22 @@ "type": "github" } }, + "blobs": { + "flake": false, + "locked": { + "lastModified": 1604995301, + "narHash": "sha256-wcLzgLec6SGJA8fx1OEN1yV/Py5b+U5iyYpksUY/yLw=", + "owner": "simple-nixos-mailserver", + "repo": "blobs", + "rev": "2cccdf1ca48316f2cfd1c9a0017e8de5a7156265", + "type": "gitlab" + }, + "original": { + "owner": "simple-nixos-mailserver", + "repo": "blobs", + "type": "gitlab" + } + }, "clan-core": { "inputs": { "data-mesher": "data-mesher", @@ -239,6 +255,22 @@ "type": "github" } }, + "flake-compat_5": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, "flake-parts": { "inputs": { "nixpkgs-lib": [ @@ -802,6 +834,29 @@ "type": "github" } }, + "nixos-mailserver": { + "inputs": { + "blobs": "blobs", + "flake-compat": "flake-compat_4", + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-24_11": "nixpkgs-24_11" + }, + "locked": { + "lastModified": 1746937334, + "narHash": "sha256-7g2GSePdYbpD1v5BxEVSCJ2Ogf4K5rc9sBB81FervUY=", + "owner": "simple-nixos-mailserver", + "repo": "nixos-mailserver", + "rev": "da66510f688b7eac54e3cac7c75be4b8dd78ce8b", + "type": "gitlab" + }, + "original": { + "owner": "simple-nixos-mailserver", + "repo": "nixos-mailserver", + "type": "gitlab" + } + }, "nixpkgs": { "locked": { "lastModified": 1736344531, @@ -817,6 +872,21 @@ "type": "indirect" } }, + "nixpkgs-24_11": { + "locked": { + "lastModified": 1734083684, + "narHash": "sha256-5fNndbndxSx5d+C/D0p/VF32xDiJCJzyOqorOYW4JEo=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "314e12ba369ccdb9b352a4db26ff419f7c49fa84", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "ref": "nixos-24.11", + "type": "indirect" + } + }, "nixpkgs-stable": { "locked": { "lastModified": 1744309437, @@ -1014,7 +1084,7 @@ }, "pre-commit-hooks": { "inputs": { - "flake-compat": "flake-compat_4", + "flake-compat": "flake-compat_5", "gitignore": "gitignore_3", "nixpkgs": "nixpkgs_7" }, @@ -1048,6 +1118,7 @@ "niri": "niri", "nix-index-database": "nix-index-database", "nix-monitored": "nix-monitored", + "nixos-mailserver": "nixos-mailserver", "nixpkgs": "nixpkgs_5", "nixvim": "nixvim", "nur": "nur", diff --git a/flake.nix b/flake.nix index 1be4ecf..50601a6 100644 --- a/flake.nix +++ b/flake.nix @@ -15,6 +15,9 @@ inputs.flake-parts.follows = "flake-parts"; }; + nixos-mailserver.url = "gitlab:simple-nixos-mailserver/nixos-mailserver"; + nixos-mailserver.inputs.nixpkgs.follows = "nixpkgs"; + home-manager.inputs.nixpkgs.follows = "nixpkgs"; home-manager.url = "github:nix-community/home-manager"; diff --git a/machines/moritz-server/configuration.nix b/machines/moritz-server/configuration.nix index a2e4b52..1fce48d 100644 --- a/machines/moritz-server/configuration.nix +++ b/machines/moritz-server/configuration.nix @@ -5,6 +5,7 @@ ../../modules/moritz/shared.nix ./reverse-proxy.nix ./ddns.nix + ./mail-server.nix ]; time.timeZone = "Europe/Berlin"; diff --git a/machines/moritz-server/mail-server.nix b/machines/moritz-server/mail-server.nix new file mode 100644 index 0000000..b8c7fb3 --- /dev/null +++ b/machines/moritz-server/mail-server.nix @@ -0,0 +1,73 @@ +{ + inputs, + pkgs, + config, + ... +}: { + imports = [ + inputs.nixos-mailserver.nixosModules.default + ./reverse-proxy.nix + ]; + mailserver = { + enable = true; + fqdn = "mail.moritz.foo"; + domains = ["moritz.foo"]; + + fullTextSearch = { + enable = true; + # index new email as they arrive + autoIndex = true; + enforced = "body"; + memoryLimit = 500; # in MiB + }; + + loginAccounts = { + "main@moritz.foo" = { + hashedPasswordFile = config.clan.core.vars.generators.mail-server.files.main-password-hash.path; + aliases = ["@moritz.foo"]; + }; + }; + + # Use Let's Encrypt certificates. Note that this needs to set up a stripped + # down nginx and opens port 80. + certificateScheme = "acme"; + acmeCertificateName = "any.moritz.foo"; + }; + + clan.core.vars.generators.mail-server = { + prompts.main-password.type = "hidden"; + prompts.main-password.persist = true; + prompts.main-password.description = "You can autogenerate a password, if you leave this prompt blank."; + files.main-password.deploy = false; + files.main-password-hash = {}; + + runtimeInputs = [ + pkgs.coreutils + pkgs.xkcdpass + pkgs.mkpasswd + ]; + script = '' + prompt_value=$(cat "$prompts"/main-password) + if [[ -n "''${prompt_value-}" ]]; then + echo "$prompt_value" | tr -d "\n" > "$out"/main-password + else + xkcdpass --numwords 3 --delimiter - --count 1 | tr -d "\n" > "$out"/main-password + fi + mkpasswd -s -m sha-512 < "$out"/main-password | tr -d "\n" > "$out"/main-password-hash + ''; + }; + + services.roundcube = { + enable = true; + hostName = "webmail.moritz.foo"; + 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"; + ''; + }; + services.nginx.virtualHosts."webmail.moritz.foo".enableACME = false; + services.nginx.virtualHosts."webmail.moritz.foo".useACMEHost = "any.moritz.foo"; +}