1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 cfg = config.nix;
8
9 nix = cfg.package;
10
11 makeNixBuildUser = nr:
12 { name = "nixbld${toString nr}";
13 description = "Nix build user ${toString nr}";
14
15 /* For consistency with the setgid(2), setuid(2), and setgroups(2)
16 calls in `libstore/build.cc', don't add any supplementary group
17 here except "nixbld". */
18 uid = builtins.add config.ids.uids.nixbld nr;
19 group = "nixbld";
20 extraGroups = [ "nixbld" ];
21 };
22
23 nixbldUsers = map makeNixBuildUser (range 1 cfg.nrBuildUsers);
24
25 nixConf =
26 let
27 # If we're using a chroot for builds, then provide /bin/sh in
28 # the chroot as a bind-mount to bash. This means we also need to
29 # include the entire closure of bash.
30 sh = pkgs.stdenv.shell;
31 binshDeps = pkgs.writeReferencesToFile sh;
32 in
33 pkgs.runCommand "nix.conf" {extraOptions = cfg.extraOptions; } ''
34 extraPaths=$(for i in $(cat ${binshDeps}); do if test -d $i; then echo $i; fi; done)
35 cat > $out <<END
36 # WARNING: this file is generated from the nix.* options in
37 # your NixOS configuration, typically
38 # /etc/nixos/configuration.nix. Do not edit it!
39 build-users-group = nixbld
40 build-max-jobs = ${toString (cfg.maxJobs)}
41 build-cores = ${toString (cfg.buildCores)}
42 build-use-chroot = ${if cfg.useChroot then "true" else "false"}
43 build-chroot-dirs = ${toString cfg.chrootDirs} /bin/sh=${sh} $(echo $extraPaths)
44 binary-caches = ${toString cfg.binaryCaches}
45 trusted-binary-caches = ${toString cfg.trustedBinaryCaches}
46 binary-cache-public-keys = ${toString cfg.binaryCachePublicKeys}
47 ${optionalString cfg.requireSignedBinaryCaches ''
48 signed-binary-caches = *
49 ''}
50 trusted-users = ${toString cfg.trustedUsers}
51 allowed-users = ${toString cfg.allowedUsers}
52 $extraOptions
53 END
54 '';
55
56in
57
58{
59
60 ###### interface
61
62 options = {
63
64 nix = {
65
66 package = mkOption {
67 type = types.package;
68 default = pkgs.nix;
69 description = ''
70 This option specifies the Nix package instance to use throughout the system.
71 '';
72 };
73
74 maxJobs = mkOption {
75 type = types.int;
76 default = 1;
77 example = 64;
78 description = ''
79 This option defines the maximum number of jobs that Nix will try
80 to build in parallel. The default is 1. You should generally
81 set it to the total number of logical cores in your system (e.g., 16
82 for two CPUs with 4 cores each and hyper-threading).
83 '';
84 };
85
86 buildCores = mkOption {
87 type = types.int;
88 default = 1;
89 example = 64;
90 description = ''
91 This option defines the maximum number of concurrent tasks during
92 one build. It affects, e.g., -j option for make. The default is 1.
93 The special value 0 means that the builder should use all
94 available CPU cores in the system. Some builds may become
95 non-deterministic with this option; use with care! Packages will
96 only be affected if enableParallelBuilding is set for them.
97 '';
98 };
99
100 useChroot = mkOption {
101 type = types.bool;
102 default = false;
103 description = "
104 If set, Nix will perform builds in a chroot-environment that it
105 will set up automatically for each build. This prevents
106 impurities in builds by disallowing access to dependencies
107 outside of the Nix store.
108 ";
109 };
110
111 chrootDirs = mkOption {
112 type = types.listOf types.str;
113 default = [];
114 example = [ "/dev" "/proc" ];
115 description =
116 ''
117 Directories from the host filesystem to be included
118 in the chroot.
119 '';
120 };
121
122 extraOptions = mkOption {
123 type = types.lines;
124 default = "";
125 example = ''
126 gc-keep-outputs = true
127 gc-keep-derivations = true
128 '';
129 description = "Additional text appended to <filename>nix.conf</filename>.";
130 };
131
132 distributedBuilds = mkOption {
133 type = types.bool;
134 default = false;
135 description = ''
136 Whether to distribute builds to the machines listed in
137 <option>nix.buildMachines</option>.
138 '';
139 };
140
141 daemonNiceLevel = mkOption {
142 type = types.int;
143 default = 0;
144 description = ''
145 Nix daemon process priority. This priority propagates to build processes.
146 0 is the default Unix process priority, 19 is the lowest.
147 '';
148 };
149
150 daemonIONiceLevel = mkOption {
151 type = types.int;
152 default = 0;
153 description = ''
154 Nix daemon process I/O priority. This priority propagates to build processes.
155 0 is the default Unix process I/O priority, 7 is the lowest.
156 '';
157 };
158
159 buildMachines = mkOption {
160 type = types.listOf types.attrs;
161 default = [];
162 example = [
163 { hostName = "voila.labs.cs.uu.nl";
164 sshUser = "nix";
165 sshKey = "/root/.ssh/id_buildfarm";
166 system = "powerpc-darwin";
167 maxJobs = 1;
168 }
169 { hostName = "linux64.example.org";
170 sshUser = "buildfarm";
171 sshKey = "/root/.ssh/id_buildfarm";
172 system = "x86_64-linux";
173 maxJobs = 2;
174 supportedFeatures = "kvm";
175 mandatoryFeatures = "perf";
176 }
177 ];
178 description = ''
179 This option lists the machines to be used if distributed
180 builds are enabled (see
181 <option>nix.distributedBuilds</option>). Nix will perform
182 derivations on those machines via SSH by copying the inputs
183 to the Nix store on the remote machine, starting the build,
184 then copying the output back to the local Nix store. Each
185 element of the list should be an attribute set containing
186 the machine's host name (<varname>hostname</varname>), the
187 user name to be used for the SSH connection
188 (<varname>sshUser</varname>), the Nix system type
189 (<varname>system</varname>, e.g.,
190 <literal>"i686-linux"</literal>), the maximum number of
191 jobs to be run in parallel on that machine
192 (<varname>maxJobs</varname>), the path to the SSH private
193 key to be used to connect (<varname>sshKey</varname>), a
194 list of supported features of the machine
195 (<varname>supportedFeatures</varname>) and a list of
196 mandatory features of the machine
197 (<varname>mandatoryFeatures</varname>). The SSH private key
198 should not have a passphrase, and the corresponding public
199 key should be added to
200 <filename>~<replaceable>sshUser</replaceable>/authorized_keys</filename>
201 on the remote machine.
202 '';
203 };
204
205 # Environment variables for running Nix.
206 envVars = mkOption {
207 type = types.attrs;
208 internal = true;
209 default = {};
210 description = "Environment variables used by Nix.";
211 };
212
213 nrBuildUsers = mkOption {
214 type = types.int;
215 description = ''
216 Number of <literal>nixbld</literal> user accounts created to
217 perform secure concurrent builds. If you receive an error
218 message saying that “all build users are currently in use”,
219 you should increase this value.
220 '';
221 };
222
223 readOnlyStore = mkOption {
224 type = types.bool;
225 default = true;
226 description = ''
227 If set, NixOS will enforce the immutability of the Nix store
228 by making <filename>/nix/store</filename> a read-only bind
229 mount. Nix will automatically make the store writable when
230 needed.
231 '';
232 };
233
234 binaryCaches = mkOption {
235 type = types.listOf types.str;
236 default = [ https://cache.nixos.org/ ];
237 description = ''
238 List of binary cache URLs used to obtain pre-built binaries
239 of Nix packages.
240 '';
241 };
242
243 trustedBinaryCaches = mkOption {
244 type = types.listOf types.str;
245 default = [ ];
246 example = [ http://hydra.nixos.org/ ];
247 description = ''
248 List of binary cache URLs that non-root users can use (in
249 addition to those specified using
250 <option>nix.binaryCaches</option> by passing
251 <literal>--option binary-caches</literal> to Nix commands.
252 '';
253 };
254
255 requireSignedBinaryCaches = mkOption {
256 type = types.bool;
257 default = true;
258 description = ''
259 If enabled, Nix will only download binaries from binary
260 caches if they are cryptographically signed with any of the
261 keys listed in
262 <option>nix.binaryCachePublicKeys</option>. If disabled (the
263 default), signatures are neither required nor checked, so
264 it's strongly recommended that you use only trustworthy
265 caches and https to prevent man-in-the-middle attacks.
266 '';
267 };
268
269 binaryCachePublicKeys = mkOption {
270 type = types.listOf types.str;
271 example = [ "hydra.nixos.org-1:CNHJZBh9K4tP3EKF6FkkgeVYsS3ohTl+oS0Qa8bezVs=" ];
272 description = ''
273 List of public keys used to sign binary caches. If
274 <option>nix.requireSignedBinaryCaches</option> is enabled,
275 then Nix will use a binary from a binary cache if and only
276 if it is signed by <emphasis>any</emphasis> of the keys
277 listed here. By default, only the key for
278 <uri>cache.nixos.org</uri> is included.
279 '';
280 };
281
282 trustedUsers = mkOption {
283 type = types.listOf types.str;
284 default = [ "root" ];
285 example = [ "root" "alice" "@wheel" ];
286 description = ''
287 A list of names of users that have additional rights when
288 connecting to the Nix daemon, such as the ability to specify
289 additional binary caches, or to import unsigned NARs. You
290 can also specify groups by prefixing them with
291 <literal>@</literal>; for instance,
292 <literal>@wheel</literal> means all users in the wheel
293 group.
294 '';
295 };
296
297 allowedUsers = mkOption {
298 type = types.listOf types.str;
299 default = [ "*" ];
300 example = [ "@wheel" "@builders" "alice" "bob" ];
301 description = ''
302 A list of names of users (separated by whitespace) that are
303 allowed to connect to the Nix daemon. As with
304 <option>nix.trustedUsers</option>, you can specify groups by
305 prefixing them with <literal>@</literal>. Also, you can
306 allow all users by specifying <literal>*</literal>. The
307 default is <literal>*</literal>. Note that trusted users are
308 always allowed to connect.
309 '';
310 };
311
312 nixPath = mkOption {
313 type = types.listOf types.str;
314 default =
315 [ "/nix/var/nix/profiles/per-user/root/channels/nixos"
316 "nixos-config=/etc/nixos/configuration.nix"
317 "/nix/var/nix/profiles/per-user/root/channels"
318 ];
319 description = ''
320 The default Nix expression search path, used by the Nix
321 evaluator to look up paths enclosed in angle brackets
322 (e.g. <literal><nixpkgs></literal>).
323 '';
324 };
325
326 };
327
328 };
329
330
331 ###### implementation
332
333 config = {
334
335 nix.binaryCachePublicKeys = [ "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" ];
336
337 environment.etc."nix/nix.conf".source = nixConf;
338
339 # List of machines for distributed Nix builds in the format
340 # expected by build-remote.pl.
341 environment.etc."nix/machines" =
342 { enable = cfg.buildMachines != [];
343 text =
344 concatMapStrings (machine:
345 "${if machine ? sshUser then "${machine.sshUser}@" else ""}${machine.hostName} "
346 + machine.system or (concatStringsSep "," machine.systems)
347 + " ${machine.sshKey or "-"} ${toString machine.maxJobs or 1} "
348 + toString (machine.speedFactor or 1)
349 + " "
350 + concatStringsSep "," (machine.mandatoryFeatures or [] ++ machine.supportedFeatures or [])
351 + " "
352 + concatStringsSep "," machine.mandatoryFeatures or []
353 + "\n"
354 ) cfg.buildMachines;
355 };
356
357 systemd.packages = [ nix ];
358
359 systemd.sockets.nix-daemon.wantedBy = [ "sockets.target" ];
360
361 systemd.services.nix-daemon =
362 { path = [ nix pkgs.openssl pkgs.utillinux config.programs.ssh.package ]
363 ++ optionals cfg.distributedBuilds [ pkgs.gzip ];
364
365 environment = cfg.envVars
366 // { CURL_CA_BUNDLE = "/etc/ssl/certs/ca-bundle.crt"; }
367 // config.networking.proxy.envVars;
368
369 serviceConfig =
370 { Nice = cfg.daemonNiceLevel;
371 IOSchedulingPriority = cfg.daemonIONiceLevel;
372 LimitNOFILE = 4096;
373 };
374
375 restartTriggers = [ nixConf ];
376 };
377
378 nix.envVars =
379 { NIX_CONF_DIR = "/etc/nix";
380
381 # Enable the copy-from-other-stores substituter, which allows
382 # builds to be sped up by copying build results from remote
383 # Nix stores. To do this, mount the remote file system on a
384 # subdirectory of /run/nix/remote-stores.
385 NIX_OTHER_STORES = "/run/nix/remote-stores/*/nix";
386 }
387
388 // optionalAttrs cfg.distributedBuilds {
389 NIX_BUILD_HOOK = "${nix}/libexec/nix/build-remote.pl";
390 NIX_REMOTE_SYSTEMS = "/etc/nix/machines";
391 NIX_CURRENT_LOAD = "/run/nix/current-load";
392 };
393
394 # Set up the environment variables for running Nix.
395 environment.sessionVariables = cfg.envVars //
396 { NIX_PATH = concatStringsSep ":" cfg.nixPath;
397 };
398
399 environment.extraInit =
400 ''
401 # Set up secure multi-user builds: non-root users build through the
402 # Nix daemon.
403 if [ "$USER" != root -o ! -w /nix/var/nix/db ]; then
404 export NIX_REMOTE=daemon
405 fi
406 '';
407
408 nix.nrBuildUsers = mkDefault (lib.max 10 cfg.maxJobs);
409
410 users.extraUsers = nixbldUsers;
411
412 services.xserver.displayManager.hiddenUsers = map ({ name, ... }: name) nixbldUsers;
413
414 system.activationScripts.nix = stringAfter [ "etc" "users" ]
415 ''
416 # Nix initialisation.
417 mkdir -m 0755 -p \
418 /nix/var/nix/gcroots \
419 /nix/var/nix/temproots \
420 /nix/var/nix/manifests \
421 /nix/var/nix/userpool \
422 /nix/var/nix/profiles \
423 /nix/var/nix/db \
424 /nix/var/log/nix/drvs \
425 /nix/var/nix/channel-cache
426 mkdir -m 1777 -p \
427 /nix/var/nix/gcroots/per-user \
428 /nix/var/nix/profiles/per-user \
429 /nix/var/nix/gcroots/tmp
430 '';
431
432 };
433
434}