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.
This commit is contained in:
Martin Weinelt 2025-07-06 03:21:25 +02:00
parent 3687f1eb34
commit 6403717045
No known key found for this signature in database
GPG Key ID: 87C1E9888F856759
3 changed files with 61 additions and 5 deletions

View File

@ -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.

View File

@ -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.
'';
}
];
};

View File

@ -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 "))
'';
}