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 ExecStart = ''
141 ${cfg.package}/bin/quickwit run --config ${quickwitYml} \
142 ${lib.escapeShellArgs cfg.extraFlags}
143 '';
144 User = cfg.user;
145 Group = cfg.group;
146 Restart = "on-failure";
147 DynamicUser = usingDefaultUserAndGroup && usingDefaultDataDir;
148 CapabilityBoundingSet = [ "" ];
149 DevicePolicy = "closed";
150 LockPersonality = true;
151 MemoryDenyWriteExecute = true;
152 NoNewPrivileges = true;
153 PrivateDevices = true;
154 ProcSubset = "pid";
155 ProtectClock = true;
156 ProtectHome = true;
157 ProtectHostname = true;
158 ProtectControlGroups = true;
159 ProtectKernelLogs = true;
160 ProtectKernelModules = true;
161 ProtectKernelTunables = true;
162 ProtectProc = "invisible";
163 ProtectSystem = "strict";
164 ReadWritePaths = [
165 cfg.dataDir
166 ];
167 RestrictAddressFamilies = [
168 "AF_NETLINK"
169 "AF_INET"
170 "AF_INET6"
171 ];
172 RestrictNamespaces = true;
173 RestrictRealtime = true;
174 RestrictSUIDSGID = true;
175 SystemCallArchitectures = "native";
176 SystemCallFilter = [
177 # 1. allow a reasonable set of syscalls
178 "@system-service @resources"
179 # 2. and deny unreasonable ones
180 "~@privileged"
181 # 3. then allow the required subset within denied groups
182 "@chown"
183 ];
184 }
185 // (lib.optionalAttrs (usingDefaultDataDir) {
186 StateDirectory = "quickwit";
187 StateDirectoryMode = "0700";
188 });
189 };
190
191 environment.systemPackages = [ cfg.package ];
192 };
193}