feat: add radicale

This commit is contained in:
Leon Schwarzäugl 2025-06-29 15:29:26 +02:00
parent e39f07eac1
commit 6b44dcb023
Signed by: swarsel
GPG key ID: 26A54C31F2A4FD84
7 changed files with 298 additions and 9 deletions

View file

@ -4701,6 +4701,7 @@ Modules that need to be loaded on the NixOS level. Note that these will not be a
kanidm = lib.mkDefault true;
firefly = lib.mkDefault true;
koillection = lib.mkDefault true;
radicale = lib.mkDefault true;
};
};
};
@ -9991,6 +9992,7 @@ To get other URLs (token, etc.), use https://<kanidmDomain>/oauth2/openid/<clien
"navidrome.access" = { };
"freshrss.access" = { };
"firefly.access" = { };
"radicale.access" = { };
};
inherit (config.repo.secrets.local) persons;
@ -10106,6 +10108,11 @@ To get other URLs (token, etc.), use https://<kanidmDomain>/oauth2/openid/<clien
"email"
"profile"
];
"radicale.access" = [
"openid"
"email"
"profile"
];
};
preferShortUsername = true;
claimMaps.groups = {
@ -10114,6 +10121,7 @@ To get other URLs (token, etc.), use https://<kanidmDomain>/oauth2/openid/<clien
"freshrss.access" = [ "ttrss_access" ];
"navidrome.access" = [ "navidrome_access" ];
"firefly.access" = [ "firefly_access" ];
"radicale.access" = [ "radicale_access" ];
};
};
};
@ -10219,8 +10227,9 @@ To get other URLs (token, etc.), use https://<kanidmDomain>/oauth2/openid/<clien
extraConfig = lib.optionalString locationSubmodule.config.setOauth2Headers ''
proxy_set_header X-User $user;
proxy_set_header Remote-User $user;
proxy_set_header X-Remote-User $user;
proxy_set_header X-Email $email;
proxy_set_header X-Access-Token $token;
# proxy_set_header X-Access-Token $token;
add_header Set-Cookie $auth_cookie;
'' + lib.optionalString locationSubmodule.config.bypassAuth ''
auth_request off;
@ -10240,7 +10249,7 @@ To get other URLs (token, etc.), use https://<kanidmDomain>/oauth2/openid/<clien
auth_request_set $user ${config.oauth2.X-User};
auth_request_set $email ${config.oauth2.X-Email};
# if you enabled --pass-access-token, this will pass the token to the backend
auth_request_set $token ${config.oauth2.X-Access-Token};
# auth_request_set $token ${config.oauth2.X-Access-Token};
# if you enabled --cookie-refresh, this is needed for it to work with auth_request
auth_request_set $auth_cookie $upstream_http_set_cookie;
'';
@ -10603,6 +10612,121 @@ To get other URLs (token, etc.), use https://<kanidmDomain>/oauth2/openid/<clien
}
#+end_src
**** Radicale
:PROPERTIES:
:CUSTOM_ID: h:c1ca2d28-51d2-45bd-83b5-05007ae94ae6
:END:
#+begin_src nix :tangle modules/nixos/server/radicale.nix
{ self, lib, config, ... }:
let
inherit (config.repo.secrets.local.radicale) user1;
sopsFile = self + /secrets/winters/secrets2.yaml;
serviceDomain = "schedule.swarsel.win";
servicePort = 8000;
serviceName = "radicale";
serviceUser = "radicale";
serviceGroup = serviceUser;
cfg = config.services."${serviceName}";
in
{
options.swarselsystems.modules.server."${serviceName}" = lib.mkEnableOption "enable ${serviceName} on server";
config = lib.mkIf config.swarselsystems.modules.server."${serviceName}" {
sops = {
secrets.radicale-user = { inherit sopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; };
templates = {
"radicale-users" = {
content = ''
${user1}:${config.sops.placeholder.radicale-user}
'';
owner = serviceUser;
group = serviceGroup;
mode = "0440";
};
};
};
topology.self.services.radicale.info = "https://${serviceDomain}";
services.radicale = {
enable = true;
settings = {
server = {
hosts = [
"0.0.0.0:${builtins.toString servicePort}"
"[::]:${builtins.toString servicePort}"
];
};
auth = {
type = "htpasswd";
htpasswd_filename = config.sops.templates.radicale-users.path;
htpasswd_encryption = "autodetect";
};
storage = {
filesystem_folder = "/Vault/data/radicale/collections";
};
};
rights = {
# all: match authenticated users only
root = {
user = ".+";
collection = "";
permissions = "R";
};
principal = {
user = ".+";
collection = "{user}";
permissions = "RW";
};
calendars = {
user = ".+";
collection = "{user}/[^/]+";
permissions = "rw";
};
};
};
systemd.tmpfiles.rules = [
"d '${cfg.settings.storage.filesystem_folder}' 0750 ${serviceUser} ${serviceGroup} - -"
];
networking.firewall.allowedTCPPorts = [ servicePort ];
networking.firewall.allowedUDPPorts = [ servicePort ];
nodes.moonside.services.nginx = {
upstreams = {
"${serviceName}" = {
servers = {
"192.168.1.2:${builtins.toString servicePort}" = { };
};
};
};
virtualHosts = {
"${serviceDomain}" = {
enableACME = true;
forceSSL = true;
acmeRoot = null;
oauth2.enable = false;
locations = {
"/" = {
proxyPass = "http://${serviceName}";
extraConfig = ''
client_max_body_size 16M;
'';
};
};
};
};
};
};
}
#+end_src
*** Darwin
:PROPERTIES:
:CUSTOM_ID: h:ac0cd8b3-06cf-4dca-ba73-6100c8fedb47

