diff --git a/nixos/doc/manual/release-notes/rl-2105.xml b/nixos/doc/manual/release-notes/rl-2105.xml index 4aadd417a09..68dde82c18e 100644 --- a/nixos/doc/manual/release-notes/rl-2105.xml +++ b/nixos/doc/manual/release-notes/rl-2105.xml @@ -721,6 +721,13 @@ environment.systemPackages = [ automatically based on , the latest version is always used because old versions are not officially supported. + + Furthermore, Radicale's systemd unit was hardened which might break some + deployments. In particular, a non-default + filesystem_folder has to be added to + if + the deprecated is used. + diff --git a/nixos/modules/services/networking/radicale.nix b/nixos/modules/services/networking/radicale.nix index 17a42abc0b7..8c632c319d3 100644 --- a/nixos/modules/services/networking/radicale.nix +++ b/nixos/modules/services/networking/radicale.nix @@ -21,6 +21,8 @@ let rightsFile = format.generate "radicale.rights" cfg.rights; + bindLocalhost = cfg.settings != { } && !hasAttrByPath [ "server" "hosts" ] cfg.settings; + in { options.services.radicale = { enable = mkEnableOption "Radicale CalDAV and CardDAV server"; @@ -138,15 +140,9 @@ in { environment.systemPackages = [ pkg ]; - users.users.radicale = - { uid = config.ids.uids.radicale; - description = "radicale user"; - home = "/var/lib/radicale"; - createHome = true; - }; + users.users.radicale.uid = config.ids.uids.radicale; - users.groups.radicale = - { gid = config.ids.gids.radicale; }; + users.groups.radicale.gid = config.ids.gids.radicale; systemd.services.radicale = { description = "A Simple Calendar and Contact Server"; @@ -161,6 +157,41 @@ in { )); User = "radicale"; Group = "radicale"; + StateDirectory = "radicale/collections"; + StateDirectoryMode = "0750"; + # Hardening + CapabilityBoundingSet = [ "" ]; + DeviceAllow = [ "/dev/stdin" ]; + DevicePolicy = "strict"; + IPAddressAllow = mkIf bindLocalhost "localhost"; + IPAddressDeny = mkIf bindLocalhost "any"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + ReadWritePaths = lib.optional + (hasAttrByPath [ "storage" "filesystem_folder" ] cfg.settings) + cfg.settings.storage.filesystem_folder; + RemoveIPC = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ]; + UMask = "0027"; }; }; }; diff --git a/nixos/tests/radicale.nix b/nixos/tests/radicale.nix index 8fa71898ee7..5101628a682 100644 --- a/nixos/tests/radicale.nix +++ b/nixos/tests/radicale.nix @@ -86,5 +86,10 @@ in { with subtest("Test web interface"): machine.succeed("curl --fail http://${user}:${password}@localhost:${port}/.web/") + + with subtest("Test security"): + output = machine.succeed("systemd-analyze security radicale.service") + machine.log(output) + assert output[-9:-1] == "SAFE :-}" ''; })