Self-host your own digital island
1{ pkgs, config, lib, ... }:
2
3with lib;
4let
5 cfg = config.eilean;
6 domain = config.networking.domain;
7 passwdDir = "/var/lib/radicale/users";
8 passwdFile = "${passwdDir}/passwd";
9 userOps = { name, ... }: {
10 options = {
11 name = mkOption {
12 type = types.str;
13 readOnly = true;
14 default = name;
15 };
16 passwordFile = mkOption { type = types.nullOr types.str; };
17 };
18 };
19in {
20 options.eilean.radicale = {
21 enable = mkEnableOption "radicale";
22 users = mkOption {
23 type = with types; nullOr (attrsOf (submodule userOps));
24 default = { };
25 };
26 };
27
28 config = mkIf cfg.radicale.enable {
29 services.radicale = {
30 enable = true;
31 settings = {
32 server = { hosts = [ "0.0.0.0:5232" ]; };
33 auth = {
34 type = "htpasswd";
35 htpasswd_filename = passwdFile;
36 htpasswd_encryption = "bcrypt";
37 };
38 storage = { filesystem_folder = "/var/lib/radicale/collections"; };
39 };
40 };
41
42 systemd.services.radicale = {
43 serviceConfig.ReadWritePaths = [ "/var/lib/radicale" ];
44 preStart = lib.mkIf (cfg.radicale.users != null)''
45 if (! test -d "${passwdDir}"); then
46 mkdir "${passwdDir}"
47 chmod 755 "${passwdDir}"
48 fi
49
50 umask 077
51
52 cat <<EOF > ${passwdFile}
53
54 ${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: value:
55 ''
56 $(${pkgs.apacheHttpd}/bin/htpasswd -nbB "${name}" "$(head -n 2 ${value.passwordFile})")'')
57 cfg.radicale.users)}
58 EOF
59 '';
60 };
61
62 services.nginx = {
63 enable = true;
64 recommendedProxySettings = true;
65 virtualHosts = {
66 "cal.${domain}" = {
67 forceSSL = true;
68 enableACME = true;
69 locations."/" = { proxyPass = "http://localhost:5232"; };
70 };
71 };
72 };
73
74 eilean.dns.enable = true;
75 eilean.services.dns.zones.${domain}.records = [{
76 name = "cal";
77 type = "CNAME";
78 value = cfg.domainName;
79 }];
80 };
81}