From 6403717045e17cbc3ebc0e8f42c5a4354b0f83b7 Mon Sep 17 00:00:00 2001 From: Martin Weinelt Date: Sun, 6 Jul 2025 03:21:25 +0200 Subject: [PATCH] Reapply "nixos/dovecot: improve and harden systemd unit" This reverts commit a794031c597bd8a1898a1305b70952508e2d3612. With the following additions: Allow `CAP_KILL`, so the dovecot master process may interrupt its child processes. Allow new privileges, so dovecot and call the setuid sendmail executable. Allow AF_NETLINK sockets, so dovecot sieve handling can use the getifaddrs syscall. Finally, we now asssert, that no options are set on the legacy dovecot2 systemd unit name, to make the user aware they need to update their overrides. --- .../manual/release-notes/rl-2511.section.md | 2 + nixos/modules/services/mail/dovecot.nix | 60 +++++++++++++++++-- nixos/tests/dovecot.nix | 4 +- 3 files changed, 61 insertions(+), 5 deletions(-) diff --git a/nixos/doc/manual/release-notes/rl-2511.section.md b/nixos/doc/manual/release-notes/rl-2511.section.md index 26e832045d03..dc7433a611e8 100644 --- a/nixos/doc/manual/release-notes/rl-2511.section.md +++ b/nixos/doc/manual/release-notes/rl-2511.section.md @@ -70,6 +70,8 @@ - []{#sec-release-25.11-incompatibilities-sourcehut-removed} The `services.sourcehut` module and corresponding `sourcehut` packages were removed due to being broken and unmaintained. +- The `dovecot` systemd service was renamed from `dovecot2` to `dovecot`. The former is now just an alias. Update any overrides on the systemd unit to the new name. + - The `yeahwm` package and `services.xserver.windowManager.yeahwm` module were removed due to the package being broken and unmaintained upstream. - The `services.postgresql` module now sets up a systemd unit `postgresql.target`. Depending on `postgresql.target` guarantees that postgres is in read-write mode and initial/ensure scripts were executed. Depending on `postgresql.service` only guarantees a read-only connection. diff --git a/nixos/modules/services/mail/dovecot.nix b/nixos/modules/services/mail/dovecot.nix index 01b9c278c630..7d7ba6b867a5 100644 --- a/nixos/modules/services/mail/dovecot.nix +++ b/nixos/modules/services/mail/dovecot.nix @@ -692,23 +692,69 @@ in environment.etc."dovecot/dovecot.conf".source = cfg.configFile; - systemd.services.dovecot2 = { + systemd.services.dovecot = { + aliases = [ "dovecot2.service" ]; description = "Dovecot IMAP/POP3 server"; + documentation = [ + "man:dovecot(1)" + "https://doc.dovecot.org" + ]; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; - restartTriggers = [ - cfg.configFile - ]; + restartTriggers = [ cfg.configFile ]; startLimitIntervalSec = 60; # 1 min serviceConfig = { Type = "notify"; ExecStart = "${dovecotPkg}/sbin/dovecot -F"; ExecReload = "${dovecotPkg}/sbin/doveadm reload"; + + CapabilityBoundingSet = [ + "CAP_CHOWN" + "CAP_DAC_OVERRIDE" + "CAP_FOWNER" + "CAP_KILL" # Required for child process management + "CAP_NET_BIND_SERVICE" + "CAP_SETGID" + "CAP_SETUID" + "CAP_SYS_CHROOT" + "CAP_SYS_RESOURCE" + ]; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = false; # e.g for sendmail + OOMPolicy = "continue"; + PrivateTmp = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = lib.mkDefault false; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "full"; + PrivateDevices = true; Restart = "on-failure"; RestartSec = "1s"; + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + "AF_NETLINK" # e.g. getifaddrs in sieve handling + "AF_UNIX" + ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = false; # sets sgid on maildirs RuntimeDirectory = [ "dovecot2" ]; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "@system-service @resources" + "~@privileged" + "@chown @setuid capset chroot" + ]; }; # When copying sieve scripts preserve the original time stamp @@ -773,6 +819,12 @@ in assertion = cfg.sieve.scripts != { } -> (cfg.mailUser != null && cfg.mailGroup != null); message = "dovecot requires mailUser and mailGroup to be set when `sieve.scripts` is set"; } + { + assertion = config.systemd.services ? dovecot2 == false; + message = '' + Your configuration sets options on the `dovecot2` systemd service. These have no effect until they're migrated to the `dovecot` service. + ''; + } ]; }; diff --git a/nixos/tests/dovecot.nix b/nixos/tests/dovecot.nix index 3d2b8c45b12c..83b3781c773d 100644 --- a/nixos/tests/dovecot.nix +++ b/nixos/tests/dovecot.nix @@ -84,11 +84,13 @@ testScript = '' machine.wait_for_unit("postfix.service") - machine.wait_for_unit("dovecot2.service") + machine.wait_for_unit("dovecot.service") machine.succeed("send-testmail") machine.succeed("send-lda") machine.wait_until_fails('[ "$(postqueue -p)" != "Mail queue is empty" ]') machine.succeed("test-imap") machine.succeed("test-pop") + + machine.log(machine.succeed("systemd-analyze security dovecot.service | grep -v ✓")) ''; }