1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 cfg = config.services.quickwit;
9
10 settingsFormat = pkgs.formats.yaml { };
11 quickwitYml = settingsFormat.generate "quickwit.yml" cfg.settings;
12
13 usingDefaultDataDir = cfg.dataDir == "/var/lib/quickwit";
14 usingDefaultUserAndGroup = cfg.user == "quickwit" && cfg.group == "quickwit";
15in
16{
17
18 options.services.quickwit = {
19 enable = lib.mkEnableOption "Quickwit";
20
21 package = lib.mkPackageOption pkgs "Quickwit" {
22 default = [ "quickwit" ];
23 };
24
25 settings = lib.mkOption {
26 type = lib.types.submodule {
27 freeformType = settingsFormat.type;
28
29 options."rest" = lib.mkOption {
30 default = { };
31 description = ''
32 Rest server configuration for Quickwit
33 '';
34
35 type = lib.types.submodule {
36 freeformType = settingsFormat.type;
37
38 options."listen_port" = lib.mkOption {
39 type = lib.types.port;
40 default = 7280;
41 description = ''
42 The port to listen on for HTTP REST traffic.
43 '';
44 };
45 };
46 };
47
48 options."grpc_listen_port" = lib.mkOption {
49 type = lib.types.port;
50 default = 7281;
51 description = ''
52 The port to listen on for gRPC traffic.
53 '';
54 };
55
56 options."listen_address" = lib.mkOption {
57 type = lib.types.str;
58 default = "127.0.0.1";
59 description = ''
60 Listen address of Quickwit.
61 '';
62 };
63
64 options."version" = lib.mkOption {
65 type = lib.types.float;
66 default = 0.7;
67 description = ''
68 Configuration file version.
69 '';
70 };
71 };
72
73 default = { };
74
75 description = ''
76 Quickwit configuration.
77 '';
78 };
79
80 dataDir = lib.mkOption {
81 type = lib.types.path;
82 default = "/var/lib/quickwit";
83 apply = lib.converge (lib.removeSuffix "/");
84 description = ''
85 Data directory for Quickwit. If you change this, you need to
86 manually create the directory. You also need to create the
87 `quickwit` user and group, or change
88 [](#opt-services.quickwit.user) and
89 [](#opt-services.quickwit.group) to existing ones with
90 access to the directory.
91 '';
92 };
93
94 user = lib.mkOption {
95 type = lib.types.str;
96 default = "quickwit";
97 description = ''
98 The user Quickwit runs as. Should be left at default unless
99 you have very specific needs.
100 '';
101 };
102
103 group = lib.mkOption {
104 type = lib.types.str;
105 default = "quickwit";
106 description = ''
107 The group quickwit runs as. Should be left at default unless
108 you have very specific needs.
109 '';
110 };
111
112 extraFlags = lib.mkOption {
113 description = "Extra command line options to pass to Quickwit.";
114 default = [ ];
115 type = lib.types.listOf lib.types.str;
116 };
117
118 restartIfChanged = lib.mkOption {
119 type = lib.types.bool;
120 description = ''
121 Automatically restart the service on config change.
122 This can be set to false to defer restarts on a server or cluster.
123 Please consider the security implications of inadvertently running an older version,
124 and the possibility of unexpected behavior caused by inconsistent versions across a cluster when disabling this option.
125 '';
126 default = true;
127 };
128 };
129
130 config = lib.mkIf cfg.enable {
131 systemd.services.quickwit = {
132 description = "Quickwit";
133 wantedBy = [ "multi-user.target" ];
134 after = [ "network.target" ];
135 inherit (cfg) restartIfChanged;
136 environment = {
137 QW_DATA_DIR = cfg.dataDir;
138 };
139 serviceConfig =
140 {
141 ExecStart = ''
142 ${cfg.package}/bin/quickwit run --config ${quickwitYml} \
143 ${lib.escapeShellArgs cfg.extraFlags}
144 '';
145 User = cfg.user;
146 Group = cfg.group;
147 Restart = "on-failure";
148 DynamicUser = usingDefaultUserAndGroup && usingDefaultDataDir;
149 CapabilityBoundingSet = [ "" ];
150 DevicePolicy = "closed";
151 LockPersonality = true;
152 MemoryDenyWriteExecute = true;
153 NoNewPrivileges = true;
154 PrivateDevices = true;
155 ProcSubset = "pid";
156 ProtectClock = true;
157 ProtectHome = true;
158 ProtectHostname = true;
159 ProtectControlGroups = true;
160 ProtectKernelLogs = true;
161 ProtectKernelModules = true;
162 ProtectKernelTunables = true;
163 ProtectProc = "invisible";
164 ProtectSystem = "strict";
165 ReadWritePaths = [
166 cfg.dataDir
167 ];
168 RestrictAddressFamilies = [
169 "AF_NETLINK"
170 "AF_INET"
171 "AF_INET6"
172 ];
173 RestrictNamespaces = true;
174 RestrictRealtime = true;
175 RestrictSUIDSGID = true;
176 SystemCallArchitectures = "native";
177 SystemCallFilter = [
178 # 1. allow a reasonable set of syscalls
179 "@system-service @resources"
180 # 2. and deny unreasonable ones
181 "~@privileged"
182 # 3. then allow the required subset within denied groups
183 "@chown"
184 ];
185 }
186 // (lib.optionalAttrs (usingDefaultDataDir) {
187 StateDirectory = "quickwit";
188 StateDirectoryMode = "0700";
189 });
190 };
191
192 environment.systemPackages = [ cfg.package ];
193 };
194}