Files
Martin Weinelt a84ad8a3fa nixos/rustical: fix unix domain socket support
While the config supported UNIX domain sockets, the runtime environment
did not. For this to work we need to relax the UMask, which is not great
and will be fixed once Rustical gains socket activation.
2026-06-16 15:50:22 +02:00

192 lines
5.3 KiB
Nix

{
config,
lib,
pkgs,
...
}:
let
inherit (lib)
mkIf
mkEnableOption
mkOption
mkPackageOption
types
;
cfg = config.services.rustical;
format = pkgs.formats.toml { };
configFile = format.generate "rustical.toml" cfg.settings;
in
{
options.services.rustical = {
enable = mkEnableOption "RustiCali CalDAV/CardDAV server";
package = mkPackageOption pkgs "rustical" { };
settings = mkOption {
description = ''
Your {file}`/etc/rustical/config.toml` as a Nix attribute set.
Possible options can be found in the [Config struct]. A default
configuration can be viewed by running `rustical gen-config`.
[Config struct]: https://lennart-k.github.io/rustical/_crate/rustical/config/struct.Config.html
'';
type = types.submodule {
freeformType = format.type;
options = {
data_store.sqlite = {
db_url = mkOption {
type = types.path;
default = "/var/lib/rustical/db.sqlite3";
description = ''
Path where the sqlite database is stored.
'';
};
};
frontend = {
enabled = mkEnableOption "the HTTP frontend" // {
default = true;
};
};
http = {
bind = mkOption {
type = types.str;
default = "unix:/run/rustical/sock";
example = "[::]:4000";
description = ''
Address and port or UNIX socket path to bind the HTTP service to.
:::{.note}
Rustical expects to be hosted behind a reverse proxy that
provides HTTPS. Without HTTPS, the web frontend and some clients
(e.g. Apple Calendar) may not work.
:::
'';
};
};
dav_push.enabled = mkOption {
type = types.bool;
default = true;
description = ''
Whether to enable [WebDav Push] support.
This allows the server to notify clients about changed data.
[WebDav Push]: https://github.com/bitfireAT/webdav-push/
'';
};
nextcloud_login.enabled = mkOption {
type = types.bool;
default = true;
description = ''
Whether to emulate the Nextcloud login flow.
This is supported in [DAVx5] and enables automatic app token generation.
[DAVx5]: https://www.davx5.com/
'';
};
};
};
};
environmentFiles = mkOption {
type = with types; listOf path;
default = [ ];
example = [ "/run/keys/rustical.env" ];
description = ''
Environment files to load into the runtime environment.
Check the documentation for how to construct [environment variables].
:::{.tip}
Environment variables can substitute any config value and are useful for
hiding secrets.
:::
[environment variables]: https://lennart-k.github.io/rustical/installation/configuration/#environment-variables
'';
};
};
config = mkIf cfg.enable {
warnings = lib.optionals (cfg.settings.http ? host || cfg.settings.http ? port) [
''
Rustical 0.13 deprecations
The following options are now deprecated and will be removed in a
future release:
- `services.rustical.settings.http.host`
- `services.rustical.settings.http.port`
Migrate to `services.rustical.settings.http.bind` instead.
''
];
# install the config at a path where the cli will find it
environment.etc."rustical/config.toml".source = configFile;
# provide the rustical cli
environment.systemPackages = [ cfg.package ];
systemd.services.rustical = {
description = "RustiCal CalDav/CardDav server";
documentation = [ "https://lennart-k.github.io/rustical/" ];
wantedBy = [ "multi-user.target" ];
restartTriggers = [ configFile ];
serviceConfig = {
DynamicUser = true;
ExecStart = lib.getExe cfg.package;
EnvironmentFile = cfg.environmentFiles;
Restart = "on-failure";
StateDirectory = "rustical";
RuntimeDirectory = "rustical";
RuntimeDirectoryMode = "0750";
CapabilityBoundingSet = "";
DevicePolicy = "closed";
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";
RemoveIPC = true;
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
"AF_UNIX"
];
RestrictNamespaces = true;
RestrictRealtime = true;
SystemCallArchitectures = "native";
SystemCallFilter = [
"@system-service"
"~@privileged @resources"
];
SystemCallErrorNumber = "EPERM";
UMask = "0007";
};
};
};
}