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