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