at 23.11-pre 6.6 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.radicale; 7 8 format = pkgs.formats.ini { 9 listToValue = concatMapStringsSep ", " (generators.mkValueStringDefault { }); 10 }; 11 12 pkg = if cfg.package == null then 13 pkgs.radicale 14 else 15 cfg.package; 16 17 confFile = if cfg.settings == { } then 18 pkgs.writeText "radicale.conf" cfg.config 19 else 20 format.generate "radicale.conf" cfg.settings; 21 22 rightsFile = format.generate "radicale.rights" cfg.rights; 23 24 bindLocalhost = cfg.settings != { } && !hasAttrByPath [ "server" "hosts" ] cfg.settings; 25 26in { 27 options.services.radicale = { 28 enable = mkEnableOption (lib.mdDoc "Radicale CalDAV and CardDAV server"); 29 30 package = mkOption { 31 description = lib.mdDoc "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 }; 38 39 config = mkOption { 40 type = types.str; 41 default = ""; 42 description = lib.mdDoc '' 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 }; 49 50 settings = mkOption { 51 type = format.type; 52 default = { }; 53 description = lib.mdDoc '' 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 }; 72 73 rights = mkOption { 74 type = format.type; 75 description = lib.mdDoc '' 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 }; 101 102 extraArgs = mkOption { 103 type = types.listOf types.str; 104 default = []; 105 description = lib.mdDoc "Extra arguments passed to the Radicale daemon."; 106 }; 107 }; 108 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 ]; 119 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 ''; 135 136 services.radicale.settings.rights = mkIf (cfg.rights != { }) { 137 type = "from_file"; 138 file = toString rightsFile; 139 }; 140 141 environment.systemPackages = [ pkg ]; 142 143 users.users.radicale = { 144 isSystemUser = true; 145 group = "radicale"; 146 }; 147 148 users.groups.radicale = {}; 149 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 }; 202 203 meta.maintainers = with lib.maintainers; [ infinisil dotlambda ]; 204}