View file

@ -73,6 +73,7 @@ in
"navidrome.access" = { };
"freshrss.access" = { };
"firefly.access" = { };
"radicale.access" = { };
};
inherit (config.repo.secrets.local) persons;
@ -188,6 +189,11 @@ in
"email"
"profile"
];
"radicale.access" = [
"openid"
"email"
"profile"
];
};
preferShortUsername = true;
claimMaps.groups = {
@ -196,6 +202,7 @@ in
"freshrss.access" = [ "ttrss_access" ];
"navidrome.access" = [ "navidrome_access" ];
"firefly.access" = [ "firefly_access" ];
"radicale.access" = [ "radicale_access" ];
};
};
};

View file

@ -58,8 +58,9 @@ in
extraConfig = lib.optionalString locationSubmodule.config.setOauth2Headers ''
proxy_set_header X-User $user;
proxy_set_header Remote-User $user;
proxy_set_header X-Remote-User $user;
proxy_set_header X-Email $email;
proxy_set_header X-Access-Token $token;
# proxy_set_header X-Access-Token $token;
add_header Set-Cookie $auth_cookie;
'' + lib.optionalString locationSubmodule.config.bypassAuth ''
auth_request off;
@ -79,7 +80,7 @@ in
auth_request_set $user ${config.oauth2.X-User};
auth_request_set $email ${config.oauth2.X-Email};
# if you enabled --pass-access-token, this will pass the token to the backend
auth_request_set $token ${config.oauth2.X-Access-Token};
# auth_request_set $token ${config.oauth2.X-Access-Token};
# if you enabled --cookie-refresh, this is needed for it to work with auth_request
auth_request_set $auth_cookie $upstream_http_set_cookie;
'';

View file

@ -0,0 +1,107 @@
{ self, lib, config, ... }:
let
inherit (config.repo.secrets.local.radicale) user1;
sopsFile = self + /secrets/winters/secrets2.yaml;
serviceDomain = "schedule.swarsel.win";
servicePort = 8000;
serviceName = "radicale";
serviceUser = "radicale";
serviceGroup = serviceUser;
cfg = config.services."${serviceName}";
in
{
options.swarselsystems.modules.server."${serviceName}" = lib.mkEnableOption "enable ${serviceName} on server";
config = lib.mkIf config.swarselsystems.modules.server."${serviceName}" {
sops = {
secrets.radicale-user = { inherit sopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; };
templates = {
"radicale-users" = {
content = ''
${user1}:${config.sops.placeholder.radicale-user}
'';
owner = serviceUser;
group = serviceGroup;
mode = "0440";
};
};
};
topology.self.services.radicale.info = "https://${serviceDomain}";
services.radicale = {
enable = true;
settings = {
server = {
hosts = [
"0.0.0.0:${builtins.toString servicePort}"
"[::]:${builtins.toString servicePort}"
];
};
auth = {
type = "htpasswd";
htpasswd_filename = config.sops.templates.radicale-users.path;
htpasswd_encryption = "autodetect";
};
storage = {
filesystem_folder = "/Vault/data/radicale/collections";
};
};
rights = {
# all: match authenticated users only
root = {
user = ".+";
collection = "";
permissions = "R";
};
principal = {
user = ".+";
collection = "{user}";
permissions = "RW";
};
calendars = {
user = ".+";
collection = "{user}/[^/]+";
permissions = "rw";
};
};
};
systemd.tmpfiles.rules = [
"d '${cfg.settings.storage.filesystem_folder}' 0750 ${serviceUser} ${serviceGroup} - -"
];
networking.firewall.allowedTCPPorts = [ servicePort ];
networking.firewall.allowedUDPPorts = [ servicePort ];
nodes.moonside.services.nginx = {
upstreams = {
"${serviceName}" = {
servers = {
"192.168.1.2:${builtins.toString servicePort}" = { };
};
};
};
virtualHosts = {
"${serviceDomain}" = {
enableACME = true;
forceSSL = true;
acmeRoot = null;
oauth2.enable = false;
locations = {
"/" = {
proxyPass = "http://${serviceName}";
extraConfig = ''
client_max_body_size 16M;
'';
};
};
};
};
};
};
}

