vuls: init at 0.27.0
[NixPkgs.git] / nixos / modules / services / networking / radicale.nix
blob62a242e88c9b454761e5ce85028860836ba64658
1 { config, lib, pkgs, ... }:
3 with lib;
5 let
6   cfg = config.services.radicale;
8   format = pkgs.formats.ini {
9     listToValue = concatMapStringsSep ", " (generators.mkValueStringDefault { });
10   };
12   pkg = if cfg.package == null then
13     pkgs.radicale
14   else
15     cfg.package;
17   confFile = if cfg.settings == { } then
18     pkgs.writeText "radicale.conf" cfg.config
19   else
20     format.generate "radicale.conf" cfg.settings;
22   rightsFile = format.generate "radicale.rights" cfg.rights;
24   bindLocalhost = cfg.settings != { } && !hasAttrByPath [ "server" "hosts" ] cfg.settings;
26 in {
27   options.services.radicale = {
28     enable = mkEnableOption "Radicale CalDAV and CardDAV server";
30     package = mkOption {
31       description = "Radicale package to use.";
32       # Default cannot be pkgs.radicale because non-null values suppress
33       # warnings about incompatible configuration and storage formats.
34       type = with types; nullOr package // { inherit (package) description; };
35       default = null;
36       defaultText = literalExpression "pkgs.radicale";
37     };
39     config = mkOption {
40       type = types.str;
41       default = "";
42       description = ''
43         Radicale configuration, this will set the service
44         configuration file.
45         This option is mutually exclusive with {option}`settings`.
46         This option is deprecated.  Use {option}`settings` instead.
47       '';
48     };
50     settings = mkOption {
51       type = format.type;
52       default = { };
53       description = ''
54         Configuration for Radicale. See
55         <https://radicale.org/3.0.html#documentation/configuration>.
56         This option is mutually exclusive with {option}`config`.
57       '';
58       example = literalExpression ''
59         server = {
60           hosts = [ "0.0.0.0:5232" "[::]:5232" ];
61         };
62         auth = {
63           type = "htpasswd";
64           htpasswd_filename = "/etc/radicale/users";
65           htpasswd_encryption = "bcrypt";
66         };
67         storage = {
68           filesystem_folder = "/var/lib/radicale/collections";
69         };
70       '';
71     };
73     rights = mkOption {
74       type = format.type;
75       description = ''
76         Configuration for Radicale's rights file. See
77         <https://radicale.org/3.0.html#documentation/authentication-and-rights>.
78         This option only works in conjunction with {option}`settings`.
79         Setting this will also set {option}`settings.rights.type` and
80         {option}`settings.rights.file` to appropriate values.
81       '';
82       default = { };
83       example = literalExpression ''
84         root = {
85           user = ".+";
86           collection = "";
87           permissions = "R";
88         };
89         principal = {
90           user = ".+";
91           collection = "{user}";
92           permissions = "RW";
93         };
94         calendars = {
95           user = ".+";
96           collection = "{user}/[^/]+";
97           permissions = "rw";
98         };
99       '';
100     };
102     extraArgs = mkOption {
103       type = types.listOf types.str;
104       default = [];
105       description = "Extra arguments passed to the Radicale daemon.";
106     };
107   };
109   config = mkIf cfg.enable {
110     assertions = [
111       {
112         assertion = cfg.settings == { } || cfg.config == "";
113         message = ''
114           The options services.radicale.config and services.radicale.settings
115           are mutually exclusive.
116         '';
117       }
118     ];
120     warnings = optional (cfg.package == null && versionOlder config.system.stateVersion "17.09") ''
121       The configuration and storage formats of your existing Radicale
122       installation might be incompatible with the newest version.
123       For upgrade instructions see
124       https://radicale.org/2.1.html#documentation/migration-from-1xx-to-2xx.
125       Set services.radicale.package to suppress this warning.
126     '' ++ optional (cfg.package == null && versionOlder config.system.stateVersion "20.09") ''
127       The configuration format of your existing Radicale installation might be
128       incompatible with the newest version.  For upgrade instructions see
129       https://github.com/Kozea/Radicale/blob/3.0.6/NEWS.md#upgrade-checklist.
130       Set services.radicale.package to suppress this warning.
131     '' ++ optional (cfg.config != "") ''
132       The option services.radicale.config is deprecated.
133       Use services.radicale.settings instead.
134     '';
136     services.radicale.settings.rights = mkIf (cfg.rights != { }) {
137       type = "from_file";
138       file = toString rightsFile;
139     };
141     environment.systemPackages = [ pkg ];
143     users.users.radicale = {
144       isSystemUser = true;
145       group = "radicale";
146     };
148     users.groups.radicale = {};
150     systemd.services.radicale = {
151       description = "A Simple Calendar and Contact Server";
152       after = [ "network.target" ];
153       requires = [ "network.target" ];
154       wantedBy = [ "multi-user.target" ];
155       serviceConfig = {
156         ExecStart = concatStringsSep " " ([
157           "${pkg}/bin/radicale" "-C" confFile
158         ] ++ (
159           map escapeShellArg cfg.extraArgs
160         ));
161         User = "radicale";
162         Group = "radicale";
163         StateDirectory = "radicale/collections";
164         StateDirectoryMode = "0750";
165         # Hardening
166         CapabilityBoundingSet = [ "" ];
167         DeviceAllow = [ "/dev/stdin" "/dev/urandom" ];
168         DevicePolicy = "strict";
169         IPAddressAllow = mkIf bindLocalhost "localhost";
170         IPAddressDeny = mkIf bindLocalhost "any";
171         LockPersonality = true;
172         MemoryDenyWriteExecute = true;
173         NoNewPrivileges = true;
174         PrivateDevices = true;
175         PrivateTmp = true;
176         PrivateUsers = true;
177         ProcSubset = "pid";
178         ProtectClock = true;
179         ProtectControlGroups = true;
180         ProtectHome = true;
181         ProtectHostname = true;
182         ProtectKernelLogs = true;
183         ProtectKernelModules = true;
184         ProtectKernelTunables = true;
185         ProtectProc = "invisible";
186         ProtectSystem = "strict";
187         ReadWritePaths = lib.optional
188           (hasAttrByPath [ "storage" "filesystem_folder" ] cfg.settings)
189           cfg.settings.storage.filesystem_folder;
190         RemoveIPC = true;
191         RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
192         RestrictNamespaces = true;
193         RestrictRealtime = true;
194         RestrictSUIDSGID = true;
195         SystemCallArchitectures = "native";
196         SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ];
197         UMask = "0027";
198         WorkingDirectory = "/var/lib/radicale";
199       };
200     };
201   };
203   meta.maintainers = with lib.maintainers; [ dotlambda ];