From ca5881d12c81ef209346fa7f88c5811c598d7627 Mon Sep 17 00:00:00 2001 From: "Ricardo (XenGi) Band" Date: Thu, 18 Jun 2026 22:48:48 +0200 Subject: [PATCH] add webdav service --- hosts/dav/default.nix | 37 ++++++++++++ hosts/dav/radicale.nix | 105 ++++++++++++++++++++++++++++++++++ secrets/radicale_htpasswd.age | Bin 0 -> 941 bytes secrets/secrets.nix | 2 + 4 files changed, 144 insertions(+) create mode 100644 hosts/dav/default.nix create mode 100644 hosts/dav/radicale.nix create mode 100644 secrets/radicale_htpasswd.age diff --git a/hosts/dav/default.nix b/hosts/dav/default.nix new file mode 100644 index 0000000..d76a35c --- /dev/null +++ b/hosts/dav/default.nix @@ -0,0 +1,37 @@ +{ ... }: + +{ + imports = [ + ../common.nix + ../../services/openssh.nix + ../../services/prometheus-node.nix + ./radicale.nix + ]; + + networking = { + hostName = "dav"; + firewall = { + allowedTCPPorts = [ + 80 # HTTP/1 + 443 # HTTP/2 + ]; + allowedUDPPorts = [ + 443 # HTTP/3 + ]; + }; + }; + + services = { + openssh.banner = '' + __ + /\ \ + \_\ \ __ __ __ + /'_` \ /'__`\ /\ \/\ \ + /\ \L\ \/\ \L\.\_\ \ \_/ | + \ \___,_\ \__/.\_\\ \___/ + \/__,_ /\/__/\/_/ \/__/ + ''; + }; + + system.stateVersion = "26.05"; +} diff --git a/hosts/dav/radicale.nix b/hosts/dav/radicale.nix new file mode 100644 index 0000000..c5ec70b --- /dev/null +++ b/hosts/dav/radicale.nix @@ -0,0 +1,105 @@ +{ config, pkgs, ... }: + +let + calendarAggregate = pkgs.writers.writePython3 "calendar-aggregate.py" { libraries = [ pkgs.python3Packages.icalendar ]; } '' + from pathlib import Path + from icalendar import Calendar + + combined = Calendar() + + for path in Path("/var/lib/radicale/collections").rglob("*.ics"): + with open(path, "rb") as f: + cal = Calendar.from_ical(f.read()) + + for component in cal.walk("VEVENT"): + combined.add_component(component) + + with open("/srv/www/calendar/all.ics", "wb") as f: + f.write(combined.to_ical()) + ''; +in +{ + imports = [ + ../../services/nginx.nix + ../../services/prometheus-nginx.nix + ]; + + systemd = { + services.calendar-aggregate = { + script = "${calendarAggregate}"; + serviceConfig.Type = "oneshot"; + }; + timers.calendar-aggregate = { + wantedBy = [ "timers.target" ]; + timerConfig = { + OnBootSec = "5m"; + OnUnitActiveSec = "5m"; + }; + }; + }; + + services = { + radicale = { + enable = true; + rights = { + readonly = { + user = ".*"; + collection = ".*"; + permissions = "r"; + }; + cccb = { + user = "cccb"; + collection = "cccb"; + permissions = "rw"; + }; + openwrt = { + user = "openwrt"; + collection = "openwrt"; + permissions = "rw"; + }; + }; + settings = { + server = { + hosts = [ "[::1]:5232" ]; + validate_user_value = "strict"; + validate_path_value = "strict"; + }; + auth = { + type = "htpasswd"; + htpasswd_filename = config.age.secrets.radicale_htpasswd.path; + htpasswd_encryption = "bcrypt"; + }; + storage.filesystem_folder = "/var/lib/radicale/collections"; + headers."Access-Control-Allow-Origin" = "*"; + }; + }; + + nginx.virtualHosts."dav.${config.networking.domain}" = { + default = true; + quic = true; + kTLS = true; + forceSSL = true; + enableACME = true; + locations = { + "/" = { + proxyPass = "http://[::1]:5232"; + recommendedProxySettings = true; + extraConfig = '' + proxy_pass_header Authorization; + ''; + }; + "/all.ics".root = "/srv/www/calendar"; + "/status" = { + proxyPass = "http://${cfg.host}:${toString cfg.port}"; + recommendedProxySettings = true; + extraConfig = '' + allow 195.160.173.14; + allow 2001:678:760:cccb::14; + deny all; + ''; + }; + }; + }; + }; +} + diff --git a/secrets/radicale_htpasswd.age b/secrets/radicale_htpasswd.age new file mode 100644 index 0000000000000000000000000000000000000000..f967a4f6c7a10c49101ce7e09500354c16789378 GIT binary patch literal 941 zcmYdHPt{G$OD?J`D9Oyv)5|YP*Do{V(zR14F3!+RO))YxHMCSH_0Y~UELRB5i7-mb zOb!k3D9LuOEOQU?Pj;>H4J=6YHpnwDDa`RJNy-Q=a11SQOy+V-&8`TrG)Ss+vn(`m z&Q2{$D$GqPG&VF!&UH`p3U_jL&kQsR@yIUlwm`Sdwah0eyj;P*G$*qx-=NsZJUPP2 zB)K%o&rH8O#3jHdEiK#J*+V}$GdM9Q+s7v;*MKW9(lXS$EUP%lF(lpD%g@o$Bdave zG1J>Q#N8>+)y&v0+s7n4(b3I4!xi1O6kqK$pFo8Ilf(em@YE_}@8s+pr*OZ#;v!cc zgY5F603W}s{K~4J@tN-Rsf+`{$UOM<;JoN`JtoqTgmyo)?tD@>h2%uAEZ z3@waGDzedS)30<6bxv3CaZmK~iK;X-%gZcvi7XAVjLOVT$&RRU&oyx?)sM`rN-}V^ z&@VJ{jY#K;Dm3y6^-uH2NHWbd@~F%-PAf8u$}0}h4l=7s&hf2GNlz;b@Nspqw7`fr zH}?Wl!*qqH;Os<`A|nF>ud+~|+=Artz?`zsB-ikg00RTlwBU68^kC=AJd;8Lmq4x{ zcQent0)JD#M3bc4GHnA>gp;aRXAmMSq2$d!mdPN)DPcHo_a&cXY-`_O>+iD-CC2L8pS|7}5K4l_j mFMnCo_Qj6n?02pmPkFSuZMQd