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