From 1f92c7c42a497771ab4be5937657595d632b2542 Mon Sep 17 00:00:00 2001 From: Martin Weinelt Date: Sat, 21 Jun 2025 13:56:26 +0200 Subject: [PATCH] nixos/dovecot: improve and harden systemd unit Remove the major version from the unit name and add an alias for the old dovecot2 name. Then restricts what the dovecot service can do, which is very interesting given that the unit runs as root and spawns less-privileged processes from there. --- nixos/modules/services/mail/dovecot.nix | 52 +++++++++++++++++++++++-- nixos/tests/dovecot.nix | 4 +- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/nixos/modules/services/mail/dovecot.nix b/nixos/modules/services/mail/dovecot.nix index 01b9c278c630..6ebf5cf95742 100644 --- a/nixos/modules/services/mail/dovecot.nix +++ b/nixos/modules/services/mail/dovecot.nix @@ -692,23 +692,67 @@ 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_NET_BIND_SERVICE" + "CAP_SETGID" + "CAP_SETUID" + "CAP_SYS_CHROOT" + "CAP_SYS_RESOURCE" + ]; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + 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_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 diff --git a/nixos/tests/dovecot.nix b/nixos/tests/dovecot.nix index 66b62cba1117..702e8cf9ef78 100644 --- a/nixos/tests/dovecot.nix +++ b/nixos/tests/dovecot.nix @@ -84,11 +84,13 @@ import ./make-test-python.nix { 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 ✓")) ''; }