diff --git a/nixos/doc/manual/release-notes/rl-2105.xml b/nixos/doc/manual/release-notes/rl-2105.xml
index 4aadd417a092..68dde82c18e3 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 17a42abc0b74..8c632c319d3c 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 8fa71898ee74..5101628a682c 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 :-}"
'';
})