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; 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 = '' 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 data = cfg.domainName; 79 }]; 80 }; 81}