1{
2 config,
3 pkgs,
4 lib,
5 utils,
6 ...
7}:
8let
9 cfg = config.services.filebrowser;
10 format = pkgs.formats.json { };
11 inherit (lib) types;
12in
13{
14 options = {
15 services.filebrowser = {
16 enable = lib.mkEnableOption "FileBrowser";
17
18 package = lib.mkPackageOption pkgs "filebrowser" { };
19
20 user = lib.mkOption {
21 type = types.str;
22 default = "filebrowser";
23 description = "User account under which FileBrowser runs.";
24 };
25
26 group = lib.mkOption {
27 type = types.str;
28 default = "filebrowser";
29 description = "Group under which FileBrowser runs.";
30 };
31
32 openFirewall = lib.mkEnableOption "opening firewall ports for FileBrowser";
33
34 settings = lib.mkOption {
35 default = { };
36 description = ''
37 Settings for FileBrowser.
38 Refer to <https://filebrowser.org/cli/filebrowser#options> for all supported values.
39 '';
40 type = types.submodule {
41 freeformType = format.type;
42
43 options = {
44 address = lib.mkOption {
45 default = "localhost";
46 description = ''
47 The address to listen on.
48 '';
49 type = types.str;
50 };
51
52 port = lib.mkOption {
53 default = 8080;
54 description = ''
55 The port to listen on.
56 '';
57 type = types.port;
58 };
59
60 root = lib.mkOption {
61 default = "/var/lib/filebrowser/data";
62 description = ''
63 The directory where FileBrowser stores files.
64 '';
65 type = types.path;
66 };
67
68 database = lib.mkOption {
69 default = "/var/lib/filebrowser/database.db";
70 description = ''
71 The path to FileBrowser's Bolt database.
72 '';
73 type = types.path;
74 };
75
76 cache-dir = lib.mkOption {
77 default = "/var/cache/filebrowser";
78 description = ''
79 The directory where FileBrowser stores its cache.
80 '';
81 type = types.path;
82 readOnly = true;
83 };
84 };
85 };
86 };
87 };
88 };
89
90 config = lib.mkIf cfg.enable {
91 systemd = {
92 services.filebrowser = {
93 after = [ "network.target" ];
94 description = "FileBrowser";
95 wantedBy = [ "multi-user.target" ];
96 serviceConfig = {
97 ExecStart =
98 let
99 args = [
100 (lib.getExe cfg.package)
101 "--config"
102 (format.generate "config.json" cfg.settings)
103 ];
104 in
105 utils.escapeSystemdExecArgs args;
106
107 StateDirectory = "filebrowser";
108 CacheDirectory = "filebrowser";
109 WorkingDirectory = cfg.settings.root;
110
111 User = cfg.user;
112 Group = cfg.group;
113 UMask = "0077";
114
115 NoNewPrivileges = true;
116 PrivateDevices = true;
117 ProtectKernelTunables = true;
118 ProtectKernelModules = true;
119 ProtectControlGroups = true;
120 MemoryDenyWriteExecute = true;
121 LockPersonality = true;
122 RestrictAddressFamilies = [
123 "AF_UNIX"
124 "AF_INET"
125 "AF_INET6"
126 ];
127 DevicePolicy = "closed";
128 RestrictNamespaces = true;
129 RestrictRealtime = true;
130 RestrictSUIDSGID = true;
131 };
132 };
133
134 tmpfiles.settings.filebrowser = {
135 "${cfg.settings.root}".d = {
136 inherit (cfg) user group;
137 mode = "0700";
138 };
139 "${cfg.settings.cache-dir}".d = {
140 inherit (cfg) user group;
141 mode = "0700";
142 };
143 "${builtins.dirOf cfg.settings.database}".d = {
144 inherit (cfg) user group;
145 mode = "0700";
146 };
147 };
148 };
149
150 users.users = lib.mkIf (cfg.user == "filebrowser") {
151 filebrowser = {
152 inherit (cfg) group;
153 isSystemUser = true;
154 };
155 };
156
157 users.groups = lib.mkIf (cfg.group == "filebrowser") {
158 filebrowser = { };
159 };
160
161 networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [ cfg.settings.port ];
162 };
163
164 meta.maintainers = [
165 lib.maintainers.lukaswrz
166 ];
167}