View file

@ -38,6 +38,7 @@
kanidm = lib.mkDefault true;
firefly = lib.mkDefault true;
koillection = lib.mkDefault true;
radicale = lib.mkDefault true;
};
};
};

View file

@ -60,12 +60,12 @@ kanidm-grafana: ENC[AES256_GCM,data:61PEA1fBcaRy8+x0dn9WrH9P0D+NOkbeZw==,iv:kbR3
kanidm-nextcloud: ENC[AES256_GCM,data:9FjsOzBos18ouHBeuzrzHIpCDowFt0Aktw==,iv:iqUQUsWsO5N+KZqHyqNxMxSija/yPrrrAqvz4b1NG1M=,tag:/WC3wg/eYXV3hLJPRVWLog==,type:str]
kanidm-oauth2-proxy: ENC[AES256_GCM,data:DQ5tj7N+P1b8vFnF+MGhaUBvbVQoE4sVhQ==,iv:Xy4bdi8fSFuFHsQKgZ3PswFFYsqtiAeqeSRam1k/H0E=,tag:9W4LRPPYtDOrSpxRDK/7sg==,type:str]
kanidm-freshrss: ENC[AES256_GCM,data:4y0X3sSOfs5pKNCmZGJhxlAKH7GD1UACdw==,iv:LuQQCfOpsTqglwQvohHMFpNGaOjoZ8PKDgG50qBP02k=,tag:Z5mVYP/9nToerQ1qui1eWQ==,type:str]
#ENC[AES256_GCM,data:8eDo+FQoBEKMTRY2,iv:ZSrV+Z+1S5AoW+jq49LBFzSwd/NJl3aZYHe7oUvGriU=,tag:3cw3hUigrPViQ+XsuMiksg==,type:comment]
#ENC[AES256_GCM,data:5wFeVBBdeDlAHZwUdA==,iv:mAmgS9gbPklWPFu425MPngjGm3SNGnUSNyR5oG4EK+E=,tag:nNUTTbs+aWAU1qNgtTsBgA==,type:comment]
oauth2-cookie-secret: ENC[AES256_GCM,data:l8BPYA7t9NG9MPFs/LDlFHqwbnwsvie7FM5v613358E+jLf2wD+tipyUb6c=,iv:1kZ6G6Z0cSQS53kc/hygh/1Ke491agWDlYHR9Yq0jT0=,tag:mi7Un2JBnrq1dnP3jZX4ng==,type:str]
kanidm-oauth2-proxy-client: ENC[AES256_GCM,data:+mcA/sz3AZuw+I44iIdOEfDmtjEVdxi2fg==,iv:m4NpieUicS7xsR+F5AgPqkcUFRF+CGOA8IK6GeS9tgM=,tag:1wypxpiHPdQBD8Td/PSdMw==,type:str]
#ENC[AES256_GCM,data:Y1BVSKrxqnK/8yk=,iv:Bg8OSExGPV9kwkuDb/69BXMyDnzbr1TAnVZEOxNWljo=,tag:Cq76DQ1g26pFLHwZP3pqYQ==,type:comment]
#ENC[AES256_GCM,data:M9U+Mr1cAhlt7NpW,iv:LY19BZEwDdQD1Nhbmgdt9/9VNJjcTkOGP7SwEDE3Xwk=,tag:TlYrhu5dBj1D+Qd72r7Ofg==,type:comment]
firefly-iii-app-key: ENC[AES256_GCM,data:hzgl8eRL0irNRP5TO7G1rNtNM7fXCkmbcaX4QoTsM0xA1rgyKwiy6a4lYDjoXZyOMy5p,iv:q5eepIELwIecyQ56A6THUOu+rebK3irKVYb7/gNHlU8=,tag:+M/KTX1JzPzXeK4TRzW42w==,type:str]
#ENC[AES256_GCM,data:IfT5loUt/GO7Nz8=,iv:ApxflDybVNaaWObZMw2S/JeUTDbYrzkiR8bRhL8WvHU=,tag:GfHdPmcX/DGqeWq2OYrpmg==,type:comment]
#ENC[AES256_GCM,data:mBlfyJvQyrhTnpkJ,iv:hHnTCsHfzCgKuBO82JjNbjYYjWV8e7+0VRkbTGw+WRE=,tag:7Dp77Q2VjWJM5LydvpbJnQ==,type:comment]
koillection-env-file: ENC[AES256_GCM,data:X1dndR7XIhGCwbRQzET5MbzW71PT7WmyryNbOhCKx2I=,iv:bP/90aJT+eA8EmwoFZ7uXxOWfOprpHfc9CvL/A9Os5M=,tag:ZxFDInJBtFrulvOL9PwNJQ==,type:str]
koillection-db-password: ENC[AES256_GCM,data:5Ue4l8CMZpjRpcryEtzPyR2Zf7M=,iv:Ol/G6nFY5H/SIY7l4o5woqFVeLfnv3FJfaAZIqI4NHA=,tag:hYorZv2nyLvsJ8AT2xTkBA==,type:str]
sops:
@ -79,8 +79,8 @@ sops:
MEZ1UWw3alF1WnJZMFZvMFBpbDFJZlUKGRnoEEgjgJ9SSblmldtY6d8MdAy01yxl
qkvEIoXbL+ky2ira7EgjD0legThzCnmlXUlcSn3SpwbkAGgcfd2kWA==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2025-06-13T12:36:23Z"
mac: ENC[AES256_GCM,data:u0OK9URSDN58qk/DE6byXeOs7X59wsxaDvbisk1vbGLPW2DJ6RPcobTSxZhBEWSHr9J4ov9J+WaXSibLyQpljNDsdLPWTW/PrU5ThHCZc/lQ2mN5WZysFCj83sAiY8Z7pCCOcn4iy5lVuJuiU5+C5kEMwyRdTXiYO2Jk2LaWN9g=,iv:s9gPp/pm7J8hxSZhf2nD9qpE/AXBbAmufyYxQ/rvBY4=,tag:iBmL3LPV+NLrcE4eMjnQ1A==,type:str]
lastmodified: "2025-06-28T23:22:53Z"
mac: ENC[AES256_GCM,data:qzxBlSs1thae4TAtkyLkR5l+vMZT/gB6q/NbrdRpv3jPKpn7JL/LLDXX4DjPuZJjDt3T95s9ZBzpasuGHwVNNesdtfj4wjepzwppsgxecSILmiqTUjZC6p1GnMp4TugeLoFBv5qjPYpmu67kwAO8R+gKn/hDhXeIWI4MjgVqsGg=,iv:Gk4vaDOM/lORN5ug96WncFQx972r+LHWTZeLE8Qh6wA=,tag:pSH6xAXJIAtr1bvBMsQ7QA==,type:str]
pgp:
- created_at: "2024-12-17T16:24:32Z"
enc: |-

View file

@ -0,0 +1,49 @@
#ENC[AES256_GCM,data:K3S1LFrPmaS5,iv:dxFzPLhN2otgy02VWzrLURmomtYdoIBHvEJ1LJ7Lj9k=,tag:stKgkBnRDZkCPlvFk+btRg==,type:comment]
radicale-user: ENC[AES256_GCM,data:2G+WXxw6jrnPXsI=,iv:bUEhBDrdTt+O/4TXMkhmqnzfkSiws4n7L54Z0zZnSOI=,tag:JGQPit5uGqITUyyCpU3OIg==,type:str]
sops:
age:
- recipient: age1h72072slm2pthn9m2qwjsyy2dsazc6hz97kpzh4gksvv0r2jqecqul8w63
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBYUHpqd0JJMEVmSHNici9W
QzRmK2pUMWFtN3ZPQURxa0hzQk9wUTRsbjM0CkxtVjBFd2VLVEpkZmhLWis1YlhY
Yzg4MHpzdHZkZmloZWNDUVZRbkF4bE0KLS0tIGhsQ0dmbHVvYjRuVHVzTzNIbGh5
ZWxwbGs1bTNzdXVNSzhpNWVESGJlUzQKzZr3cYBF6s5ihgW/6CreOKWvQpqITrFX
pW6gwbRbxaxDPRRdfn8qswcezxq5AwOk9drbOH+qgcwL2owRGxEhcQ==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2025-06-28T23:22:45Z"
mac: ENC[AES256_GCM,data:gfvgF71vpP2NWy8GEYDffy6ZEI4xq2Pb+ydlmhAxuVtkVveVc3xOzJH+/IJ4IOh8s2ik/b5KMP1u2OdU7gZzDbAQvMn6t+1NN6wun2TLTMCf/z1KuJYEddYS1KHtKmyj+CS7cEZtaW5EKDrATZ9WJOlLY0IADIPicO6kwIqGQew=,iv:1Cg7Mp6GXL6Ade4KfZoHETYNNKojheQHNOlJh2+BCGg=,tag:CvLNFSAkiJDrLMYUldn36Q==,type:str]
pgp:
- created_at: "2025-06-28T23:22:37Z"
enc: |-
-----BEGIN PGP MESSAGE-----
hQIMAwDh3VI7VctTARAAqpPsEJZDZZ2Wezne03E310ix6dOcJ7ZdpPNkPJBprJh5
ip4PmWKnYPnNS5SZV6+1jHOrconChnF4hAxVMH4bgZ/hCkPVNijac3xl2en5NP3a
gPcKSQl6SdiCvfcs+bU7hdCn1glL/PHeilUl+ORJ3olbK4fjrdj82qxxlWVPpqyX
iwjhvcS6rjJ3rTv0KAVRmpGQjFN6cBnei1Yv2wQqypcmD7D6kcsTp8w9MXz8waPI
3Qm0gGjDSUWCrSF8FAwy7QSkJ/Mwjz7yTtQfVIDUQU+MOx0kLY9sIYNkqwZV63RV
HxfxrvIOOdkeCi71x6v0s+Kf3PoqxJgqRAV5pOmoMT6cnLH6+g4q4PHKoRY9d1Dj
EP84VtQkqDLt9JYJJfN2FyVukhUZf38ECdzC+DrTeXuVcd5VV0t7q9LJWG4wL0ba
8P5rFzRcR6TnW/7Ku1rwebnawxeMoppaWlD8hbX5gEtuLKjLfGtwG7qcMdY2YWVZ
HQsLrMsOrNrY4hvT3j1Tgs67sFpa3b4zCbs57ecUPihiFElpnRueFtIFJY73UYF6
5VNSxhWNhXigFOBoX7Z6LmGB2hGrMi1FnYU93Kx1uz6rqypolQ2sG8UUbkH203gN
4R1xA2mD/uQ5824Uo9I3W090XASI6w784pduDdtzCruZVhvuRd3tRtsAJBGwKPWF
AgwDC9FRLmchgYQBD/sE6rUSTcVVmgbr/FYPgglMFw0AM9Zwrpw2vn19eIBwkDc/
HS3J23UVqHlzBFQo2oLQQQ2NZB9ObrYNxwsEqkQnEDp0vBVZHQMlFHYOzvJo8TeL
lxUhT4cSa5mkTWLD+1XjDllNzudVB82uJERLvcM5qRAiksaZpBUchUlDIGXiH7kI
/C4gRYN/JoMPrIOx/1LBZDHiP/9vcnK68P8XdV2l9pvIEo/S1aEHcvtey/WnX1ze
YIREcghXmvith8D2CL6Yd3vrKcfi3QG2CPoKSk61G+NpjcRpozI78T2KU8jL3jgz
XqHqLpmH696z2vnh9IzGtMMvUvck1519M6CLOa9cey3+ByDs5JYHWAxH/jepqCIy
kPfxHVhoqiHJiRRSAkBveYLAlInppH1FLY74emJpo9UJyweJZMpn714EuKIQXcxQ
S/3p9FGM7fyTAKK3EK6t8FRYb0ees//u1Ol+iiWbnR4cmsksN4gEx22IDDBcxPGE
V0oW82pGoR9ZfDWpy8IdPsir3feIcU7cyoYCvhOKemJt+kg9hqxJjACuBbrvS1N6
lF4tzelsZeFQ/HZMINnNvsgdz7tbZsNQWa0EXe2kzBAtLPD3rIfRi9sFsUkT5X7Q
T+fKstJA06o1QafjjPWKnY3lMV1BvbMMHJ2EgaeZr3IYjmsN9zOvIb/71u6vj9Jc
AZcfV02X9OFN5T/NJ3WQ487L0B/T1vyFxijB+APW57noWoNjroIrIozL+Ke7wiKL
c4of/1Hsw4O7QclIMvLl4Vk4nZj7CfedbYHCFocL5rIB/oQe6GV4N2o6e/M=
=fult
-----END PGP MESSAGE-----
fp: 4BE7925262289B476DBBC17B76FD3810215AE097
unencrypted_suffix: _unencrypted
version: 3.10.2