diff options
Diffstat (limited to 'nixos/modules/services/networking/radicale.nix')
-rw-r--r-- | nixos/modules/services/networking/radicale.nix | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/nixos/modules/services/networking/radicale.nix b/nixos/modules/services/networking/radicale.nix new file mode 100644 index 00000000000..c6c40777ed7 --- /dev/null +++ b/nixos/modules/services/networking/radicale.nix @@ -0,0 +1,204 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.radicale; + + format = pkgs.formats.ini { + listToValue = concatMapStringsSep ", " (generators.mkValueStringDefault { }); + }; + + pkg = if isNull cfg.package then + pkgs.radicale + else + cfg.package; + + confFile = if cfg.settings == { } then + pkgs.writeText "radicale.conf" cfg.config + else + format.generate "radicale.conf" cfg.settings; + + 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"; + + package = mkOption { + description = "Radicale package to use."; + # Default cannot be pkgs.radicale because non-null values suppress + # warnings about incompatible configuration and storage formats. + type = with types; nullOr package // { inherit (package) description; }; + default = null; + defaultText = literalExpression "pkgs.radicale"; + }; + + config = mkOption { + type = types.str; + default = ""; + description = '' + Radicale configuration, this will set the service + configuration file. + This option is mutually exclusive with <option>settings</option>. + This option is deprecated. Use <option>settings</option> instead. + ''; + }; + + settings = mkOption { + type = format.type; + default = { }; + description = '' + Configuration for Radicale. See + <link xlink:href="https://radicale.org/3.0.html#documentation/configuration" />. + This option is mutually exclusive with <option>config</option>. + ''; + example = literalExpression '' + server = { + hosts = [ "0.0.0.0:5232" "[::]:5232" ]; + }; + auth = { + type = "htpasswd"; + htpasswd_filename = "/etc/radicale/users"; + htpasswd_encryption = "bcrypt"; + }; + storage = { + filesystem_folder = "/var/lib/radicale/collections"; + }; + ''; + }; + + rights = mkOption { + type = format.type; + description = '' + Configuration for Radicale's rights file. See + <link xlink:href="https://radicale.org/3.0.html#documentation/authentication-and-rights" />. + This option only works in conjunction with <option>settings</option>. + Setting this will also set <option>settings.rights.type</option> and + <option>settings.rights.file</option> to approriate values. + ''; + default = { }; + example = literalExpression '' + root = { + user = ".+"; + collection = ""; + permissions = "R"; + }; + principal = { + user = ".+"; + collection = "{user}"; + permissions = "RW"; + }; + calendars = { + user = ".+"; + collection = "{user}/[^/]+"; + permissions = "rw"; + }; + ''; + }; + + extraArgs = mkOption { + type = types.listOf types.str; + default = []; + description = "Extra arguments passed to the Radicale daemon."; + }; + }; + + config = mkIf cfg.enable { + assertions = [ + { + assertion = cfg.settings == { } || cfg.config == ""; + message = '' + The options services.radicale.config and services.radicale.settings + are mutually exclusive. + ''; + } + ]; + + warnings = optional (isNull cfg.package && versionOlder config.system.stateVersion "17.09") '' + The configuration and storage formats of your existing Radicale + installation might be incompatible with the newest version. + For upgrade instructions see + https://radicale.org/2.1.html#documentation/migration-from-1xx-to-2xx. + Set services.radicale.package to suppress this warning. + '' ++ optional (isNull cfg.package && versionOlder config.system.stateVersion "20.09") '' + The configuration format of your existing Radicale installation might be + incompatible with the newest version. For upgrade instructions see + https://github.com/Kozea/Radicale/blob/3.0.6/NEWS.md#upgrade-checklist. + Set services.radicale.package to suppress this warning. + '' ++ optional (cfg.config != "") '' + The option services.radicale.config is deprecated. + Use services.radicale.settings instead. + ''; + + services.radicale.settings.rights = mkIf (cfg.rights != { }) { + type = "from_file"; + file = toString rightsFile; + }; + + environment.systemPackages = [ pkg ]; + + users.users.radicale = { + isSystemUser = true; + group = "radicale"; + }; + + users.groups.radicale = {}; + + systemd.services.radicale = { + description = "A Simple Calendar and Contact Server"; + after = [ "network.target" ]; + requires = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + ExecStart = concatStringsSep " " ([ + "${pkg}/bin/radicale" "-C" confFile + ] ++ ( + map escapeShellArg cfg.extraArgs + )); + 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"; + WorkingDirectory = "/var/lib/radicale"; + }; + }; + }; + + meta.maintainers = with lib.maintainers; [ aneeshusa infinisil dotlambda ]; +} |