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>&lt;nixpkgs&gt;</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}