1{ config, lib, pkgs, ... }:
2
3with lib;
4let
5 cfg = config.services.webdav-server-rs;
6 format = pkgs.formats.toml { };
7 settings = recursiveUpdate
8 {
9 server.uid = config.users.users."${cfg.user}".uid;
10 server.gid = config.users.groups."${cfg.group}".gid;
11 }
12 cfg.settings;
13in
14{
15 options = {
16 services.webdav-server-rs = {
17 enable = mkEnableOption (lib.mdDoc "WebDAV server");
18
19 user = mkOption {
20 type = types.str;
21 default = "webdav";
22 description = lib.mdDoc "User to run under when setuid is not enabled.";
23 };
24
25 group = mkOption {
26 type = types.str;
27 default = "webdav";
28 description = lib.mdDoc "Group to run under when setuid is not enabled.";
29 };
30
31 debug = mkOption {
32 type = types.bool;
33 default = false;
34 description = lib.mdDoc "Enable debug mode.";
35 };
36
37 settings = mkOption {
38 type = format.type;
39 default = { };
40 description = lib.mdDoc ''
41 Attrset that is converted and passed as config file. Available
42 options can be found at
43 [here](https://github.com/miquels/webdav-server-rs/blob/master/webdav-server.toml).
44 '';
45 example = literalExpression ''
46 {
47 server.listen = [ "0.0.0.0:4918" "[::]:4918" ];
48 accounts = {
49 auth-type = "htpasswd.default";
50 acct-type = "unix";
51 };
52 htpasswd.default = {
53 htpasswd = "/etc/htpasswd";
54 };
55 location = [
56 {
57 route = [ "/public/*path" ];
58 directory = "/srv/public";
59 handler = "filesystem";
60 methods = [ "webdav-ro" ];
61 autoindex = true;
62 auth = "false";
63 }
64 {
65 route = [ "/user/:user/*path" ];
66 directory = "~";
67 handler = "filesystem";
68 methods = [ "webdav-rw" ];
69 autoindex = true;
70 auth = "true";
71 setuid = true;
72 }
73 ];
74 }
75 '';
76 };
77
78 configFile = mkOption {
79 type = types.path;
80 default = format.generate "webdav-server.toml" settings;
81 defaultText = "Config file generated from services.webdav-server-rs.settings";
82 description = lib.mdDoc ''
83 Path to config file. If this option is set, it will override any
84 configuration done in services.webdav-server-rs.settings.
85 '';
86 example = "/etc/webdav-server.toml";
87 };
88 };
89 };
90
91 config = mkIf cfg.enable {
92 assertions = [
93 {
94 assertion = hasAttr cfg.user config.users.users && config.users.users."${cfg.user}".uid != null;
95 message = "users.users.${cfg.user} and users.users.${cfg.user}.uid must be defined.";
96 }
97 {
98 assertion = hasAttr cfg.group config.users.groups && config.users.groups."${cfg.group}".gid != null;
99 message = "users.groups.${cfg.group} and users.groups.${cfg.group}.gid must be defined.";
100 }
101 ];
102
103 users.users = optionalAttrs (cfg.user == "webdav") {
104 webdav = {
105 description = "WebDAV user";
106 group = cfg.group;
107 uid = config.ids.uids.webdav;
108 };
109 };
110
111 users.groups = optionalAttrs (cfg.group == "webdav") {
112 webdav.gid = config.ids.gids.webdav;
113 };
114
115 systemd.services.webdav-server-rs = {
116 description = "WebDAV server";
117 after = [ "network.target" ];
118 wantedBy = [ "multi-user.target" ];
119 serviceConfig = {
120 ExecStart = "${pkgs.webdav-server-rs}/bin/webdav-server ${lib.optionalString cfg.debug "--debug"} -c ${cfg.configFile}";
121
122 CapabilityBoundingSet = [
123 "CAP_SETUID"
124 "CAP_SETGID"
125 ];
126
127 NoExecPaths = [ "/" ];
128 ExecPaths = [ "/nix/store" ];
129
130 # This program actively detects if it is running in root user account
131 # when it starts and uses root privilege to switch process uid to
132 # respective unix user when a user logs in. Maybe we can enable
133 # DynamicUser in the future when it's able to detect CAP_SETUID and
134 # CAP_SETGID capabilities.
135
136 NoNewPrivileges = true;
137 PrivateDevices = true;
138 PrivateTmp = true;
139 ProtectClock = true;
140 ProtectControlGroups = true;
141 ProtectKernelLogs = true;
142 ProtectKernelModules = true;
143 ProtectKernelTunables = true;
144 ProtectSystem = true;
145 };
146 };
147 };
148
149 meta.maintainers = with maintainers; [ pmy ];
150}