nixos/rustical: init (#516007)
This commit is contained in:
@@ -110,6 +110,8 @@
|
||||
|
||||
- [dsearch](https://github.com/AvengeMedia/danksearch), a fast filesystem search service with fuzzy matching. Available as [programs.dsearch](#opt-programs.dsearch.enable).
|
||||
|
||||
- [Rustical](https://github.com/lennart-k/rustical), a CalDav/CardDav server aiming to be simple, fast and passwordless. Available as [services.rustical](options.html#opt-services.rustical.enable).
|
||||
|
||||
- [Elephant](https://github.com/abenz1267/elephant), a data provider service and backend for building custom application launchers. Available as [services.elephant](#opt-services.elephant.enable).
|
||||
|
||||
- [Dunst](https://github.com/dunst-project/dunst), a lightweight and customizable notification daemon. Available as [services.dunst](#opt-services.dunst.enable).
|
||||
|
||||
@@ -1768,6 +1768,7 @@
|
||||
./services/web-apps/rimgo.nix
|
||||
./services/web-apps/rss-bridge.nix
|
||||
./services/web-apps/rsshub.nix
|
||||
./services/web-apps/rustical.nix
|
||||
./services/web-apps/rutorrent.nix
|
||||
./services/web-apps/screego.nix
|
||||
./services/web-apps/selfoss.nix
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
{
|
||||
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 = {
|
||||
host = mkOption {
|
||||
type = types.str;
|
||||
default = "[::1]";
|
||||
example = "[::]";
|
||||
description = ''
|
||||
Host address 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.
|
||||
:::
|
||||
'';
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 4000;
|
||||
description = ''
|
||||
Port to bind the HTTP service to.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
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 {
|
||||
# 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";
|
||||
|
||||
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"
|
||||
];
|
||||
RestrictNamespaces = true;
|
||||
RestrictRealtime = true;
|
||||
SystemCallArchitectures = "native";
|
||||
SystemCallFilter = [
|
||||
"@system-service"
|
||||
"~@privileged @resources"
|
||||
];
|
||||
SystemCallErrorNumber = "EPERM";
|
||||
UMask = "0077";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1469,6 +1469,7 @@ in
|
||||
rtkit = runTest ./rtkit.nix;
|
||||
rtorrent = runTest ./rtorrent.nix;
|
||||
rush = runTest ./rush.nix;
|
||||
rustical = runTest ./web-apps/rustical.nix;
|
||||
rustls-libssl = runTest ./rustls-libssl.nix;
|
||||
rxe = runTest ./rxe.nix;
|
||||
sabnzbd = runTest ./sabnzbd.nix;
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
name = "rustical";
|
||||
|
||||
meta.maintainers = pkgs.rustical.meta.maintainers;
|
||||
|
||||
nodes.machine =
|
||||
{
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
services.rustical.enable = true;
|
||||
environment.systemPackages = with pkgs; [ calendar-cli ];
|
||||
};
|
||||
|
||||
testScript =
|
||||
{
|
||||
nodes,
|
||||
...
|
||||
}:
|
||||
let
|
||||
port = toString nodes.machine.services.rustical.settings.http.port;
|
||||
url = "http://localhost:${toString port}";
|
||||
|
||||
createPrincipalScript = pkgs.writeScript "rustical-create-principal" ''
|
||||
#!${lib.getExe pkgs.expect}
|
||||
spawn rustical principals create alice --password
|
||||
expect "Enter your password:\r"
|
||||
send "foobar\r"
|
||||
expect eof
|
||||
'';
|
||||
|
||||
calendarCliConfig = (pkgs.formats.json { }).generate "rustical-test-calendar-cli.json" {
|
||||
default = {
|
||||
caldav_user = "alice";
|
||||
caldav_url = "${url}/caldav/";
|
||||
calendar_url = "${url}/caldav/principal/alice";
|
||||
};
|
||||
testcal = {
|
||||
inherits = "default";
|
||||
calendar_url = "${url}/caldav/principal/alice/testcal";
|
||||
};
|
||||
};
|
||||
in
|
||||
# python
|
||||
''
|
||||
machine.wait_for_unit("rustical.service")
|
||||
machine.wait_for_open_port(${port})
|
||||
|
||||
with subtest("Smoketest"):
|
||||
machine.succeed("curl --fail ${url}")
|
||||
|
||||
with subtest("Create principal"):
|
||||
machine.succeed("${createPrincipalScript}")
|
||||
machine.succeed("rustical principals list | grep alice")
|
||||
|
||||
with subtest("Generate token for principal"):
|
||||
machine.succeed("curl -f -c cookies.txt -d 'username=alice&password=foobar' ${url}/frontend/login")
|
||||
machine.succeed("curl -f -b cookies.txt -d 'name=mytoken' ${url}/frontend/user/alice/app_token > token.txt")
|
||||
|
||||
with subtest("Interact with caldav"):
|
||||
machine.succeed('calendar-cli --config-file ${calendarCliConfig} --caldav-pass "$(cat token.txt)" calendar create testcal')
|
||||
machine.succeed('calendar-cli --config-file ${calendarCliConfig} --config-section testcal --caldav-pass "$(cat token.txt)" calendar add 2013-10-01 testevent')
|
||||
|
||||
machine.log(machine.execute("systemd-analyze security rustical.service | grep -v ✓")[1])
|
||||
'';
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
fetchFromGitHub,
|
||||
pkg-config,
|
||||
openssl,
|
||||
nixosTests,
|
||||
}:
|
||||
|
||||
rustPlatform.buildRustPackage (finalAttrs: {
|
||||
@@ -20,10 +21,15 @@ rustPlatform.buildRustPackage (finalAttrs: {
|
||||
cargoHash = "sha256-uP1lZarcwQhBKyASQIiNUs053EuxJy112P2e3hy2uZY=";
|
||||
|
||||
nativeBuildInputs = [ pkg-config ];
|
||||
|
||||
buildInputs = [ openssl ];
|
||||
|
||||
env.OPENSSL_NO_VENDOR = true;
|
||||
|
||||
passthru.tests = {
|
||||
inherit (nixosTests) rustical;
|
||||
};
|
||||
|
||||
meta = {
|
||||
description = "Yet another calendar server aiming to be simple, fast and passwordless";
|
||||
homepage = "https://github.com/lennart-k/rustical";
|
||||
|
||||
Reference in New Issue
Block a user